diff --git a/HISTORY.md b/HISTORY.md
index f155fe7a..c655ef17 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,15 @@
# History
+## 3.0.0-beta.2 -> 3.0.0-beta.3
+
+- Complete the Jade Template Engine support, {{ render }} and {{ url }} done also.
+
+- Fix Mail().Send
+
+- Iriscontrol plugin: Replace login using session to basic authentication
+
+And other not-too-important fixes
+
## 3.0.0-beta -> 3.0.0-beta.2
- NEW: Wildcard(dynamic) subdomains, read [here](https://kataras.gitbooks.io/iris/content/subdomains.html)
diff --git a/README.md b/README.md
index 9591e31d..1e2e9b3f 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[Travis]: http://travis-ci.org/kataras/iris
[License Widget]: https://img.shields.io/badge/license-Apache%20License%202.0-E91E63.svg?style=flat-square
[License]: https://github.com/kataras/iris/blob/master/LICENSE
-[Release Widget]: https://img.shields.io/badge/release-v3.0.0--beta.2-blue.svg?style=flat-square
+[Release Widget]: https://img.shields.io/badge/release-v3.0.0--beta.3-blue.svg?style=flat-square
[Release]: https://github.com/kataras/iris/releases
[Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square
[Gitter]: https://gitter.im/kataras/iris
@@ -116,7 +116,7 @@ Iris suggests you to use [this](https://github.com/gavv/httpexpect) new suite t
Versioning
------------
-Current: **v3.0.0-beta.2**
+Current: **v3.0.0-beta.3**
> Iris is an active project
diff --git a/config/render.go b/config/render.go
index 2c2ca0bb..1d6908e2 100644
--- a/config/render.go
+++ b/config/render.go
@@ -104,13 +104,13 @@ type (
// HTMLTemplate contains specific configs for HTMLTemplate standard html/template
HTMLTemplate HTMLTemplate
- // Pongo contains specific configs for for pongo2
+ // Jade contains specific configs for Jade
+ Jade Jade
+ // Pongo contains specific configs for pongo2
Pongo Pongo
- // Markdown contains specific configs for for markdown
+ // Markdown contains specific configs for markdown
// this doesn't supports Layout & binding context
Markdown Markdown
- // Jade contains specific configs for jade
- Jade Jade
// Amber contains specific configs for amber
Amber Amber
}
@@ -132,6 +132,8 @@ type (
// these can override the Funcs inside no-layout templates also, use it when you know what you're doing
LayoutFuncs map[string]interface{}
}
+ // Jade the configs for JadeEngine
+ Jade HTMLTemplate
// Pongo the configs for PongoEngine
Pongo struct {
// Filters for pongo2, map[name of the filter] the filter function . The filters are auto register
@@ -145,17 +147,6 @@ type (
Sanitize bool // if true then returns safe html, default is false
}
- // Jade the configs for JadeEngine
- Jade struct {
- // Funcs like html/template
- Funcs map[string]interface{}
- // LayoutFuncs like html/template
- // the difference from Funcs is that these funcs
- // can be used inside a layout and can override the predefined (yield,partial...) or add more custom funcs
- // these can override the Funcs inside no-layout templates also, use it when you know what you're doing
- LayoutFuncs map[string]interface{}
- }
-
// Amber the configs for AmberEngine
Amber struct {
// Funcs for the html/template result, amber default funcs are not overrided so use it without worries
@@ -213,10 +204,10 @@ func DefaultTemplate() Template {
Charset: "UTF-8",
Layout: "", // currently this is the only config which not working for pongo2 yet but I will find a way
HTMLTemplate: HTMLTemplate{Left: "{{", Right: "}}", Funcs: make(map[string]interface{}, 0), LayoutFuncs: make(map[string]interface{}, 0)},
+ Jade: Jade{Left: "{{", Right: "}}", Funcs: make(map[string]interface{}, 0), LayoutFuncs: make(map[string]interface{}, 0)},
Pongo: Pongo{Filters: make(map[string]pongo2.FilterFunction, 0), Globals: make(map[string]interface{}, 0)},
Markdown: Markdown{Sanitize: false},
Amber: Amber{Funcs: template.FuncMap{}},
- Jade: Jade{Funcs: template.FuncMap{}, LayoutFuncs: template.FuncMap{}},
}
}
diff --git a/iris.go b/iris.go
index 806f8390..c9794766 100644
--- a/iris.go
+++ b/iris.go
@@ -1,4 +1,4 @@
-// Package iris v3.0.0-beta.2
+// Package iris the fastest golang web framework, so far.
//
// Note: When 'Station', we mean the Iris type.
package iris
@@ -11,7 +11,6 @@ import (
"time"
"github.com/fatih/color"
- "github.com/flosch/pongo2"
"github.com/kataras/iris/config"
"github.com/kataras/iris/logger"
"github.com/kataras/iris/mail"
@@ -30,7 +29,7 @@ import (
const (
// Version of the iris
- Version = "v3.0.0-beta.2"
+ Version = "v3.0.0-beta.3"
banner = ` _____ _
|_ _| (_)
| | ____ _ ___
@@ -113,85 +112,17 @@ func (s *Iris) newContextPool() sync.Pool {
}}
}
+///TODO HERE MOVE THEM SOMEWHERE ELSE OR MAKE IT MORE BUETY->
func (s *Iris) initTemplates() {
if s.templates == nil { // because if .Templates() called before server's listen, s.templates != nil when PreListen
// init the templates
-
- // set the custom iris-direct-integration functions, layout and no-layout if HTMLEngine is used
- templateConfig := &s.config.Render.Template
- ///TODO gia AMber episis
- if templateConfig.Engine == config.HTMLEngine || templateConfig.Engine == config.AmberEngine {
- funcs := map[string]interface{}{
-
- "url": func(routeName string, args ...interface{}) (string, error) {
- r := s.RouteByName(routeName)
- // check if not found
- if r.GetMethod() == "" {
- return "", ErrRenderRouteNotFound.Format(routeName)
- }
-
- return r.ParseURI(args...), nil
-
- },
+ template.RegisterSharedFunc("url", func(routeName string, args ...interface{}) string {
+ if url, err := s.UriOf(routeName, args...); err == nil {
+ return url
+ } else {
+ return err.Error()
}
-
- // these should be already a non-nil map but if .New(cfg) it's not, is mergo's bug, temporary:
- if templateConfig.Engine == config.HTMLEngine {
- if templateConfig.HTMLTemplate.LayoutFuncs == nil {
- templateConfig.HTMLTemplate.LayoutFuncs = make(map[string]interface{}, len(funcs))
- }
-
- if templateConfig.HTMLTemplate.Funcs == nil {
- templateConfig.HTMLTemplate.Funcs = make(map[string]interface{}, len(funcs))
- }
-
- for k, v := range funcs {
- // we don't want to override the user's LayoutFuncs, user should be able to override anything.
- if templateConfig.HTMLTemplate.LayoutFuncs[k] == nil {
- templateConfig.HTMLTemplate.LayoutFuncs[k] = v
- }
-
- if templateConfig.HTMLTemplate.Funcs[k] == nil {
- templateConfig.HTMLTemplate.Funcs[k] = v
- }
-
- }
-
- } else if templateConfig.Engine == config.AmberEngine {
- if templateConfig.Amber.Funcs == nil {
- templateConfig.Amber.Funcs = make(map[string]interface{}, len(funcs))
- }
-
- for k, v := range funcs {
- if templateConfig.Amber.Funcs[k] == nil {
- templateConfig.Amber.Funcs[k] = v
- }
- }
- }
-
- //
-
- } else if templateConfig.Engine == config.PongoEngine {
- if templateConfig.Pongo.Globals == nil {
- templateConfig.Pongo.Globals = make(map[string]interface{}, 1)
- }
-
- urlFunc := func(routeName string, args ...interface{}) (out *pongo2.Value) {
-
- r := s.RouteByName(routeName)
- // check if not found
- if r.GetMethod() == "" {
- return pongo2.AsValue(ErrRenderRouteNotFound.Format(routeName).Error())
- }
-
- return pongo2.AsValue(r.ParseURI(args...))
- }
-
- // register it to the global context, no as Filter.
- templateConfig.Pongo.Globals["url"] = urlFunc
-
- }
-
+ })
s.templates = template.New(s.config.Render.Template)
}
diff --git a/mail/service.go b/mail/service.go
index 9012cf28..22431553 100644
--- a/mail/service.go
+++ b/mail/service.go
@@ -1,17 +1,16 @@
package mail
import (
+ "encoding/base64"
"fmt"
+ "net/mail"
"net/smtp"
"strings"
- "text/template"
"github.com/kataras/iris/config"
"github.com/kataras/iris/utils"
)
-const tmpl = `From: {{.From}}
To: {{.To}}
Subject: {{.Subject}}
MIME-version: 1.0
Content-Type: text/html; charset="UTF-8"
{{.Body}}`
-
var buf = utils.NewBufferPool(64)
type (
@@ -24,6 +23,7 @@ type (
mailer struct {
config config.Mail
+ fromAddr mail.Address
auth smtp.Auth
authenticated bool
}
@@ -31,7 +31,14 @@ type (
// New creates and returns a new Service
func New(cfg config.Mail) Service {
- return &mailer{config: cfg}
+ m := &mailer{config: cfg}
+
+ // not necessary
+ if !cfg.UseCommand && cfg.Username != "" && strings.Contains(cfg.Username, "@") {
+ m.fromAddr = mail.Address{cfg.Username[0:strings.IndexByte(cfg.Username, '@')], cfg.Username}
+ }
+
+ return m
}
// Send sends a mail to recipients
@@ -56,19 +63,31 @@ func (m *mailer) sendSMTP(to []string, subject, body string) error {
m.authenticated = true
}
- mailArgs := map[string]string{"To": strings.Join(to, ","), "Subject": subject, "Body": body}
- template := template.Must(template.New("mailTmpl").Parse(tmpl))
+ fullhost := fmt.Sprintf("%s:%d", m.config.Host, m.config.Port)
- if err := template.Execute(buffer, mailArgs); err != nil {
- return err
+ /* START: This one helped me https://gist.github.com/andelf/5004821 */
+ header := make(map[string]string)
+ header["From"] = m.fromAddr.String()
+ header["To"] = strings.Join(to, ",")
+ header["Subject"] = subject
+ header["MIME-Version"] = "1.0"
+ header["Content-Type"] = "text/html; charset=\"utf-8\""
+ header["Content-Transfer-Encoding"] = "base64"
+
+ message := ""
+ for k, v := range header {
+ message += fmt.Sprintf("%s: %s\r\n", k, v)
}
+ message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
+
+ /* END */
return smtp.SendMail(
- fmt.Sprintf("%s:%d", m.config.Host, m.config.Port),
+ fmt.Sprintf(fullhost),
m.auth,
m.config.Username,
to,
- buffer.Bytes(),
+ []byte(message),
)
}
diff --git a/plugin/iriscontrol/control_panel.go b/plugin/iriscontrol/control_panel.go
index e741b485..ac8c2092 100644
--- a/plugin/iriscontrol/control_panel.go
+++ b/plugin/iriscontrol/control_panel.go
@@ -26,13 +26,15 @@ func (i *irisControlPlugin) startControlPanel() {
}
i.server = iris.New()
+ i.server.Config().DisableBanner = true
i.server.Config().Render.Template.Directory = installationPath + "templates"
//i.server.SetRenderConfig(i.server.Config.Render)
i.setPluginsInfo()
i.setPanelRoutes()
go i.server.Listen(strconv.Itoa(i.options.Port))
- i.pluginContainer.Printf("[%s] %s is running at port %d with %d authenticated users", time.Now().UTC().String(), Name, i.options.Port, len(i.auth.authenticatedUsers))
+
+ i.pluginContainer.Printf("[%s] %s is running at port %d", time.Now().UTC().String(), Name, i.options.Port)
}
@@ -67,21 +69,10 @@ func (i *irisControlPlugin) installAssets() (err error) {
func (i *irisControlPlugin) setPanelRoutes() {
i.server.Static("/public", installationPath+"static", 1)
- i.server.Get("/login", func(ctx *iris.Context) {
- ctx.Render("login", nil)
- })
- i.server.Post("/login", func(ctx *iris.Context) {
- i.auth.login(ctx)
- })
-
- i.server.Use(i.auth)
+ i.server.Use(i.authFunc)
i.server.Get("/", func(ctx *iris.Context) {
- ctx.Render("index", DashboardPage{ServerIsRunning: i.station.Server().IsListening(), Routes: i.routes.All(), Plugins: i.plugins})
- })
-
- i.server.Post("/logout", func(ctx *iris.Context) {
- i.auth.logout(ctx)
+ ctx.Render("index.html", DashboardPage{ServerIsRunning: i.station.Server().IsListening(), Routes: i.routes.All(), Plugins: i.plugins})
})
//the controls
diff --git a/plugin/iriscontrol/iriscontrol.go b/plugin/iriscontrol/iriscontrol.go
index 223b3e53..e25771a8 100644
--- a/plugin/iriscontrol/iriscontrol.go
+++ b/plugin/iriscontrol/iriscontrol.go
@@ -5,6 +5,7 @@ import (
"github.com/kataras/iris"
"github.com/kataras/iris/config"
+ "github.com/kataras/iris/middleware/basicauth"
"github.com/kataras/iris/plugin/routesinfo"
"github.com/kataras/iris/server"
)
@@ -29,7 +30,7 @@ type irisControlPlugin struct {
plugins []PluginInfo
//
- auth *userAuth
+ authFunc iris.HandlerFunc
}
// New returns the plugin which is ready-to-use inside iris.Plugin method
@@ -39,12 +40,13 @@ func New(cfg ...config.IrisControl) iris.IPlugin {
if len(cfg) > 0 {
c = cfg[0]
}
- auth := newUserAuth(c.Users)
- if auth == nil {
+ if c.Users == nil || len(c.Users) == 0 {
panic(Name + " Error: you should pass authenticated users map to the options, refer to the docs!")
}
- return &irisControlPlugin{options: c, auth: auth, routes: routesinfo.RoutesInfo()}
+ auth := basicauth.Default(c.Users)
+
+ return &irisControlPlugin{options: c, authFunc: auth, routes: routesinfo.RoutesInfo()}
}
// Web set the options for the plugin and return the plugin which is ready-to-use inside iris.Plugin method
@@ -106,7 +108,6 @@ func (i *irisControlPlugin) Destroy() {
i.station = nil
i.server.Close()
i.pluginContainer = nil
- i.auth.Destroy()
- i.auth = nil
+ i.authFunc = nil
i.pluginContainer.Printf("[%s] %s is turned off", time.Now().UTC().String(), Name)
}
diff --git a/plugin/iriscontrol/user_auth.go b/plugin/iriscontrol/user_auth.go
deleted file mode 100644
index 3122e418..00000000
--- a/plugin/iriscontrol/user_auth.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package iriscontrol
-
-import (
- "strings"
-
- "github.com/kataras/iris"
- "github.com/kataras/iris/sessions"
- // _ empty because it auto-registers
- _ "github.com/kataras/iris/sessions/providers/memory"
-)
-
-var panelSessions *sessions.Manager
-
-func init() {
- //using the default
- panelSessions = sessions.New()
-}
-
-type user struct {
- username string
- password string
-}
-type userAuth struct {
- authenticatedUsers []user
-}
-
-// newUserAuth returns a new userAuth object, parameter is the authenticated users as map
-func newUserAuth(usersMap map[string]string) *userAuth {
- if usersMap != nil {
- obj := &userAuth{make([]user, 0)}
- for key, val := range usersMap {
- obj.authenticatedUsers = append(obj.authenticatedUsers, user{key, val})
- }
-
- return obj
- }
-
- return nil
-}
-
-func (u *userAuth) login(ctx *iris.Context) {
- session := panelSessions.Start(ctx)
-
- username := ctx.PostFormValue("username")
- password := ctx.PostFormValue("password")
-
- for _, authenticatedUser := range u.authenticatedUsers {
- if authenticatedUser.username == username && authenticatedUser.password == password {
- session.Set("username", username)
- session.Set("password", password)
- ctx.Write("success")
- return
- }
- }
- ctx.Write("fail")
-
-}
-
-func (u *userAuth) logout(ctx *iris.Context) {
- session := panelSessions.Start(ctx)
- session.Set("user", nil)
-
- ctx.Redirect("/login")
-}
-
-// check if session stored, then check if this user is the correct, each time, then continue, else not
-func (u *userAuth) Serve(ctx *iris.Context) {
- if ctx.PathString() == "/login" || strings.HasPrefix(ctx.PathString(), "/public") {
- ctx.Next()
- return
- }
- session := panelSessions.Start(ctx)
-
- if sessionVal := session.Get("username"); sessionVal != nil {
- username := sessionVal.(string)
- password := session.GetString("password")
- if username != "" && password != "" {
-
- for _, authenticatedUser := range u.authenticatedUsers {
- if authenticatedUser.username == username && authenticatedUser.password == password {
- ctx.Next()
-
- return
- }
- }
- }
-
- }
- //if not logged in the redirect to the /login
- ctx.Redirect("/login")
-
-}
-
-// Destroy this is called on PreClose by the iriscontrol.go
-func (u *userAuth) Destroy() {
-
-}
diff --git a/render/template/engine/html/html.go b/render/template/engine/html/html.go
index 404a0a1c..175ffcff 100644
--- a/render/template/engine/html/html.go
+++ b/render/template/engine/html/html.go
@@ -10,20 +10,15 @@ import (
"path/filepath"
"strings"
+ "github.com/Joker/jade"
"github.com/kataras/iris/config"
)
type (
- // Engine the html/template engine
+ // Engine the html/template engine & Jade
Engine struct {
Config *config.Template
Templates *template.Template
- // Middleware
- // Note:
- // I see that many template engines returns html/template as result
- // so I decided that the HTMLTemplate should accept a middleware for the final string content will be parsed to the main *html/template.Template
- // for example user of this property is Jade, currently
- Middleware func(string, string) (string, error)
}
)
@@ -36,7 +31,7 @@ var emptyFuncs = template.FuncMap{
},
"current": func() (string, error) {
return "", nil
- }, "render": func(string) (string, error) {
+ }, "render": func() (string, error) {
return "", nil
},
}
@@ -73,7 +68,6 @@ func (s *Engine) buildFromDir() error {
dir := s.Config.Directory
s.Templates = template.New(dir)
s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
- hasMiddleware := s.Middleware != nil
// 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() {
@@ -107,8 +101,8 @@ func (s *Engine) buildFromDir() error {
name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name)
- if hasMiddleware {
- contents, err = s.Middleware(name, contents)
+ if s.Config.Engine == config.JadeEngine {
+ contents, err = jade.Parse(name, contents)
}
if err != nil {
@@ -117,11 +111,11 @@ func (s *Engine) buildFromDir() error {
}
// Add our funcmaps.
- //if s.Config.HTMLTemplate.Funcs != nil {
- // tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
- //}
+ if s.Config.HTMLTemplate.Funcs != nil {
+ tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
+ }
- tmpl.Funcs(s.Config.HTMLTemplate.Funcs).Parse(contents)
+ tmpl.Funcs(emptyFuncs).Parse(contents)
break
}
}
@@ -159,16 +153,20 @@ func (s *Engine) buildFromAsset() error {
if err != nil {
panic(err)
}
+ contents := string(buf)
name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name)
- // Add our funcmaps.
- //for _, funcs := range s.Config.HTMLTemplate.Funcs {
- /*if s.Config.HTMLTemplate.Funcs != nil {
- tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
- }*/
+ if s.Config.Engine == config.JadeEngine {
+ contents, err = jade.Parse(name, contents)
+ }
- tmpl.Funcs(emptyFuncs).Funcs(s.Config.HTMLTemplate.Funcs).Parse(string(buf))
+ // Add our funcmaps.
+ if s.Config.HTMLTemplate.Funcs != nil {
+ tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
+ }
+
+ tmpl.Funcs(emptyFuncs).Parse(contents)
break
}
}
@@ -178,25 +176,11 @@ func (s *Engine) buildFromAsset() error {
func (s *Engine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) {
buf := new(bytes.Buffer)
- /*
- var err error
- if s.Middleware != nil {
- contents, err := s.Middleware(name, buf.String())
- if err != nil {
- return buf, err
- }
- buf.WriteString(contents)
- }
- */
err := s.Templates.ExecuteTemplate(buf, name, binding)
return buf, err
}
-func (s *Engine) ExecuteTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) {
- return s.executeTemplateBuf(name, binding)
-}
-
func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
funcs := template.FuncMap{
"yield": func() (template.HTML, error) {
@@ -211,15 +195,12 @@ func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
fullPartialName := fmt.Sprintf("%s-%s", partialName, name)
if s.Config.HTMLTemplate.RequirePartials || s.Templates.Lookup(fullPartialName) != nil {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
- // Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
}
return "", nil
},
"render": func(fullPartialName string) (template.HTML, error) {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
- println("html.go:217-> " + buf.String())
- // Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
},
}
@@ -234,12 +215,28 @@ func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
}
}
+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 != "" && layout != config.NoLayout {
+ s.layoutFuncsFor(name, binding)
name = layout
+
+ } else {
+ s.runtimeFuncsFor(name, binding)
}
- s.layoutFuncsFor(name, binding)
return s.Templates.ExecuteTemplate(out, name, binding)
}
diff --git a/render/template/engine/jade/jade.go b/render/template/engine/jade/jade.go
index 92ce8119..67c8f4eb 100644
--- a/render/template/engine/jade/jade.go
+++ b/render/template/engine/jade/jade.go
@@ -1,24 +1,15 @@
+// Package jade the JadeEngine's functionality lives inside ../html now
package jade
import (
- "github.com/Joker/jade"
"github.com/kataras/iris/config"
"github.com/kataras/iris/render/template/engine/html"
)
-// Engine the JadeEngine
-type Engine struct {
- *html.Engine
-}
-
-// New creates and returns a new JadeEngine with its configs
-func New(cfg config.Template) *Engine {
- cfg.HTMLTemplate.Funcs = cfg.Jade.Funcs //copy the jade's funcs to the underline HTMLEngine
- cfg.HTMLTemplate.LayoutFuncs = cfg.Jade.LayoutFuncs
- underline := &Engine{Engine: html.New(cfg)}
- underline.Middleware = func(relativeName string, fileContents string) (string, error) {
- return jade.Parse(relativeName, fileContents)
- }
-
- return underline
+// New creates and returns the HTMLTemplate template engine
+func New(c config.Template) *html.Engine {
+ // copy the Jade to the HTMLTemplate
+ c.HTMLTemplate = config.HTMLTemplate(c.Jade)
+ s := &html.Engine{Config: &c}
+ return s
}
diff --git a/render/template/template.go b/render/template/template.go
index 719837d8..f0f8d6c2 100644
--- a/render/template/template.go
+++ b/render/template/template.go
@@ -55,21 +55,48 @@ type (
}
)
+// 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.JadeEngine:
- e = jade.New(c) // Jade
case config.AmberEngine:
+ setSharedFuncs(sharedFuncs, c.Amber.Funcs)
e = amber.New(c) // Amber
default: // config.NoEngine
return nil
@@ -97,6 +124,14 @@ func New(c config.Template) *Template {
}
+// 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
+}
+
// Render renders a template using the context's writer
func (t *Template) Render(ctx context.IContext, name string, binding interface{}, layout ...string) (err error) {
diff --git a/router.go b/router.go
index 1df88bac..bd3ed3bd 100644
--- a/router.go
+++ b/router.go
@@ -118,15 +118,32 @@ func (r *router) addRoute(route IRoute) {
// RouteByName returns a route by its name,if not found then returns a route with empty path
// Note that the searching is case-sensitive
-func (r *router) RouteByName(lookUpName string) IRoute {
+func (r *router) RouteByName(routeName string) IRoute {
for _, route := range r.lookups {
- if route.GetName() == lookUpName {
+ if route.GetName() == routeName {
return route
}
}
return &Route{}
}
+// UriOf returns the parsed URI of a route
+// receives two parameters
+// the first is the route's name (string)
+// the second is a variadic, if the route is dynamic (receives named parameters) then pass the value of these parameters here
+// overview of the result is: scheme(http or https if ListenTLS)/yourhost.com:PORT/profile/theusername/friends/theid
+//
+// example /profile/:username/friends/:friendId with name "profile" -> .UriOf("profile","kataras",8) will give http://127.0.0.1:8080/profile/kataras/friends/8
+func (r *router) UriOf(routeName string, args ...interface{}) (string, error) {
+ route := r.RouteByName(routeName)
+ // check if not found
+ if route.GetMethod() == "" {
+ return "", ErrRenderRouteNotFound.Format(routeName)
+ }
+
+ return route.ParseURI(args...), nil
+}
+
//check if any tree has cors setted to true, means that cors middleware is added
func (r *router) cors() (has bool) {
r.garden.visitAll(func(i int, tree *tree) {