You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

helper.go 8.9 kB

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