You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
6.1 KiB

3 years ago
3 years ago
3 years ago
3 years ago
8 months ago
3 years ago
8 months ago
3 years ago
3 years ago
3 years ago
3 years ago
8 months ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
8 months ago
3 years ago
8 months ago
3 years ago
3 years ago
  1. // Copyright 2018 Lars Hoogestraat
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package middleware
  5. import (
  6. "database/sql"
  7. "fmt"
  8. "html"
  9. "html/template"
  10. "net/http"
  11. "os"
  12. "path/filepath"
  13. "strings"
  14. "time"
  15. "git.hoogi.eu/snafu/cfg"
  16. "git.hoogi.eu/snafu/go-blog/httperror"
  17. "git.hoogi.eu/snafu/go-blog/logger"
  18. "git.hoogi.eu/snafu/go-blog/models"
  19. "git.hoogi.eu/snafu/go-blog/settings"
  20. )
  21. // Template contains the information about the template to render.
  22. // The DisplayMsg in models.Error will be the ErrorMsg in the flash bubble,
  23. // the SuccessMsg is an optional variable which is also displayed as a green flash bubble,
  24. // both are appended to the data map with keys 'ErrorMsg' or 'SuccessMsg' in the AppHandler
  25. type Template struct {
  26. Name string
  27. Active string
  28. Data map[string]interface{}
  29. SuccessMsg string
  30. WarnMsg string
  31. RedirectPath string
  32. Err error
  33. }
  34. // Templates defines in which directory should be looked for template
  35. type Templates struct {
  36. Directory string
  37. FuncMap template.FuncMap
  38. }
  39. // NotFound returned if no route matches
  40. func NotFound(ctx *AppContext, rw http.ResponseWriter, r *http.Request) *Template {
  41. //For deleting flash cookies
  42. getFlash(rw, r, "ErrorMsg")
  43. getFlash(rw, r, "SuccessMsg")
  44. session, _ := ctx.SessionService.Get(rw, r)
  45. if session != nil && strings.HasPrefix(r.URL.EscapedPath(), "/admin") {
  46. return &Template{
  47. Name: "admin/error",
  48. Err: httperror.New(http.StatusNotFound, "Nothing was found at this location", fmt.Errorf("page %s not found", r.URL.EscapedPath())),
  49. }
  50. } else {
  51. return &Template{
  52. Name: "front/error",
  53. Err: httperror.New(http.StatusNotFound, "Nothing was found at this location", fmt.Errorf("page %s not found", r.URL.EscapedPath())),
  54. }
  55. }
  56. }
  57. // FuncMap some function that can be used in templates
  58. func FuncMap(ss models.SiteService, settings *settings.Settings) template.FuncMap {
  59. return template.FuncMap{
  60. "GetMetadata": func(data map[string]interface{}) template.HTML {
  61. var meta, desc string
  62. if len(settings.Description) > 0 {
  63. desc = settings.Description
  64. if len(desc) > 200 {
  65. desc = desc[0:200] + "..."
  66. }
  67. meta = fmt.Sprintf("<meta name=\"description\" content=\"%s\">\n", html.EscapeString(desc))
  68. }
  69. if value, ok := data["article"]; ok {
  70. if art, ok := value.(*models.Article); ok {
  71. desc = art.Teaser
  72. if len(desc) > 200 {
  73. desc = desc[0:200] + "..."
  74. }
  75. meta = fmt.Sprintf("<meta name=\"description\" content=\"%s\">\n", html.EscapeString(desc))
  76. meta += fmt.Sprintf("\t\t<meta name=\"author\" content=\"%s\">\n", html.EscapeString(art.Author.DisplayName))
  77. }
  78. }
  79. if value, ok := data["site"]; ok {
  80. if site, ok := value.(*models.Site); ok {
  81. desc = site.Content
  82. if len(desc) > 200 {
  83. desc = desc[0:200] + "..."
  84. }
  85. meta = fmt.Sprintf("<meta name=\"description\" content=\"%s\">\n", html.EscapeString(desc))
  86. meta += fmt.Sprintf("\t\t<meta name=\"author\" content=\"%s\">\n", html.EscapeString(site.Author.DisplayName))
  87. }
  88. }
  89. return template.HTML(meta)
  90. },
  91. "GetTitle": func(data map[string]interface{}) string {
  92. if value, ok := data["article"]; ok {
  93. if art, ok := value.(*models.Article); ok {
  94. return art.Headline
  95. }
  96. }
  97. if value, ok := data["site"]; ok {
  98. if site, ok := value.(*models.Site); ok {
  99. return site.Title
  100. }
  101. }
  102. return settings.Title
  103. },
  104. "Language": func() string {
  105. return settings.Language
  106. },
  107. "ApplicationURL": func() string {
  108. return settings.Application.Domain
  109. },
  110. "CustomCSS": func() string {
  111. return settings.Application.CustomCSS
  112. },
  113. "OverwriteCSS": func() bool {
  114. return settings.Application.OverwriteCSS
  115. },
  116. "KeepAliveInterval": func() int64 {
  117. return (settings.Session.TTL.Nanoseconds() / 1e9) - 5
  118. },
  119. "PageTitle": func() string {
  120. return settings.Title
  121. },
  122. "BuildVersion": func() string {
  123. return settings.BuildVersion
  124. },
  125. "BuildGitHash": func() string {
  126. return settings.BuildGitHash
  127. },
  128. "NilString": func(s sql.NullString) string {
  129. if !s.Valid {
  130. return ""
  131. }
  132. return s.String
  133. },
  134. "FormatNilDateTime": func(t models.NullTime) string {
  135. if !t.Valid {
  136. return ""
  137. }
  138. return t.Time.In(time.Local).Format("January 2, 2006 at 3:04 PM")
  139. },
  140. "HumanizeFilesize": func(size int64) string {
  141. return cfg.FileSize(size).HumanReadable()
  142. },
  143. "FormatDateTime": func(t time.Time) string {
  144. return t.In(time.Local).Format("January 2, 2006 at 3:04 PM")
  145. },
  146. "FormatNilDate": func(t models.NullTime) string {
  147. if !t.Valid {
  148. return ""
  149. }
  150. return t.Time.In(time.Local).Format("January 2, 2006")
  151. },
  152. "FormatDate": func(t time.Time) string {
  153. return t.In(time.Local).Format("January 2, 2006")
  154. },
  155. "BoolToIcon": func(b bool) template.HTML {
  156. if b {
  157. return template.HTML(`<img alt="circle-checked" src="../assets/svg/circle-check.svg">`)
  158. }
  159. return template.HTML("")
  160. },
  161. "PaginationBar": func(p models.Pagination) template.HTML {
  162. return p.PaginationBar()
  163. },
  164. "ParseMarkdown": func(s string) template.HTML {
  165. return template.HTML(models.MarkdownToHTML([]byte(s)))
  166. },
  167. "NToBr": func(in string) template.HTML {
  168. return template.HTML(models.NewlineToBr(models.EscapeHTML(in)))
  169. },
  170. "EscapeHTML": func(in string) string {
  171. return html.EscapeString(in)
  172. },
  173. "GetSites": func() []models.Site {
  174. sites, err := ss.List(models.OnlyPublished, nil)
  175. if err != nil {
  176. logger.Log.Error(err)
  177. return nil
  178. }
  179. return sites
  180. },
  181. }
  182. }
  183. // Load walks threw directory and parses templates ending with html
  184. func (ts Templates) Load() (*template.Template, error) {
  185. tpl := template.New("").Funcs(ts.FuncMap)
  186. err := filepath.Walk(ts.Directory, func(path string, info os.FileInfo, err error) error {
  187. if err != nil {
  188. return err
  189. }
  190. if strings.Contains(path, ".html") {
  191. tpls, err := tpl.ParseFiles(path)
  192. if err != nil {
  193. return err
  194. }
  195. template.Must(tpls, err)
  196. }
  197. return nil
  198. })
  199. return tpl, err
  200. }
  201. // RedirectURL builds a URL for redirecting
  202. func (t Template) RedirectURL() string {
  203. if t.RedirectPath[0] == byte('/') {
  204. return t.RedirectPath
  205. }
  206. return "/" + t.RedirectPath
  207. }