Fix mail send, complete jade support, fix iriscontrol index , replace iriscontrol session to basicauth

This commit is contained in:
Makis Maropoulos 2016-06-06 01:37:32 +03:00
parent 91b45ebfdb
commit 48aaca5bc0
12 changed files with 168 additions and 282 deletions

View File

@ -1,5 +1,15 @@
# History # 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 ## 3.0.0-beta -> 3.0.0-beta.2
- NEW: Wildcard(dynamic) subdomains, read [here](https://kataras.gitbooks.io/iris/content/subdomains.html) - NEW: Wildcard(dynamic) subdomains, read [here](https://kataras.gitbooks.io/iris/content/subdomains.html)

View File

@ -6,7 +6,7 @@
[Travis]: http://travis-ci.org/kataras/iris [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 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 [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 [Release]: https://github.com/kataras/iris/releases
[Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square [Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square
[Gitter]: https://gitter.im/kataras/iris [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 Versioning
------------ ------------
Current: **v3.0.0-beta.2** Current: **v3.0.0-beta.3**
> Iris is an active project > Iris is an active project

View File

@ -104,13 +104,13 @@ type (
// HTMLTemplate contains specific configs for HTMLTemplate standard html/template // HTMLTemplate contains specific configs for HTMLTemplate standard html/template
HTMLTemplate HTMLTemplate 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 Pongo Pongo
// Markdown contains specific configs for for markdown // Markdown contains specific configs for markdown
// this doesn't supports Layout & binding context // this doesn't supports Layout & binding context
Markdown Markdown Markdown Markdown
// Jade contains specific configs for jade
Jade Jade
// Amber contains specific configs for amber // Amber contains specific configs for amber
Amber 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 // these can override the Funcs inside no-layout templates also, use it when you know what you're doing
LayoutFuncs map[string]interface{} LayoutFuncs map[string]interface{}
} }
// Jade the configs for JadeEngine
Jade HTMLTemplate
// Pongo the configs for PongoEngine // Pongo the configs for PongoEngine
Pongo struct { Pongo struct {
// Filters for pongo2, map[name of the filter] the filter function . The filters are auto register // 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 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 the configs for AmberEngine
Amber struct { Amber struct {
// Funcs for the html/template result, amber default funcs are not overrided so use it without worries // 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", Charset: "UTF-8",
Layout: "", // currently this is the only config which not working for pongo2 yet but I will find a way 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)}, 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)}, Pongo: Pongo{Filters: make(map[string]pongo2.FilterFunction, 0), Globals: make(map[string]interface{}, 0)},
Markdown: Markdown{Sanitize: false}, Markdown: Markdown{Sanitize: false},
Amber: Amber{Funcs: template.FuncMap{}}, Amber: Amber{Funcs: template.FuncMap{}},
Jade: Jade{Funcs: template.FuncMap{}, LayoutFuncs: template.FuncMap{}},
} }
} }

87
iris.go
View File

@ -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. // Note: When 'Station', we mean the Iris type.
package iris package iris
@ -11,7 +11,6 @@ import (
"time" "time"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/flosch/pongo2"
"github.com/kataras/iris/config" "github.com/kataras/iris/config"
"github.com/kataras/iris/logger" "github.com/kataras/iris/logger"
"github.com/kataras/iris/mail" "github.com/kataras/iris/mail"
@ -30,7 +29,7 @@ import (
const ( const (
// Version of the iris // Version of the iris
Version = "v3.0.0-beta.2" Version = "v3.0.0-beta.3"
banner = ` _____ _ 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() { func (s *Iris) initTemplates() {
if s.templates == nil { // because if .Templates() called before server's listen, s.templates != nil when PreListen if s.templates == nil { // because if .Templates() called before server's listen, s.templates != nil when PreListen
// init the templates // init the templates
template.RegisterSharedFunc("url", func(routeName string, args ...interface{}) string {
// set the custom iris-direct-integration functions, layout and no-layout if HTMLEngine is used if url, err := s.UriOf(routeName, args...); err == nil {
templateConfig := &s.config.Render.Template return url
///TODO gia AMber episis } else {
if templateConfig.Engine == config.HTMLEngine || templateConfig.Engine == config.AmberEngine { return err.Error()
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
},
} }
})
// 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) s.templates = template.New(s.config.Render.Template)
} }

View File

@ -1,17 +1,16 @@
package mail package mail
import ( import (
"encoding/base64"
"fmt" "fmt"
"net/mail"
"net/smtp" "net/smtp"
"strings" "strings"
"text/template"
"github.com/kataras/iris/config" "github.com/kataras/iris/config"
"github.com/kataras/iris/utils" "github.com/kataras/iris/utils"
) )
const tmpl = `From: {{.From}}<br /> To: {{.To}}<br /> Subject: {{.Subject}}<br /> MIME-version: 1.0<br /> Content-Type: text/html; charset=&quot;UTF-8&quot;<br /> <br /> {{.Body}}`
var buf = utils.NewBufferPool(64) var buf = utils.NewBufferPool(64)
type ( type (
@ -24,6 +23,7 @@ type (
mailer struct { mailer struct {
config config.Mail config config.Mail
fromAddr mail.Address
auth smtp.Auth auth smtp.Auth
authenticated bool authenticated bool
} }
@ -31,7 +31,14 @@ type (
// New creates and returns a new Service // New creates and returns a new Service
func New(cfg config.Mail) 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 // Send sends a mail to recipients
@ -56,19 +63,31 @@ func (m *mailer) sendSMTP(to []string, subject, body string) error {
m.authenticated = true m.authenticated = true
} }
mailArgs := map[string]string{"To": strings.Join(to, ","), "Subject": subject, "Body": body} fullhost := fmt.Sprintf("%s:%d", m.config.Host, m.config.Port)
template := template.Must(template.New("mailTmpl").Parse(tmpl))
if err := template.Execute(buffer, mailArgs); err != nil { /* START: This one helped me https://gist.github.com/andelf/5004821 */
return err 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( return smtp.SendMail(
fmt.Sprintf("%s:%d", m.config.Host, m.config.Port), fmt.Sprintf(fullhost),
m.auth, m.auth,
m.config.Username, m.config.Username,
to, to,
buffer.Bytes(), []byte(message),
) )
} }

View File

@ -26,13 +26,15 @@ func (i *irisControlPlugin) startControlPanel() {
} }
i.server = iris.New() i.server = iris.New()
i.server.Config().DisableBanner = true
i.server.Config().Render.Template.Directory = installationPath + "templates" i.server.Config().Render.Template.Directory = installationPath + "templates"
//i.server.SetRenderConfig(i.server.Config.Render) //i.server.SetRenderConfig(i.server.Config.Render)
i.setPluginsInfo() i.setPluginsInfo()
i.setPanelRoutes() i.setPanelRoutes()
go i.server.Listen(strconv.Itoa(i.options.Port)) 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() { func (i *irisControlPlugin) setPanelRoutes() {
i.server.Static("/public", installationPath+"static", 1) 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.server.Use(i.authFunc)
i.auth.login(ctx)
})
i.server.Use(i.auth)
i.server.Get("/", func(ctx *iris.Context) { i.server.Get("/", func(ctx *iris.Context) {
ctx.Render("index", DashboardPage{ServerIsRunning: i.station.Server().IsListening(), Routes: i.routes.All(), Plugins: i.plugins}) ctx.Render("index.html", 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)
}) })
//the controls //the controls

View File

@ -5,6 +5,7 @@ import (
"github.com/kataras/iris" "github.com/kataras/iris"
"github.com/kataras/iris/config" "github.com/kataras/iris/config"
"github.com/kataras/iris/middleware/basicauth"
"github.com/kataras/iris/plugin/routesinfo" "github.com/kataras/iris/plugin/routesinfo"
"github.com/kataras/iris/server" "github.com/kataras/iris/server"
) )
@ -29,7 +30,7 @@ type irisControlPlugin struct {
plugins []PluginInfo plugins []PluginInfo
// //
auth *userAuth authFunc iris.HandlerFunc
} }
// New returns the plugin which is ready-to-use inside iris.Plugin method // 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 { if len(cfg) > 0 {
c = cfg[0] c = cfg[0]
} }
auth := newUserAuth(c.Users) if c.Users == nil || len(c.Users) == 0 {
if auth == nil {
panic(Name + " Error: you should pass authenticated users map to the options, refer to the docs!") 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 // 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.station = nil
i.server.Close() i.server.Close()
i.pluginContainer = nil i.pluginContainer = nil
i.auth.Destroy() i.authFunc = nil
i.auth = nil
i.pluginContainer.Printf("[%s] %s is turned off", time.Now().UTC().String(), Name) i.pluginContainer.Printf("[%s] %s is turned off", time.Now().UTC().String(), Name)
} }

View File

@ -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() {
}

View File

@ -10,20 +10,15 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/Joker/jade"
"github.com/kataras/iris/config" "github.com/kataras/iris/config"
) )
type ( type (
// Engine the html/template engine // Engine the html/template engine & Jade
Engine struct { Engine struct {
Config *config.Template Config *config.Template
Templates *template.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) { "current": func() (string, error) {
return "", nil return "", nil
}, "render": func(string) (string, error) { }, "render": func() (string, error) {
return "", nil return "", nil
}, },
} }
@ -73,7 +68,6 @@ func (s *Engine) buildFromDir() error {
dir := s.Config.Directory dir := s.Config.Directory
s.Templates = template.New(dir) s.Templates = template.New(dir)
s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right) 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. // 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 { filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() { if info == nil || info.IsDir() {
@ -107,8 +101,8 @@ func (s *Engine) buildFromDir() error {
name := filepath.ToSlash(rel) name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name) tmpl := s.Templates.New(name)
if hasMiddleware { if s.Config.Engine == config.JadeEngine {
contents, err = s.Middleware(name, contents) contents, err = jade.Parse(name, contents)
} }
if err != nil { if err != nil {
@ -117,11 +111,11 @@ func (s *Engine) buildFromDir() error {
} }
// Add our funcmaps. // Add our funcmaps.
//if s.Config.HTMLTemplate.Funcs != nil { if s.Config.HTMLTemplate.Funcs != nil {
// tmpl.Funcs(s.Config.HTMLTemplate.Funcs) tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
//} }
tmpl.Funcs(s.Config.HTMLTemplate.Funcs).Parse(contents) tmpl.Funcs(emptyFuncs).Parse(contents)
break break
} }
} }
@ -159,16 +153,20 @@ func (s *Engine) buildFromAsset() error {
if err != nil { if err != nil {
panic(err) panic(err)
} }
contents := string(buf)
name := filepath.ToSlash(rel) name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name) tmpl := s.Templates.New(name)
// Add our funcmaps. if s.Config.Engine == config.JadeEngine {
//for _, funcs := range s.Config.HTMLTemplate.Funcs { contents, err = jade.Parse(name, contents)
/*if s.Config.HTMLTemplate.Funcs != nil { }
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
}*/
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 break
} }
} }
@ -178,25 +176,11 @@ func (s *Engine) buildFromAsset() error {
func (s *Engine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) { func (s *Engine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) {
buf := new(bytes.Buffer) 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) err := s.Templates.ExecuteTemplate(buf, name, binding)
return buf, err 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{}) { func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
funcs := template.FuncMap{ funcs := template.FuncMap{
"yield": func() (template.HTML, error) { "yield": func() (template.HTML, error) {
@ -211,15 +195,12 @@ func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
fullPartialName := fmt.Sprintf("%s-%s", partialName, name) fullPartialName := fmt.Sprintf("%s-%s", partialName, name)
if s.Config.HTMLTemplate.RequirePartials || s.Templates.Lookup(fullPartialName) != nil { if s.Config.HTMLTemplate.RequirePartials || s.Templates.Lookup(fullPartialName) != nil {
buf, err := s.executeTemplateBuf(fullPartialName, binding) buf, err := s.executeTemplateBuf(fullPartialName, binding)
// Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err return template.HTML(buf.String()), err
} }
return "", nil return "", nil
}, },
"render": func(fullPartialName string) (template.HTML, error) { "render": func(fullPartialName string) (template.HTML, error) {
buf, err := s.executeTemplateBuf(fullPartialName, binding) 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 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 // 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 { func (s *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
if layout != "" && layout != config.NoLayout { if layout != "" && layout != config.NoLayout {
s.layoutFuncsFor(name, binding)
name = layout name = layout
} else {
s.runtimeFuncsFor(name, binding)
} }
s.layoutFuncsFor(name, binding)
return s.Templates.ExecuteTemplate(out, name, binding) return s.Templates.ExecuteTemplate(out, name, binding)
} }

View File

@ -1,24 +1,15 @@
// Package jade the JadeEngine's functionality lives inside ../html now
package jade package jade
import ( import (
"github.com/Joker/jade"
"github.com/kataras/iris/config" "github.com/kataras/iris/config"
"github.com/kataras/iris/render/template/engine/html" "github.com/kataras/iris/render/template/engine/html"
) )
// Engine the JadeEngine // New creates and returns the HTMLTemplate template engine
type Engine struct { func New(c config.Template) *html.Engine {
*html.Engine // copy the Jade to the HTMLTemplate
} c.HTMLTemplate = config.HTMLTemplate(c.Jade)
s := &html.Engine{Config: &c}
// New creates and returns a new JadeEngine with its configs return s
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
} }

View File

@ -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 // New creates and returns a Template instance which keeps the Template Engine and helps with render
func New(c config.Template) *Template { func New(c config.Template) *Template {
defer func() {
sharedFuncs = nil
}()
var e Engine var e Engine
// [ENGINE-2] // [ENGINE-2]
switch c.Engine { switch c.Engine {
case config.HTMLEngine: case config.HTMLEngine:
setSharedFuncs(sharedFuncs, c.HTMLTemplate.Funcs)
e = html.New(c) // HTMLTemplate e = html.New(c) // HTMLTemplate
case config.JadeEngine:
setSharedFuncs(sharedFuncs, c.Jade.Funcs)
e = jade.New(c) // Jade
case config.PongoEngine: case config.PongoEngine:
setSharedFuncs(sharedFuncs, c.Pongo.Globals)
e = pongo.New(c) // Pongo2 e = pongo.New(c) // Pongo2
case config.MarkdownEngine: case config.MarkdownEngine:
e = markdown.New(c) // Markdown e = markdown.New(c) // Markdown
case config.JadeEngine:
e = jade.New(c) // Jade
case config.AmberEngine: case config.AmberEngine:
setSharedFuncs(sharedFuncs, c.Amber.Funcs)
e = amber.New(c) // Amber e = amber.New(c) // Amber
default: // config.NoEngine default: // config.NoEngine
return nil 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 // Render renders a template using the context's writer
func (t *Template) Render(ctx context.IContext, name string, binding interface{}, layout ...string) (err error) { func (t *Template) Render(ctx context.IContext, name string, binding interface{}, layout ...string) (err error) {

View File

@ -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 // RouteByName returns a route by its name,if not found then returns a route with empty path
// Note that the searching is case-sensitive // 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 { for _, route := range r.lookups {
if route.GetName() == lookUpName { if route.GetName() == routeName {
return route return route
} }
} }
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 //check if any tree has cors setted to true, means that cors middleware is added
func (r *router) cors() (has bool) { func (r *router) cors() (has bool) {
r.garden.visitAll(func(i int, tree *tree) { r.garden.visitAll(func(i int, tree *tree) {