Skip to content

Commit 241b74f

Browse files
wxiaoguangGiteaBot
andauthored
Improve template helper (#24417)
It seems that we really need the "context function" soon. So we should clean up the helper functions first. Major changes: * Improve StringUtils and add JsonUtils * Remove one-time-use helper functions like CompareLink * Move other code (no change) to util_avatar/util_render/util_misc (no need to propose changes for them) I have tested the changed templates: ![image](https://user-images.githubusercontent.com/2114189/235283862-608dbf6b-2da3-4d06-8157-b523ca93edb4.png) ![image](https://user-images.githubusercontent.com/2114189/235283888-1dfc0471-e622-4d64-9d76-7859819580d3.png) ![image](https://user-images.githubusercontent.com/2114189/235283903-d559f14d-4abb-4a50-915f-2b9cbc381a7a.png) ![image](https://user-images.githubusercontent.com/2114189/235283955-b7b5adea-aca3-4758-b38a-3aae3f7c6048.png) --------- Co-authored-by: Giteabot <[email protected]>
1 parent 5a5ab8e commit 241b74f

File tree

17 files changed

+650
-571
lines changed

17 files changed

+650
-571
lines changed

modules/templates/helper.go

Lines changed: 10 additions & 547 deletions
Large diffs are not rendered by default.

modules/templates/util_avatar.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package templates
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"html"
10+
"html/template"
11+
12+
activities_model "code.gitea.io/gitea/models/activities"
13+
"code.gitea.io/gitea/models/avatars"
14+
"code.gitea.io/gitea/models/organization"
15+
repo_model "code.gitea.io/gitea/models/repo"
16+
user_model "code.gitea.io/gitea/models/user"
17+
gitea_html "code.gitea.io/gitea/modules/html"
18+
"code.gitea.io/gitea/modules/setting"
19+
)
20+
21+
// AvatarHTML creates the HTML for an avatar
22+
func AvatarHTML(src string, size int, class, name string) template.HTML {
23+
sizeStr := fmt.Sprintf(`%d`, size)
24+
25+
if name == "" {
26+
name = "avatar"
27+
}
28+
29+
return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
30+
}
31+
32+
// Avatar renders user avatars. args: user, size (int), class (string)
33+
func Avatar(ctx context.Context, item interface{}, others ...interface{}) template.HTML {
34+
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
35+
36+
switch t := item.(type) {
37+
case *user_model.User:
38+
src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
39+
if src != "" {
40+
return AvatarHTML(src, size, class, t.DisplayName())
41+
}
42+
case *repo_model.Collaborator:
43+
src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
44+
if src != "" {
45+
return AvatarHTML(src, size, class, t.DisplayName())
46+
}
47+
case *organization.Organization:
48+
src := t.AsUser().AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor)
49+
if src != "" {
50+
return AvatarHTML(src, size, class, t.AsUser().DisplayName())
51+
}
52+
}
53+
54+
return template.HTML("")
55+
}
56+
57+
// AvatarByAction renders user avatars from action. args: action, size (int), class (string)
58+
func AvatarByAction(ctx context.Context, action *activities_model.Action, others ...interface{}) template.HTML {
59+
action.LoadActUser(ctx)
60+
return Avatar(ctx, action.ActUser, others...)
61+
}
62+
63+
// RepoAvatar renders repo avatars. args: repo, size(int), class (string)
64+
func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML {
65+
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
66+
67+
src := repo.RelAvatarLink()
68+
if src != "" {
69+
return AvatarHTML(src, size, class, repo.FullName())
70+
}
71+
return template.HTML("")
72+
}
73+
74+
// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
75+
func AvatarByEmail(ctx context.Context, email, name string, others ...interface{}) template.HTML {
76+
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
77+
src := avatars.GenerateEmailAvatarFastLink(ctx, email, size*setting.Avatar.RenderedSizeFactor)
78+
79+
if src != "" {
80+
return AvatarHTML(src, size, class, name)
81+
}
82+
83+
return template.HTML("")
84+
}

modules/templates/util_json.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package templates
5+
6+
import (
7+
"bytes"
8+
9+
"code.gitea.io/gitea/modules/json"
10+
)
11+
12+
type JsonUtils struct{} //nolint:revive
13+
14+
var jsonUtils = JsonUtils{}
15+
16+
func NewJsonUtils() *JsonUtils { //nolint:revive
17+
return &jsonUtils
18+
}
19+
20+
func (su *JsonUtils) EncodeToString(v any) string {
21+
out, err := json.Marshal(v)
22+
if err != nil {
23+
return ""
24+
}
25+
return string(out)
26+
}
27+
28+
func (su *JsonUtils) PrettyIndent(s string) string {
29+
var out bytes.Buffer
30+
err := json.Indent(&out, []byte(s), "", " ")
31+
if err != nil {
32+
return ""
33+
}
34+
return out.String()
35+
}

modules/templates/util_misc.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package templates
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"html/template"
10+
"mime"
11+
"path/filepath"
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() string
62+
GetRepoUserName() string
63+
GetRepoName() string
64+
GetRepoPath() string
65+
GetRepoLink() 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, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionDeleteBranch:
78+
return "git-commit"
79+
case activities_model.ActionCreateIssue:
80+
return "issue-opened"
81+
case activities_model.ActionCreatePullRequest:
82+
return "git-pull-request"
83+
case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
84+
return "comment-discussion"
85+
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
86+
return "git-merge"
87+
case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
88+
return "issue-closed"
89+
case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
90+
return "issue-reopened"
91+
case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
92+
return "mirror"
93+
case activities_model.ActionApprovePullRequest:
94+
return "check"
95+
case activities_model.ActionRejectPullRequest:
96+
return "diff"
97+
case activities_model.ActionPublishRelease:
98+
return "tag"
99+
case activities_model.ActionPullReviewDismissed:
100+
return "x"
101+
default:
102+
return "question"
103+
}
104+
}
105+
106+
// ActionContent2Commits converts action content to push commits
107+
func ActionContent2Commits(act Actioner) *repository.PushCommits {
108+
push := repository.NewPushCommits()
109+
110+
if act == nil || act.GetContent() == "" {
111+
return push
112+
}
113+
114+
if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
115+
log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
116+
}
117+
118+
if push.Len == 0 {
119+
push.Len = len(push.Commits)
120+
}
121+
122+
return push
123+
}
124+
125+
// DiffLineTypeToStr returns diff line type name
126+
func DiffLineTypeToStr(diffType int) string {
127+
switch diffType {
128+
case 2:
129+
return "add"
130+
case 3:
131+
return "del"
132+
case 4:
133+
return "tag"
134+
}
135+
return "same"
136+
}
137+
138+
// MigrationIcon returns a SVG name matching the service an issue/comment was migrated from
139+
func MigrationIcon(hostname string) string {
140+
switch hostname {
141+
case "github.com":
142+
return "octicon-mark-github"
143+
default:
144+
return "gitea-git"
145+
}
146+
}
147+
148+
type remoteAddress struct {
149+
Address string
150+
Username string
151+
Password string
152+
}
153+
154+
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress {
155+
a := remoteAddress{}
156+
157+
remoteURL := m.OriginalURL
158+
if ignoreOriginalURL || remoteURL == "" {
159+
var err error
160+
remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
161+
if err != nil {
162+
log.Error("GetRemoteURL %v", err)
163+
return a
164+
}
165+
}
166+
167+
u, err := giturl.Parse(remoteURL)
168+
if err != nil {
169+
log.Error("giturl.Parse %v", err)
170+
return a
171+
}
172+
173+
if u.Scheme != "ssh" && u.Scheme != "file" {
174+
if u.User != nil {
175+
a.Username = u.User.Username()
176+
a.Password, _ = u.User.Password()
177+
}
178+
u.User = nil
179+
}
180+
a.Address = u.String()
181+
182+
return a
183+
}
184+
185+
func FilenameIsImage(filename string) bool {
186+
mimeType := mime.TypeByExtension(filepath.Ext(filename))
187+
return strings.HasPrefix(mimeType, "image/")
188+
}
189+
190+
func TabSizeClass(ec interface{}, filename string) string {
191+
var (
192+
value *editorconfig.Editorconfig
193+
ok bool
194+
)
195+
if ec != nil {
196+
if value, ok = ec.(*editorconfig.Editorconfig); !ok || value == nil {
197+
return "tab-size-8"
198+
}
199+
def, err := value.GetDefinitionForFilename(filename)
200+
if err != nil {
201+
log.Error("tab size class: getting definition for filename: %v", err)
202+
return "tab-size-8"
203+
}
204+
if def.TabWidth > 0 {
205+
return fmt.Sprintf("tab-size-%d", def.TabWidth)
206+
}
207+
}
208+
return "tab-size-8"
209+
}

0 commit comments

Comments
 (0)