A webmail client. Forked from https://git.sr.ht/~migadu/alps
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

157 linhas
3.6 KiB

  1. package koushin
  2. import (
  3. "fmt"
  4. "html/template"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "github.com/labstack/echo/v4"
  9. )
  10. const themesDir = "themes"
  11. // GlobalRenderData contains data available in all templates.
  12. type GlobalRenderData struct {
  13. LoggedIn bool
  14. // if logged in
  15. Username string
  16. // TODO: list of mailboxes
  17. // additional plugin-specific data
  18. Extra map[string]interface{}
  19. }
  20. // BaseRenderData is the base type for templates. It should be extended with
  21. // additional template-specific fields:
  22. //
  23. // type MyRenderData struct {
  24. // BaseRenderData
  25. // // add additional fields here
  26. // }
  27. type BaseRenderData struct {
  28. GlobalData GlobalRenderData
  29. // additional plugin-specific data
  30. Extra map[string]interface{}
  31. }
  32. // Global implements RenderData.
  33. func (brd *BaseRenderData) Global() *GlobalRenderData {
  34. return &brd.GlobalData
  35. }
  36. // RenderData is implemented by template data structs. It can be used to inject
  37. // additional data to all templates.
  38. type RenderData interface {
  39. // GlobalData returns a pointer to the global render data.
  40. Global() *GlobalRenderData
  41. }
  42. // NewBaseRenderData initializes a new BaseRenderData.
  43. //
  44. // It can be used by routes to pre-fill the base data:
  45. //
  46. // type MyRenderData struct {
  47. // BaseRenderData
  48. // // add additional fields here
  49. // }
  50. //
  51. // data := &MyRenderData{
  52. // BaseRenderData: *koushin.NewBaseRenderData(ctx),
  53. // // other fields...
  54. // }
  55. func NewBaseRenderData(ctx *Context) *BaseRenderData {
  56. global := GlobalRenderData{Extra: make(map[string]interface{})}
  57. if ctx.Session != nil {
  58. global.LoggedIn = true
  59. global.Username = ctx.Session.username
  60. }
  61. return &BaseRenderData{
  62. GlobalData: global,
  63. Extra: make(map[string]interface{}),
  64. }
  65. }
  66. type renderer struct {
  67. base *template.Template
  68. themes map[string]*template.Template
  69. defaultTheme string
  70. }
  71. func (r *renderer) Render(w io.Writer, name string, data interface{}, ectx echo.Context) error {
  72. // ectx is the raw *echo.context, not our own *Context
  73. ctx := ectx.Get("context").(*Context)
  74. for _, plugin := range ctx.Server.Plugins {
  75. if err := plugin.Inject(ctx, name, data.(RenderData)); err != nil {
  76. return fmt.Errorf("failed to run plugin '%v': %v", plugin.Name(), err)
  77. }
  78. }
  79. // TODO: per-user theme selection
  80. t := r.base
  81. if r.defaultTheme != "" {
  82. t = r.themes[r.defaultTheme]
  83. }
  84. return t.ExecuteTemplate(w, name, data)
  85. }
  86. func loadTheme(name string, base *template.Template) (*template.Template, error) {
  87. theme, err := base.Clone()
  88. if err != nil {
  89. return nil, err
  90. }
  91. theme, err = theme.ParseGlob(themesDir + "/" + name + "/*.html")
  92. if err != nil {
  93. return nil, err
  94. }
  95. return theme, nil
  96. }
  97. func loadTemplates(logger echo.Logger, defaultTheme string, plugins []Plugin) (*renderer, error) {
  98. base := template.New("")
  99. for _, p := range plugins {
  100. if err := p.LoadTemplate(base); err != nil {
  101. return nil, fmt.Errorf("failed to load template for plugin '%v': %v", p.Name(), err)
  102. }
  103. }
  104. themes := make(map[string]*template.Template)
  105. files, err := ioutil.ReadDir(themesDir)
  106. if err != nil && !os.IsNotExist(err) {
  107. return nil, err
  108. }
  109. for _, fi := range files {
  110. if !fi.IsDir() {
  111. continue
  112. }
  113. logger.Printf("Loading theme '%v'", fi.Name())
  114. var err error
  115. if themes[fi.Name()], err = loadTheme(fi.Name(), base); err != nil {
  116. return nil, fmt.Errorf("failed to load theme '%v': %v", fi.Name(), err)
  117. }
  118. }
  119. if defaultTheme != "" {
  120. if _, ok := themes[defaultTheme]; !ok {
  121. return nil, fmt.Errorf("failed to find default theme '%v'", defaultTheme)
  122. }
  123. }
  124. return &renderer{
  125. base: base,
  126. themes: themes,
  127. defaultTheme: defaultTheme,
  128. }, nil
  129. }