A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

242 lignes
6.7 KiB

  1. /*
  2. * Copyright © 2018-2021 Musing Studio LLC.
  3. *
  4. * This file is part of WriteFreely.
  5. *
  6. * WriteFreely is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License, included
  8. * in the LICENSE file in this source code package.
  9. */
  10. package writefreely
  11. import (
  12. "errors"
  13. "html/template"
  14. "io"
  15. "io/ioutil"
  16. "net/http"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "github.com/dustin/go-humanize"
  21. "github.com/writeas/web-core/l10n"
  22. "github.com/writeas/web-core/log"
  23. "github.com/writefreely/writefreely/config"
  24. )
  25. var (
  26. templates = map[string]*template.Template{}
  27. pages = map[string]*template.Template{}
  28. userPages = map[string]*template.Template{}
  29. funcMap = template.FuncMap{
  30. "largeNumFmt": largeNumFmt,
  31. "pluralize": pluralize,
  32. "isRTL": isRTL,
  33. "isLTR": isLTR,
  34. "localstr": localStr,
  35. "localhtml": localHTML,
  36. "tolower": strings.ToLower,
  37. "title": strings.Title,
  38. "hasPrefix": strings.HasPrefix,
  39. "hasSuffix": strings.HasSuffix,
  40. "dict": dict,
  41. }
  42. )
  43. const (
  44. templatesDir = "templates"
  45. pagesDir = "pages"
  46. )
  47. func showUserPage(w http.ResponseWriter, name string, obj interface{}) {
  48. if obj == nil {
  49. log.Error("showUserPage: data is nil!")
  50. return
  51. }
  52. if err := userPages[filepath.Join("user", name+".tmpl")].ExecuteTemplate(w, name, obj); err != nil {
  53. log.Error("Error parsing %s: %v", name, err)
  54. }
  55. }
  56. func initTemplate(parentDir, name string) {
  57. if debugging {
  58. log.Info(" " + filepath.Join(parentDir, templatesDir, name+".tmpl"))
  59. }
  60. files := []string{
  61. filepath.Join(parentDir, templatesDir, name+".tmpl"),
  62. filepath.Join(parentDir, templatesDir, "include", "footer.tmpl"),
  63. filepath.Join(parentDir, templatesDir, "base.tmpl"),
  64. filepath.Join(parentDir, templatesDir, "user", "include", "silenced.tmpl"),
  65. }
  66. if name == "collection" || name == "collection-tags" || name == "chorus-collection" || name == "read" {
  67. // These pages list out collection posts, so we also parse templatesDir + "include/posts.tmpl"
  68. files = append(files, filepath.Join(parentDir, templatesDir, "include", "posts.tmpl"))
  69. }
  70. if name == "chorus-collection" || name == "chorus-collection-post" {
  71. files = append(files, filepath.Join(parentDir, templatesDir, "user", "include", "header.tmpl"))
  72. }
  73. if name == "collection" || name == "collection-tags" || name == "collection-post" || name == "post" || name == "chorus-collection" || name == "chorus-collection-post" {
  74. files = append(files, filepath.Join(parentDir, templatesDir, "include", "post-render.tmpl"))
  75. }
  76. templates[name] = template.Must(template.New("").Funcs(funcMap).ParseFiles(files...))
  77. }
  78. func initPage(parentDir, path, key string) {
  79. if debugging {
  80. log.Info(" [%s] %s", key, path)
  81. }
  82. files := []string{
  83. path,
  84. filepath.Join(parentDir, templatesDir, "include", "footer.tmpl"),
  85. filepath.Join(parentDir, templatesDir, "base.tmpl"),
  86. filepath.Join(parentDir, templatesDir, "user", "include", "silenced.tmpl"),
  87. }
  88. if key == "login.tmpl" || key == "landing.tmpl" || key == "signup.tmpl" {
  89. files = append(files, filepath.Join(parentDir, templatesDir, "include", "oauth.tmpl"))
  90. }
  91. pages[key] = template.Must(template.New("").Funcs(funcMap).ParseFiles(files...))
  92. }
  93. func initUserPage(parentDir, path, key string) {
  94. if debugging {
  95. log.Info(" [%s] %s", key, path)
  96. }
  97. userPages[key] = template.Must(template.New(key).Funcs(funcMap).ParseFiles(
  98. path,
  99. filepath.Join(parentDir, templatesDir, "user", "include", "header.tmpl"),
  100. filepath.Join(parentDir, templatesDir, "user", "include", "footer.tmpl"),
  101. filepath.Join(parentDir, templatesDir, "user", "include", "silenced.tmpl"),
  102. filepath.Join(parentDir, templatesDir, "user", "include", "nav.tmpl"),
  103. ))
  104. }
  105. // InitTemplates loads all template files from the configured parent dir.
  106. func InitTemplates(cfg *config.Config) error {
  107. log.Info("Loading templates...")
  108. tmplFiles, err := ioutil.ReadDir(filepath.Join(cfg.Server.TemplatesParentDir, templatesDir))
  109. if err != nil {
  110. return err
  111. }
  112. for _, f := range tmplFiles {
  113. if !f.IsDir() && !strings.HasPrefix(f.Name(), ".") {
  114. parts := strings.Split(f.Name(), ".")
  115. key := parts[0]
  116. initTemplate(cfg.Server.TemplatesParentDir, key)
  117. }
  118. }
  119. log.Info("Loading pages...")
  120. // Initialize all static pages that use the base template
  121. err = filepath.Walk(filepath.Join(cfg.Server.PagesParentDir, pagesDir), func(path string, i os.FileInfo, err error) error {
  122. if err != nil {
  123. return err
  124. }
  125. if !i.IsDir() && !strings.HasPrefix(i.Name(), ".") {
  126. key := i.Name()
  127. initPage(cfg.Server.PagesParentDir, path, key)
  128. }
  129. return nil
  130. })
  131. if err != nil {
  132. return err
  133. }
  134. log.Info("Loading user pages...")
  135. // Initialize all user pages that use base templates
  136. err = filepath.Walk(filepath.Join(cfg.Server.TemplatesParentDir, templatesDir, "user"), func(path string, f os.FileInfo, err error) error {
  137. if err != nil {
  138. return err
  139. }
  140. if !f.IsDir() && !strings.HasPrefix(f.Name(), ".") {
  141. corePath := path
  142. if cfg.Server.TemplatesParentDir != "" {
  143. corePath = corePath[len(cfg.Server.TemplatesParentDir)+1:]
  144. }
  145. parts := strings.Split(corePath, string(filepath.Separator))
  146. key := f.Name()
  147. if len(parts) > 2 {
  148. key = filepath.Join(parts[1], f.Name())
  149. }
  150. initUserPage(cfg.Server.TemplatesParentDir, path, key)
  151. }
  152. return nil
  153. })
  154. if err != nil {
  155. return err
  156. }
  157. return nil
  158. }
  159. // renderPage retrieves the given template and renders it to the given io.Writer.
  160. // If something goes wrong, the error is logged and returned.
  161. func renderPage(w io.Writer, tmpl string, data interface{}) error {
  162. err := pages[tmpl].ExecuteTemplate(w, "base", data)
  163. if err != nil {
  164. log.Error("%v", err)
  165. }
  166. return err
  167. }
  168. func largeNumFmt(n int64) string {
  169. return humanize.Comma(n)
  170. }
  171. func pluralize(singular, plural string, n int64) string {
  172. if n == 1 {
  173. return singular
  174. }
  175. return plural
  176. }
  177. func isRTL(d string) bool {
  178. return d == "rtl"
  179. }
  180. func isLTR(d string) bool {
  181. return d == "ltr" || d == "auto"
  182. }
  183. func localStr(term, lang string) string {
  184. s := l10n.Strings(lang)[term]
  185. if s == "" {
  186. s = l10n.Strings("")[term]
  187. }
  188. return s
  189. }
  190. func localHTML(term, lang string) template.HTML {
  191. s := l10n.Strings(lang)[term]
  192. if s == "" {
  193. s = l10n.Strings("")[term]
  194. }
  195. s = strings.Replace(s, "write.as", "<a href=\"https://writefreely.org\">writefreely</a>", 1)
  196. return template.HTML(s)
  197. }
  198. // from: https://stackoverflow.com/a/18276968/1549194
  199. func dict(values ...interface{}) (map[string]interface{}, error) {
  200. if len(values)%2 != 0 {
  201. return nil, errors.New("dict: invalid number of parameters")
  202. }
  203. dict := make(map[string]interface{}, len(values)/2)
  204. for i := 0; i < len(values); i += 2 {
  205. key, ok := values[i].(string)
  206. if !ok {
  207. return nil, errors.New("dict: keys must be strings")
  208. }
  209. dict[key] = values[i+1]
  210. }
  211. return dict, nil
  212. }