A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
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.
 
 
 
 
 

194 lines
4.7 KiB

  1. /*
  2. * Copyright © 2018 A Bunch Tell 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. "fmt"
  13. "github.com/dustin/go-humanize"
  14. "github.com/writeas/web-core/l10n"
  15. "github.com/writeas/web-core/log"
  16. "html/template"
  17. "io"
  18. "io/ioutil"
  19. "net/http"
  20. "os"
  21. "path/filepath"
  22. "strings"
  23. )
  24. var (
  25. templates = map[string]*template.Template{}
  26. pages = map[string]*template.Template{}
  27. userPages = map[string]*template.Template{}
  28. funcMap = template.FuncMap{
  29. "largeNumFmt": largeNumFmt,
  30. "pluralize": pluralize,
  31. "isRTL": isRTL,
  32. "isLTR": isLTR,
  33. "localstr": localStr,
  34. "localhtml": localHTML,
  35. "tolower": strings.ToLower,
  36. }
  37. )
  38. const (
  39. templatesDir = "templates"
  40. pagesDir = "pages"
  41. )
  42. func showUserPage(w http.ResponseWriter, name string, obj interface{}) {
  43. if obj == nil {
  44. log.Error("showUserPage: data is nil!")
  45. return
  46. }
  47. if err := userPages[filepath.Join("user", name+".tmpl")].ExecuteTemplate(w, name, obj); err != nil {
  48. log.Error("Error parsing %s: %v", name, err)
  49. }
  50. }
  51. func initTemplate(name string) {
  52. if debugging {
  53. log.Info(" %s%s%s.tmpl", templatesDir, string(filepath.Separator), name)
  54. }
  55. files := []string{
  56. filepath.Join(templatesDir, name+".tmpl"),
  57. filepath.Join(templatesDir, "include", "footer.tmpl"),
  58. filepath.Join(templatesDir, "base.tmpl"),
  59. }
  60. if name == "collection" || name == "collection-tags" {
  61. // These pages list out collection posts, so we also parse templatesDir + "include/posts.tmpl"
  62. files = append(files, filepath.Join(templatesDir, "include", "posts.tmpl"))
  63. }
  64. if name == "collection" || name == "collection-tags" || name == "collection-post" || name == "post" {
  65. files = append(files, filepath.Join(templatesDir, "include", "post-render.tmpl"))
  66. }
  67. templates[name] = template.Must(template.New("").Funcs(funcMap).ParseFiles(files...))
  68. }
  69. func initPage(path, key string) {
  70. if debugging {
  71. log.Info(" %s", key)
  72. }
  73. pages[key] = template.Must(template.New("").Funcs(funcMap).ParseFiles(
  74. path,
  75. filepath.Join(templatesDir, "include", "footer.tmpl"),
  76. filepath.Join(templatesDir, "base.tmpl"),
  77. ))
  78. }
  79. func initUserPage(path, key string) {
  80. if debugging {
  81. log.Info(" %s", key)
  82. }
  83. userPages[key] = template.Must(template.New(key).Funcs(funcMap).ParseFiles(
  84. path,
  85. filepath.Join(templatesDir, "user", "include", "header.tmpl"),
  86. filepath.Join(templatesDir, "user", "include", "footer.tmpl"),
  87. ))
  88. }
  89. func initTemplates() error {
  90. log.Info("Loading templates...")
  91. tmplFiles, err := ioutil.ReadDir(templatesDir)
  92. if err != nil {
  93. return err
  94. }
  95. for _, f := range tmplFiles {
  96. if !f.IsDir() && !strings.HasPrefix(f.Name(), ".") {
  97. parts := strings.Split(f.Name(), ".")
  98. key := parts[0]
  99. initTemplate(key)
  100. }
  101. }
  102. log.Info("Loading pages...")
  103. // Initialize all static pages that use the base template
  104. filepath.Walk(pagesDir, func(path string, i os.FileInfo, err error) error {
  105. if !i.IsDir() && !strings.HasPrefix(i.Name(), ".") {
  106. parts := strings.Split(path, string(filepath.Separator))
  107. key := i.Name()
  108. if len(parts) > 2 {
  109. key = fmt.Sprintf("%s%s%s", parts[1], string(filepath.Separator), i.Name())
  110. }
  111. initPage(path, key)
  112. }
  113. return nil
  114. })
  115. log.Info("Loading user pages...")
  116. // Initialize all user pages that use base templates
  117. filepath.Walk(filepath.Join(templatesDir, "user"), func(path string, f os.FileInfo, err error) error {
  118. if !f.IsDir() && !strings.HasPrefix(f.Name(), ".") {
  119. parts := strings.Split(path, string(filepath.Separator))
  120. key := f.Name()
  121. if len(parts) > 2 {
  122. key = filepath.Join(parts[1], f.Name())
  123. }
  124. initUserPage(path, key)
  125. }
  126. return nil
  127. })
  128. return nil
  129. }
  130. // renderPage retrieves the given template and renders it to the given io.Writer.
  131. // If something goes wrong, the error is logged and returned.
  132. func renderPage(w io.Writer, tmpl string, data interface{}) error {
  133. err := pages[tmpl].ExecuteTemplate(w, "base", data)
  134. if err != nil {
  135. log.Error("%v", err)
  136. }
  137. return err
  138. }
  139. func largeNumFmt(n int64) string {
  140. return humanize.Comma(n)
  141. }
  142. func pluralize(singular, plural string, n int64) string {
  143. if n == 1 {
  144. return singular
  145. }
  146. return plural
  147. }
  148. func isRTL(d string) bool {
  149. return d == "rtl"
  150. }
  151. func isLTR(d string) bool {
  152. return d == "ltr" || d == "auto"
  153. }
  154. func localStr(term, lang string) string {
  155. s := l10n.Strings(lang)[term]
  156. if s == "" {
  157. s = l10n.Strings("")[term]
  158. }
  159. return s
  160. }
  161. func localHTML(term, lang string) template.HTML {
  162. s := l10n.Strings(lang)[term]
  163. if s == "" {
  164. s = l10n.Strings("")[term]
  165. }
  166. s = strings.Replace(s, "write.as", "<a href=\"https://writefreely.org\">write freely</a>", 1)
  167. return template.HTML(s)
  168. }