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.

336 lines
8.6 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package templates
  5. import (
  6. "bytes"
  7. "container/list"
  8. "encoding/json"
  9. "fmt"
  10. "html/template"
  11. "mime"
  12. "path/filepath"
  13. "runtime"
  14. "strings"
  15. "time"
  16. "github.com/microcosm-cc/bluemonday"
  17. "golang.org/x/net/html/charset"
  18. "golang.org/x/text/transform"
  19. "gopkg.in/editorconfig/editorconfig-core-go.v1"
  20. "code.gitea.io/gitea/models"
  21. "code.gitea.io/gitea/modules/base"
  22. "code.gitea.io/gitea/modules/log"
  23. "code.gitea.io/gitea/modules/markdown"
  24. "code.gitea.io/gitea/modules/setting"
  25. )
  26. // NewFuncMap returns functions for injecting to templates
  27. func NewFuncMap() []template.FuncMap {
  28. return []template.FuncMap{map[string]interface{}{
  29. "GoVer": func() string {
  30. return strings.Title(runtime.Version())
  31. },
  32. "UseHTTPS": func() bool {
  33. return strings.HasPrefix(setting.AppURL, "https")
  34. },
  35. "AppName": func() string {
  36. return setting.AppName
  37. },
  38. "AppSubUrl": func() string {
  39. return setting.AppSubURL
  40. },
  41. "AppUrl": func() string {
  42. return setting.AppURL
  43. },
  44. "AppVer": func() string {
  45. return setting.AppVer
  46. },
  47. "AppBuiltWith": func() string {
  48. return setting.AppBuiltWith
  49. },
  50. "AppDomain": func() string {
  51. return setting.Domain
  52. },
  53. "DisableGravatar": func() bool {
  54. return setting.DisableGravatar
  55. },
  56. "ShowFooterTemplateLoadTime": func() bool {
  57. return setting.ShowFooterTemplateLoadTime
  58. },
  59. "LoadTimes": func(startTime time.Time) string {
  60. return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
  61. },
  62. "AvatarLink": base.AvatarLink,
  63. "Safe": Safe,
  64. "Sanitize": bluemonday.UGCPolicy().Sanitize,
  65. "Str2html": Str2html,
  66. "TimeSince": base.TimeSince,
  67. "RawTimeSince": base.RawTimeSince,
  68. "FileSize": base.FileSize,
  69. "Subtract": base.Subtract,
  70. "Add": func(a, b int) int {
  71. return a + b
  72. },
  73. "ActionIcon": ActionIcon,
  74. "DateFmtLong": func(t time.Time) string {
  75. return t.Format(time.RFC1123Z)
  76. },
  77. "DateFmtShort": func(t time.Time) string {
  78. return t.Format("Jan 02, 2006")
  79. },
  80. "SizeFmt": func(s int64) string {
  81. return base.FileSize(s)
  82. },
  83. "List": List,
  84. "SubStr": func(str string, start, length int) string {
  85. if len(str) == 0 {
  86. return ""
  87. }
  88. end := start + length
  89. if length == -1 {
  90. end = len(str)
  91. }
  92. if len(str) < end {
  93. return str
  94. }
  95. return str[start:end]
  96. },
  97. "EllipsisString": base.EllipsisString,
  98. "DiffTypeToStr": DiffTypeToStr,
  99. "DiffLineTypeToStr": DiffLineTypeToStr,
  100. "Sha1": Sha1,
  101. "ShortSha": base.ShortSha,
  102. "MD5": base.EncodeMD5,
  103. "ActionContent2Commits": ActionContent2Commits,
  104. "EscapePound": func(str string) string {
  105. return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str)
  106. },
  107. "RenderCommitMessage": RenderCommitMessage,
  108. "ThemeColorMetaTag": func() string {
  109. return setting.UI.ThemeColorMetaTag
  110. },
  111. "MetaAuthor": func() string {
  112. return setting.UI.Meta.Author
  113. },
  114. "MetaDescription": func() string {
  115. return setting.UI.Meta.Description
  116. },
  117. "MetaKeywords": func() string {
  118. return setting.UI.Meta.Keywords
  119. },
  120. "FilenameIsImage": func(filename string) bool {
  121. mimeType := mime.TypeByExtension(filepath.Ext(filename))
  122. return strings.HasPrefix(mimeType, "image/")
  123. },
  124. "TabSizeClass": func(ec *editorconfig.Editorconfig, filename string) string {
  125. if ec != nil {
  126. def := ec.GetDefinitionForFilename(filename)
  127. if def.TabWidth > 0 {
  128. return fmt.Sprintf("tab-size-%d", def.TabWidth)
  129. }
  130. }
  131. return "tab-size-8"
  132. },
  133. "SubJumpablePath": func(str string) []string {
  134. var path []string
  135. index := strings.LastIndex(str, "/")
  136. if index != -1 && index != len(str) {
  137. path = append(path, str[0:index+1])
  138. path = append(path, str[index+1:])
  139. } else {
  140. path = append(path, str)
  141. }
  142. return path
  143. },
  144. "JsonPrettyPrint": func(in string) string {
  145. var out bytes.Buffer
  146. err := json.Indent(&out, []byte(in), "", " ")
  147. if err != nil {
  148. return ""
  149. }
  150. return out.String()
  151. },
  152. }}
  153. }
  154. // Safe render raw as HTML
  155. func Safe(raw string) template.HTML {
  156. return template.HTML(raw)
  157. }
  158. // Str2html render Markdown text to HTML
  159. func Str2html(raw string) template.HTML {
  160. return template.HTML(markdown.Sanitize(raw))
  161. }
  162. // List traversings the list
  163. func List(l *list.List) chan interface{} {
  164. e := l.Front()
  165. c := make(chan interface{})
  166. go func() {
  167. for e != nil {
  168. c <- e.Value
  169. e = e.Next()
  170. }
  171. close(c)
  172. }()
  173. return c
  174. }
  175. // Sha1 returns sha1 sum of string
  176. func Sha1(str string) string {
  177. return base.EncodeSha1(str)
  178. }
  179. // ToUTF8WithErr converts content to UTF8 encoding
  180. func ToUTF8WithErr(content []byte) (string, error) {
  181. charsetLabel, err := base.DetectEncoding(content)
  182. if err != nil {
  183. return "", err
  184. } else if charsetLabel == "UTF-8" {
  185. return string(content), nil
  186. }
  187. encoding, _ := charset.Lookup(charsetLabel)
  188. if encoding == nil {
  189. return string(content), fmt.Errorf("Unknown encoding: %s", charsetLabel)
  190. }
  191. // If there is an error, we concatenate the nicely decoded part and the
  192. // original left over. This way we won't loose data.
  193. result, n, err := transform.String(encoding.NewDecoder(), string(content))
  194. if err != nil {
  195. result = result + string(content[n:])
  196. }
  197. return result, err
  198. }
  199. // ToUTF8 converts content to UTF8 encoding and ignore error
  200. func ToUTF8(content string) string {
  201. res, _ := ToUTF8WithErr([]byte(content))
  202. return res
  203. }
  204. // ReplaceLeft replaces all prefixes 'old' in 's' with 'new'.
  205. func ReplaceLeft(s, old, new string) string {
  206. oldLen, newLen, i, n := len(old), len(new), 0, 0
  207. for ; i < len(s) && strings.HasPrefix(s[i:], old); n++ {
  208. i += oldLen
  209. }
  210. // simple optimization
  211. if n == 0 {
  212. return s
  213. }
  214. // allocating space for the new string
  215. curLen := n*newLen + len(s[i:])
  216. replacement := make([]byte, curLen, curLen)
  217. j := 0
  218. for ; j < n*newLen; j += newLen {
  219. copy(replacement[j:j+newLen], new)
  220. }
  221. copy(replacement[j:], s[i:])
  222. return string(replacement)
  223. }
  224. // RenderCommitMessage renders commit message with XSS-safe and special links.
  225. func RenderCommitMessage(full bool, msg, urlPrefix string, metas map[string]string) template.HTML {
  226. cleanMsg := template.HTMLEscapeString(msg)
  227. fullMessage := string(markdown.RenderIssueIndexPattern([]byte(cleanMsg), urlPrefix, metas))
  228. msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n")
  229. numLines := len(msgLines)
  230. if numLines == 0 {
  231. return template.HTML("")
  232. } else if !full {
  233. return template.HTML(msgLines[0])
  234. } else if numLines == 1 || (numLines >= 2 && len(msgLines[1]) == 0) {
  235. // First line is a header, standalone or followed by empty line
  236. header := fmt.Sprintf("<h3>%s</h3>", msgLines[0])
  237. if numLines >= 2 {
  238. fullMessage = header + fmt.Sprintf("\n<pre>%s</pre>", strings.Join(msgLines[2:], "\n"))
  239. } else {
  240. fullMessage = header
  241. }
  242. } else {
  243. // Non-standard git message, there is no header line
  244. fullMessage = fmt.Sprintf("<h4>%s</h4>", strings.Join(msgLines, "<br>"))
  245. }
  246. return template.HTML(fullMessage)
  247. }
  248. // Actioner describes an action
  249. type Actioner interface {
  250. GetOpType() int
  251. GetActUserName() string
  252. GetRepoUserName() string
  253. GetRepoName() string
  254. GetRepoPath() string
  255. GetRepoLink() string
  256. GetBranch() string
  257. GetContent() string
  258. GetCreate() time.Time
  259. GetIssueInfos() []string
  260. }
  261. // ActionIcon accepts a int that represents action operation type
  262. // and returns a icon class name.
  263. func ActionIcon(opType int) string {
  264. switch opType {
  265. case 1, 8: // Create and transfer repository
  266. return "repo"
  267. case 5, 9: // Commit repository
  268. return "git-commit"
  269. case 6: // Create issue
  270. return "issue-opened"
  271. case 7: // New pull request
  272. return "git-pull-request"
  273. case 10: // Comment issue
  274. return "comment-discussion"
  275. case 11: // Merge pull request
  276. return "git-merge"
  277. case 12, 14: // Close issue or pull request
  278. return "issue-closed"
  279. case 13, 15: // Reopen issue or pull request
  280. return "issue-reopened"
  281. default:
  282. return "invalid type"
  283. }
  284. }
  285. // ActionContent2Commits converts action content to push commits
  286. func ActionContent2Commits(act Actioner) *models.PushCommits {
  287. push := models.NewPushCommits()
  288. if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
  289. log.Error(4, "json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
  290. }
  291. return push
  292. }
  293. // DiffTypeToStr returns diff type name
  294. func DiffTypeToStr(diffType int) string {
  295. diffTypes := map[int]string{
  296. 1: "add", 2: "modify", 3: "del", 4: "rename",
  297. }
  298. return diffTypes[diffType]
  299. }
  300. // DiffLineTypeToStr returns diff line type name
  301. func DiffLineTypeToStr(diffType int) string {
  302. switch diffType {
  303. case 2:
  304. return "add"
  305. case 3:
  306. return "del"
  307. case 4:
  308. return "tag"
  309. }
  310. return "same"
  311. }