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.

288 lines
7.2 KiB

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