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.

223 lines
4.6 KiB

  1. // +build bindata
  2. // Copyright 2016 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package templates
  6. import (
  7. "bytes"
  8. "fmt"
  9. "html/template"
  10. "io"
  11. "io/ioutil"
  12. "path"
  13. "strings"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/setting"
  16. "github.com/Unknwon/com"
  17. "gopkg.in/macaron.v1"
  18. )
  19. var (
  20. templates = template.New("")
  21. )
  22. type templateFileSystem struct {
  23. files []macaron.TemplateFile
  24. }
  25. func (templates templateFileSystem) ListFiles() []macaron.TemplateFile {
  26. return templates.files
  27. }
  28. func (templates templateFileSystem) Get(name string) (io.Reader, error) {
  29. for i := range templates.files {
  30. if templates.files[i].Name()+templates.files[i].Ext() == name {
  31. return bytes.NewReader(templates.files[i].Data()), nil
  32. }
  33. }
  34. return nil, fmt.Errorf("file '%s' not found", name)
  35. }
  36. func NewTemplateFileSystem() templateFileSystem {
  37. fs := templateFileSystem{}
  38. fs.files = make([]macaron.TemplateFile, 0, 10)
  39. for _, assetPath := range AssetNames() {
  40. if strings.HasPrefix(assetPath, "mail/") {
  41. continue
  42. }
  43. if !strings.HasSuffix(assetPath, ".tmpl") {
  44. continue
  45. }
  46. content, err := Asset(assetPath)
  47. if err != nil {
  48. log.Warn("Failed to read embedded %s template. %v", assetPath, err)
  49. continue
  50. }
  51. fs.files = append(fs.files, macaron.NewTplFile(
  52. strings.TrimSuffix(
  53. assetPath,
  54. ".tmpl",
  55. ),
  56. content,
  57. ".tmpl",
  58. ))
  59. }
  60. customDir := path.Join(setting.CustomPath, "templates")
  61. if com.IsDir(customDir) {
  62. files, err := com.StatDir(customDir)
  63. if err != nil {
  64. log.Warn("Failed to read %s templates dir. %v", customDir, err)
  65. } else {
  66. for _, filePath := range files {
  67. if strings.HasPrefix(filePath, "mail/") {
  68. continue
  69. }
  70. if !strings.HasSuffix(filePath, ".tmpl") {
  71. continue
  72. }
  73. content, err := ioutil.ReadFile(path.Join(customDir, filePath))
  74. if err != nil {
  75. log.Warn("Failed to read custom %s template. %v", filePath, err)
  76. continue
  77. }
  78. fs.files = append(fs.files, macaron.NewTplFile(
  79. strings.TrimSuffix(
  80. filePath,
  81. ".tmpl",
  82. ),
  83. content,
  84. ".tmpl",
  85. ))
  86. }
  87. }
  88. }
  89. return fs
  90. }
  91. // HTMLRenderer implements the macaron handler for serving HTML templates.
  92. func HTMLRenderer() macaron.Handler {
  93. return macaron.Renderer(macaron.RenderOptions{
  94. Funcs: NewFuncMap(),
  95. TemplateFileSystem: NewTemplateFileSystem(),
  96. })
  97. }
  98. // JSONRenderer implements the macaron handler for serving JSON templates.
  99. func JSONRenderer() macaron.Handler {
  100. return macaron.Renderer(macaron.RenderOptions{
  101. Funcs: NewFuncMap(),
  102. TemplateFileSystem: NewTemplateFileSystem(),
  103. HTMLContentType: "application/json",
  104. })
  105. }
  106. // JSRenderer implements the macaron handler for serving JS templates.
  107. func JSRenderer() macaron.Handler {
  108. return macaron.Renderer(macaron.RenderOptions{
  109. Funcs: NewFuncMap(),
  110. TemplateFileSystem: NewTemplateFileSystem(),
  111. HTMLContentType: "application/javascript",
  112. })
  113. }
  114. // Mailer provides the templates required for sending notification mails.
  115. func Mailer() *template.Template {
  116. for _, funcs := range NewFuncMap() {
  117. templates.Funcs(funcs)
  118. }
  119. for _, assetPath := range AssetNames() {
  120. if !strings.HasPrefix(assetPath, "mail/") {
  121. continue
  122. }
  123. if !strings.HasSuffix(assetPath, ".tmpl") {
  124. continue
  125. }
  126. content, err := Asset(assetPath)
  127. if err != nil {
  128. log.Warn("Failed to read embedded %s template. %v", assetPath, err)
  129. continue
  130. }
  131. templates.New(
  132. strings.TrimPrefix(
  133. strings.TrimSuffix(
  134. assetPath,
  135. ".tmpl",
  136. ),
  137. "mail/",
  138. ),
  139. ).Parse(string(content))
  140. }
  141. customDir := path.Join(setting.CustomPath, "templates", "mail")
  142. if com.IsDir(customDir) {
  143. files, err := com.StatDir(customDir)
  144. if err != nil {
  145. log.Warn("Failed to read %s templates dir. %v", customDir, err)
  146. } else {
  147. for _, filePath := range files {
  148. if !strings.HasSuffix(filePath, ".tmpl") {
  149. continue
  150. }
  151. content, err := ioutil.ReadFile(path.Join(customDir, filePath))
  152. if err != nil {
  153. log.Warn("Failed to read custom %s template. %v", filePath, err)
  154. continue
  155. }
  156. templates.New(
  157. strings.TrimSuffix(
  158. filePath,
  159. ".tmpl",
  160. ),
  161. ).Parse(string(content))
  162. }
  163. }
  164. }
  165. return templates
  166. }
  167. func Asset(name string) ([]byte, error) {
  168. f, err := Assets.Open("/" + name)
  169. if err != nil {
  170. return nil, err
  171. }
  172. defer f.Close()
  173. return ioutil.ReadAll(f)
  174. }
  175. func AssetNames() []string {
  176. realFS := Assets.(vfsgen۰FS)
  177. var results = make([]string, 0, len(realFS))
  178. for k := range realFS {
  179. results = append(results, k[1:])
  180. }
  181. return results
  182. }