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
//