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.

helper.go 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // Copyright 2014 The Gogs Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package templates
  5. import (
  6. "fmt"
  7. "html"
  8. "html/template"
  9. "net/url"
  10. "strings"
  11. "time"
  12. "code.gitea.io/gitea/modules/base"
  13. "code.gitea.io/gitea/modules/emoji"
  14. "code.gitea.io/gitea/modules/markup"
  15. "code.gitea.io/gitea/modules/setting"
  16. "code.gitea.io/gitea/modules/svg"
  17. "code.gitea.io/gitea/modules/templates/eval"
  18. "code.gitea.io/gitea/modules/timeutil"
  19. "code.gitea.io/gitea/modules/util"
  20. "code.gitea.io/gitea/services/gitdiff"
  21. )
  22. // NewFuncMap returns functions for injecting to templates
  23. func NewFuncMap() template.FuncMap {
  24. return map[string]any{
  25. "ctx": func() any { return nil }, // template context function
  26. "DumpVar": dumpVar,
  27. // -----------------------------------------------------------------
  28. // html/template related functions
  29. "dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names.
  30. "Eval": Eval,
  31. "Safe": Safe,
  32. "Escape": html.EscapeString,
  33. "QueryEscape": url.QueryEscape,
  34. "JSEscape": template.JSEscapeString,
  35. "Str2html": Str2html, // TODO: rename it to SanitizeHTML
  36. "URLJoin": util.URLJoin,
  37. "DotEscape": DotEscape,
  38. "PathEscape": url.PathEscape,
  39. "PathEscapeSegments": util.PathEscapeSegments,
  40. // utils
  41. "StringUtils": NewStringUtils,
  42. "SliceUtils": NewSliceUtils,
  43. "JsonUtils": NewJsonUtils,
  44. // -----------------------------------------------------------------
  45. // svg / avatar / icon
  46. "svg": svg.RenderHTML,
  47. "EntryIcon": base.EntryIcon,
  48. "MigrationIcon": MigrationIcon,
  49. "ActionIcon": ActionIcon,
  50. "SortArrow": SortArrow,
  51. // -----------------------------------------------------------------
  52. // time / number / format
  53. "FileSize": base.FileSize,
  54. "CountFmt": base.FormatNumberSI,
  55. "TimeSince": timeutil.TimeSince,
  56. "TimeSinceUnix": timeutil.TimeSinceUnix,
  57. "DateTime": timeutil.DateTime,
  58. "Sec2Time": util.SecToTime,
  59. "LoadTimes": func(startTime time.Time) string {
  60. return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
  61. },
  62. // -----------------------------------------------------------------
  63. // setting
  64. "AppName": func() string {
  65. return setting.AppName
  66. },
  67. "AppSubUrl": func() string {
  68. return setting.AppSubURL
  69. },
  70. "AssetUrlPrefix": func() string {
  71. return setting.StaticURLPrefix + "/assets"
  72. },
  73. "AppUrl": func() string {
  74. // The usage of AppUrl should be avoided as much as possible,
  75. // because the AppURL(ROOT_URL) may not match user's visiting site and the ROOT_URL in app.ini may be incorrect.
  76. // And it's difficult for Gitea to guess absolute URL correctly with zero configuration,
  77. // because Gitea doesn't know whether the scheme is HTTP or HTTPS unless the reverse proxy could tell Gitea.
  78. return setting.AppURL
  79. },
  80. "AppVer": func() string {
  81. return setting.AppVer
  82. },
  83. "AppDomain": func() string { // documented in mail-templates.md
  84. return setting.Domain
  85. },
  86. "AssetVersion": func() string {
  87. return setting.AssetVersion
  88. },
  89. "DefaultShowFullName": func() bool {
  90. return setting.UI.DefaultShowFullName
  91. },
  92. "ShowFooterTemplateLoadTime": func() bool {
  93. return setting.Other.ShowFooterTemplateLoadTime
  94. },
  95. "AllowedReactions": func() []string {
  96. return setting.UI.Reactions
  97. },
  98. "CustomEmojis": func() map[string]string {
  99. return setting.UI.CustomEmojisMap
  100. },
  101. "MetaAuthor": func() string {
  102. return setting.UI.Meta.Author
  103. },
  104. "MetaDescription": func() string {
  105. return setting.UI.Meta.Description
  106. },
  107. "MetaKeywords": func() string {
  108. return setting.UI.Meta.Keywords
  109. },
  110. "EnableTimetracking": func() bool {
  111. return setting.Service.EnableTimetracking
  112. },
  113. "DisableGitHooks": func() bool {
  114. return setting.DisableGitHooks
  115. },
  116. "DisableWebhooks": func() bool {
  117. return setting.DisableWebhooks
  118. },
  119. "DisableImportLocal": func() bool {
  120. return !setting.ImportLocalPaths
  121. },
  122. "DefaultTheme": func() string {
  123. return setting.UI.DefaultTheme
  124. },
  125. "NotificationSettings": func() map[string]any {
  126. return map[string]any{
  127. "MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond),
  128. "TimeoutStep": int(setting.UI.Notification.TimeoutStep / time.Millisecond),
  129. "MaxTimeout": int(setting.UI.Notification.MaxTimeout / time.Millisecond),
  130. "EventSourceUpdateTime": int(setting.UI.Notification.EventSourceUpdateTime / time.Millisecond),
  131. }
  132. },
  133. "MermaidMaxSourceCharacters": func() int {
  134. return setting.MermaidMaxSourceCharacters
  135. },
  136. // -----------------------------------------------------------------
  137. // render
  138. "RenderCommitMessage": RenderCommitMessage,
  139. "RenderCommitMessageLinkSubject": RenderCommitMessageLinkSubject,
  140. "RenderCommitBody": RenderCommitBody,
  141. "RenderCodeBlock": RenderCodeBlock,
  142. "RenderIssueTitle": RenderIssueTitle,
  143. "RenderEmoji": RenderEmoji,
  144. "RenderEmojiPlain": emoji.ReplaceAliases,
  145. "ReactionToEmoji": ReactionToEmoji,
  146. "RenderMarkdownToHtml": RenderMarkdownToHtml,
  147. "RenderLabel": RenderLabel,
  148. "RenderLabels": RenderLabels,
  149. // -----------------------------------------------------------------
  150. // misc
  151. "ShortSha": base.ShortSha,
  152. "ActionContent2Commits": ActionContent2Commits,
  153. "IsMultilineCommitMessage": IsMultilineCommitMessage,
  154. "CommentMustAsDiff": gitdiff.CommentMustAsDiff,
  155. "MirrorRemoteAddress": mirrorRemoteAddress,
  156. "FilenameIsImage": FilenameIsImage,
  157. "TabSizeClass": TabSizeClass,
  158. }
  159. }
  160. // Safe render raw as HTML
  161. func Safe(raw string) template.HTML {
  162. return template.HTML(raw)
  163. }
  164. // Str2html render Markdown text to HTML
  165. func Str2html(raw string) template.HTML {
  166. return template.HTML(markup.Sanitize(raw))
  167. }
  168. // DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls
  169. func DotEscape(raw string) string {
  170. return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
  171. }
  172. // Eval the expression and return the result, see the comment of eval.Expr for details.
  173. // To use this helper function in templates, pass each token as a separate parameter.
  174. //
  175. // {{ $int64 := Eval $var "+" 1 }}
  176. // {{ $float64 := Eval $var "+" 1.0 }}
  177. //
  178. // Golang's template supports comparable int types, so the int64 result can be used in later statements like {{if lt $int64 10}}
  179. func Eval(tokens ...any) (any, error) {
  180. n, err := eval.Expr(tokens...)
  181. return n.Value, err
  182. }