template.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Copyright 2017 Frédéric Guilloe. 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
  5. import (
  6. "bytes"
  7. "html/template"
  8. "io"
  9. "net/mail"
  10. "strings"
  11. "time"
  12. "github.com/miniflux/miniflux/config"
  13. "github.com/miniflux/miniflux/errors"
  14. "github.com/miniflux/miniflux/locale"
  15. "github.com/miniflux/miniflux/logger"
  16. "github.com/miniflux/miniflux/server/route"
  17. "github.com/miniflux/miniflux/server/template/helper"
  18. "github.com/miniflux/miniflux/server/ui/filter"
  19. "github.com/miniflux/miniflux/url"
  20. "github.com/gorilla/mux"
  21. )
  22. // Engine handles the templating system.
  23. type Engine struct {
  24. templates map[string]*template.Template
  25. router *mux.Router
  26. translator *locale.Translator
  27. currentLocale *locale.Language
  28. cfg *config.Config
  29. }
  30. func (e *Engine) parseAll() {
  31. funcMap := template.FuncMap{
  32. "baseURL": func() string {
  33. return e.cfg.Get("BASE_URL", config.DefaultBaseURL)
  34. },
  35. "hasOAuth2Provider": func(provider string) bool {
  36. return e.cfg.Get("OAUTH2_PROVIDER", "") == provider
  37. },
  38. "hasKey": func(dict map[string]string, key string) bool {
  39. if value, found := dict[key]; found {
  40. return value != ""
  41. }
  42. return false
  43. },
  44. "route": func(name string, args ...interface{}) string {
  45. return route.Path(e.router, name, args...)
  46. },
  47. "noescape": func(str string) template.HTML {
  48. return template.HTML(str)
  49. },
  50. "proxyFilter": func(data string) string {
  51. return filter.ImageProxyFilter(e.router, data)
  52. },
  53. "proxyURL": func(link string) string {
  54. if url.IsHTTPS(link) {
  55. return link
  56. }
  57. return filter.Proxify(e.router, link)
  58. },
  59. "domain": func(websiteURL string) string {
  60. return url.Domain(websiteURL)
  61. },
  62. "isEmail": func(str string) bool {
  63. _, err := mail.ParseAddress(str)
  64. if err != nil {
  65. return false
  66. }
  67. return true
  68. },
  69. "hasPrefix": func(str, prefix string) bool {
  70. return strings.HasPrefix(str, prefix)
  71. },
  72. "contains": func(str, substr string) bool {
  73. return strings.Contains(str, substr)
  74. },
  75. "isodate": func(ts time.Time) string {
  76. return ts.Format("2006-01-02 15:04:05")
  77. },
  78. "elapsed": func(ts time.Time) string {
  79. return helper.GetElapsedTime(e.currentLocale, ts)
  80. },
  81. "t": func(key interface{}, args ...interface{}) string {
  82. switch key.(type) {
  83. case string:
  84. return e.currentLocale.Get(key.(string), args...)
  85. case errors.LocalizedError:
  86. err := key.(errors.LocalizedError)
  87. return err.Localize(e.currentLocale)
  88. case error:
  89. return key.(error).Error()
  90. default:
  91. return ""
  92. }
  93. },
  94. "plural": func(key string, n int, args ...interface{}) string {
  95. return e.currentLocale.Plural(key, n, args...)
  96. },
  97. }
  98. commonTemplates := ""
  99. for _, content := range templateCommonMap {
  100. commonTemplates += content
  101. }
  102. for name, content := range templateViewsMap {
  103. logger.Debug("[Template] Parsing: %s", name)
  104. e.templates[name] = template.Must(template.New("main").Funcs(funcMap).Parse(commonTemplates + content))
  105. }
  106. }
  107. // SetLanguage change the language for template processing.
  108. func (e *Engine) SetLanguage(language string) {
  109. e.currentLocale = e.translator.GetLanguage(language)
  110. }
  111. // Execute process a template.
  112. func (e *Engine) Execute(w io.Writer, name string, data interface{}) {
  113. tpl, ok := e.templates[name]
  114. if !ok {
  115. logger.Fatal("[Template] The template %s does not exists", name)
  116. }
  117. var b bytes.Buffer
  118. err := tpl.ExecuteTemplate(&b, "base", data)
  119. if err != nil {
  120. logger.Fatal("[Template] Unable to render template: %v", err)
  121. }
  122. b.WriteTo(w)
  123. }
  124. // NewEngine returns a new template Engine.
  125. func NewEngine(cfg *config.Config, router *mux.Router, translator *locale.Translator) *Engine {
  126. tpl := &Engine{
  127. templates: make(map[string]*template.Template),
  128. router: router,
  129. translator: translator,
  130. cfg: cfg,
  131. }
  132. tpl.parseAll()
  133. return tpl
  134. }