From 9fdccc3a4beaf36195a14d7dd17801681f93d45b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 20 Jan 2020 22:05:42 +0100 Subject: Rename template.go to renderer.go --- renderer.go | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ template.go | 178 ------------------------------------------------------------ 2 files changed, 178 insertions(+), 178 deletions(-) create mode 100644 renderer.go delete mode 100644 template.go diff --git a/renderer.go b/renderer.go new file mode 100644 index 0000000..b229d34 --- /dev/null +++ b/renderer.go @@ -0,0 +1,178 @@ +package koushin + +import ( + "fmt" + "html/template" + "io" + "io/ioutil" + "os" + + "github.com/labstack/echo/v4" +) + +const themesDir = "themes" + +// GlobalRenderData contains data available in all templates. +type GlobalRenderData struct { + Path string + + LoggedIn bool + + // if logged in + Username string + // TODO: list of mailboxes + + // additional plugin-specific data + Extra map[string]interface{} +} + +// BaseRenderData is the base type for templates. It should be extended with +// additional template-specific fields: +// +// type MyRenderData struct { +// BaseRenderData +// // add additional fields here +// } +type BaseRenderData struct { + GlobalData GlobalRenderData + // additional plugin-specific data + Extra map[string]interface{} +} + +// Global implements RenderData. +func (brd *BaseRenderData) Global() *GlobalRenderData { + return &brd.GlobalData +} + +// RenderData is implemented by template data structs. It can be used to inject +// additional data to all templates. +type RenderData interface { + // GlobalData returns a pointer to the global render data. + Global() *GlobalRenderData +} + +// NewBaseRenderData initializes a new BaseRenderData. +// +// It can be used by routes to pre-fill the base data: +// +// type MyRenderData struct { +// BaseRenderData +// // add additional fields here +// } +// +// data := &MyRenderData{ +// BaseRenderData: *koushin.NewBaseRenderData(ctx), +// // other fields... +// } +func NewBaseRenderData(ctx *Context) *BaseRenderData { + global := GlobalRenderData{Extra: make(map[string]interface{})} + + if ctx.Session != nil { + global.LoggedIn = true + global.Username = ctx.Session.username + } + + global.Path = ctx.Request().URL.String() + + return &BaseRenderData{ + GlobalData: global, + Extra: make(map[string]interface{}), + } +} + +type renderer struct { + logger echo.Logger + defaultTheme string + + base *template.Template + themes map[string]*template.Template +} + +func (r *renderer) Render(w io.Writer, name string, data interface{}, ectx echo.Context) error { + // ectx is the raw *echo.context, not our own *Context + ctx := ectx.Get("context").(*Context) + + var renderData RenderData + if data == nil { + renderData = &struct{ BaseRenderData }{*NewBaseRenderData(ctx)} + } else { + var ok bool + renderData, ok = data.(RenderData) + if !ok { + return fmt.Errorf("data passed to template '%v' doesn't implement RenderData", name) + } + } + + for _, plugin := range ctx.Server.plugins { + if err := plugin.Inject(ctx, name, renderData); err != nil { + return fmt.Errorf("failed to run plugin '%v': %v", plugin.Name(), err) + } + } + + // TODO: per-user theme selection + t := r.base + if r.defaultTheme != "" { + t = r.themes[r.defaultTheme] + } + return t.ExecuteTemplate(w, name, data) +} + +func loadTheme(name string, base *template.Template) (*template.Template, error) { + theme, err := base.Clone() + if err != nil { + return nil, err + } + + theme, err = theme.ParseGlob(themesDir + "/" + name + "/*.html") + if err != nil { + return nil, err + } + + return theme, nil +} + +func (r *renderer) Load(plugins []Plugin) error { + base := template.New("") + + for _, p := range plugins { + if err := p.LoadTemplate(base); err != nil { + return fmt.Errorf("failed to load template for plugin '%v': %v", p.Name(), err) + } + } + + themes := make(map[string]*template.Template) + + files, err := ioutil.ReadDir(themesDir) + if err != nil && !os.IsNotExist(err) { + return err + } + + for _, fi := range files { + if !fi.IsDir() { + continue + } + + r.logger.Printf("Loading theme '%v'", fi.Name()) + var err error + if themes[fi.Name()], err = loadTheme(fi.Name(), base); err != nil { + return fmt.Errorf("failed to load theme '%v': %v", fi.Name(), err) + } + } + + if r.defaultTheme != "" { + if _, ok := themes[r.defaultTheme]; !ok { + return fmt.Errorf("failed to find default theme '%v'", r.defaultTheme) + } + } + + r.base = base + r.themes = themes + return nil +} + +func newRenderer(logger echo.Logger, defaultTheme string) *renderer { + return &renderer{ + logger: logger, + defaultTheme: defaultTheme, + } +} diff --git a/template.go b/template.go deleted file mode 100644 index b229d34..0000000 --- a/template.go +++ /dev/null @@ -1,178 +0,0 @@ -package koushin - -import ( - "fmt" - "html/template" - "io" - "io/ioutil" - "os" - - "github.com/labstack/echo/v4" -) - -const themesDir = "themes" - -// GlobalRenderData contains data available in all templates. -type GlobalRenderData struct { - Path string - - LoggedIn bool - - // if logged in - Username string - // TODO: list of mailboxes - - // additional plugin-specific data - Extra map[string]interface{} -} - -// BaseRenderData is the base type for templates. It should be extended with -// additional template-specific fields: -// -// type MyRenderData struct { -// BaseRenderData -// // add additional fields here -// } -type BaseRenderData struct { - GlobalData GlobalRenderData - // additional plugin-specific data - Extra map[string]interface{} -} - -// Global implements RenderData. -func (brd *BaseRenderData) Global() *GlobalRenderData { - return &brd.GlobalData -} - -// RenderData is implemented by template data structs. It can be used to inject -// additional data to all templates. -type RenderData interface { - // GlobalData returns a pointer to the global render data. - Global() *GlobalRenderData -} - -// NewBaseRenderData initializes a new BaseRenderData. -// -// It can be used by routes to pre-fill the base data: -// -// type MyRenderData struct { -// BaseRenderData -// // add additional fields here -// } -// -// data := &MyRenderData{ -// BaseRenderData: *koushin.NewBaseRenderData(ctx), -// // other fields... -// } -func NewBaseRenderData(ctx *Context) *BaseRenderData { - global := GlobalRenderData{Extra: make(map[string]interface{})} - - if ctx.Session != nil { - global.LoggedIn = true - global.Username = ctx.Session.username - } - - global.Path = ctx.Request().URL.String() - - return &BaseRenderData{ - GlobalData: global, - Extra: make(map[string]interface{}), - } -} - -type renderer struct { - logger echo.Logger - defaultTheme string - - base *template.Template - themes map[string]*template.Template -} - -func (r *renderer) Render(w io.Writer, name string, data interface{}, ectx echo.Context) error { - // ectx is the raw *echo.context, not our own *Context - ctx := ectx.Get("context").(*Context) - - var renderData RenderData - if data == nil { - renderData = &struct{ BaseRenderData }{*NewBaseRenderData(ctx)} - } else { - var ok bool - renderData, ok = data.(RenderData) - if !ok { - return fmt.Errorf("data passed to template '%v' doesn't implement RenderData", name) - } - } - - for _, plugin := range ctx.Server.plugins { - if err := plugin.Inject(ctx, name, renderData); err != nil { - return fmt.Errorf("failed to run plugin '%v': %v", plugin.Name(), err) - } - } - - // TODO: per-user theme selection - t := r.base - if r.defaultTheme != "" { - t = r.themes[r.defaultTheme] - } - return t.ExecuteTemplate(w, name, data) -} - -func loadTheme(name string, base *template.Template) (*template.Template, error) { - theme, err := base.Clone() - if err != nil { - return nil, err - } - - theme, err = theme.ParseGlob(themesDir + "/" + name + "/*.html") - if err != nil { - return nil, err - } - - return theme, nil -} - -func (r *renderer) Load(plugins []Plugin) error { - base := template.New("") - - for _, p := range plugins { - if err := p.LoadTemplate(base); err != nil { - return fmt.Errorf("failed to load template for plugin '%v': %v", p.Name(), err) - } - } - - themes := make(map[string]*template.Template) - - files, err := ioutil.ReadDir(themesDir) - if err != nil && !os.IsNotExist(err) { - return err - } - - for _, fi := range files { - if !fi.IsDir() { - continue - } - - r.logger.Printf("Loading theme '%v'", fi.Name()) - var err error - if themes[fi.Name()], err = loadTheme(fi.Name(), base); err != nil { - return fmt.Errorf("failed to load theme '%v': %v", fi.Name(), err) - } - } - - if r.defaultTheme != "" { - if _, ok := themes[r.defaultTheme]; !ok { - return fmt.Errorf("failed to find default theme '%v'", r.defaultTheme) - } - } - - r.base = base - r.themes = themes - return nil -} - -func newRenderer(logger echo.Logger, defaultTheme string) *renderer { - return &renderer{ - logger: logger, - defaultTheme: defaultTheme, - } -} -- cgit v1.2.3-59-g8ed1b