Add RenderData interface

This allows Go plugins to easily inject global data.
This commit is contained in:
Simon Ser 2019-12-17 15:01:15 +01:00
parent d8a875a5f7
commit ca3672df2a
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
5 changed files with 57 additions and 27 deletions

View file

@ -18,7 +18,7 @@ type Plugin interface {
SetRoutes(group *echo.Group) SetRoutes(group *echo.Group)
// Inject is called prior to rendering a template. It can extend the // Inject is called prior to rendering a template. It can extend the
// template data by setting new items in the Extra map. // template data by setting new items in the Extra map.
Inject(name string, data interface{}) error Inject(name string, data RenderData) error
// Close is called when the plugin is unloaded. // Close is called when the plugin is unloaded.
Close() error Close() error
} }

View file

@ -40,7 +40,7 @@ func (p *goPlugin) SetRoutes(group *echo.Group) {
group.Static("/plugins/"+p.p.Name+"/assets", pluginDir+"/"+p.p.Name+"/public/assets") group.Static("/plugins/"+p.p.Name+"/assets", pluginDir+"/"+p.p.Name+"/public/assets")
} }
func (p *goPlugin) Inject(name string, data interface{}) error { func (p *goPlugin) Inject(name string, data RenderData) error {
if f, ok := p.p.injectFuncs["*"]; ok { if f, ok := p.p.injectFuncs["*"]; ok {
if err := f(data); err != nil { if err := f(data); err != nil {
return err return err
@ -114,7 +114,7 @@ func (p *GoPlugin) TemplateFuncs(funcs template.FuncMap) {
} }
// InjectFunc is a function that injects data prior to rendering a template. // InjectFunc is a function that injects data prior to rendering a template.
type InjectFunc func(data interface{}) error type InjectFunc func(data RenderData) error
// Inject registers a function to execute prior to rendering a template. The // Inject registers a function to execute prior to rendering a template. The
// special name "*" matches any template. // special name "*" matches any template.

View file

@ -68,7 +68,7 @@ func (p *luaPlugin) setRoute(l *lua.LState) int {
return 0 return 0
} }
func (p *luaPlugin) inject(name string, data interface{}) error { func (p *luaPlugin) inject(name string, data RenderData) error {
f, ok := p.renderCallbacks[name] f, ok := p.renderCallbacks[name]
if !ok { if !ok {
return nil return nil
@ -86,7 +86,7 @@ func (p *luaPlugin) inject(name string, data interface{}) error {
return nil return nil
} }
func (p *luaPlugin) Inject(name string, data interface{}) error { func (p *luaPlugin) Inject(name string, data RenderData) error {
if err := p.inject("*", data); err != nil { if err := p.inject("*", data); err != nil {
return err return err
} }

View file

@ -116,12 +116,12 @@ func handleGetMailbox(ectx echo.Context) error {
return ctx.Render(http.StatusOK, "mailbox.html", &MailboxRenderData{ return ctx.Render(http.StatusOK, "mailbox.html", &MailboxRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx), BaseRenderData: *koushin.NewBaseRenderData(ctx),
Mailbox: mbox, Mailbox: mbox,
Mailboxes: mailboxes, Mailboxes: mailboxes,
Messages: msgs, Messages: msgs,
PrevPage: prevPage, PrevPage: prevPage,
NextPage: nextPage, NextPage: nextPage,
Query: query, Query: query,
}) })
} }
@ -239,14 +239,14 @@ func handleGetPart(ctx *koushin.Context, raw bool) error {
} }
return ctx.Render(http.StatusOK, "message.html", &MessageRenderData{ return ctx.Render(http.StatusOK, "message.html", &MessageRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx), BaseRenderData: *koushin.NewBaseRenderData(ctx),
Mailboxes: mailboxes, Mailboxes: mailboxes,
Mailbox: mbox, Mailbox: mbox,
Message: msg, Message: msg,
Body: body, Body: body,
PartPath: partPathString, PartPath: partPathString,
MailboxPage: int(mbox.Messages-msg.SeqNum) / messagesPerPage, MailboxPage: int(mbox.Messages-msg.SeqNum) / messagesPerPage,
Flags: flags, Flags: flags,
}) })
} }
@ -354,7 +354,7 @@ func handleCompose(ectx echo.Context) error {
return ctx.Render(http.StatusOK, "compose.html", &ComposeRenderData{ return ctx.Render(http.StatusOK, "compose.html", &ComposeRenderData{
BaseRenderData: *koushin.NewBaseRenderData(ctx), BaseRenderData: *koushin.NewBaseRenderData(ctx),
Message: &msg, Message: &msg,
}) })
} }

View file

@ -20,18 +20,48 @@ type GlobalRenderData struct {
Username string Username string
// TODO: list of mailboxes // TODO: list of mailboxes
// Additional plugin-specific data // additional plugin-specific data
Extra map[string]interface{} Extra map[string]interface{}
} }
// BaseRenderData is the base type for templates. It should be extended with // BaseRenderData is the base type for templates. It should be extended with
// new template-specific fields. // additional template-specific fields:
//
// type MyRenderData struct {
// BaseRenderData
// // add additional fields here
// }
type BaseRenderData struct { type BaseRenderData struct {
Global GlobalRenderData GlobalData GlobalRenderData
// Additional plugin-specific data // additional plugin-specific data
Extra map[string]interface{} 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 { func NewBaseRenderData(ctx *Context) *BaseRenderData {
global := GlobalRenderData{Extra: make(map[string]interface{})} global := GlobalRenderData{Extra: make(map[string]interface{})}
@ -41,8 +71,8 @@ func NewBaseRenderData(ctx *Context) *BaseRenderData {
} }
return &BaseRenderData{ return &BaseRenderData{
Global: global, GlobalData: global,
Extra: make(map[string]interface{}), Extra: make(map[string]interface{}),
} }
} }
@ -57,7 +87,7 @@ func (r *renderer) Render(w io.Writer, name string, data interface{}, ectx echo.
ctx := ectx.Get("context").(*Context) ctx := ectx.Get("context").(*Context)
for _, plugin := range ctx.Server.Plugins { for _, plugin := range ctx.Server.Plugins {
if err := plugin.Inject(name, data); err != nil { if err := plugin.Inject(name, data.(RenderData)); err != nil {
return fmt.Errorf("failed to run plugin '%v': %v", plugin.Name(), err) return fmt.Errorf("failed to run plugin '%v': %v", plugin.Name(), err)
} }
} }