engine.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. "strings"
  9. "time"
  10. "miniflux.app/v2/internal/errors"
  11. "miniflux.app/v2/internal/locale"
  12. "miniflux.app/v2/internal/logger"
  13. "github.com/gorilla/mux"
  14. )
  15. //go:embed templates/common/*.html
  16. var commonTemplateFiles embed.FS
  17. //go:embed templates/views/*.html
  18. var viewTemplateFiles embed.FS
  19. //go:embed templates/standalone/*.html
  20. var standaloneTemplateFiles embed.FS
  21. // Engine handles the templating system.
  22. type Engine struct {
  23. templates map[string]*template.Template
  24. funcMap *funcMap
  25. }
  26. // NewEngine returns a new template engine.
  27. func NewEngine(router *mux.Router) *Engine {
  28. return &Engine{
  29. templates: make(map[string]*template.Template),
  30. funcMap: &funcMap{router},
  31. }
  32. }
  33. // ParseTemplates parses template files embed into the application.
  34. func (e *Engine) ParseTemplates() error {
  35. var commonTemplateContents strings.Builder
  36. dirEntries, err := commonTemplateFiles.ReadDir("templates/common")
  37. if err != nil {
  38. return err
  39. }
  40. for _, dirEntry := range dirEntries {
  41. fileData, err := commonTemplateFiles.ReadFile("templates/common/" + dirEntry.Name())
  42. if err != nil {
  43. return err
  44. }
  45. commonTemplateContents.Write(fileData)
  46. }
  47. dirEntries, err = viewTemplateFiles.ReadDir("templates/views")
  48. if err != nil {
  49. return err
  50. }
  51. for _, dirEntry := range dirEntries {
  52. templateName := dirEntry.Name()
  53. fileData, err := viewTemplateFiles.ReadFile("templates/views/" + dirEntry.Name())
  54. if err != nil {
  55. return err
  56. }
  57. var templateContents strings.Builder
  58. templateContents.WriteString(commonTemplateContents.String())
  59. templateContents.Write(fileData)
  60. logger.Debug("[Template] Parsing: %s", templateName)
  61. e.templates[templateName] = template.Must(template.New("main").Funcs(e.funcMap.Map()).Parse(templateContents.String()))
  62. }
  63. dirEntries, err = standaloneTemplateFiles.ReadDir("templates/standalone")
  64. if err != nil {
  65. return err
  66. }
  67. for _, dirEntry := range dirEntries {
  68. templateName := dirEntry.Name()
  69. fileData, err := standaloneTemplateFiles.ReadFile("templates/standalone/" + dirEntry.Name())
  70. if err != nil {
  71. return err
  72. }
  73. logger.Debug("[Template] Parsing: %s", templateName)
  74. e.templates[templateName] = template.Must(template.New("base").Funcs(e.funcMap.Map()).Parse(string(fileData)))
  75. }
  76. return nil
  77. }
  78. // Render process a template.
  79. func (e *Engine) Render(name string, data map[string]interface{}) []byte {
  80. tpl, ok := e.templates[name]
  81. if !ok {
  82. logger.Fatal("[Template] The template %s does not exists", name)
  83. }
  84. printer := locale.NewPrinter(data["language"].(string))
  85. // Functions that need to be declared at runtime.
  86. tpl.Funcs(template.FuncMap{
  87. "elapsed": func(timezone string, t time.Time) string {
  88. return elapsedTime(printer, timezone, t)
  89. },
  90. "t": func(key interface{}, args ...interface{}) string {
  91. switch k := key.(type) {
  92. case string:
  93. return printer.Printf(k, args...)
  94. case errors.LocalizedError:
  95. return k.Localize(printer)
  96. case *errors.LocalizedError:
  97. return k.Localize(printer)
  98. case error:
  99. return k.Error()
  100. default:
  101. return ""
  102. }
  103. },
  104. "plural": func(key string, n int, args ...interface{}) string {
  105. return printer.Plural(key, n, args...)
  106. },
  107. })
  108. var b bytes.Buffer
  109. err := tpl.ExecuteTemplate(&b, "base", data)
  110. if err != nil {
  111. logger.Fatal("[Template] Unable to render template: %v", err)
  112. }
  113. return b.Bytes()
  114. }