iris/render/template/template.go

177 lines
4.9 KiB
Go
Raw Normal View History

2016-05-30 16:08:09 +02:00
package template
import (
"fmt"
"io"
"github.com/klauspost/compress/gzip"
"sync"
"github.com/kataras/iris/config"
"github.com/kataras/iris/context"
"github.com/kataras/iris/render/template/engine/amber"
"github.com/kataras/iris/render/template/engine/html"
"github.com/kataras/iris/render/template/engine/jade"
"github.com/kataras/iris/render/template/engine/markdown"
"github.com/kataras/iris/render/template/engine/pongo"
"github.com/kataras/iris/utils"
)
type (
2016-05-31 10:05:42 +02:00
// Engine the interface that all template engines must inheritance
2016-05-30 16:08:09 +02:00
Engine interface {
2016-05-31 10:05:42 +02:00
// BuildTemplates builds the templates for a directory
2016-05-30 16:08:09 +02:00
BuildTemplates() error
2016-05-31 10:05:42 +02:00
// ExecuteWriter finds and execute a template and write its result to the out writer
2016-05-30 16:08:09 +02:00
ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error
}
2016-05-31 10:05:42 +02:00
// Template the internal configs for the common configs for the template engines
2016-05-30 16:08:09 +02:00
Template struct {
2016-05-31 10:05:42 +02:00
// Engine the type of the Engine
Engine Engine
// Gzip enable gzip compression
// default is false
Gzip bool
// IsDevelopment re-builds the templates on each request
// default is false
IsDevelopment bool
// Directory the system path which the templates live
// default is ./templates
Directory string
// Extensions the allowed file extension
// default is []string{".html"}
Extensions []string
// ContentType is the Content-Type response header
// default is text/html but you can change if if needed
ContentType string
// Layout the template file ( with its extension) which is the mother of all
// use it to have it as a root file, and include others with {{ yield }}, refer the docs
Layout string
2016-05-30 16:08:09 +02:00
buffer *utils.BufferPool // this is used only for RenderString
gzipWriterPool sync.Pool
}
)
// New creates and returns a Template instance which keeps the Template Engine and helps with render
func New(c config.Template) *Template {
var e Engine
// [ENGINE-2]
switch c.Engine {
case config.HTMLEngine:
e = html.New(c) // HTMLTemplate
case config.PongoEngine:
e = pongo.New(c) // Pongo2
case config.MarkdownEngine:
e = markdown.New(c) // Markdown
case config.JadeEngine:
e = jade.New(c) // Jade
case config.AmberEngine:
e = amber.New(c) // Amber
default: // config.NoEngine
return nil
}
if err := e.BuildTemplates(); err != nil { // first build the templates, if error then panic because this is called before server's run
panic(err)
}
compiledContentType := c.ContentType + "; charset=" + c.Charset
t := &Template{
Engine: e,
IsDevelopment: c.IsDevelopment,
Gzip: c.Gzip,
ContentType: compiledContentType,
Layout: c.Layout,
buffer: utils.NewBufferPool(64),
gzipWriterPool: sync.Pool{New: func() interface{} {
return &gzip.Writer{}
}},
}
return t
}
2016-05-31 10:05:42 +02:00
// Render renders a template using the context's writer
2016-05-30 16:08:09 +02:00
func (t *Template) Render(ctx context.IContext, name string, binding interface{}, layout ...string) (err error) {
if t == nil { // No engine was given but .Render was called
ctx.WriteHTML(403, "<b> Iris </b> <br/> Templates are disabled via config.NoEngine, check your iris' configuration please.")
return fmt.Errorf("[IRIS TEMPLATES] Templates are disabled via config.NoEngine, check your iris' configuration please.\n")
}
// build templates again on each render if IsDevelopment.
if t.IsDevelopment {
if err = t.Engine.BuildTemplates(); err != nil {
return
}
}
// I don't like this, something feels wrong
_layout := ""
if len(layout) > 0 {
_layout = layout[0]
}
if _layout == "" {
_layout = t.Layout
}
//
ctx.GetRequestCtx().Response.Header.Set("Content-Type", t.ContentType)
var out io.Writer
if t.Gzip {
ctx.GetRequestCtx().Response.Header.Add("Content-Encoding", "gzip")
gzipWriter := t.gzipWriterPool.Get().(*gzip.Writer)
gzipWriter.Reset(ctx.GetRequestCtx().Response.BodyWriter())
defer gzipWriter.Close()
defer t.gzipWriterPool.Put(gzipWriter)
out = gzipWriter
} else {
out = ctx.GetRequestCtx().Response.BodyWriter()
}
err = t.Engine.ExecuteWriter(out, name, binding, _layout)
return
}
2016-05-31 10:05:42 +02:00
// RenderString executes a template and returns its contents result (string)
2016-05-30 16:08:09 +02:00
func (t *Template) RenderString(name string, binding interface{}, layout ...string) (result string, err error) {
if t == nil { // No engine was given but .Render was called
err = fmt.Errorf("[IRIS TEMPLATES] Templates are disabled via config.NoEngine, check your iris' configuration please.\n")
return
}
// build templates again on each render if IsDevelopment.
if t.IsDevelopment {
if err = t.Engine.BuildTemplates(); err != nil {
return
}
}
// I don't like this, something feels wrong
_layout := ""
if len(layout) > 0 {
_layout = layout[0]
}
if _layout == "" {
_layout = t.Layout
}
out := t.buffer.Get()
// if we have problems later consider that -> out.Reset()
defer t.buffer.Put(out)
err = t.Engine.ExecuteWriter(out, name, binding, _layout)
if err == nil {
result = out.String()
}
return
}