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) {