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.

util_misc.go 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package templates
  4. import (
  5. "context"
  6. "html/template"
  7. "mime"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "time"
  12. activities_model "code.gitea.io/gitea/models/activities"
  13. repo_model "code.gitea.io/gitea/models/repo"
  14. "code.gitea.io/gitea/modules/git"
  15. giturl "code.gitea.io/gitea/modules/git/url"
  16. "code.gitea.io/gitea/modules/json"
  17. "code.gitea.io/gitea/modules/log"
  18. "code.gitea.io/gitea/modules/repository"
  19. "code.gitea.io/gitea/modules/svg"
  20. "github.com/editorconfig/editorconfig-core-go/v2"
  21. )
  22. func SortArrow(normSort, revSort, urlSort string, isDefault bool) template.HTML {
  23. // if needed
  24. if len(normSort) == 0 || len(urlSort) == 0 {
  25. return ""
  26. }
  27. if len(urlSort) == 0 && isDefault {
  28. // if sort is sorted as default add arrow tho this table header
  29. if isDefault {
  30. return svg.RenderHTML("octicon-triangle-down", 16)
  31. }
  32. } else {
  33. // if sort arg is in url test if it correlates with column header sort arguments
  34. // the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev)
  35. if urlSort == normSort {
  36. // the table is sorted with this header normal
  37. return svg.RenderHTML("octicon-triangle-up", 16)
  38. } else if urlSort == revSort {
  39. // the table is sorted with this header reverse
  40. return svg.RenderHTML("octicon-triangle-down", 16)
  41. }
  42. }
  43. // the table is NOT sorted with this header
  44. return ""
  45. }
  46. // IsMultilineCommitMessage checks to see if a commit message contains multiple lines.
  47. func IsMultilineCommitMessage(msg string) bool {
  48. return strings.Count(strings.TrimSpace(msg), "\n") >= 1
  49. }
  50. // Actioner describes an action
  51. type Actioner interface {
  52. GetOpType() activities_model.ActionType
  53. GetActUserName(ctx context.Context) string
  54. GetRepoUserName(ctx context.Context) string
  55. GetRepoName(ctx context.Context) string
  56. GetRepoPath(ctx context.Context) string
  57. GetRepoLink(ctx context.Context) string
  58. GetBranch() string
  59. GetContent() string
  60. GetCreate() time.Time
  61. GetIssueInfos() []string
  62. }
  63. // ActionIcon accepts an action operation type and returns an icon class name.
  64. func ActionIcon(opType activities_model.ActionType) string {
  65. switch opType {
  66. case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
  67. return "repo"
  68. case activities_model.ActionCommitRepo:
  69. return "git-commit"
  70. case activities_model.ActionDeleteBranch:
  71. return "git-branch"
  72. case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
  73. return "git-merge"
  74. case activities_model.ActionCreatePullRequest:
  75. return "git-pull-request"
  76. case activities_model.ActionClosePullRequest:
  77. return "git-pull-request-closed"
  78. case activities_model.ActionCreateIssue:
  79. return "issue-opened"
  80. case activities_model.ActionCloseIssue:
  81. return "issue-closed"
  82. case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
  83. return "issue-reopened"
  84. case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
  85. return "comment-discussion"
  86. case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
  87. return "mirror"
  88. case activities_model.ActionApprovePullRequest:
  89. return "check"
  90. case activities_model.ActionRejectPullRequest:
  91. return "file-diff"
  92. case activities_model.ActionPublishRelease, activities_model.ActionPushTag, activities_model.ActionDeleteTag:
  93. return "tag"
  94. case activities_model.ActionPullReviewDismissed:
  95. return "x"
  96. default:
  97. return "question"
  98. }
  99. }
  100. // ActionContent2Commits converts action content to push commits
  101. func ActionContent2Commits(act Actioner) *repository.PushCommits {
  102. push := repository.NewPushCommits()
  103. if act == nil || act.GetContent() == "" {
  104. return push
  105. }
  106. if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
  107. log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
  108. }
  109. if push.Len == 0 {
  110. push.Len = len(push.Commits)
  111. }
  112. return push
  113. }
  114. // MigrationIcon returns a SVG name matching the service an issue/comment was migrated from
  115. func MigrationIcon(hostname string) string {
  116. switch hostname {
  117. case "github.com":
  118. return "octicon-mark-github"
  119. default:
  120. return "gitea-git"
  121. }
  122. }
  123. type remoteAddress struct {
  124. Address string
  125. Username string
  126. Password string
  127. }
  128. func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
  129. ret := remoteAddress{}
  130. remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
  131. if err != nil {
  132. log.Error("GetRemoteURL %v", err)
  133. return ret
  134. }
  135. u, err := giturl.Parse(remoteURL)
  136. if err != nil {
  137. log.Error("giturl.Parse %v", err)
  138. return ret
  139. }
  140. if u.Scheme != "ssh" && u.Scheme != "file" {
  141. if u.User != nil {
  142. ret.Username = u.User.Username()
  143. ret.Password, _ = u.User.Password()
  144. }
  145. }
  146. // The URL stored in the git repo could contain authentication,
  147. // erase it, or it will be shown in the UI.
  148. u.User = nil
  149. ret.Address = u.String()
  150. // Why not use m.OriginalURL to set ret.Address?
  151. // It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository.
  152. // However, the old code has already stored authentication in m.OriginalURL when updating mirror settings.
  153. // That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased.
  154. // Instead of doing this, why not directly use the authentication-erased URL from the Git repository?
  155. // It should be the same as long as there are no bugs.
  156. return ret
  157. }
  158. func FilenameIsImage(filename string) bool {
  159. mimeType := mime.TypeByExtension(filepath.Ext(filename))
  160. return strings.HasPrefix(mimeType, "image/")
  161. }
  162. func TabSizeClass(ec *editorconfig.Editorconfig, filename string) string {
  163. if ec != nil {
  164. def, err := ec.GetDefinitionForFilename(filename)
  165. if err == nil && def.TabWidth >= 1 && def.TabWidth <= 16 {
  166. return "tab-size-" + strconv.Itoa(def.TabWidth)
  167. }
  168. }
  169. return "tab-size-4"
  170. }