diff --git a/HISTORY.md b/HISTORY.md
index 699bd9cb..3e3c9ae3 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -2,6 +2,26 @@
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras/iris` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
+
+## 3.0.0 -> 4.0.0-alpha.1
+
+[logger](https://github.com/iris-contrib/logger), [rest](https://github.com/iris-contrib/rest) and all [template engines](https://github.com/iris-contrib/template) **moved** to the [iris-contrib](https://github.com/iris-contrib).
+
+- `config.Logger` -> `iris.Logger.Config`
+- `config.Render/config.Render.Rest/config.Render.Template` -> **Removed**
+- `config.Render.Rest` -> `rest.Config`
+- `config.Render.Template` -> `$TEMPLATE_ENGINE.Config` except Directory,Extensions, Assets, AssetNames,
+- `config.Render.Template.Directory` -> `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates", ".html")`
+- `config.Render.Template.Assets` -> `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates",".html").Binary(assetFn func(name string) ([]byte, error), namesFn func() []string)`
+
+- `context.ExecuteTemplate` -> **Removed**, you can use the `context.Response.BodyWriter()` to get its writer and execute html/template engine manually, but this is useless because we support the best support for template engines among all other (golang) web frameworks
+- **Added** `config.Server.ReadBufferSize & config.Server.WriteBufferSize` which can be passed as configuration fields inside `iris.ListenTo(config.Server{...})`, which does the same job as `iris.Listen`
+- **Added** `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates", ".html")` to register a template engine, now iris supports multi template engines, each template engine has its own file extension, no big changes on context.Render except the last parameter:
+- `context.Render(filename string, binding interface{}, layout string{})` -> `context.Render(filename string, binding interface{}, options ...map[string]interface{}) | context.Render("myfile.html", myPage{}, iris.Map{"gzip":true,"layout":"layouts/MyLayout.html"}) |`
+
+E-book and examples are not yet updated, no big changes.
+
+
## 3.0.0-rc.4 -> 3.0.0-pre.release
- `context.PostFormValue` -> `context.FormValueString`, old func stays until the next revision
diff --git a/README.md b/README.md
index fb789271..b99af9b4 100644
--- a/README.md
+++ b/README.md
@@ -2,10 +2,10 @@
[![Travis Widget]][Travis] [![Release Widget]][Release] [![Report Widget]][Report] [![License Widget]][License] [![Chat Widget]][Chat] [![Documentation Widget]][Documentation]
-The [fastest](#benchmarks) web framework for Go.
+The fastest web framework for Go.
-[![Alt: The results have been updated on July 1, 2016](https://raw.githubusercontent.com/smallnest/go-web-framework-benchmark/master/benchmark.png)](https://github.com/smallnest/go-web-framework-benchmark)
+[![Alt: Benchmark results with pipeline and 500ms processing time](https://raw.githubusercontent.com/iris-contrib/website/gh-pages/assets/benchmark_horizontal_transparent.png)](#benchmarks)
@@ -22,7 +22,7 @@ func main() {
c.JSON(iris.StatusOK, iris.Map{
"Name": "Iris",
"Born": "13 March 2016",
- "Stars": 3693,
+ "Stars": 4262,
})
})
iris.Listen(":8080")
@@ -46,7 +46,7 @@ The only requirement is the [Go Programming Language](https://golang.org/dl), at
$ go get -u github.com/kataras/iris/iris
```
- >If you have installation issues and you are connected to the Internet through China please, [click here](https://kataras.gitbooks.io/iris/content/install.html).
+ >If you have installation issues or you are connected to the Internet through China please, [click here](https://kataras.gitbooks.io/iris/content/install.html).
FAQ
------------
@@ -76,6 +76,12 @@ Features
| Name | Description | Usage |
| ------------------|:---------------------:|-------:|
+| [HTML/Default Engine ](https://github.com/iris-contrib/template/tree/master/html) | HTML Template Engine (Default) |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_html_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
+| [Django Engine ](https://github.com/iris-contrib/template/tree/master/django) | Django Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_django_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/brender_templates.html)
+| [Pug/Jade Engine ](https://github.com/iris-contrib/template/tree/master/pug) | Pug Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_pug_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
+| [Handlebars Engine ](https://github.com/iris-contrib/template/tree/master/handlebars) | Handlebars Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_handlebars_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
+| [Amber Engine ](https://github.com/iris-contrib/template/tree/master/amber) | Amber Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_amber_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
+| [Markdown Engine ](https://github.com/iris-contrib/template/tree/master/markdown) | Markdown Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_markdown_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
| [Basicauth Middleware ](https://github.com/iris-contrib/middleware/tree/master/basicauth) | HTTP Basic authentication |[example 1](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_1/main.go), [example 2](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_2/main.go), [book section](https://kataras.gitbooks.io/iris/content/basic-authentication.html) |
| [JWT Middleware ](https://github.com/iris-contrib/middleware/tree/master/jwt) | JSON Web Tokens |[example ](https://github.com/iris-contrib/examples/blob/master/middleware_jwt/main.go), [book section](https://kataras.gitbooks.io/iris/content/jwt.html) |
| [Cors Middleware ](https://github.com/iris-contrib/middleware/tree/master/cors) | Cross Origin Resource Sharing W3 specification | [how to use ](https://github.com/iris-contrib/middleware/tree/master/cors#how-to-use) |
@@ -133,30 +139,27 @@ Iris does not force you to use any specific ORM or template engine. With support
Testing
------------
-Tests are located to the [iris-contrib/tests repository](https://github.com/iris-contrib/tests), community should write some code there!
+Community should write third-party or iris base tests to the [iris-contrib/tests repository](https://github.com/iris-contrib/tests).
I recommend writing your API tests using this new library, [httpexpect](https://github.com/gavv/httpexpect) which supports Iris and fasthttp now, after my request [here](https://github.com/gavv/httpexpect/issues/2).
Versioning
------------
-Current: **v3.0.0**
+Current: **v4.0.0-alpha.1**
> Iris is an active project
-Read more about Semantic Versioning 2.0.0
-
- - http://semver.org/
- - https://en.wikipedia.org/wiki/Software_versioning
- - https://wiki.debian.org/UpstreamGuide#Releases_and_Versions
-
Todo
------------
> for the next version 'v4'
-- [ ] Refactor & extend view engine, separate the engines from the main code base, easier for the community to create new view engines.
-- [ ] Create a router as optional plugin, for optional path parts. Its name, 'ryan', taken from the community-member and donator who requested this feature.
+- [x] Refactor & extend view engine, separate the engines from the main code base, easier for the community to create new view engines
+- [ ] Implement all [opened community's feature requests](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aopen+label%3A%22feature+request%22)
+- [ ] Extend i18n middleware for easier and better internalization support
+- [ ] Create a router as optional plugin, for optional path parts. Its name, 'ryan', taken from the community-member and donator who requested this feature
- [ ] Extend the iris control plugin
-- [ ] Remove deprecated functions.
+- [ ] Remove deprecated functions
+- [ ] Will think more :)
> completed for release 'v3'
@@ -191,7 +194,7 @@ License can be found [here](LICENSE).
[Travis]: http://travis-ci.org/kataras/iris
[License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square
[License]: https://github.com/kataras/iris/blob/master/LICENSE
-[Release Widget]: https://img.shields.io/badge/release-v3.0.0-blue.svg?style=flat-square
+[Release Widget]: https://img.shields.io/badge/release-v4.0.0--alpha.1-blue.svg?style=flat-square
[Release]: https://github.com/kataras/iris/releases
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
[Chat]: https://kataras.rocket.chat/channel/iris
diff --git a/config/config.go b/config/config.go
index 9d1bc1b8..4bd0bb1b 100644
--- a/config/config.go
+++ b/config/config.go
@@ -6,6 +6,13 @@ import (
)
var (
+ // Charset character encoding for template rendering
+ Charset = "UTF-8"
+)
+
+var (
+ // TimeFormat default time format for any kind of datetime parsing
+ TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
// StaticCacheDuration expiration duration for INACTIVE file handlers
StaticCacheDuration = 20 * time.Second
// CompressedFileSuffix is the suffix to add to the name of
@@ -13,4 +20,14 @@ var (
//
// Defaults to iris-fasthttp.gz
CompressedFileSuffix = "iris-fasthttp.gz"
+
+ // ContentTypeHTML defaults to text/html but you can change it, changes the template's content type also
+ ContentTypeHTML = "text/html"
+)
+
+const (
+ // NoLayout to disable layout for a particular template file
+ NoLayout = "@.|.@iris_no_layout@.|.@"
+ // TemplateLayoutContextKey is the name of the user values which can be used to set a template layout from a middleware and override the parent's
+ TemplateLayoutContextKey = "templateLayout"
)
diff --git a/config/iris.go b/config/iris.go
index 247356a2..716c2b74 100644
--- a/config/iris.go
+++ b/config/iris.go
@@ -1,6 +1,9 @@
package config
-import "github.com/imdario/mergo"
+import (
+ "github.com/imdario/mergo"
+ "github.com/iris-contrib/rest"
+)
// Default values for base Iris conf
const (
@@ -78,16 +81,19 @@ type (
// http://debug.yourdomain:PORT/threadcreate
// http://debug.yourdomain:PORT/pprof/block
ProfilePath string
-
- // Logger the configuration for the logger
- // Iris logs ONLY SEMANTIC errors and the banner if enabled
- Logger Logger
+ // DisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
+ // default is false
+ DisableTemplateEngines bool
+ // IsDevelopment iris will act like a developer, for example
+ // If true then re-builds the templates on each request
+ // default is false
+ IsDevelopment bool
// Sessions contains the configs for sessions
Sessions Sessions
- // Render contains the configs for template and rest configuration
- Render Render
+ // Rest contains the configs for rest render configuration
+ Rest rest.Config
// Websocket contains the configs for Websocket's server integration
Websocket *Websocket
@@ -95,41 +101,21 @@ type (
// Tester contains the configs for the test framework, so far we have only one because all test framework's configs are setted by the iris itself
Tester Tester
}
-
- // Render struct keeps organise all configuration about rendering, templates and rest currently.
- Render struct {
- // Template the configs for template
- Template Template
- // Rest configs for rendering.
- //
- // these options inside this config don't have any relation with the TemplateEngine
- // from github.com/kataras/iris/rest
- Rest Rest
- }
)
-// DefaultRender returns default configuration for templates and rest rendering
-func DefaultRender() Render {
- return Render{
- // set the default template config both not nil and default Engine to Standar
- Template: DefaultTemplate(),
- // set the default configs for rest
- Rest: DefaultRest(),
- }
-}
-
// Default returns the default configuration for the Iris staton
func Default() Iris {
return Iris{
- DisablePathCorrection: DefaultDisablePathCorrection,
- DisablePathEscape: DefaultDisablePathEscape,
- DisableBanner: false,
- ProfilePath: "",
- Logger: DefaultLogger(),
- Sessions: DefaultSessions(),
- Render: DefaultRender(),
- Websocket: DefaultWebsocket(),
- Tester: DefaultTester(),
+ DisablePathCorrection: DefaultDisablePathCorrection,
+ DisablePathEscape: DefaultDisablePathEscape,
+ DisableBanner: false,
+ DisableTemplateEngines: false,
+ IsDevelopment: false,
+ ProfilePath: "",
+ Sessions: DefaultSessions(),
+ Rest: rest.DefaultConfig(),
+ Websocket: DefaultWebsocket(),
+ Tester: DefaultTester(),
}
}
@@ -158,18 +144,3 @@ func (c Iris) MergeSingle(cfg Iris) (config Iris) {
return
}
-
-/* maybe some day
-// FromFile returns the configuration for Iris station
-//
-// receives one parameter
-// pathIni(string) the file path of the configuration-ini style
-//
-// returns an error if something bad happens
-func FromFile(pathIni string) (c Iris, err error) {
- c = Iris{}
- err = ini.MapTo(&c, pathIni)
-
- return
-}
-*/
diff --git a/config/server.go b/config/server.go
index 0a04f671..6c4c450f 100644
--- a/config/server.go
+++ b/config/server.go
@@ -69,6 +69,7 @@ type Server struct {
//
// Default buffer size is used if not set.
WriteBufferSize int
+
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
//
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
diff --git a/context.go b/context.go
index 87393756..465049dd 100644
--- a/context.go
+++ b/context.go
@@ -10,7 +10,6 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
- "html/template"
"io"
"net"
"os"
@@ -476,23 +475,23 @@ func (ctx *Context) Data(status int, v []byte) error {
// RenderWithStatus builds up the response from the specified template and bindings.
// Note: parameter layout has meaning only when using the iris.HTMLTemplate
-func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, layout ...string) error {
+func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, options ...map[string]interface{}) error {
ctx.SetStatusCode(status)
- return ctx.framework.templates.Render(ctx, name, binding, layout...)
+ return ctx.framework.templates.GetBy(name).Execute(ctx, name, binding, options...)
}
// Render same as .RenderWithStatus but with status to iris.StatusOK (200)
-func (ctx *Context) Render(name string, binding interface{}, layout ...string) error {
+func (ctx *Context) Render(name string, binding interface{}, options ...map[string]interface{}) error {
errCode := ctx.RequestCtx.Response.StatusCode()
if errCode <= 0 {
errCode = StatusOK
}
- return ctx.RenderWithStatus(errCode, name, binding, layout...)
+ return ctx.RenderWithStatus(errCode, name, binding, options...)
}
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
-func (ctx *Context) MustRender(name string, binding interface{}, layout ...string) {
- if err := ctx.Render(name, binding, layout...); err != nil {
+func (ctx *Context) MustRender(name string, binding interface{}, options ...map[string]interface{}) {
+ if err := ctx.Render(name, binding, options...); err != nil {
ctx.Panic()
ctx.framework.Logger.Dangerf("MustRender panics for client with IP: %s On template: %s.Trace: %s\n", ctx.RemoteAddr(), name, err)
}
@@ -500,8 +499,8 @@ func (ctx *Context) MustRender(name string, binding interface{}, layout ...strin
// TemplateString accepts a template filename, its context data and returns the result of the parsed template (string)
// if any error returns empty string
-func (ctx *Context) TemplateString(name string, binding interface{}, layout ...string) string {
- return ctx.framework.TemplateString(name, binding, layout...)
+func (ctx *Context) TemplateString(name string, binding interface{}, options ...map[string]interface{}) string {
+ return ctx.framework.TemplateString(name, binding, options...)
}
// JSON marshals the given interface object and writes the JSON response.
@@ -537,17 +536,6 @@ func (ctx *Context) Markdown(status int, markdown string) {
ctx.HTML(status, ctx.MarkdownString(markdown))
}
-// ExecuteTemplate executes a simple html template, you can use that if you already have the cached templates
-// the recommended way to render is to use iris.Templates("./templates/path/*.html") and ctx.RenderFile("filename.html",struct{})
-// accepts 2 parameters
-// the first parameter is the template (*template.Template)
-// the second parameter is the page context (interfac{})
-// returns an error if any errors occurs while executing this template
-func (ctx *Context) ExecuteTemplate(tmpl *template.Template, pageContext interface{}) error {
- ctx.RequestCtx.SetContentType(contentHTML + ctx.framework.rest.CompiledCharset)
- return errTemplateExecute.With(tmpl.Execute(ctx.RequestCtx.Response.BodyWriter(), pageContext))
-}
-
// ServeContent serves content, headers are autoset
// receives three parameters, it's low-level function, instead you can use .ServeFile(string)
//
diff --git a/context/context.go b/context/context.go
index 166455dc..0d4f0f33 100644
--- a/context/context.go
+++ b/context/context.go
@@ -2,7 +2,6 @@ package context
import (
"bufio"
- "html/template"
"io"
"time"
@@ -46,17 +45,16 @@ type (
Write(string, ...interface{})
HTML(int, string)
Data(int, []byte) error
- RenderWithStatus(int, string, interface{}, ...string) error
- Render(string, interface{}, ...string) error
- MustRender(string, interface{}, ...string)
- TemplateString(string, interface{}, ...string) string
+ RenderWithStatus(int, string, interface{}, ...map[string]interface{}) error
+ Render(string, interface{}, ...map[string]interface{}) error
+ MustRender(string, interface{}, ...map[string]interface{})
+ TemplateString(string, interface{}, ...map[string]interface{}) string
MarkdownString(string) string
Markdown(int, string)
JSON(int, interface{}) error
JSONP(int, string, interface{}) error
Text(int, string) error
XML(int, interface{}) error
- ExecuteTemplate(*template.Template, interface{}) error
ServeContent(io.ReadSeeker, string, time.Time, bool) error
ServeFile(string, bool) error
SendFile(string, string) error
diff --git a/deprecated.go b/deprecated.go
index 6357741e..8da1ea37 100644
--- a/deprecated.go
+++ b/deprecated.go
@@ -89,9 +89,7 @@ func MustUseFunc(handlersFn ...HandlerFunc) {
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains
// It can be called after other, (but before .Listen of course)
func (s *Framework) MustUse(handlers ...Handler) {
- for _, r := range s.mux.lookups {
- r.middleware = append(handlers, r.middleware...)
- }
+ s.UseGlobal(handlers...)
}
// MustUseFunc registers HandlerFunc middleware to the beginning, prepends them instead of append
@@ -99,7 +97,7 @@ func (s *Framework) MustUse(handlers ...Handler) {
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains
// It can be called after other, (but before .Listen of course)
func (s *Framework) MustUseFunc(handlersFn ...HandlerFunc) {
- s.MustUse(convertToHandlers(handlersFn)...)
+ s.UseGlobalFunc(handlersFn...)
}
// PostFormMulti returns a slice of string from post request's data
diff --git a/glide.lock b/glide.lock
index 99f93ca0..ce131ecb 100644
--- a/glide.lock
+++ b/glide.lock
@@ -101,7 +101,7 @@ imports:
subpackages:
- github.com\valyala\bytebufferpool
- name: github.com/valyala/fasthttp
- version: 97d96cb3b7feaf22ce9b813313cd8ae20b3589b9
+ version: 886e5411604884629c566961ea8ed2cec074e4b1
subpackages:
- github.com\valyala\fasthttp
- github.com\valyala\fasthttp\fasthttpadaptor
diff --git a/http.go b/http.go
index 02e44f9c..ec7e932d 100644
--- a/http.go
+++ b/http.go
@@ -13,8 +13,8 @@ import (
"time"
"github.com/iris-contrib/errors"
+ "github.com/iris-contrib/logger"
"github.com/kataras/iris/config"
- "github.com/kataras/iris/logger"
"github.com/kataras/iris/utils"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpadaptor"
@@ -262,6 +262,11 @@ func newServer(cfg config.Server) *Server {
// prepare just prepares the listening addr
func (s *Server) prepare() {
s.Config.ListeningAddr = config.ServerParseAddr(s.Config.ListeningAddr)
+ if s.Server != nil {
+ s.Server.MaxRequestBodySize = s.Config.MaxRequestBodySize
+ s.Server.ReadBufferSize = s.Config.ReadBufferSize
+ s.Server.WriteBufferSize = s.Config.WriteBufferSize
+ }
}
// IsListening returns true if server is listening/started, otherwise false
@@ -399,10 +404,6 @@ func (s *Server) Open(h fasthttp.RequestHandler) error {
s.prepare() // do it again for any case
- s.Server.MaxRequestBodySize = s.Config.MaxRequestBodySize
- s.Server.ReadBufferSize = s.Config.ReadBufferSize
- s.Server.WriteBufferSize = s.Config.WriteBufferSize
-
if s.Config.RedirectTo != "" {
// override the handler and redirect all requests to this addr
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {
diff --git a/iris.go b/iris.go
index 9463783a..2a1197ee 100644
--- a/iris.go
+++ b/iris.go
@@ -65,11 +65,11 @@ import (
"github.com/gavv/httpexpect"
"github.com/iris-contrib/errors"
+ "github.com/iris-contrib/logger"
+ "github.com/iris-contrib/rest"
+ "github.com/iris-contrib/template/html"
"github.com/kataras/iris/config"
"github.com/kataras/iris/context"
- "github.com/kataras/iris/logger"
- "github.com/kataras/iris/render/rest"
- "github.com/kataras/iris/render/template"
"github.com/kataras/iris/sessions"
"github.com/kataras/iris/utils"
"github.com/kataras/iris/websocket"
@@ -81,27 +81,7 @@ import (
const (
// Version of the iris
- Version = "3.0.0"
-
- // HTMLEngine conversion for config.HTMLEngine
- HTMLEngine = config.HTMLEngine
- // PongoEngine conversion for config.PongoEngine
- PongoEngine = config.PongoEngine
- // MarkdownEngine conversion for config.MarkdownEngine
- MarkdownEngine = config.MarkdownEngine
- // JadeEngine conversion for config.JadeEngine
- JadeEngine = config.JadeEngine
- // AmberEngine conversion for config.AmberEngine
- AmberEngine = config.AmberEngine
- // HandlebarsEngine conversion for config.HandlebarsEngine
- HandlebarsEngine = config.HandlebarsEngine
- // DefaultEngine conversion for config.DefaultEngine
- DefaultEngine = config.DefaultEngine
- // NoEngine conversion for config.NoEngine
- NoEngine = config.NoEngine
- // NoLayout to disable layout for a particular template file
- // conversion for config.NoLayout
- NoLayout = config.NoLayout
+ Version = "4.0.0-alpha.1"
banner = ` _____ _
|_ _| (_)
@@ -177,7 +157,7 @@ type (
Lookups() []Route
Path(string, ...interface{}) string
URL(string, ...interface{}) string
- TemplateString(string, interface{}, ...string) string
+ TemplateString(string, interface{}, ...map[string]interface{}) string
Tester(t *testing.T) *httpexpect.Expect
}
@@ -187,12 +167,14 @@ type (
Framework struct {
*muxAPI
rest *rest.Render
- templates *template.Template
sessions *sessions.Manager
+ templates *TemplateEngines
+
// fields which are useful to the user/dev
// the last added server is the main server
- Servers *ServerList
- Config *config.Iris
+ Servers *ServerList
+ Config *config.Iris
+ // configuration by instance.Logger.Config
Logger *logger.Logger
Plugins PluginContainer
Websocket websocket.Server
@@ -217,9 +199,17 @@ func New(cfg ...config.Iris) *Framework {
{
///NOTE: set all with s.Config pointer
// set the Logger
- s.Logger = logger.New(s.Config.Logger)
+ s.Logger = logger.New(logger.DefaultConfig())
// set the plugin container
s.Plugins = &pluginContainer{logger: s.Logger}
+ // set the templates
+ s.templates = &TemplateEngines{
+ helpers: map[string]interface{}{
+ "url": s.URL,
+ "urlpath": s.Path,
+ },
+ engines: make([]*TemplateEngineWrapper, 0),
+ }
// set the websocket server
s.Websocket = websocket.NewServer(s.Config.Websocket)
// set the servemux, which will provide us the public API also, with its context pool
@@ -241,10 +231,18 @@ func (s *Framework) initialize() {
}
// set the rest
- s.rest = rest.New(s.Config.Render.Rest)
-
- // set templates if not already setted
- s.prepareTemplates()
+ s.rest = rest.New(s.Config.Rest)
+ // prepare the templates if enabled
+ if !s.Config.DisableTemplateEngines {
+ if err := s.templates.loadAll(); err != nil {
+ s.Logger.Panic(err) // panic on templates loading before listening if we have an error.
+ }
+ // check and prepare the templates
+ if len(s.templates.engines) == 0 { // no template engine is registered, let's use the default
+ s.UseEngine(html.New())
+ }
+ s.templates.setReload(s.Config.IsDevelopment)
+ }
// listen to websocket connections
websocket.RegisterServer(s, s.Websocket, s.Logger)
@@ -258,22 +256,6 @@ func (s *Framework) initialize() {
}
}
-// prepareTemplates sets the templates if not nil, we make this check because of .TemplateString, which can be called before Listen
-func (s *Framework) prepareTemplates() {
- // prepare the templates
- if s.templates == nil {
- // These functions are directly contact with Iris' functionality.
- funcs := map[string]interface{}{
- "url": s.URL,
- "urlpath": s.Path,
- }
-
- template.RegisterSharedFuncs(funcs)
-
- s.templates = template.New(s.Config.Render.Template)
- }
-}
-
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual
func Go() error {
return Default.Go()
@@ -290,7 +272,6 @@ func (s *Framework) Go() error {
// print the banner
if !s.Config.DisableBanner {
-
openedServers := s.Servers.GetAllOpened()
l := len(openedServers)
hosts := make([]string, l, l)
@@ -495,6 +476,31 @@ func (s *Framework) Close() error {
return s.Servers.CloseAll()
}
+/*
+
+// set the template engines
+s.renderer = &renderer{
+ engines: make([]TemplateEngine, 0),
+ buffer: utils.NewBufferPool(64),
+ helpers: map[string]interface{}{
+ "url": s.URL,
+ "urlpath": s.Path,
+ },
+ contentType: s.Config.Render.Template.ContentType + "; " + s.Config.Render.Template.Charset,
+}*/
+
+// UseEngine adds a template engine to the iris view system
+// it does not build/load them yet
+func UseEngine(e TemplateEngine) *TemplateEngineLocation {
+ return Default.UseEngine(e)
+}
+
+// UseEngine adds a template engine to the iris view system
+// it does not build/load them yet
+func (s *Framework) UseEngine(e TemplateEngine) *TemplateEngineLocation {
+ return s.templates.Add(e)
+}
+
// UseGlobal registers Handler middleware to the beginning, prepends them instead of append
//
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains
@@ -713,17 +719,19 @@ func (s *Framework) URL(routeName string, args ...interface{}) (url string) {
return
}
-// TemplateString executes a template and returns its result as string, useful when you want it for sending rich e-mails
+// TemplateString executes a template from the default template engine and returns its result as string, useful when you want it for sending rich e-mails
// returns empty string on error
-func TemplateString(templateFile string, pageContext interface{}, layout ...string) string {
- return Default.TemplateString(templateFile, pageContext, layout...)
+func TemplateString(templateFile string, pageContext interface{}, options ...map[string]interface{}) string {
+ return Default.TemplateString(templateFile, pageContext, options...)
}
-// TemplateString executes a template and returns its result as string, useful when you want it for sending rich e-mails
+// TemplateString executes a template from the default template engine and returns its result as string, useful when you want it for sending rich e-mails
// returns empty string on error
-func (s *Framework) TemplateString(templateFile string, pageContext interface{}, layout ...string) string {
- s.prepareTemplates()
- res, err := s.templates.RenderString(templateFile, pageContext, layout...)
+func (s *Framework) TemplateString(templateFile string, pageContext interface{}, options ...map[string]interface{}) string {
+ if s.Config.DisableTemplateEngines {
+ return ""
+ }
+ res, err := s.templates.GetBy(templateFile).ExecuteToString(templateFile, pageContext, options...)
if err != nil {
return ""
}
diff --git a/iris/main.go b/iris/main.go
index d3eccb91..cc3c7163 100644
--- a/iris/main.go
+++ b/iris/main.go
@@ -3,10 +3,9 @@ package main
import (
"os"
+ "github.com/iris-contrib/logger"
"github.com/kataras/cli"
"github.com/kataras/iris"
- "github.com/kataras/iris/config"
- "github.com/kataras/iris/logger"
)
const (
@@ -49,7 +48,7 @@ func init() {
app.Command(runAndWatchCmd)
// init the logger
- printer = logger.New(config.DefaultLogger())
+ printer = logger.New(logger.DefaultConfig())
}
func main() {
diff --git a/logger/README.md b/logger/README.md
deleted file mode 100644
index 50ad3565..00000000
--- a/logger/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-## Package information
-
-I decide to split the logger from the main iris package because logger.go doesn't depends on any of the iris' types.
-
-
-**Examples and more info will be added soon.**
diff --git a/logger/logger.go b/logger/logger.go
deleted file mode 100644
index 23747fc0..00000000
--- a/logger/logger.go
+++ /dev/null
@@ -1,186 +0,0 @@
-package logger
-
-import (
- "os"
-
- "github.com/fatih/color"
- "github.com/kataras/iris/config"
- "github.com/mattn/go-colorable"
-)
-
-var (
- // Prefix is the prefix for the logger, default is [IRIS]
- Prefix = "[IRIS] "
- // bannersRan keeps track of the logger's print banner count
- bannersRan = 0
-)
-
-// Logger the logger
-type Logger struct {
- config *config.Logger
- underline *color.Color
-}
-
-// attr takes a color integer and converts it to color.Attribute
-func attr(sgr int) color.Attribute {
- return color.Attribute(sgr)
-}
-
-// check if background color > 0 and if so then set it
-func (l *Logger) setBg(sgr int) {
- if sgr > 0 {
- l.underline.Add(attr(sgr))
- }
-}
-
-// New creates a new Logger from config.Logger configuration
-func New(c config.Logger) *Logger {
- color.Output = colorable.NewColorable(c.Out)
-
- l := &Logger{&c, color.New(attr(c.ColorFgDefault))}
- l.setBg(c.ColorBgDefault)
-
- return l
-}
-
-// SetEnable true enables, false disables the Logger
-func (l *Logger) SetEnable(enable bool) {
- l.config.Disabled = !enable
-}
-
-// IsEnabled returns true if Logger is enabled, otherwise false
-func (l *Logger) IsEnabled() bool {
- return !l.config.Disabled
-}
-
-// ResetColors sets the colors to the default
-// this func is called every time a success, info, warning, or danger message is printed
-func (l *Logger) ResetColors() {
- l.underline.Add(attr(l.config.ColorFgDefault))
- l.setBg(l.config.ColorBgDefault)
-}
-
-// PrintBanner prints a text (banner) with BannerFgColor, BannerBgColor and a success message at the end
-// It doesn't cares if the logger is disabled or not, it will print this
-func (l *Logger) PrintBanner(banner string, successMessage string) {
- c := color.New(attr(l.config.ColorFgBanner))
- if l.config.ColorBgDefault > 0 {
- c.Add(attr(l.config.ColorBgDefault))
- }
- c.Println(banner)
- bannersRan++
-
- if successMessage != "" {
- c.Add(attr(l.config.ColorFgSuccess))
- if l.config.ColorBgSuccess > 0 {
- c.Add(attr(l.config.ColorBgSuccess))
- }
- if bannersRan > 1 {
- c.Printf("Server[%#v]\n", bannersRan)
-
- }
- c.Println(successMessage)
- }
-
- c.DisableColor()
- c = nil
-}
-
-// Printf calls l.Output to print to the logger.
-// Arguments are handled in the manner of fmt.Printf.
-func (l *Logger) Printf(format string, a ...interface{}) {
- if !l.config.Disabled {
- l.underline.Printf(l.config.Prefix+format, a...)
- }
-}
-
-// Print calls l.Output to print to the logger.
-// Arguments are handled in the manner of fmt.Print.
-func (l *Logger) Print(a interface{}) {
- if !l.config.Disabled {
- l.ResetColors()
- l.Printf("%#v", a)
- }
-}
-
-// Println calls l.Output to print to the logger.
-// Arguments are handled in the manner of fmt.Println.
-func (l *Logger) Println(a interface{}) {
- if !l.config.Disabled {
- l.Printf("%#v\n", a)
- }
-}
-
-// Fatal is equivalent to l.Dangerf("%#v",interface{}) followed by a call to panic().
-func (l *Logger) Fatal(a interface{}) {
- l.Warningf("%#v", a)
- panic("")
-}
-
-// Fatalf is equivalent to l.Warningf() followed by a call to os.Exit(1).
-func (l *Logger) Fatalf(format string, a ...interface{}) {
- l.Warningf(format, a...)
- os.Exit(1)
-}
-
-// Panic is equivalent to l.Dangerf("%#v",interface{}) followed by a call to panic().
-func (l *Logger) Panic(a interface{}) {
- l.Dangerf("%s\n", a)
- panic(a)
-}
-
-// Panicf is equivalent to l.Dangerf() followed by a call to panic().
-func (l *Logger) Panicf(format string, a ...interface{}) {
- l.Dangerf(format, a...)
- panic("")
-}
-
-// Successf calls l.Output to print to the logger with the Success colors.
-// Arguments are handled in the manner of fmt.Printf.
-func (l *Logger) Successf(format string, a ...interface{}) {
- if !l.config.Disabled {
- l.underline.Add(attr(l.config.ColorFgSuccess))
- l.setBg(l.config.ColorBgSuccess)
- l.Printf(format, a...)
- }
-}
-
-// Infof calls l.Output to print to the logger with the Info colors.
-// Arguments are handled in the manner of fmt.Printf.
-func (l *Logger) Infof(format string, a ...interface{}) {
- if !l.config.Disabled {
- l.underline.Add(attr(l.config.ColorFgInfo))
- l.setBg(l.config.ColorBgInfo)
- l.Printf(format, a...)
- }
-}
-
-// Warningf calls l.Output to print to the logger with the Warning colors.
-// Arguments are handled in the manner of fmt.Printf.
-func (l *Logger) Warningf(format string, a ...interface{}) {
- if !l.config.Disabled {
- l.underline.Add(attr(l.config.ColorFgWarning))
- l.setBg(l.config.ColorBgWarning)
- l.Printf(format, a...)
- }
-}
-
-// Dangerf calls l.Output to print to the logger with the Danger colors.
-// Arguments are handled in the manner of fmt.Printf.
-func (l *Logger) Dangerf(format string, a ...interface{}) {
- if !l.config.Disabled {
- l.underline.Add(attr(l.config.ColorFgDanger))
- l.setBg(l.config.ColorBgDanger)
- l.Printf(format, a...)
- }
-}
-
-// Otherf calls l.Output to print to the logger with the Other colors.
-// Arguments are handled in the manner of fmt.Printf.
-func (l *Logger) Otherf(format string, a ...interface{}) {
- if !l.config.Disabled {
- l.underline.Add(attr(l.config.ColorFgOther))
- l.setBg(l.config.ColorBgOther)
- l.Printf(format, a...)
- }
-}
diff --git a/plugin.go b/plugin.go
index cb1ec492..94b5bc96 100644
--- a/plugin.go
+++ b/plugin.go
@@ -5,7 +5,7 @@ import (
"github.com/iris-contrib/errors"
- "github.com/kataras/iris/logger"
+ "github.com/iris-contrib/logger"
"github.com/kataras/iris/utils"
)
diff --git a/render/rest/engine.go b/render/rest/engine.go
deleted file mode 100644
index 9566dd99..00000000
--- a/render/rest/engine.go
+++ /dev/null
@@ -1,317 +0,0 @@
-package rest
-
-import (
- "bytes"
- "encoding/json"
- "encoding/xml"
-
- "github.com/klauspost/compress/gzip"
- "github.com/valyala/fasthttp"
-)
-
-// Engine is the generic interface for all responses.
-type Engine interface {
- Render(*fasthttp.RequestCtx, interface{}) error
- //used only if config gzip is enabled
- RenderGzip(*fasthttp.RequestCtx, interface{}) error
-}
-
-// Head defines the basic ContentType and Status fields.
-type Head struct {
- ContentType string
- Status int
-}
-
-// Data built-in renderer.
-type Data struct {
- Head
-}
-
-// JSON built-in renderer.
-type JSON struct {
- Head
- Indent bool
- UnEscapeHTML bool
- Prefix []byte
- StreamingJSON bool
-}
-
-// JSONP built-in renderer.
-type JSONP struct {
- Head
- Indent bool
- Callback string
-}
-
-// Text built-in renderer.
-type Text struct {
- Head
-}
-
-// XML built-in renderer.
-type XML struct {
- Head
- Indent bool
- Prefix []byte
-}
-
-// Write outputs the header content.
-func (h Head) Write(ctx *fasthttp.RequestCtx) {
- ctx.Response.Header.Set(ContentType, h.ContentType)
- ctx.SetStatusCode(h.Status)
-}
-
-// Render a data response.
-func (d Data) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
- c := string(ctx.Request.Header.Peek(ContentType))
- w := ctx.Response.BodyWriter()
- if c != "" {
- d.Head.ContentType = c
- }
-
- d.Head.Write(ctx)
- w.Write(v.([]byte))
- return nil
-
-}
-
-// RenderGzip a data response using gzip compression.
-func (d Data) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
- c := string(ctx.Request.Header.Peek(ContentType))
- if c != "" {
- d.Head.ContentType = c
- }
-
- d.Head.Write(ctx)
- _, err := fasthttp.WriteGzip(ctx.Response.BodyWriter(), v.([]byte))
- if err == nil {
- ctx.Response.Header.Add("Content-Encoding", "gzip")
- }
- return err
-}
-
-// Render a JSON response.
-func (j JSON) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
- if j.StreamingJSON {
- return j.renderStreamingJSON(ctx, v)
- }
-
- var result []byte
- var err error
-
- if j.Indent {
- result, err = json.MarshalIndent(v, "", " ")
- result = append(result, '\n')
- } else {
- result, err = json.Marshal(v)
- }
- if err != nil {
- return err
- }
-
- // Unescape HTML if needed.
- if j.UnEscapeHTML {
- result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
- result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
- result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
- }
- w := ctx.Response.BodyWriter()
- // JSON marshaled fine, write out the result.
- j.Head.Write(ctx)
- if len(j.Prefix) > 0 {
- w.Write(j.Prefix)
- }
- w.Write(result)
- return nil
-}
-
-// RenderGzip a JSON response using gzip compression.
-func (j JSON) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
- if j.StreamingJSON {
- return j.renderStreamingJSONGzip(ctx, v)
- }
-
- var result []byte
- var err error
-
- if j.Indent {
- result, err = json.MarshalIndent(v, "", " ")
- result = append(result, '\n')
- } else {
- result, err = json.Marshal(v)
- }
- if err != nil {
- return err
- }
- ctx.Response.Header.Add("Content-Encoding", "gzip")
-
- // Unescape HTML if needed.
- if j.UnEscapeHTML {
- result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
- result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
- result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
- }
- w := gzip.NewWriter(ctx.Response.BodyWriter())
- // JSON marshaled fine, write out the result.
- j.Head.Write(ctx)
- if len(j.Prefix) > 0 {
- w.Write(j.Prefix)
- }
- w.Write(result)
- w.Close()
- return nil
-}
-
-func (j JSON) renderStreamingJSON(ctx *fasthttp.RequestCtx, v interface{}) error {
- j.Head.Write(ctx)
- w := ctx.Response.BodyWriter()
- if len(j.Prefix) > 0 {
- w.Write(j.Prefix)
- }
- return json.NewEncoder(w).Encode(v)
-}
-
-func (j JSON) renderStreamingJSONGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
- ctx.Response.Header.Add("Content-Encoding", "gzip")
- j.Head.Write(ctx)
- w := gzip.NewWriter(ctx.Response.BodyWriter())
- if len(j.Prefix) > 0 {
- w.Write(j.Prefix)
- }
- w.Close()
- return json.NewEncoder(w).Encode(v)
-}
-
-// Render a JSONP response.
-func (j JSONP) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
- var result []byte
- var err error
-
- if j.Indent {
- result, err = json.MarshalIndent(v, "", " ")
- } else {
- result, err = json.Marshal(v)
- }
- if err != nil {
- return err
- }
- w := ctx.Response.BodyWriter()
-
- // JSON marshaled fine, write out the result.
- j.Head.Write(ctx)
- w.Write([]byte(j.Callback + "("))
- w.Write(result)
- w.Write([]byte(");"))
-
- // If indenting, append a new line.
- if j.Indent {
- w.Write([]byte("\n"))
- }
- return nil
-}
-
-// RenderGzip a JSONP response using gzip compression.
-func (j JSONP) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
- var result []byte
- var err error
-
- if j.Indent {
- result, err = json.MarshalIndent(v, "", " ")
- } else {
- result, err = json.Marshal(v)
- }
- if err != nil {
- return err
- }
- w := gzip.NewWriter(ctx.Response.BodyWriter())
-
- ctx.Response.Header.Add("Content-Encoding", "gzip")
- // JSON marshaled fine, write out the result.
- j.Head.Write(ctx)
- w.Write([]byte(j.Callback + "("))
- w.Write(result)
- w.Write([]byte(");"))
-
- // If indenting, append a new line.
- if j.Indent {
- w.Write([]byte("\n"))
- }
- w.Close()
- return nil
-}
-
-// Render a text response.
-func (t Text) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
- c := string(ctx.Request.Header.Peek(ContentType))
- if c != "" {
- t.Head.ContentType = c
- }
- w := ctx.Response.BodyWriter()
- t.Head.Write(ctx)
- w.Write([]byte(v.(string)))
- return nil
-}
-
-// RenderGzip a Text response using gzip compression.
-func (t Text) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
- c := string(ctx.Request.Header.Peek(ContentType))
- if c != "" {
- t.Head.ContentType = c
- }
- ctx.Response.Header.Add("Content-Encoding", "gzip")
- t.Head.Write(ctx)
- fasthttp.WriteGzip(ctx.Response.BodyWriter(), []byte(v.(string)))
-
- return nil
-}
-
-// Render an XML response.
-func (x XML) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
- var result []byte
- var err error
-
- if x.Indent {
- result, err = xml.MarshalIndent(v, "", " ")
- result = append(result, '\n')
- } else {
- result, err = xml.Marshal(v)
- }
- if err != nil {
- return err
- }
-
- // XML marshaled fine, write out the result.
- x.Head.Write(ctx)
- w := ctx.Response.BodyWriter()
- if len(x.Prefix) > 0 {
- w.Write(x.Prefix)
- }
- w.Write(result)
- return nil
-}
-
-// RenderGzip an XML response using gzip compression.
-func (x XML) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
- var result []byte
- var err error
-
- if x.Indent {
- result, err = xml.MarshalIndent(v, "", " ")
- result = append(result, '\n')
- } else {
- result, err = xml.Marshal(v)
- }
- if err != nil {
- return err
- }
- ctx.Response.Header.Add("Content-Encoding", "gzip")
- // XML marshaled fine, write out the result.
- x.Head.Write(ctx)
- w := gzip.NewWriter(ctx.Response.BodyWriter())
- if len(x.Prefix) > 0 {
- w.Write(x.Prefix)
- }
- w.Write(result)
- w.Close()
- return nil
-}
diff --git a/render/rest/render.go b/render/rest/render.go
deleted file mode 100644
index 9037574d..00000000
--- a/render/rest/render.go
+++ /dev/null
@@ -1,173 +0,0 @@
-// Package rest is an edited file of https://github.com/unrolled/render
-package rest
-
-import (
- "github.com/kataras/iris/config"
- "github.com/kataras/iris/utils"
- "github.com/microcosm-cc/bluemonday"
- "github.com/russross/blackfriday"
- "github.com/valyala/fasthttp"
-)
-
-const (
- // ContentBinary header value for binary data.
- ContentBinary = "application/octet-stream"
- // ContentJSON header value for JSON data.
- ContentJSON = "application/json"
- // ContentJSONP header value for JSONP data.
- ContentJSONP = "application/javascript"
- // ContentLength header constant.
- ContentLength = "Content-Length"
- // ContentText header value for Text data.
- ContentText = "text/plain"
- // ContentType header constant.
- ContentType = "Content-Type"
- // ContentXML header value for XML data.
- ContentXML = "text/xml"
-)
-
-// bufPool represents a reusable buffer pool for executing templates into.
-var bufPool *utils.BufferPool
-
-// Render is a service that provides functions for easily writing JSON, XML,
-// binary data, and HTML templates out to a HTTP Response.
-type Render struct {
- // Customize Secure with an Options struct.
- Config config.Rest
- CompiledCharset string
-}
-
-// New constructs a new Render instance with the supplied configs.
-func New(cfg ...config.Rest) *Render {
- if bufPool == nil {
- bufPool = utils.NewBufferPool(64)
- }
-
- c := config.DefaultRest().Merge(cfg)
-
- r := &Render{
- Config: c,
- }
-
- r.prepareConfig()
-
- return r
-}
-
-func (r *Render) prepareConfig() {
- // Fill in the defaults if need be.
- if len(r.Config.Charset) == 0 {
- r.Config.Charset = config.Charset
- }
- r.CompiledCharset = "; charset=" + r.Config.Charset
-}
-
-// Render is the generic function called by XML, JSON, Data, HTML, and can be called by custom implementations.
-func (r *Render) Render(ctx *fasthttp.RequestCtx, e Engine, data interface{}) error {
- var err error
- if r.Config.Gzip {
- err = e.RenderGzip(ctx, data)
- } else {
- err = e.Render(ctx, data)
- }
-
- if err != nil && !r.Config.DisableHTTPErrorRendering {
- ctx.Response.SetBodyString(err.Error())
- ctx.Response.SetStatusCode(fasthttp.StatusInternalServerError)
- }
- return err
-}
-
-// Data writes out the raw bytes as binary data.
-func (r *Render) Data(ctx *fasthttp.RequestCtx, status int, v []byte) error {
- head := Head{
- ContentType: ContentBinary,
- Status: status,
- }
-
- d := Data{
- Head: head,
- }
-
- return r.Render(ctx, d, v)
-}
-
-// JSON marshals the given interface object and writes the JSON response.
-func (r *Render) JSON(ctx *fasthttp.RequestCtx, status int, v interface{}) error {
- head := Head{
- ContentType: ContentJSON + r.CompiledCharset,
- Status: status,
- }
-
- j := JSON{
- Head: head,
- Indent: r.Config.IndentJSON,
- Prefix: r.Config.PrefixJSON,
- UnEscapeHTML: r.Config.UnEscapeHTML,
- StreamingJSON: r.Config.StreamingJSON,
- }
-
- return r.Render(ctx, j, v)
-}
-
-// JSONP marshals the given interface object and writes the JSON response.
-func (r *Render) JSONP(ctx *fasthttp.RequestCtx, status int, callback string, v interface{}) error {
- head := Head{
- ContentType: ContentJSONP + r.CompiledCharset,
- Status: status,
- }
-
- j := JSONP{
- Head: head,
- Indent: r.Config.IndentJSON,
- Callback: callback,
- }
-
- return r.Render(ctx, j, v)
-}
-
-// Text writes out a string as plain text.
-func (r *Render) Text(ctx *fasthttp.RequestCtx, status int, v string) error {
- head := Head{
- ContentType: ContentText + r.CompiledCharset,
- Status: status,
- }
-
- t := Text{
- Head: head,
- }
-
- return r.Render(ctx, t, v)
-}
-
-// XML marshals the given interface object and writes the XML response.
-func (r *Render) XML(ctx *fasthttp.RequestCtx, status int, v interface{}) error {
- head := Head{
- ContentType: ContentXML + r.CompiledCharset,
- Status: status,
- }
-
- x := XML{
- Head: head,
- Indent: r.Config.IndentXML,
- Prefix: r.Config.PrefixXML,
- }
-
- return r.Render(ctx, x, v)
-}
-
-// Markdown parses and returns the converted html from a markdown []byte
-// accepts two parameters
-// first is the http status code
-// second is the markdown string
-//
-// Note that: Works different than the other rest's functions.
-func (r *Render) Markdown(markdownBytes []byte) string {
- buf := blackfriday.MarkdownCommon(markdownBytes)
- if r.Config.MarkdownSanitize {
- buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
- }
-
- return string(buf)
-
-}
diff --git a/render/template/README.md b/render/template/README.md
deleted file mode 100644
index 4fad6e8e..00000000
--- a/render/template/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Folder Information
-
-This folder contains the template support for Iris. The folder name is singular (template) so the `/template/engine`, because you can use **ONLY ONE** at the same time.
-
-
-## How to use
-
-**Refer to the Book**
diff --git a/render/template/engine/amber/amber.go b/render/template/engine/amber/amber.go
deleted file mode 100644
index dea8033e..00000000
--- a/render/template/engine/amber/amber.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package amber
-
-import (
- "html/template"
-
- "fmt"
- "io"
- "path/filepath"
- "sync"
-
- "github.com/eknkc/amber"
- "github.com/kataras/iris/config"
-)
-
-// Engine the amber template engine
-type Engine struct {
- Config *config.Template
- templateCache map[string]*template.Template
- mu sync.Mutex
-}
-
-// New creates and returns a new amber engine
-func New(cfg config.Template) *Engine {
- return &Engine{Config: &cfg}
-}
-
-// BuildTemplates builds the amber templates
-func (e *Engine) BuildTemplates() error {
- opt := amber.DirOptions{}
- opt.Recursive = true
- if e.Config.Extensions == nil || len(e.Config.Extensions) == 0 {
- e.Config.Extensions = []string{".html"}
- }
-
- // prepare the global amber funcs
- funcs := template.FuncMap{}
- for k, v := range amber.FuncMap { // add the amber's default funcs
- funcs[k] = v
- }
- if e.Config.Amber.Funcs != nil { // add the config's funcs
- for k, v := range e.Config.Amber.Funcs {
- funcs[k] = v
- }
- }
-
- amber.FuncMap = funcs //set the funcs
-
- opt.Ext = e.Config.Extensions[0]
- templates, err := amber.CompileDir(e.Config.Directory, opt, amber.DefaultOptions) // this returns the map with stripped extension, we want extension so we copy the map
- if err == nil {
- e.templateCache = make(map[string]*template.Template)
- for k, v := range templates {
- name := filepath.ToSlash(k + opt.Ext)
- e.templateCache[name] = v
- delete(templates, k)
- }
-
- }
- return err
-
-}
-func (e *Engine) fromCache(relativeName string) *template.Template {
- e.mu.Lock()
- tmpl, ok := e.templateCache[relativeName]
- if ok {
- e.mu.Unlock()
- return tmpl
- }
- e.mu.Unlock()
- return nil
-}
-
-// ExecuteWriter executes a templates and write its results to the out writer
-func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
- if tmpl := e.fromCache(name); tmpl != nil {
- return tmpl.ExecuteTemplate(out, name, binding)
- }
-
- return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory)
-}
diff --git a/render/template/engine/handlebars/handlebars.go b/render/template/engine/handlebars/handlebars.go
deleted file mode 100644
index e1ec963d..00000000
--- a/render/template/engine/handlebars/handlebars.go
+++ /dev/null
@@ -1,162 +0,0 @@
-// Package handlebars the HandlebarsEngine's functionality
-package handlebars
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "sync"
-
- "github.com/aymerick/raymond"
- "github.com/kataras/iris/config"
-)
-
-type (
- // Engine the Handlebars engine
- Engine struct {
- Config *config.Template
- templateCache map[string]*raymond.Template
- mu sync.Mutex
- }
-)
-
-// New creates and returns the Handlebars template engine
-func New(c config.Template) *Engine {
- s := &Engine{Config: &c, templateCache: make(map[string]*raymond.Template, 0)}
- return s
-}
-
-// BuildTemplates builds the handlebars templates
-func (e *Engine) BuildTemplates() error {
- if e.Config.Extensions == nil || len(e.Config.Extensions) == 0 {
- e.Config.Extensions = []string{".html"}
- }
-
- // register the global helpers
- if e.Config.Handlebars.Helpers != nil {
- raymond.RegisterHelpers(e.Config.Handlebars.Helpers)
- }
-
- // the render works like {{ render "myfile.html" theContext.PartialContext}}
- // instead of the html/template engine which works like {{ render "myfile.html"}} and accepts the parent binding, with handlebars we can't do that because of lack of runtime helpers (dublicate error)
- raymond.RegisterHelper("render", func(partial string, binding interface{}) raymond.SafeString {
- contents, err := e.executeTemplateBuf(partial, binding)
- if err != nil {
- return raymond.SafeString("Template with name: " + partial + " couldn't not be found.")
- }
- return raymond.SafeString(contents)
- })
-
- var templateErr error
-
- dir := e.Config.Directory
- filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
- if info == nil || info.IsDir() {
- return nil
- }
-
- rel, err := filepath.Rel(dir, path)
- if err != nil {
- return err
- }
-
- ext := ""
- if strings.Index(rel, ".") != -1 {
- ext = filepath.Ext(rel)
- }
-
- for _, extension := range e.Config.Extensions {
- if ext == extension {
-
- buf, err := ioutil.ReadFile(path)
- contents := string(buf)
-
- if err != nil {
- templateErr = err
- break
- }
-
- name := filepath.ToSlash(rel)
-
- tmpl, err := raymond.Parse(contents)
- if err != nil {
- templateErr = err
- continue
- }
- e.mu.Lock()
- e.templateCache[name] = tmpl
- e.mu.Unlock()
-
- break
- }
- }
- return nil
- })
-
- return templateErr
-
-}
-func (e *Engine) fromCache(relativeName string) *raymond.Template {
- e.mu.Lock()
- tmpl, ok := e.templateCache[relativeName]
- if ok {
- e.mu.Unlock()
- return tmpl
- }
- e.mu.Unlock()
- return nil
-}
-
-func (e *Engine) executeTemplateBuf(name string, binding interface{}) (string, error) {
- if tmpl := e.fromCache(name); tmpl != nil {
- return tmpl.Exec(binding)
- }
- return "", nil
-}
-
-// ExecuteWriter executes a templates and write its results to the out writer
-func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
-
- isLayout := false
-
- renderFilename := name
- if layout != "" {
- isLayout = true
- renderFilename = layout // the render becomes the layout, and the name is the partial.
- }
-
- if tmpl := e.fromCache(renderFilename); tmpl != nil {
- if isLayout {
- var context map[string]interface{}
- if m, is := binding.(map[string]interface{}); is { //handlebars accepts maps,
- context = m
- } else {
- return fmt.Errorf("Please provide a map[string]interface{} type as the binding instead of the %#v", binding)
- }
-
- contents, err := e.executeTemplateBuf(name, binding)
- if err != nil {
- return err
- }
- if context == nil {
- context = make(map[string]interface{}, 1)
- }
- // I'm implemented the {{ yield }} as with the rest of template engines, so this is not inneed for iris, but the user can do that manually if want
- // there is no performanrce different: raymond.RegisterPartialTemplate(name, tmpl)
- context["yield"] = raymond.SafeString(contents)
- }
-
- res, err := tmpl.Exec(binding)
-
- if err != nil {
- return err
- }
- _, err = fmt.Fprint(out, res)
- return err
- }
-
- return fmt.Errorf("[IRIS TEMPLATES] Template with name %s[original name = %s] doesn't exists in the dir %s", renderFilename, name, e.Config.Directory)
-}
diff --git a/render/template/engine/html/html.go b/render/template/engine/html/html.go
deleted file mode 100644
index f1a4448f..00000000
--- a/render/template/engine/html/html.go
+++ /dev/null
@@ -1,245 +0,0 @@
-package html
-
-import (
- "bytes"
- "fmt"
- "html/template"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/Joker/jade"
- "github.com/kataras/iris/config"
-)
-
-type (
- // Engine the html/template engine & Jade
- Engine struct {
- Config *config.Template
- Templates *template.Template
- }
-)
-
-var emptyFuncs = template.FuncMap{
- "yield": func() (string, error) {
- return "", fmt.Errorf("yield was called, yet no layout defined")
- },
- "partial": func() (string, error) {
- return "", fmt.Errorf("block was called, yet no layout defined")
- },
- "current": func() (string, error) {
- return "", nil
- }, "render": func() (string, error) {
- return "", nil
- },
-}
-
-// New creates and returns the HTMLTemplate template engine
-func New(c config.Template) *Engine {
- s := &Engine{Config: &c}
- return s
-}
-
-// BuildTemplates builds the templates
-func (s *Engine) BuildTemplates() error {
-
- if s.Config.Asset == nil || s.Config.AssetNames == nil {
- return s.buildFromDir()
-
- }
- return s.buildFromAsset()
-
-}
-
-func (s *Engine) buildFromDir() error {
- if s.Config.Directory == "" {
- return nil //we don't return fill error here(yet)
- }
-
- var templateErr error
- /*var minifier *minify.M
- if s.Config.Minify {
- minifier = minify.New()
- minifier.AddFunc("text/html", htmlMinifier.Minify)
- } // Note: minifier has bugs, I complety remove this from Iris.
- */
- dir := s.Config.Directory
- s.Templates = template.New(dir)
- s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
- // Walk the supplied directory and compile any files that match our extension list.
- filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
- if info == nil || info.IsDir() {
- return nil
- }
-
- rel, err := filepath.Rel(dir, path)
- if err != nil {
- return err
- }
-
- ext := ""
- if strings.Index(rel, ".") != -1 {
- ext = filepath.Ext(rel)
- }
-
- for _, extension := range s.Config.Extensions {
- if ext == extension {
-
- buf, err := ioutil.ReadFile(path)
- contents := string(buf)
- /*if s.Config.Minify {
- buf, err = minifier.Bytes("text/html", buf)
- }*/
-
- if err != nil {
- templateErr = err
- break
- }
-
- name := filepath.ToSlash(rel)
- tmpl := s.Templates.New(name)
-
- if s.Config.Engine == config.JadeEngine {
- contents, err = jade.Parse(name, contents)
- }
-
- if err != nil {
- templateErr = err
- break
- }
-
- // Add our funcmaps.
- if s.Config.HTMLTemplate.Funcs != nil {
- tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
- }
-
- tmpl.Funcs(emptyFuncs).Parse(contents)
- break
- }
- }
- return nil
- })
-
- return templateErr
-}
-
-func (s *Engine) buildFromAsset() error {
- var templateErr error
- dir := s.Config.Directory
- s.Templates = template.New(dir)
- s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
-
- for _, path := range s.Config.AssetNames() {
- if !strings.HasPrefix(path, dir) {
- continue
- }
-
- rel, err := filepath.Rel(dir, path)
- if err != nil {
- panic(err)
- }
-
- ext := ""
- if strings.Index(rel, ".") != -1 {
- ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
- }
-
- for _, extension := range s.Config.Extensions {
- if ext == extension {
-
- buf, err := s.Config.Asset(path)
- if err != nil {
- panic(err)
- }
- contents := string(buf)
- name := filepath.ToSlash(rel)
- tmpl := s.Templates.New(name)
-
- if s.Config.Engine == config.JadeEngine {
- contents, err = jade.Parse(name, contents)
- }
- if err != nil {
- return err
- }
-
- // Add our funcmaps.
- if s.Config.HTMLTemplate.Funcs != nil {
- tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
- }
-
- tmpl.Funcs(emptyFuncs).Parse(contents)
- break
- }
- }
- }
- return templateErr
-}
-
-func (s *Engine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) {
- buf := new(bytes.Buffer)
- err := s.Templates.ExecuteTemplate(buf, name, binding)
-
- return buf, err
-}
-
-func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
- funcs := template.FuncMap{
- "yield": func() (template.HTML, error) {
- buf, err := s.executeTemplateBuf(name, binding)
- // Return safe HTML here since we are rendering our own template.
- return template.HTML(buf.String()), err
- },
- "current": func() (string, error) {
- return name, nil
- },
- "partial": func(partialName string) (template.HTML, error) {
- fullPartialName := fmt.Sprintf("%s-%s", partialName, name)
- if s.Config.HTMLTemplate.RequirePartials || s.Templates.Lookup(fullPartialName) != nil {
- buf, err := s.executeTemplateBuf(fullPartialName, binding)
- return template.HTML(buf.String()), err
- }
- return "", nil
- },
- "render": func(fullPartialName string) (template.HTML, error) {
- buf, err := s.executeTemplateBuf(fullPartialName, binding)
- return template.HTML(buf.String()), err
- },
- }
- _userLayoutFuncs := s.Config.HTMLTemplate.LayoutFuncs
- if _userLayoutFuncs != nil && len(_userLayoutFuncs) > 0 {
- for k, v := range _userLayoutFuncs {
- funcs[k] = v
- }
- }
- if tpl := s.Templates.Lookup(name); tpl != nil {
- tpl.Funcs(funcs)
- }
-}
-
-func (s *Engine) runtimeFuncsFor(name string, binding interface{}) {
- funcs := template.FuncMap{
- "render": func(fullPartialName string) (template.HTML, error) {
- buf, err := s.executeTemplateBuf(fullPartialName, binding)
- return template.HTML(buf.String()), err
- },
- }
-
- if tpl := s.Templates.Lookup(name); tpl != nil {
- tpl.Funcs(funcs)
- }
-}
-
-// ExecuteWriter executes a templates and write its results to the out writer
-func (s *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
- if layout != "" {
- s.layoutFuncsFor(name, binding)
- name = layout
-
- } else {
- s.runtimeFuncsFor(name, binding)
- }
-
- return s.Templates.ExecuteTemplate(out, name, binding)
-}
diff --git a/render/template/engine/jade/jade.go b/render/template/engine/jade/jade.go
deleted file mode 100644
index 67c8f4eb..00000000
--- a/render/template/engine/jade/jade.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Package jade the JadeEngine's functionality lives inside ../html now
-package jade
-
-import (
- "github.com/kataras/iris/config"
- "github.com/kataras/iris/render/template/engine/html"
-)
-
-// New creates and returns the HTMLTemplate template engine
-func New(c config.Template) *html.Engine {
- // copy the Jade to the HTMLTemplate
- c.HTMLTemplate = config.HTMLTemplate(c.Jade)
- s := &html.Engine{Config: &c}
- return s
-}
diff --git a/render/template/engine/markdown/markdown.go b/render/template/engine/markdown/markdown.go
deleted file mode 100644
index 5726d43b..00000000
--- a/render/template/engine/markdown/markdown.go
+++ /dev/null
@@ -1,155 +0,0 @@
-package markdown
-
-import (
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "sync"
-
- "fmt"
-
- "github.com/kataras/iris/config"
- "github.com/microcosm-cc/bluemonday"
- "github.com/russross/blackfriday"
-)
-
-// Supports RAW markdown only, no context binding or layout, to use dynamic markdown with other template engine use the context.Markdown/MarkdownString
-
-type (
- // Engine the jade engine
- Engine struct {
- Config *config.Template
- templateCache map[string][]byte
- mu sync.Mutex
- }
-)
-
-// New creates and returns a Pongo template engine
-func New(c config.Template) *Engine {
- return &Engine{Config: &c, templateCache: make(map[string][]byte)}
-}
-
-// BuildTemplates builds the templates
-func (e *Engine) BuildTemplates() error {
- if e.Config.Asset == nil || e.Config.AssetNames == nil {
- return e.buildFromDir()
- }
- return e.buildFromAsset()
-
-}
-
-func (e *Engine) buildFromDir() (templateErr error) {
- if e.Config.Directory == "" {
- return nil //we don't return fill error here(yet)
- }
- dir := e.Config.Directory
-
- // Walk the supplied directory and compile any files that match our extension list.
- filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
-
- if info == nil || info.IsDir() {
- return nil
- }
-
- rel, err := filepath.Rel(dir, path)
- if err != nil {
- return err
- }
-
- ext := ""
- if strings.Index(rel, ".") != -1 {
- ext = filepath.Ext(rel)
- }
-
- for _, extension := range e.Config.Extensions {
- if ext == extension {
- buf, err := ioutil.ReadFile(path)
- if err != nil {
- templateErr = err
- break
- }
-
- buf = blackfriday.MarkdownCommon(buf)
- if e.Config.Markdown.Sanitize {
- buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
- }
-
- if err != nil {
- templateErr = err
- break
- }
- name := filepath.ToSlash(rel)
- e.templateCache[name] = buf
- break
- }
- }
- return nil
- })
-
- return nil
-}
-
-func (e *Engine) buildFromAsset() error {
- var templateErr error
- dir := e.Config.Directory
- for _, path := range e.Config.AssetNames() {
- if !strings.HasPrefix(path, dir) {
- continue
- }
-
- rel, err := filepath.Rel(dir, path)
- if err != nil {
- panic(err)
- }
-
- ext := ""
- if strings.Index(rel, ".") != -1 {
- ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
- }
-
- for _, extension := range e.Config.Extensions {
- if ext == extension {
-
- buf, err := e.Config.Asset(path)
- if err != nil {
- templateErr = err
- break
- }
- b := blackfriday.MarkdownCommon(buf)
- if e.Config.Markdown.Sanitize {
- b = bluemonday.UGCPolicy().SanitizeBytes(b)
- }
- name := filepath.ToSlash(rel)
- e.templateCache[name] = b
- break
- }
- }
- }
- return templateErr
-}
-
-func (e *Engine) fromCache(relativeName string) []byte {
- e.mu.Lock()
-
- tmpl, ok := e.templateCache[relativeName]
-
- if ok {
- e.mu.Unlock() // defer is slow
- return tmpl
- }
- e.mu.Unlock() // defer is slow
- return nil
-}
-
-// ExecuteWriter executes a templates and write its results to the out writer
-// layout here is useless
-func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
- if tmpl := e.fromCache(name); tmpl != nil {
- _, err := out.Write(tmpl)
- return err
- }
-
- return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory)
-}
diff --git a/render/template/engine/pongo/pongo.go b/render/template/engine/pongo/pongo.go
deleted file mode 100644
index 113d14ed..00000000
--- a/render/template/engine/pongo/pongo.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package pongo
-
-import (
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "sync"
-
- "fmt"
-
- "github.com/flosch/pongo2"
- "github.com/kataras/iris/config"
-)
-
-type (
- // Engine the pongo2 engine
- Engine struct {
- Config *config.Template
- templateCache map[string]*pongo2.Template
- mu sync.Mutex
- }
-)
-
-// New creates and returns a Pongo template engine
-func New(c config.Template) *Engine {
- return &Engine{Config: &c, templateCache: make(map[string]*pongo2.Template)}
-}
-
-// BuildTemplates builds the templates
-func (p *Engine) BuildTemplates() error {
- // Add our filters. first
- for k, v := range p.Config.Pongo.Filters {
- pongo2.RegisterFilter(k, v)
- }
- if p.Config.Asset == nil || p.Config.AssetNames == nil {
- return p.buildFromDir()
-
- }
- return p.buildFromAsset()
-
-}
-
-func (p *Engine) buildFromDir() (templateErr error) {
- if p.Config.Directory == "" {
- return nil //we don't return fill error here(yet)
- }
- dir := p.Config.Directory
-
- fsLoader, err := pongo2.NewLocalFileSystemLoader(dir) // I see that this doesn't read the content if already parsed, so do it manually via filepath.Walk
- if err != nil {
- return err
- }
-
- set := pongo2.NewSet("", fsLoader)
- set.Globals = getPongoContext(p.Config.Pongo.Globals)
- // Walk the supplied directory and compile any files that match our extension list.
- filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
- // Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html".
- // These dirs should be excluded as they are not valid golang templates, but files under
- // them should be treat as normal.
- // If is a dir, return immediately (dir is not a valid golang template).
- if info == nil || info.IsDir() {
- return nil
- }
-
- rel, err := filepath.Rel(dir, path)
- if err != nil {
- return err
- }
-
- ext := ""
- if strings.Index(rel, ".") != -1 {
- ext = filepath.Ext(rel)
- }
-
- for _, extension := range p.Config.Extensions {
- if ext == extension {
- buf, err := ioutil.ReadFile(path)
- if err != nil {
- templateErr = err
- break
- }
- if err != nil {
- templateErr = err
- break
- }
- name := filepath.ToSlash(rel)
- p.templateCache[name], templateErr = set.FromString(string(buf))
-
- if templateErr != nil {
- return templateErr
- }
- break
- }
- }
- return nil
- })
-
- return
-}
-
-func (p *Engine) buildFromAsset() error {
- var templateErr error
- dir := p.Config.Directory
- fsLoader, err := pongo2.NewLocalFileSystemLoader(dir)
- if err != nil {
- return err
- }
- set := pongo2.NewSet("", fsLoader)
- set.Globals = getPongoContext(p.Config.Pongo.Globals)
- for _, path := range p.Config.AssetNames() {
- if !strings.HasPrefix(path, dir) {
- continue
- }
-
- rel, err := filepath.Rel(dir, path)
- if err != nil {
- panic(err)
- }
-
- ext := ""
- if strings.Index(rel, ".") != -1 {
- ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
- }
-
- for _, extension := range p.Config.Extensions {
- if ext == extension {
-
- buf, err := p.Config.Asset(path)
- if err != nil {
- templateErr = err
- break
- }
- name := filepath.ToSlash(rel)
- p.templateCache[name], err = set.FromString(string(buf))
- if err != nil {
- templateErr = err
- break
- }
- break
- }
- }
- }
- return templateErr
-}
-
-// getPongoContext returns the pongo2.Context from map[string]interface{} or from pongo2.Context, used internaly
-func getPongoContext(templateData interface{}) pongo2.Context {
- if templateData == nil {
- return nil
- }
-
- if contextData, isPongoContext := templateData.(pongo2.Context); isPongoContext {
- return contextData
- }
-
- return templateData.(map[string]interface{})
-}
-
-func (p *Engine) fromCache(relativeName string) *pongo2.Template {
- p.mu.Lock() // defer is slow
-
- tmpl, ok := p.templateCache[relativeName]
-
- if ok {
- p.mu.Unlock()
- return tmpl
- }
- p.mu.Unlock()
- return nil
-}
-
-// ExecuteWriter executes a templates and write its results to the out writer
-// layout here is useless
-func (p *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
- if tmpl := p.fromCache(name); tmpl != nil {
- return tmpl.ExecuteWriter(getPongoContext(binding), out)
- }
-
- return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, p.Config.Directory)
-}
diff --git a/render/template/template.go b/render/template/template.go
deleted file mode 100644
index 644128e1..00000000
--- a/render/template/template.go
+++ /dev/null
@@ -1,231 +0,0 @@
-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/handlebars"
- "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 (
- // Engine the interface that all template engines must inheritance
- Engine interface {
- // BuildTemplates builds the templates for a directory
- BuildTemplates() error
- // ExecuteWriter finds and execute a template and write its result to the out writer
- ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error
- }
-
- // Template the internal configs for the common configs for the template engines
- Template struct {
- // 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
-
- buffer *utils.BufferPool // this is used only for RenderString
- gzipWriterPool sync.Pool
- }
-)
-
-// sharedFuncs the funcs should be exists in all supported view template engines
-var sharedFuncs map[string]interface{}
-
-// we do this because we don't want to override the user's funcs
-func setSharedFuncs(source map[string]interface{}, target map[string]interface{}) {
- if source == nil {
- return
- }
-
- if target == nil {
- target = make(map[string]interface{}, len(source))
- }
-
- for k, v := range source {
- if target[k] == nil {
- target[k] = v
- }
- }
-}
-
-// New creates and returns a Template instance which keeps the Template Engine and helps with render
-func New(c config.Template) *Template {
- defer func() {
- sharedFuncs = nil
- }()
-
- var e Engine
- // [ENGINE-2]
- switch c.Engine {
- case config.HTMLEngine:
- setSharedFuncs(sharedFuncs, c.HTMLTemplate.Funcs)
- e = html.New(c) // HTMLTemplate
- case config.JadeEngine:
- setSharedFuncs(sharedFuncs, c.Jade.Funcs)
- e = jade.New(c) // Jade
- case config.PongoEngine:
- setSharedFuncs(sharedFuncs, c.Pongo.Globals)
- e = pongo.New(c) // Pongo2
- case config.MarkdownEngine:
- e = markdown.New(c) // Markdown
- case config.AmberEngine:
- setSharedFuncs(sharedFuncs, c.Amber.Funcs)
- e = amber.New(c) // Amber
- case config.HandlebarsEngine:
- setSharedFuncs(sharedFuncs, c.Handlebars.Helpers)
- e = handlebars.New(c)
- 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
-
-}
-
-// RegisterSharedFunc registers a functionality that should be inherited from all supported template engines
-func RegisterSharedFunc(name string, fn interface{}) {
- if sharedFuncs == nil {
- sharedFuncs = make(map[string]interface{})
- }
- sharedFuncs[name] = fn
-}
-
-// RegisterSharedFuncs registers functionalities that should be inherited from all supported template engines
-func RegisterSharedFuncs(theFuncs map[string]interface{}) {
- if sharedFuncs == nil || len(sharedFuncs) == 0 {
- sharedFuncs = theFuncs
- return
- }
- for k, v := range theFuncs {
- sharedFuncs[k] = v
- }
-
-}
-
-func (t *Template) getLayout(ctx context.IContext, passedLayouts []string) string {
-
- _layout := ""
- if len(passedLayouts) > 0 {
- _layout = passedLayouts[0]
- } else if ctx != nil {
- if layoutFromCtx := ctx.GetString(config.TemplateLayoutContextKey); layoutFromCtx != "" {
- _layout = layoutFromCtx
- }
- }
- if _layout == "" && _layout != config.NoLayout {
- if t.Layout != config.NoLayout { // the config's Layout is disabled if "" , config.NoLayout should be passed only on ctx.Render but for any case check if user set it as config.NoLayout also
- _layout = t.Layout
- }
- }
-
- return _layout
-}
-
-// Render renders a template using the context's writer
-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.HTML(403, " Iris
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
- }
- }
-
- _layout := t.getLayout(ctx, 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
-}
-
-// RenderString executes a template and returns its contents result (string)
-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
- }
- }
-
- _layout := t.getLayout(nil, 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
-}
diff --git a/template.go b/template.go
new file mode 100644
index 00000000..9dbb4379
--- /dev/null
+++ b/template.go
@@ -0,0 +1,277 @@
+package iris
+
+import (
+ "compress/gzip"
+ "io"
+
+ "path/filepath"
+ "sync"
+
+ "github.com/iris-contrib/errors"
+ "github.com/kataras/iris/config"
+ "github.com/kataras/iris/utils"
+)
+
+var (
+ builtinFuncs = [...]string{"url", "urlpath"}
+ // these are used inside load all in order when no directory and no extension is provided by the dev
+
+ // DefaultTemplateDirectory the default directory if empty setted
+ DefaultTemplateDirectory = "." + utils.PathSeparator + "templates"
+ // DefaultTemplateExtension the default file extension if empty setted
+ DefaultTemplateExtension = ".html"
+
+ // for conversional, exists on config because are shared:
+
+ // NoLayout pass it to the layout option on the context.Render to disable layout for this execution
+ NoLayout = config.NoLayout
+)
+
+type (
+ // TemplateEngine the interface that all template engines must implement
+ TemplateEngine interface {
+ // LoadDirectory builds the templates, usually by directory and extension but these are engine's decisions
+ LoadDirectory(directory string, extension string) error
+ // LoadAssets loads the templates by binary
+ // assetFn is a func which returns bytes, use it to load the templates by binary
+ // namesFn returns the template filenames
+ LoadAssets(virtualDirectory string, virtualExtension string, assetFn func(name string) ([]byte, error), namesFn func() []string) error
+
+ // ExecuteWriter finds, execute a template and write its result to the out writer
+ // options are the optional runtime options can be passed by user and catched by the template engine when render
+ // an example of this is the "layout" or "gzip" option
+ ExecuteWriter(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) error
+ }
+
+ // TemplateEngineFuncs is optional interface for the TemplateEngine
+ // used to insert the Iris' standard funcs, see var 'usedFuncs'
+ TemplateEngineFuncs interface {
+ // Funcs should returns the context or the funcs,
+ // this property is used in order to register the iris' helper funcs
+ Funcs() map[string]interface{}
+ }
+)
+
+type (
+ // TemplateFuncs is is a helper type for map[string]interface{}
+ TemplateFuncs map[string]interface{}
+ // RenderOptions is a helper type for the optional runtime options can be passed by user and catched by the template engine when render
+ // an example of this is the "layout" option
+ RenderOptions map[string]interface{}
+)
+
+// IsFree returns true if a function can be inserted to this map
+// return false if this key is already used by Iris
+func (t TemplateFuncs) IsFree(key string) bool {
+ for i := range builtinFuncs {
+ if builtinFuncs[i] == key {
+ return false
+ }
+ }
+ return true
+}
+
+type (
+ // TemplateEngineLocation contains the funcs to set the location for the templates by directory or by binary
+ TemplateEngineLocation struct {
+ directory string
+ extension string
+ assetFn func(name string) ([]byte, error)
+ namesFn func() []string
+ }
+ // TemplateEngineBinaryLocation called after TemplateEngineLocation's Directory, used when files are distrubuted inside the app executable
+ TemplateEngineBinaryLocation struct {
+ location *TemplateEngineLocation
+ }
+)
+
+// Directory sets the directory to load from
+// returns the Binary location which is optional
+func (t *TemplateEngineLocation) Directory(dir string, fileExtension string) TemplateEngineBinaryLocation {
+ t.directory = dir
+ t.extension = fileExtension
+ return TemplateEngineBinaryLocation{location: t}
+}
+
+// Binary sets the asset(s) and asssets names to load from, works with Directory
+func (t *TemplateEngineBinaryLocation) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) {
+ t.location.assetFn = assetFn
+ t.location.namesFn = namesFn
+ // if extension is not static(setted by .Directory)
+ if t.location.extension == "" {
+ if names := namesFn(); len(names) > 0 {
+ t.location.extension = filepath.Ext(names[0]) // we need the extension to get the correct template engine on the Render method
+ }
+ }
+}
+
+func (t *TemplateEngineLocation) isBinary() bool {
+ return t.assetFn != nil && t.namesFn != nil
+}
+
+// TemplateEngineWrapper is the wrapper of a template engine
+type TemplateEngineWrapper struct {
+ TemplateEngine
+ location *TemplateEngineLocation
+ buffer *utils.BufferPool
+ gzipWriterPool sync.Pool
+ reload bool
+ combiledContentType string
+}
+
+var (
+ errMissingDirectoryOrAssets = errors.New("Missing Directory or Assets by binary for the template engine!")
+ errNoTemplateEngineForExt = errors.New("No template engine found to manage '%s' extensions")
+)
+
+func (t *TemplateEngineWrapper) load() error {
+ if t.location.isBinary() {
+ t.LoadAssets(t.location.directory, t.location.extension, t.location.assetFn, t.location.namesFn)
+ } else if t.location.directory != "" {
+ t.LoadDirectory(t.location.directory, t.location.extension)
+ } else {
+ return errMissingDirectoryOrAssets.Return()
+ }
+ return nil
+}
+
+// Execute execute a template and write its result to the context's body
+// options are the optional runtime options can be passed by user and catched by the template engine when render
+// an example of this is the "layout"
+// note that gzip option is an iris dynamic option which exists for all template engines
+func (t *TemplateEngineWrapper) Execute(ctx *Context, filename string, binding interface{}, options ...map[string]interface{}) (err error) {
+ if t == nil {
+ //file extension, but no template engine registered, this caused by context, and TemplateEngines. GetBy
+ return errNoTemplateEngineForExt.Format(filepath.Ext(filename))
+ }
+ if t.reload {
+ if err = t.load(); err != nil {
+ return
+ }
+ }
+
+ // we do all these because we don't want to initialize a new map for each execution...
+ gzipEnabled := false
+ if len(options) > 0 {
+ gzipOpt := options[0]["gzip"] // we only need that, so don't create new map to keep the options.
+ if b, isBool := gzipOpt.(bool); isBool {
+ gzipEnabled = b
+ }
+ }
+
+ ctxLayout := ctx.GetString(config.TemplateLayoutContextKey)
+ if ctxLayout != "" {
+ if len(options) > 0 {
+ options[0]["layout"] = ctxLayout
+ } else {
+ options = []map[string]interface{}{map[string]interface{}{"layout": ctxLayout}}
+ }
+ }
+
+ var out io.Writer
+ if gzipEnabled {
+ ctx.Response.Header.Add("Content-Encoding", "gzip")
+ gzipWriter := t.gzipWriterPool.Get().(*gzip.Writer)
+ gzipWriter.Reset(ctx.Response.BodyWriter())
+ defer gzipWriter.Close()
+ defer t.gzipWriterPool.Put(gzipWriter)
+ out = gzipWriter
+ } else {
+ out = ctx.Response.BodyWriter()
+ }
+ ctx.SetHeader("Content-Type", t.combiledContentType)
+
+ return t.ExecuteWriter(out, filename, binding, options...)
+}
+
+// ExecuteToString executes a template from a specific template engine and returns its contents result as string, it doesn't renders
+func (t *TemplateEngineWrapper) ExecuteToString(filename string, binding interface{}, opt ...map[string]interface{}) (result string, err error) {
+ if t == nil {
+ //file extension, but no template engine registered, this caused by context, and TemplateEngines. GetBy
+ return "", errNoTemplateEngineForExt.Format(filepath.Ext(filename))
+ }
+ if t.reload {
+ if err = t.load(); err != nil {
+ return
+ }
+ }
+
+ out := t.buffer.Get()
+ defer t.buffer.Put(out)
+ err = t.ExecuteWriter(out, filename, binding, opt...)
+ if err == nil {
+ result = out.String()
+ }
+ return
+}
+
+// TemplateEngines is the container and manager of the template engines
+type TemplateEngines struct {
+ engines []*TemplateEngineWrapper
+ helpers map[string]interface{}
+ reload bool
+}
+
+func (t *TemplateEngines) setReload(b bool) {
+ t.reload = b
+}
+
+// GetBy receives a filename, gets its extension and returns the template engine responsible for that file extension
+func (t *TemplateEngines) GetBy(filename string) *TemplateEngineWrapper {
+ extension := filepath.Ext(filename)
+ for i, n := 0, len(t.engines); i < n; i++ {
+ e := t.engines[i]
+
+ if e.location.extension == extension {
+ return e
+ }
+ }
+ return nil
+}
+
+// Add adds but not loads a template engine
+func (t *TemplateEngines) Add(e TemplateEngine) *TemplateEngineLocation {
+
+ location := &TemplateEngineLocation{}
+ // add the iris helper funcs
+ if funcer, ok := e.(TemplateEngineFuncs); ok {
+ if funcer.Funcs() != nil {
+ for k, v := range t.helpers {
+ funcer.Funcs()[k] = v
+ }
+ }
+ }
+
+ tmplEngine := &TemplateEngineWrapper{
+ TemplateEngine: e,
+ location: location,
+ buffer: utils.NewBufferPool(20),
+ gzipWriterPool: sync.Pool{New: func() interface{} {
+ return &gzip.Writer{}
+ }},
+ reload: t.reload,
+ combiledContentType: config.ContentTypeHTML + "; " + config.Charset,
+ }
+
+ t.engines = append(t.engines, tmplEngine)
+ return location
+}
+
+// loadAll loads all templates using all template engines, returns the first error
+// called on iris' initialize
+func (t *TemplateEngines) loadAll() error {
+ for i, n := 0, len(t.engines); i < n; i++ {
+ e := t.engines[i]
+ if e.location.directory == "" {
+ e.location.directory = DefaultTemplateDirectory // the defualt dir ./templates
+ }
+ if e.location.extension == "" {
+ e.location.extension = DefaultTemplateExtension // the default file ext .html
+ }
+
+ if err := e.load(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/websocket/websocket.go b/websocket/websocket.go
index dc7e31b9..71efaf55 100644
--- a/websocket/websocket.go
+++ b/websocket/websocket.go
@@ -1,9 +1,9 @@
package websocket
import (
+ "github.com/iris-contrib/logger"
"github.com/kataras/iris/config"
"github.com/kataras/iris/context"
- "github.com/kataras/iris/logger"
)
// to avoid the import cycle to /kataras/iris. The ws package is used inside iris' station configuration