mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
Update to v4.0.0-alpha.1
This commit is contained in:
parent
5cfe19c793
commit
b99afb2875
20
HISTORY.md
20
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`.
|
**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
|
## 3.0.0-rc.4 -> 3.0.0-pre.release
|
||||||
|
|
||||||
- `context.PostFormValue` -> `context.FormValueString`, old func stays until the next revision
|
- `context.PostFormValue` -> `context.FormValueString`, old func stays until the next revision
|
||||||
|
|
35
README.md
35
README.md
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
[![Travis Widget]][Travis] [![Release Widget]][Release] [![Report Widget]][Report] [![License Widget]][License] [![Chat Widget]][Chat] [![Documentation Widget]][Documentation]
|
[![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{
|
c.JSON(iris.StatusOK, iris.Map{
|
||||||
"Name": "Iris",
|
"Name": "Iris",
|
||||||
"Born": "13 March 2016",
|
"Born": "13 March 2016",
|
||||||
"Stars": 3693,
|
"Stars": 4262,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
iris.Listen(":8080")
|
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
|
$ 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
|
FAQ
|
||||||
------------
|
------------
|
||||||
|
@ -76,6 +76,12 @@ Features
|
||||||
|
|
||||||
| Name | Description | Usage |
|
| 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) |
|
| [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) |
|
| [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) |
|
| [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
|
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).
|
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
|
Versioning
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Current: **v3.0.0**
|
Current: **v4.0.0-alpha.1**
|
||||||
> Iris is an active project
|
> 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
|
Todo
|
||||||
------------
|
------------
|
||||||
> for the next version 'v4'
|
> 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.
|
- [x] 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.
|
- [ ] 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
|
- [ ] Extend the iris control plugin
|
||||||
- [ ] Remove deprecated functions.
|
- [ ] Remove deprecated functions
|
||||||
|
- [ ] Will think more :)
|
||||||
|
|
||||||
> completed for release 'v3'
|
> completed for release 'v3'
|
||||||
|
|
||||||
|
@ -191,7 +194,7 @@ License can be found [here](LICENSE).
|
||||||
[Travis]: http://travis-ci.org/kataras/iris
|
[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 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
|
[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
|
[Release]: https://github.com/kataras/iris/releases
|
||||||
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
|
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
|
||||||
[Chat]: https://kataras.rocket.chat/channel/iris
|
[Chat]: https://kataras.rocket.chat/channel/iris
|
||||||
|
|
|
@ -6,6 +6,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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 expiration duration for INACTIVE file handlers
|
||||||
StaticCacheDuration = 20 * time.Second
|
StaticCacheDuration = 20 * time.Second
|
||||||
// CompressedFileSuffix is the suffix to add to the name of
|
// CompressedFileSuffix is the suffix to add to the name of
|
||||||
|
@ -13,4 +20,14 @@ var (
|
||||||
//
|
//
|
||||||
// Defaults to iris-fasthttp.gz
|
// Defaults to iris-fasthttp.gz
|
||||||
CompressedFileSuffix = "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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import "github.com/imdario/mergo"
|
import (
|
||||||
|
"github.com/imdario/mergo"
|
||||||
|
"github.com/iris-contrib/rest"
|
||||||
|
)
|
||||||
|
|
||||||
// Default values for base Iris conf
|
// Default values for base Iris conf
|
||||||
const (
|
const (
|
||||||
|
@ -78,16 +81,19 @@ type (
|
||||||
// http://debug.yourdomain:PORT/threadcreate
|
// http://debug.yourdomain:PORT/threadcreate
|
||||||
// http://debug.yourdomain:PORT/pprof/block
|
// http://debug.yourdomain:PORT/pprof/block
|
||||||
ProfilePath string
|
ProfilePath string
|
||||||
|
// DisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
|
||||||
// Logger the configuration for the logger
|
// default is false
|
||||||
// Iris logs ONLY SEMANTIC errors and the banner if enabled
|
DisableTemplateEngines bool
|
||||||
Logger Logger
|
// 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 contains the configs for sessions
|
||||||
Sessions Sessions
|
Sessions Sessions
|
||||||
|
|
||||||
// Render contains the configs for template and rest configuration
|
// Rest contains the configs for rest render configuration
|
||||||
Render Render
|
Rest rest.Config
|
||||||
|
|
||||||
// Websocket contains the configs for Websocket's server integration
|
// Websocket contains the configs for Websocket's server integration
|
||||||
Websocket *Websocket
|
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 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
|
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
|
// Default returns the default configuration for the Iris staton
|
||||||
func Default() Iris {
|
func Default() Iris {
|
||||||
return Iris{
|
return Iris{
|
||||||
DisablePathCorrection: DefaultDisablePathCorrection,
|
DisablePathCorrection: DefaultDisablePathCorrection,
|
||||||
DisablePathEscape: DefaultDisablePathEscape,
|
DisablePathEscape: DefaultDisablePathEscape,
|
||||||
DisableBanner: false,
|
DisableBanner: false,
|
||||||
ProfilePath: "",
|
DisableTemplateEngines: false,
|
||||||
Logger: DefaultLogger(),
|
IsDevelopment: false,
|
||||||
Sessions: DefaultSessions(),
|
ProfilePath: "",
|
||||||
Render: DefaultRender(),
|
Sessions: DefaultSessions(),
|
||||||
Websocket: DefaultWebsocket(),
|
Rest: rest.DefaultConfig(),
|
||||||
Tester: DefaultTester(),
|
Websocket: DefaultWebsocket(),
|
||||||
|
Tester: DefaultTester(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,18 +144,3 @@ func (c Iris) MergeSingle(cfg Iris) (config Iris) {
|
||||||
|
|
||||||
return
|
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
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ type Server struct {
|
||||||
//
|
//
|
||||||
// Default buffer size is used if not set.
|
// Default buffer size is used if not set.
|
||||||
WriteBufferSize int
|
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)
|
// 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
|
// 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
|
||||||
|
|
28
context.go
28
context.go
|
@ -10,7 +10,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"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.
|
// RenderWithStatus builds up the response from the specified template and bindings.
|
||||||
// Note: parameter layout has meaning only when using the iris.HTMLTemplate
|
// 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)
|
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)
|
// 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()
|
errCode := ctx.RequestCtx.Response.StatusCode()
|
||||||
if errCode <= 0 {
|
if errCode <= 0 {
|
||||||
errCode = StatusOK
|
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
|
// 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) {
|
func (ctx *Context) MustRender(name string, binding interface{}, options ...map[string]interface{}) {
|
||||||
if err := ctx.Render(name, binding, layout...); err != nil {
|
if err := ctx.Render(name, binding, options...); err != nil {
|
||||||
ctx.Panic()
|
ctx.Panic()
|
||||||
ctx.framework.Logger.Dangerf("MustRender panics for client with IP: %s On template: %s.Trace: %s\n", ctx.RemoteAddr(), name, err)
|
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)
|
// TemplateString accepts a template filename, its context data and returns the result of the parsed template (string)
|
||||||
// if any error returns empty string
|
// if any error returns empty string
|
||||||
func (ctx *Context) TemplateString(name string, binding interface{}, layout ...string) string {
|
func (ctx *Context) TemplateString(name string, binding interface{}, options ...map[string]interface{}) string {
|
||||||
return ctx.framework.TemplateString(name, binding, layout...)
|
return ctx.framework.TemplateString(name, binding, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON marshals the given interface object and writes the JSON response.
|
// 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))
|
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
|
// ServeContent serves content, headers are autoset
|
||||||
// receives three parameters, it's low-level function, instead you can use .ServeFile(string)
|
// receives three parameters, it's low-level function, instead you can use .ServeFile(string)
|
||||||
//
|
//
|
||||||
|
|
|
@ -2,7 +2,6 @@ package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"html/template"
|
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -46,17 +45,16 @@ type (
|
||||||
Write(string, ...interface{})
|
Write(string, ...interface{})
|
||||||
HTML(int, string)
|
HTML(int, string)
|
||||||
Data(int, []byte) error
|
Data(int, []byte) error
|
||||||
RenderWithStatus(int, string, interface{}, ...string) error
|
RenderWithStatus(int, string, interface{}, ...map[string]interface{}) error
|
||||||
Render(string, interface{}, ...string) error
|
Render(string, interface{}, ...map[string]interface{}) error
|
||||||
MustRender(string, interface{}, ...string)
|
MustRender(string, interface{}, ...map[string]interface{})
|
||||||
TemplateString(string, interface{}, ...string) string
|
TemplateString(string, interface{}, ...map[string]interface{}) string
|
||||||
MarkdownString(string) string
|
MarkdownString(string) string
|
||||||
Markdown(int, string)
|
Markdown(int, string)
|
||||||
JSON(int, interface{}) error
|
JSON(int, interface{}) error
|
||||||
JSONP(int, string, interface{}) error
|
JSONP(int, string, interface{}) error
|
||||||
Text(int, string) error
|
Text(int, string) error
|
||||||
XML(int, interface{}) error
|
XML(int, interface{}) error
|
||||||
ExecuteTemplate(*template.Template, interface{}) error
|
|
||||||
ServeContent(io.ReadSeeker, string, time.Time, bool) error
|
ServeContent(io.ReadSeeker, string, time.Time, bool) error
|
||||||
ServeFile(string, bool) error
|
ServeFile(string, bool) error
|
||||||
SendFile(string, string) error
|
SendFile(string, string) error
|
||||||
|
|
|
@ -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
|
// 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)
|
// It can be called after other, (but before .Listen of course)
|
||||||
func (s *Framework) MustUse(handlers ...Handler) {
|
func (s *Framework) MustUse(handlers ...Handler) {
|
||||||
for _, r := range s.mux.lookups {
|
s.UseGlobal(handlers...)
|
||||||
r.middleware = append(handlers, r.middleware...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustUseFunc registers HandlerFunc middleware to the beginning, prepends them instead of append
|
// 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
|
// 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)
|
// It can be called after other, (but before .Listen of course)
|
||||||
func (s *Framework) MustUseFunc(handlersFn ...HandlerFunc) {
|
func (s *Framework) MustUseFunc(handlersFn ...HandlerFunc) {
|
||||||
s.MustUse(convertToHandlers(handlersFn)...)
|
s.UseGlobalFunc(handlersFn...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostFormMulti returns a slice of string from post request's data
|
// PostFormMulti returns a slice of string from post request's data
|
||||||
|
|
2
glide.lock
generated
2
glide.lock
generated
|
@ -101,7 +101,7 @@ imports:
|
||||||
subpackages:
|
subpackages:
|
||||||
- github.com\valyala\bytebufferpool
|
- github.com\valyala\bytebufferpool
|
||||||
- name: github.com/valyala/fasthttp
|
- name: github.com/valyala/fasthttp
|
||||||
version: 97d96cb3b7feaf22ce9b813313cd8ae20b3589b9
|
version: 886e5411604884629c566961ea8ed2cec074e4b1
|
||||||
subpackages:
|
subpackages:
|
||||||
- github.com\valyala\fasthttp
|
- github.com\valyala\fasthttp
|
||||||
- github.com\valyala\fasthttp\fasthttpadaptor
|
- github.com\valyala\fasthttp\fasthttpadaptor
|
||||||
|
|
11
http.go
11
http.go
|
@ -13,8 +13,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/iris-contrib/errors"
|
"github.com/iris-contrib/errors"
|
||||||
|
"github.com/iris-contrib/logger"
|
||||||
"github.com/kataras/iris/config"
|
"github.com/kataras/iris/config"
|
||||||
"github.com/kataras/iris/logger"
|
|
||||||
"github.com/kataras/iris/utils"
|
"github.com/kataras/iris/utils"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||||
|
@ -262,6 +262,11 @@ func newServer(cfg config.Server) *Server {
|
||||||
// prepare just prepares the listening addr
|
// prepare just prepares the listening addr
|
||||||
func (s *Server) prepare() {
|
func (s *Server) prepare() {
|
||||||
s.Config.ListeningAddr = config.ServerParseAddr(s.Config.ListeningAddr)
|
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
|
// 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.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 != "" {
|
if s.Config.RedirectTo != "" {
|
||||||
// override the handler and redirect all requests to this addr
|
// override the handler and redirect all requests to this addr
|
||||||
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {
|
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {
|
||||||
|
|
122
iris.go
122
iris.go
|
@ -65,11 +65,11 @@ import (
|
||||||
|
|
||||||
"github.com/gavv/httpexpect"
|
"github.com/gavv/httpexpect"
|
||||||
"github.com/iris-contrib/errors"
|
"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/config"
|
||||||
"github.com/kataras/iris/context"
|
"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/sessions"
|
||||||
"github.com/kataras/iris/utils"
|
"github.com/kataras/iris/utils"
|
||||||
"github.com/kataras/iris/websocket"
|
"github.com/kataras/iris/websocket"
|
||||||
|
@ -81,27 +81,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Version of the iris
|
// Version of the iris
|
||||||
Version = "3.0.0"
|
Version = "4.0.0-alpha.1"
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
banner = ` _____ _
|
banner = ` _____ _
|
||||||
|_ _| (_)
|
|_ _| (_)
|
||||||
|
@ -177,7 +157,7 @@ type (
|
||||||
Lookups() []Route
|
Lookups() []Route
|
||||||
Path(string, ...interface{}) string
|
Path(string, ...interface{}) string
|
||||||
URL(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
|
Tester(t *testing.T) *httpexpect.Expect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,12 +167,14 @@ type (
|
||||||
Framework struct {
|
Framework struct {
|
||||||
*muxAPI
|
*muxAPI
|
||||||
rest *rest.Render
|
rest *rest.Render
|
||||||
templates *template.Template
|
|
||||||
sessions *sessions.Manager
|
sessions *sessions.Manager
|
||||||
|
templates *TemplateEngines
|
||||||
|
|
||||||
// fields which are useful to the user/dev
|
// fields which are useful to the user/dev
|
||||||
// the last added server is the main server
|
// the last added server is the main server
|
||||||
Servers *ServerList
|
Servers *ServerList
|
||||||
Config *config.Iris
|
Config *config.Iris
|
||||||
|
// configuration by instance.Logger.Config
|
||||||
Logger *logger.Logger
|
Logger *logger.Logger
|
||||||
Plugins PluginContainer
|
Plugins PluginContainer
|
||||||
Websocket websocket.Server
|
Websocket websocket.Server
|
||||||
|
@ -217,9 +199,17 @@ func New(cfg ...config.Iris) *Framework {
|
||||||
{
|
{
|
||||||
///NOTE: set all with s.Config pointer
|
///NOTE: set all with s.Config pointer
|
||||||
// set the Logger
|
// set the Logger
|
||||||
s.Logger = logger.New(s.Config.Logger)
|
s.Logger = logger.New(logger.DefaultConfig())
|
||||||
// set the plugin container
|
// set the plugin container
|
||||||
s.Plugins = &pluginContainer{logger: s.Logger}
|
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
|
// set the websocket server
|
||||||
s.Websocket = websocket.NewServer(s.Config.Websocket)
|
s.Websocket = websocket.NewServer(s.Config.Websocket)
|
||||||
// set the servemux, which will provide us the public API also, with its context pool
|
// 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
|
// set the rest
|
||||||
s.rest = rest.New(s.Config.Render.Rest)
|
s.rest = rest.New(s.Config.Rest)
|
||||||
|
// prepare the templates if enabled
|
||||||
// set templates if not already setted
|
if !s.Config.DisableTemplateEngines {
|
||||||
s.prepareTemplates()
|
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
|
// listen to websocket connections
|
||||||
websocket.RegisterServer(s, s.Websocket, s.Logger)
|
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
|
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual
|
||||||
func Go() error {
|
func Go() error {
|
||||||
return Default.Go()
|
return Default.Go()
|
||||||
|
@ -290,7 +272,6 @@ func (s *Framework) Go() error {
|
||||||
|
|
||||||
// print the banner
|
// print the banner
|
||||||
if !s.Config.DisableBanner {
|
if !s.Config.DisableBanner {
|
||||||
|
|
||||||
openedServers := s.Servers.GetAllOpened()
|
openedServers := s.Servers.GetAllOpened()
|
||||||
l := len(openedServers)
|
l := len(openedServers)
|
||||||
hosts := make([]string, l, l)
|
hosts := make([]string, l, l)
|
||||||
|
@ -495,6 +476,31 @@ func (s *Framework) Close() error {
|
||||||
return s.Servers.CloseAll()
|
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
|
// 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
|
// 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
|
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
|
// returns empty string on error
|
||||||
func TemplateString(templateFile string, pageContext interface{}, layout ...string) string {
|
func TemplateString(templateFile string, pageContext interface{}, options ...map[string]interface{}) string {
|
||||||
return Default.TemplateString(templateFile, pageContext, layout...)
|
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
|
// returns empty string on error
|
||||||
func (s *Framework) TemplateString(templateFile string, pageContext interface{}, layout ...string) string {
|
func (s *Framework) TemplateString(templateFile string, pageContext interface{}, options ...map[string]interface{}) string {
|
||||||
s.prepareTemplates()
|
if s.Config.DisableTemplateEngines {
|
||||||
res, err := s.templates.RenderString(templateFile, pageContext, layout...)
|
return ""
|
||||||
|
}
|
||||||
|
res, err := s.templates.GetBy(templateFile).ExecuteToString(templateFile, pageContext, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,9 @@ package main
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/iris-contrib/logger"
|
||||||
"github.com/kataras/cli"
|
"github.com/kataras/cli"
|
||||||
"github.com/kataras/iris"
|
"github.com/kataras/iris"
|
||||||
"github.com/kataras/iris/config"
|
|
||||||
"github.com/kataras/iris/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -49,7 +48,7 @@ func init() {
|
||||||
app.Command(runAndWatchCmd)
|
app.Command(runAndWatchCmd)
|
||||||
|
|
||||||
// init the logger
|
// init the logger
|
||||||
printer = logger.New(config.DefaultLogger())
|
printer = logger.New(logger.DefaultConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -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.**
|
|
186
logger/logger.go
186
logger/logger.go
|
@ -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...)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/iris-contrib/errors"
|
"github.com/iris-contrib/errors"
|
||||||
|
|
||||||
"github.com/kataras/iris/logger"
|
"github.com/iris-contrib/logger"
|
||||||
"github.com/kataras/iris/utils"
|
"github.com/kataras/iris/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
|
|
||||||
}
|
|
|
@ -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**
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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, "<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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_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
|
|
||||||
}
|
|
277
template.go
Normal file
277
template.go
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
package websocket
|
package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/iris-contrib/logger"
|
||||||
"github.com/kataras/iris/config"
|
"github.com/kataras/iris/config"
|
||||||
"github.com/kataras/iris/context"
|
"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
|
// to avoid the import cycle to /kataras/iris. The ws package is used inside iris' station configuration
|
||||||
|
|
Loading…
Reference in New Issue
Block a user