From b363492ccaaa4d7dbccbc6d96d3bbbe984b46648 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Wed, 5 Aug 2020 06:46:45 +0300 Subject: [PATCH] add 'iris.Blocks' template engine read more about its features at: https://github.com/kataras/blocks --- HISTORY.md | 4 +- NOTICE | 21 +- _examples/README.md | 2 + .../view/embedding-templates-into-app/main.go | 3 +- _examples/view/template_blocks_0/main.go | 35 ++ .../view/template_blocks_0/views/500.html | 12 + .../view/template_blocks_0/views/index.html | 1 + .../views/layouts/error.html | 13 + .../template_blocks_0/views/layouts/main.html | 13 + .../views/partials/footer.html | 1 + .../template_blocks_1_embedded/bindata.go | 343 ++++++++++++++++++ .../view/template_blocks_1_embedded/main.go | 40 ++ aliases.go | 18 +- context/context.go | 2 +- go.mod | 3 +- view/README.md | 27 +- view/ace.go | 1 + view/amber.go | 1 + view/blocks.go | 109 ++++++ view/django.go | 3 +- view/handlebars.go | 1 + view/html.go | 16 +- view/jet.go | 1 + view/pug.go | 1 + 24 files changed, 631 insertions(+), 40 deletions(-) create mode 100644 _examples/view/template_blocks_0/main.go create mode 100644 _examples/view/template_blocks_0/views/500.html create mode 100644 _examples/view/template_blocks_0/views/index.html create mode 100644 _examples/view/template_blocks_0/views/layouts/error.html create mode 100644 _examples/view/template_blocks_0/views/layouts/main.html create mode 100644 _examples/view/template_blocks_0/views/partials/footer.html create mode 100644 _examples/view/template_blocks_1_embedded/bindata.go create mode 100644 _examples/view/template_blocks_1_embedded/main.go create mode 100644 view/blocks.go diff --git a/HISTORY.md b/HISTORY.md index cbcc2789..eb9b1db1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -359,7 +359,9 @@ Response: Other Improvements: -- Add [Ace](_examples/view/template_ace_0) template parser to the view engine and other minor improvements. +- Add [Blocks](_examples/view/template_blocks_0) template engine. + +- Add [Ace](_examples/view/template_ace_0) template parser to the view engine and other minor improvements. - Fix huge repo size of 55.7MB, which slows down the overall Iris installation experience. Now, go-get performs ~3 times faster. I 've managed it using the [bfg-repo-cleaner](https://github.com/rtyley/bfg-repo-cleaner) tool - an alternative to git-filter-branch command. Watch the small gif below to learn how: diff --git a/NOTICE b/NOTICE index c7754fd4..a6837ce9 100644 --- a/NOTICE +++ b/NOTICE @@ -6,7 +6,7 @@ The following 3rd-party software packages may be used by or distributed with iris. This document was automatically generated by FOSSA on 2020-5-8; any information relevant to third-party vendors listed below are collected using common, reasonable means. -Revision ID: d1c07411df0bb21f6b21f5b5d9325fac6f29c911 +Revision ID: 6844be57ea3a098ef28fce3468959bf5bb6f735a ----------------- ----------------- ------------------------------------------ Package Version Website @@ -20,12 +20,15 @@ Revision ID: d1c07411df0bb21f6b21f5b5d9325fac6f29c911 bbolt a8af23b57f672fe https://github.com/etcd-io/bbolt f05637de531bba5 aa00013364 - blackfriday 48b3da6a6f3865c https://github.com/iris-contrib/ - 7eb1eba96d74cf0 blackfriday - a16f63faca - bluemonday 0a75d7616912ab9 https://github.com/microcosm-cc/ - beb9cc6f7283ec1 bluemonday + blackfriday d3b5b032dc8e892 https://github.com/russross/blackfriday + 7d31a5071b56e14 + c89f045135 + bluemonday 0a75d7616912ab9 https://github.com/microcosm-cc/bluemonday + beb9cc6f7283ec1 917c61b135 + blocks 39dac49c58634f7 https://github.com/kataras/blocks + 9bc54fcd5c299da + fe08dbd738 brotli c3da72aa01ed78f https://github.com/andybalholm/brotli 164593b9624fd91 d25082d2d2 @@ -71,9 +74,9 @@ Revision ID: d1c07411df0bb21f6b21f5b5d9325fac6f29c911 pio 2e3d576cc65913a https://github.com/kataras/pio dd6106f1ce02837 2c7e6d943c - pongo2 0738cc45f23da6a https://github.com/iris-contrib/pongo2 - 8c14959779a75d3 - 37b7944fb6 + pongo2 5abacdfa4915f8a https://github.com/flosch/pongo2 + fb6de6231455488 + 066273bea6 protobuf 6c66de79d66478d https://github.com/golang/protobuf 166c7ea05f5d2cc af016fbd6b diff --git a/_examples/README.md b/_examples/README.md index 1cafd46b..0faa8fb5 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -103,6 +103,8 @@ * [Inject Data Between Handlers](view/context-view-data/main.go) * [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go) * [Write to a custom `io.Writer`](view/write-to) + * [Blocks](view/template_blocks_0) + * [Blocks Embedded](view/template_blocks_1_embedded) * [Pug: Greeting](view/template_pug_0) * [Pug: `Actions`](view/template_pug_1) * [Pug: `Includes`](view/template_pug_2) diff --git a/_examples/view/embedding-templates-into-app/main.go b/_examples/view/embedding-templates-into-app/main.go index 2bfcb30d..da087a86 100644 --- a/_examples/view/embedding-templates-into-app/main.go +++ b/_examples/view/embedding-templates-into-app/main.go @@ -15,8 +15,7 @@ func main() { // $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata // $ go-bindata ./templates/... - // $ go build - // $ ./embedding-templates-into-app + // $ go run . // html files are not used, you can delete the folder and run the example. tmpl.Binary(Asset, AssetNames) // <-- IMPORTANT diff --git a/_examples/view/template_blocks_0/main.go b/_examples/view/template_blocks_0/main.go new file mode 100644 index 00000000..8e46a506 --- /dev/null +++ b/_examples/view/template_blocks_0/main.go @@ -0,0 +1,35 @@ +package main + +import "github.com/kataras/iris/v12" + +func main() { + app := iris.New() + // Read about its syntax at: https://github.com/kataras/blocks + app.RegisterView(iris.Blocks("./views", ".html").Reload(true)) + + app.Get("/", index) + app.Get("/500", internalServerError) + + app.Listen(":8080") +} + +func index(ctx iris.Context) { + data := iris.Map{ + "Title": "Page Title", + } + + ctx.ViewLayout("main") + ctx.View("index", data) +} + +func internalServerError(ctx iris.Context) { + ctx.StatusCode(iris.StatusInternalServerError) + + data := iris.Map{ + "Code": iris.StatusInternalServerError, + "Message": "Internal Server Error", + } + + ctx.ViewLayout("error") + ctx.View("500", data) +} diff --git a/_examples/view/template_blocks_0/views/500.html b/_examples/view/template_blocks_0/views/500.html new file mode 100644 index 00000000..37c58b58 --- /dev/null +++ b/_examples/view/template_blocks_0/views/500.html @@ -0,0 +1,12 @@ + +{{ define "content" }} +

Internal Server Error

+{{ end }} + +{{ define "message" }} +

{{.Message}}

+{{ end }} \ No newline at end of file diff --git a/_examples/view/template_blocks_0/views/index.html b/_examples/view/template_blocks_0/views/index.html new file mode 100644 index 00000000..b11758cf --- /dev/null +++ b/_examples/view/template_blocks_0/views/index.html @@ -0,0 +1 @@ +

Index Body

\ No newline at end of file diff --git a/_examples/view/template_blocks_0/views/layouts/error.html b/_examples/view/template_blocks_0/views/layouts/error.html new file mode 100644 index 00000000..48598789 --- /dev/null +++ b/_examples/view/template_blocks_0/views/layouts/error.html @@ -0,0 +1,13 @@ + + + + + + {{.Code}} + + + {{ template "content" .}} + + {{block "message" .}}{{end}} + + \ No newline at end of file diff --git a/_examples/view/template_blocks_0/views/layouts/main.html b/_examples/view/template_blocks_0/views/layouts/main.html new file mode 100644 index 00000000..5b5a509f --- /dev/null +++ b/_examples/view/template_blocks_0/views/layouts/main.html @@ -0,0 +1,13 @@ + + + + + + {{ if .Title }}{{ .Title }}{{ else }}Default Main Title{{ end }} + + + {{ template "content" . }} + + + + \ No newline at end of file diff --git a/_examples/view/template_blocks_0/views/partials/footer.html b/_examples/view/template_blocks_0/views/partials/footer.html new file mode 100644 index 00000000..61ee8461 --- /dev/null +++ b/_examples/view/template_blocks_0/views/partials/footer.html @@ -0,0 +1 @@ +

Footer Partial

\ No newline at end of file diff --git a/_examples/view/template_blocks_1_embedded/bindata.go b/_examples/view/template_blocks_1_embedded/bindata.go new file mode 100644 index 00000000..465eb47c --- /dev/null +++ b/_examples/view/template_blocks_1_embedded/bindata.go @@ -0,0 +1,343 @@ +// Code generated by go-bindata. (@generated) DO NOT EDIT. + +//Package main generated by go-bindata.// sources: +// ../template_blocks_0/views/500.html +// ../template_blocks_0/views/index.html +// ../template_blocks_0/views/layouts/error.html +// ../template_blocks_0/views/layouts/main.html +// ../template_blocks_0/views/partials/footer.html +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +// Name return file name +func (fi bindataFileInfo) Name() string { + return fi.name +} + +// Size return file size +func (fi bindataFileInfo) Size() int64 { + return fi.size +} + +// Mode return file mode +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} + +// ModTime return file modify time +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} + +// IsDir return file whether a directory +func (fi bindataFileInfo) IsDir() bool { + return fi.mode&os.ModeDir != 0 +} + +// Sys return file is sys mode +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _views500Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x8f\xb1\x4e\xf3\x30\x14\x85\xf7\x48\x79\x87\xfb\x67\xe9\x8f\xd4\xa4\xea\x5a\x42\x37\x06\x06\xa6\x22\x21\x46\xc7\x3e\xad\x2d\x9c\x7b\x23\xdb\x49\x89\xa2\xbc\x3b\x6a\x5a\x04\x2c\xac\xf7\x7e\xe7\x3b\x3a\xf5\xbf\xb2\xa4\x37\xe9\x49\x2b\x26\x83\xa3\x63\x50\x2b\x01\x94\xac\x62\x12\x06\x35\x5e\xf4\x7b\x95\x67\x2f\x16\x17\x40\xf5\x3e\x2d\x77\x17\xa9\xd0\xc2\x09\x9c\x0a\x3a\x5b\xa7\x2d\x45\x2b\xbd\x37\xd4\x5c\xd2\xa0\x56\x39\xa6\x84\xb6\xf3\x2a\x61\x15\xa9\x11\x33\x56\x79\x76\x90\x35\x61\x00\x93\x3b\x92\x4b\xab\x48\xad\x8b\xd1\xf1\x89\xfe\x47\x80\x1c\x1b\x7c\x54\x36\xb5\xfe\x6e\x7d\x7d\x2b\x63\x60\x48\xf5\x49\x5a\x95\x9c\x56\xde\x8f\xd4\x8c\x4b\xc3\xe0\x70\x26\xf0\xc9\x31\xaa\x3c\x7b\xb5\x60\x1a\xa5\x27\x06\x0c\x25\xf9\x63\xce\x7a\xe1\xac\x1a\x70\xe1\x9a\x1b\x13\x3b\x68\x77\x74\x7a\x97\x67\x65\xb9\xcf\xb3\x69\xfa\x52\x7c\x0f\x9d\xe7\x3c\xab\xed\x76\xff\xc4\x09\x81\x95\xa7\x03\xc2\x80\x40\x8f\x21\x48\xa8\x37\x76\x7b\xcd\x81\xcd\x82\xfe\x92\xb4\x88\x51\x9d\x70\x93\x74\x14\xd3\xe8\xf1\x50\x68\xf1\x12\x76\x01\xe6\xbe\xd8\x4f\x53\xf5\x7c\xa5\xe6\xb9\xde\x74\x3f\x65\x9f\x01\x00\x00\xff\xff\xb3\xfa\x91\xbd\xaa\x01\x00\x00") + +func views500HtmlBytes() ([]byte, error) { + return bindataRead( + _views500Html, + "views/500.html", + ) +} + +func views500Html() (*asset, error) { + bytes, err := views500HtmlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "views/500.html", size: 426, mode: os.FileMode(438), modTime: time.Unix(1596515591, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _viewsIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb4\xf3\xcc\x4b\x49\xad\x50\x70\xca\x4f\xa9\xb4\xd1\xcf\x30\xb4\x03\x04\x00\x00\xff\xff\xcc\x4b\x98\x69\x13\x00\x00\x00") + +func viewsIndexHtmlBytes() ([]byte, error) { + return bindataRead( + _viewsIndexHtml, + "views/index.html", + ) +} + +func viewsIndexHtml() (*asset, error) { + bytes, err := viewsIndexHtmlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "views/index.html", size: 19, mode: os.FileMode(438), modTime: time.Unix(1596514225, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _viewsLayoutsErrorHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\xbf\x4e\xf3\x40\x10\xc4\x7b\x4b\x7e\x87\xfd\xb6\xfe\x6c\x43\x47\x71\xe7\x26\x40\x0b\x45\x28\x28\x37\x77\xa3\xf8\xc4\xfd\x89\xe2\x55\x22\x74\xf2\xbb\xa3\x18\x83\x44\xb5\xda\x99\x9f\x66\x57\x63\xfe\x3d\xbe\xec\xf6\xef\xaf\x4f\x34\x69\x8a\x63\xdb\x98\xdb\xa4\x28\xf9\x68\x19\x99\x57\x05\xe2\xc7\xb6\x21\x22\x32\x09\x2a\xe4\x26\x39\xcf\x50\xcb\x6f\xfb\xe7\xee\x81\xff\x78\x59\x12\x2c\x5f\x02\xae\xa7\x72\x56\x26\x57\xb2\x22\xab\xe5\x6b\xf0\x3a\x59\x8f\x4b\x70\xe8\xd6\xe5\x3f\x85\x1c\x34\x48\xec\x66\x27\x11\xf6\xbe\xbf\xfb\xcd\xd2\xa0\x11\x63\xad\xfd\xae\x78\x2c\x8b\x19\xbe\x85\xb6\x31\xc3\xf6\x8e\x39\x14\xff\xb9\xe1\xb5\x92\x22\x9d\xa2\x28\x88\xb7\x8b\x4c\xfd\xb2\xb4\xcd\x0f\x70\x88\xc5\x7d\x10\x27\xcc\xb3\x1c\xb1\x9a\xb5\x22\xfb\x1b\x63\x86\x2d\xcb\x0c\x6b\x0b\x5f\x01\x00\x00\xff\xff\xbe\xb7\x11\x67\x15\x01\x00\x00") + +func viewsLayoutsErrorHtmlBytes() ([]byte, error) { + return bindataRead( + _viewsLayoutsErrorHtml, + "views/layouts/error.html", + ) +} + +func viewsLayoutsErrorHtml() (*asset, error) { + bytes, err := viewsLayoutsErrorHtmlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "views/layouts/error.html", size: 277, mode: os.FileMode(438), modTime: time.Unix(1596514340, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _viewsLayoutsMainHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\xb1\x4e\xf4\x30\x10\x84\xfb\x48\x79\x87\xf9\x5d\xff\x49\xa0\xa3\xb0\xd3\x70\xd0\x21\x28\x42\x41\xb9\x24\x1b\x62\xc9\x71\xa2\x64\xb9\x13\xb2\xfc\xee\xc8\x39\x0b\xe9\x2a\xcf\xfa\xb3\x66\xc6\xab\xff\x9d\x5e\x1f\xbb\x8f\xb7\x27\x4c\x32\xbb\xb6\x2c\x74\x3a\xe1\xc8\x7f\x19\xc5\x5e\x1d\x37\x4c\x43\x5b\x16\x00\xa0\x67\x16\x42\x3f\xd1\xb6\xb3\x18\xf5\xde\x3d\x57\x0f\xea\x86\x79\x9a\xd9\xa8\xb3\xe5\xcb\xba\x6c\xa2\xd0\x2f\x5e\xd8\x8b\x51\x17\x3b\xc8\x64\x06\x3e\xdb\x9e\xab\x63\xf8\x0f\xeb\xad\x58\x72\xd5\xde\x93\x63\x73\x5f\xdf\xfd\x79\x89\x15\xc7\x6d\x08\xb0\x23\xea\x2e\x0d\x88\x31\x84\x1b\xcd\x6e\x4f\xea\xc4\x23\x7d\x3b\xc1\x0b\x59\x8f\x03\x27\xe6\x07\xc4\xa8\x9b\xab\x4f\x59\xe8\x26\xff\x42\x7f\x2e\xc3\x4f\x4e\x09\x01\xc2\xf3\xea\x48\x18\x2a\x17\x55\xa8\x11\x63\x59\x94\x85\x1e\x97\x45\x78\x4b\x25\x56\xda\x52\x4f\xa8\x2c\xf6\xe6\xca\x14\xea\x14\x92\x1f\xa6\x94\xec\xae\x9b\x63\x9d\xbf\x01\x00\x00\xff\xff\x44\x95\x63\x98\x5e\x01\x00\x00") + +func viewsLayoutsMainHtmlBytes() ([]byte, error) { + return bindataRead( + _viewsLayoutsMainHtml, + "views/layouts/main.html", + ) +} + +func viewsLayoutsMainHtml() (*asset, error) { + bytes, err := viewsLayoutsMainHtmlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "views/layouts/main.html", size: 350, mode: os.FileMode(438), modTime: time.Unix(1596514155, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _viewsPartialsFooterHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb6\x73\xcb\xcf\x2f\x49\x2d\x52\x08\x48\x2c\x2a\xc9\x4c\xcc\xb1\xd1\xcf\x30\xb6\x03\x04\x00\x00\xff\xff\x08\xe6\xe9\xf8\x17\x00\x00\x00") + +func viewsPartialsFooterHtmlBytes() ([]byte, error) { + return bindataRead( + _viewsPartialsFooterHtml, + "views/partials/footer.html", + ) +} + +func viewsPartialsFooterHtml() (*asset, error) { + bytes, err := viewsPartialsFooterHtmlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "views/partials/footer.html", size: 23, mode: os.FileMode(438), modTime: time.Unix(1596514093, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "views/500.html": views500Html, + "views/index.html": viewsIndexHtml, + "views/layouts/error.html": viewsLayoutsErrorHtml, + "views/layouts/main.html": viewsLayoutsMainHtml, + "views/partials/footer.html": viewsPartialsFooterHtml, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "views": {nil, map[string]*bintree{ + "500.html": {views500Html, map[string]*bintree{}}, + "index.html": {viewsIndexHtml, map[string]*bintree{}}, + "layouts": {nil, map[string]*bintree{ + "error.html": {viewsLayoutsErrorHtml, map[string]*bintree{}}, + "main.html": {viewsLayoutsMainHtml, map[string]*bintree{}}, + }}, + "partials": {nil, map[string]*bintree{ + "footer.html": {viewsPartialsFooterHtml, map[string]*bintree{}}, + }}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/_examples/view/template_blocks_1_embedded/main.go b/_examples/view/template_blocks_1_embedded/main.go new file mode 100644 index 00000000..020a2235 --- /dev/null +++ b/_examples/view/template_blocks_1_embedded/main.go @@ -0,0 +1,40 @@ +package main + +import "github.com/kataras/iris/v12" + +// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata +// $ go-bindata -prefix "../template_blocks_0" ../template_blocks_0/views/... +// $ go run . +// # OR go-bindata -prefix "../template_blocks_0/views" ../template_blocks_0/views/... with iris.Blocks("").Binary(...) +// System files are not used, you can optionally delete the folder and run the example now. + +func main() { + app := iris.New() + app.RegisterView(iris.Blocks("./views", ".html").Binary(Asset, AssetNames)) + + app.Get("/", index) + app.Get("/500", internalServerError) + + app.Listen(":8080") +} + +func index(ctx iris.Context) { + data := iris.Map{ + "Title": "Page Title", + } + + ctx.ViewLayout("main") + ctx.View("index", data) +} + +func internalServerError(ctx iris.Context) { + ctx.StatusCode(iris.StatusInternalServerError) + + data := iris.Map{ + "Code": iris.StatusInternalServerError, + "Message": "Internal Server Error", + } + + ctx.ViewLayout("error") + ctx.View("500", data) +} diff --git a/aliases.go b/aliases.go index 57d2945a..37bd621d 100644 --- a/aliases.go +++ b/aliases.go @@ -190,25 +190,29 @@ const NoLayout = view.NoLayout var ( // HTML view engine. - // Shortcut of the kataras/iris/view.HTML. + // Shortcut of the view.HTML. HTML = view.HTML + // Blocks view engine. + // Can be used as a faster alternative of the HTML engine. + // Shortcut of the view.Blocks. + Blocks = view.Blocks // Django view engine. - // Shortcut of the kataras/iris/view.Django. + // Shortcut of the view.Django. Django = view.Django // Handlebars view engine. - // Shortcut of the kataras/iris/view.Handlebars. + // Shortcut of the view.Handlebars. Handlebars = view.Handlebars // Pug view engine. - // Shortcut of the kataras/iris/view.Pug. + // Shortcut of the view.Pug. Pug = view.Pug // Amber view engine. - // Shortcut of the kataras/iris/view.Amber. + // Shortcut of the view.Amber. Amber = view.Amber // Jet view engine. - // Shortcut of the kataras/iris/view.Jet. + // Shortcut of the view.Jet. Jet = view.Jet // Ace view engine. - // Shortcut of the kataras/iris/view.Ace. + // Shortcut of the view.Ace. Ace = view.Ace ) diff --git a/context/context.go b/context/context.go index f80a0b5f..3986bb33 100644 --- a/context/context.go +++ b/context/context.go @@ -30,10 +30,10 @@ import ( "github.com/Shopify/goreferrer" "github.com/fatih/structs" - "github.com/iris-contrib/blackfriday" "github.com/iris-contrib/schema" jsoniter "github.com/json-iterator/go" "github.com/microcosm-cc/bluemonday" + "github.com/russross/blackfriday/v2" "github.com/vmihailenco/msgpack/v5" "golang.org/x/net/publicsuffix" "golang.org/x/time/rate" diff --git a/go.mod b/go.mod index 883aec67..7035daa0 100644 --- a/go.mod +++ b/go.mod @@ -14,12 +14,12 @@ require ( github.com/gomodule/redigo v1.8.2 github.com/google/uuid v1.1.2-0.20200519141726-cb32006e483f github.com/hashicorp/go-version v1.2.1 - github.com/iris-contrib/blackfriday v2.0.0+incompatible github.com/iris-contrib/httpexpect/v2 v2.0.5 github.com/iris-contrib/jade v1.1.4 github.com/iris-contrib/pongo2 v0.0.1 github.com/iris-contrib/schema v0.0.2 github.com/json-iterator/go v1.1.10 + github.com/kataras/blocks v0.0.1 github.com/kataras/golog v0.0.18 github.com/kataras/neffos v0.0.16 github.com/kataras/pio v0.0.8 @@ -28,6 +28,7 @@ require ( github.com/klauspost/compress v1.10.10 github.com/mediocregopher/radix/v3 v3.5.2 github.com/microcosm-cc/bluemonday v1.0.3 + github.com/russross/blackfriday/v2 v2.0.1 github.com/ryanuber/columnize v2.1.0+incompatible github.com/schollz/closestmatch v2.1.0+incompatible github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect diff --git a/view/README.md b/view/README.md index 0ff73293..a515008a 100644 --- a/view/README.md +++ b/view/README.md @@ -1,18 +1,21 @@ # View -Iris supports 7 template engines out-of-the-box, developers can still use any external golang template engine, +Iris supports 8 template engines out-of-the-box, developers can still use any external golang template engine, as `Context.ResponseWriter()` is an `io.Writer`. -All of these six template engines have common features with common API, -like Layout, Template Funcs, Party-specific layout, partial rendering and more. +All template engines share a common API i.e. +Parse using embedded assets, Layouts and Party-specific layout, Template Funcs, Partial Render and more. -- The standard html, its template parser is the [golang.org/pkg/html/template/](https://golang.org/pkg/html/template/) -- Django, its template parser is the [github.com/iris-contrib/pongo2](https://github.com/iris-contrib/pongo2) -- Pug(Jade), its template parser is the [github.com/Joker/jade](https://github.com/Joker/jade) -- Handlebars, its template parser is the [github.com/aymerick/raymond](https://github.com/aymerick/raymond) -- Amber, its template parser is the [github.com/eknkc/amber](https://github.com/eknkc/amber) -- Jet, its template parser is the [github.com/CloudyKit/jet](https://github.com/CloudyKit/jet) -- Ace, its template parser is the [github.com/yosssi/ace](https://github.com/yosssi/ace) +| # | Name | Parser | +|:---|:-----------|----------| +| 1 | HTML | [html/template](https://pkg.go.dev/html/template) | +| 2 | Blocks | [kataras/blocks](https://github.com/kataras/blocks) | +| 3 | Django | [flosch/pongo2](https://github.com/flosch/pongo2) | +| 4 | Pug | [Joker/jade](https://github.com/Joker/jade) | +| 5 | Handlebars | [aymerick/raymond](https://github.com/aymerick/raymond) | +| 6 | Amber | [eknkc/amber](https://github.com/eknkc/amber) | +| 7 | Jet | [CloudyKit/jet](https://github.com/CloudyKit/jet) | +| 8 | Ace | [yosssi/ace](https://github.com/yosssi/ace) | ## Examples @@ -24,6 +27,8 @@ like Layout, Template Funcs, Party-specific layout, partial rendering and more. - [The `url` tmpl func](https://github.com/kataras/iris/blob/master/_examples/view/template_html_4/main.go) - [Inject Data Between Handlers](https://github.com/kataras/iris/blob/master/_examples/view/context-view-data/main.go) - [Embedding Templates Into App Executable File](https://github.com/kataras/iris/blob/master/_examples/view/embedding-templates-into-app/main.go) +- [Blocks](https://github.com/kataras/iris/blob/master/_examples/view/template_blocks_0) +- [Blocks Embedded](https://github.com/kataras/iris/blob/master/_examples/view/template_blocks_1_embedded) - [Greeting with Pug (Jade)`](view/template_pug_0) - [Pug (Jade) Actions`](https://github.com/kataras/iris/blob/master/_examples/view/template_pug_1) - [Pug (Jade) Includes`](https://github.com/kataras/iris/blob/master/_examples/view/template_pug_2) @@ -32,7 +37,7 @@ like Layout, Template Funcs, Party-specific layout, partial rendering and more. - [Jet Embedded](https://github.com/kataras/iris/blob/master/_examples/view/template_jet_1_embedded) - [Ace](https://github.com/kataras/iris/blob/master/_examples/view/template_ace_0) -You can serve [quicktemplate](https://github.com/valyala/quicktemplate) files too, simply by using the `context#ResponseWriter`, take a look at the [iris/_examples/view/quicktemplate](https://github.com/kataras/iris/tree/master/_examples/view/quicktemplate) example. +You can serve [quicktemplate](https://github.com/valyala/quicktemplate) files too, simply by using the `Context.ResponseWriter`, take a look at the [iris/_examples/view/quicktemplate](https://github.com/kataras/iris/tree/master/_examples/view/quicktemplate) example. ## Overview diff --git a/view/ace.go b/view/ace.go index 134f2cf4..329ffc1f 100644 --- a/view/ace.go +++ b/view/ace.go @@ -10,6 +10,7 @@ import ( // Ace returns a new ace view engine. // It shares the same exactly logic with the // html view engine, it uses the same exactly configuration. +// The given "extension" MUST begin with a dot. // // Read more about the Ace Go Parser: https://github.com/yosssi/ace func Ace(directory, extension string) *HTMLEngine { diff --git a/view/amber.go b/view/amber.go index c8a7fa7c..9d1d153f 100644 --- a/view/amber.go +++ b/view/amber.go @@ -29,6 +29,7 @@ type AmberEngine struct { var _ Engine = (*AmberEngine)(nil) // Amber creates and returns a new amber view engine. +// The given "extension" MUST begin with a dot. func Amber(directory, extension string) *AmberEngine { s := &AmberEngine{ directory: directory, diff --git a/view/blocks.go b/view/blocks.go new file mode 100644 index 00000000..04e1e3dc --- /dev/null +++ b/view/blocks.go @@ -0,0 +1,109 @@ +package view + +import ( + "html/template" + "io" + + "github.com/kataras/blocks" +) + +// BlocksEngine is an Iris view engine adapter for the blocks view engine. +// The blocks engine is based on the html/template standard Go package. +// +// To initialize a fresh one use the `Blocks` function. +// To wrap an existing one use the `WrapBlocks` function. +// +// It contains the following four default template functions: +// - url "routename" parameters... +// - urlpath "routename" parameters... +// - tr "language" "key" arguments... +// - partial "template_name" data +// +// Read more at: https://github.com/kataras/blocks. +type BlocksEngine struct { + Engine *blocks.Blocks +} + +var _ Engine = (*BlocksEngine)(nil) + +// WrapBlocks wraps an initialized blocks engine and returns its Iris adapter. +// See `Blocks` package-level function too. +func WrapBlocks(v *blocks.Blocks) *BlocksEngine { + return &BlocksEngine{Engine: v} +} + +// Blocks returns a new blocks view engine. +// The given "extension" MUST begin with a dot. +// +// See `WrapBlocks` package-level function too. +func Blocks(directory, extension string) *BlocksEngine { + return WrapBlocks(blocks.New(directory).Extension(extension)) +} + +// Ext returns empty ext as this template engine +// supports template blocks without file suffix. +// Note that, if more than one view engine is registered to a single +// Iris application then, this Blocks engine should be the last entry one. +func (s *BlocksEngine) Ext() string { + return "" +} + +// AddFunc implements the `EngineFuncer` which is being used +// by the framework to add template functions like: +// - url func(routeName string, args ...string) string +// - urlpath func(routeName string, args ...string) string +// - tr func(lang, key string, args ...interface{}) string +func (s *BlocksEngine) AddFunc(funcName string, funcBody interface{}) *BlocksEngine { + s.Engine.Funcs(template.FuncMap{funcName: funcBody}) + return s +} + +// AddLayoutFunc adds a template function for templates that are marked as layouts. +func (s *BlocksEngine) AddLayoutFunc(funcName string, funcBody interface{}) *BlocksEngine { + s.Engine.LayoutFuncs(template.FuncMap{funcName: funcBody}) + return s +} + +// Binary sets the function which reads contents based on a filename +// and a function that returns all the filenames. +// These functions are used to parse the corresponding files into templates. +// You do not need to set them when the given "rootDir" was a system directory. +// It's mostly useful when the application contains embedded template files, +// e.g. pass go-bindata's `Asset` and `AssetNames` functions +// to load templates from go-bindata generated content. +func (s *BlocksEngine) Binary(asset blocks.AssetFunc, assetNames blocks.AssetNamesFunc) *BlocksEngine { + s.Engine.Assets(asset, assetNames) + return s +} + +// Layout sets the default layout which inside should use +// the {{ template "content" . }} to render the main template. +// +// Example for ./views/layouts/main.html: +// Blocks("./views", ".html").Layout("layouts/main") +func (s *BlocksEngine) Layout(layoutName string) *BlocksEngine { + s.Engine.DefaultLayout(layoutName) + return s +} + +// Reload if called with a true parameter, +// each `ExecuteWriter` call will re-parse the templates. +// Useful when the application is at a development stage. +func (s *BlocksEngine) Reload(b bool) *BlocksEngine { + s.Engine.Reload(b) + return s +} + +// Load parses the files into templates. +func (s *BlocksEngine) Load() error { + return s.Engine.Load() +} + +// ExecuteWriter renders a template on "w". +func (s *BlocksEngine) ExecuteWriter(w io.Writer, tmplName, layoutName string, data interface{}) error { + if layoutName == NoLayout { + layoutName = "" + } + + return s.Engine.ExecuteTemplate(w, tmplName, layoutName, data) +} diff --git a/view/django.go b/view/django.go index 2732e86d..b0305c05 100644 --- a/view/django.go +++ b/view/django.go @@ -108,7 +108,8 @@ type DjangoEngine struct { var _ Engine = (*DjangoEngine)(nil) -// Django creates and returns a new amber view engine. +// Django creates and returns a new django view engine. +// The given "extension" MUST begin with a dot. func Django(directory, extension string) *DjangoEngine { s := &DjangoEngine{ directory: directory, diff --git a/view/handlebars.go b/view/handlebars.go index f1130b96..f6557b8d 100644 --- a/view/handlebars.go +++ b/view/handlebars.go @@ -30,6 +30,7 @@ type HandlebarsEngine struct { var _ Engine = (*HandlebarsEngine)(nil) // Handlebars creates and returns a new handlebars view engine. +// The given "extension" MUST begin with a dot. func Handlebars(directory, extension string) *HandlebarsEngine { s := &HandlebarsEngine{ directory: directory, diff --git a/view/html.go b/view/html.go index a2ad1fe3..0001354f 100644 --- a/view/html.go +++ b/view/html.go @@ -61,6 +61,7 @@ var emptyFuncs = template.FuncMap{ // HTML creates and returns a new html view engine. // The html engine used like the "html/template" standard go package // but with a lot of extra features. +// The given "extension" MUST begin with a dot. func HTML(directory, extension string) *HTMLEngine { s := &HTMLEngine{ directory: directory, @@ -173,6 +174,7 @@ func (s *HTMLEngine) AddLayoutFunc(funcName string, funcBody interface{}) *HTMLE // - url func(routeName string, args ...string) string // - urlpath func(routeName string, args ...string) string // - render func(fullPartialName string) (template.HTML, error). +// - tr func(lang, key string, args ...interface{}) string func (s *HTMLEngine) AddFunc(funcName string, funcBody interface{}) *HTMLEngine { s.rmu.Lock() s.funcs[funcName] = funcBody @@ -206,7 +208,7 @@ func (s *HTMLEngine) Funcs(funcMap template.FuncMap) *HTMLEngine { // Load parses the templates to the engine. // It's also responsible to add the necessary global functions. // -// Returns an error if something bad happens, user is responsible to catch it. +// Returns an error if something bad happens, caller is responsible to handle that. func (s *HTMLEngine) Load() error { // No need to make this with a complicated and "pro" way, just add lockers to the `ExecuteWriter`. // if `Reload(true)` and add a note for non conc access on dev mode. @@ -500,6 +502,12 @@ func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bind } } + t := s.Templates.Lookup(name) + if t == nil { + return fmt.Errorf("template: %s does not exist in the dir: %s", name, s.directory) + } + s.runtimeFuncsFor(t, name, bindingData) + if layout = getLayout(layout, s.layout); layout != "" { lt := s.Templates.Lookup(layout) if lt == nil { @@ -510,11 +518,5 @@ func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bind return lt.Execute(w, bindingData) } - t := s.Templates.Lookup(name) - if t == nil { - return fmt.Errorf("template: %s does not exist in the dir: %s", name, s.directory) - } - s.runtimeFuncsFor(t, name, bindingData) - return t.Execute(w, bindingData) } diff --git a/view/jet.go b/view/jet.go index 6b92ded7..6ad10b96 100644 --- a/view/jet.go +++ b/view/jet.go @@ -47,6 +47,7 @@ var jetExtensions = [...]string{ } // Jet creates and returns a new jet view engine. +// The given "extension" MUST begin with a dot. func Jet(directory, extension string) *JetEngine { // if _, err := os.Stat(directory); os.IsNotExist(err) { // panic(err) diff --git a/view/pug.go b/view/pug.go index f317efe4..2182aaac 100644 --- a/view/pug.go +++ b/view/pug.go @@ -12,6 +12,7 @@ import ( // Pug (or Jade) returns a new pug view engine. // It shares the same exactly logic with the // html view engine, it uses the same exactly configuration. +// The given "extension" MUST begin with a dot. // // Read more about the Jade Go Parser: https://github.com/Joker/jade //