engine.go 3.5 KB

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