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