engine.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package template // import "miniflux.app/v2/internal/template"
  4. import (
  5. "bytes"
  6. "embed"
  7. "html/template"
  8. "log/slog"
  9. "time"
  10. "miniflux.app/v2/internal/locale"
  11. "github.com/gorilla/mux"
  12. )
  13. //go:embed templates/common/*.html
  14. var commonTemplateFiles embed.FS
  15. //go:embed templates/views/*.html
  16. var viewTemplateFiles embed.FS
  17. //go:embed templates/standalone/*.html
  18. var standaloneTemplateFiles embed.FS
  19. // Engine handles the templating system.
  20. type Engine struct {
  21. templates map[string]*template.Template
  22. funcMap *funcMap
  23. }
  24. // NewEngine returns a new template engine.
  25. func NewEngine(router *mux.Router) *Engine {
  26. return &Engine{
  27. templates: make(map[string]*template.Template),
  28. funcMap: &funcMap{router},
  29. }
  30. }
  31. // ParseTemplates parses template files embed into the application.
  32. func (e *Engine) ParseTemplates() error {
  33. funcMap := e.funcMap.Map()
  34. commonTemplates := template.Must(template.New("").Funcs(funcMap).ParseFS(commonTemplateFiles, "templates/common/*.html"))
  35. dirEntries, err := viewTemplateFiles.ReadDir("templates/views")
  36. if err != nil {
  37. return err
  38. }
  39. for _, dirEntry := range dirEntries {
  40. fullName := "templates/views/" + dirEntry.Name()
  41. slog.Debug("Parsing template", slog.String("template_name", fullName))
  42. commonTemplatesClone, err := commonTemplates.Clone()
  43. if err != nil {
  44. panic("Unable to clone the common template")
  45. }
  46. e.templates[dirEntry.Name()] = template.Must(commonTemplatesClone.ParseFS(viewTemplateFiles, fullName))
  47. }
  48. dirEntries, err = standaloneTemplateFiles.ReadDir("templates/standalone")
  49. if err != nil {
  50. return err
  51. }
  52. for _, dirEntry := range dirEntries {
  53. fullName := "templates/standalone/" + dirEntry.Name()
  54. slog.Debug("Parsing template", slog.String("template_name", fullName))
  55. e.templates[dirEntry.Name()] = template.Must(template.New(dirEntry.Name()).Funcs(funcMap).ParseFS(standaloneTemplateFiles, fullName))
  56. }
  57. return nil
  58. }
  59. // Render process a template.
  60. func (e *Engine) Render(name string, data map[string]any) []byte {
  61. tpl, ok := e.templates[name]
  62. if !ok {
  63. panic("This template does not exists: " + name)
  64. }
  65. printer := locale.NewPrinter(data["language"].(string))
  66. // Functions that need to be declared at runtime.
  67. tpl.Funcs(template.FuncMap{
  68. "elapsed": func(timezone string, t time.Time) string {
  69. return elapsedTime(printer, timezone, t)
  70. },
  71. "t": printer.Printf,
  72. "plural": printer.Plural,
  73. })
  74. var b bytes.Buffer
  75. err := tpl.ExecuteTemplate(&b, "base", data)
  76. if err != nil {
  77. panic(err)
  78. }
  79. return b.Bytes()
  80. }