summaryrefslogtreecommitdiffstats
path: root/modules/templates/util_misc.go
blob: 774385483b4c66693ed46f1591dd16608120d152 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package templates

import (
	"context"
	"html/template"
	"mime"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	activities_model "code.gitea.io/gitea/models/activities"
	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/modules/git"
	giturl "code.gitea.io/gitea/modules/git/url"
	"code.gitea.io/gitea/modules/json"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/repository"
	"code.gitea.io/gitea/modules/svg"

	"github.com/editorconfig/editorconfig-core-go/v2"
)

func SortArrow(normSort, revSort, urlSort string, isDefault bool) template.HTML {
	// if needed
	if len(normSort) == 0 || len(urlSort) == 0 {
		return ""
	}

	if len(urlSort) == 0 && isDefault {
		// if sort is sorted as default add arrow tho this table header
		if isDefault {
			return svg.RenderHTML("octicon-triangle-down", 16)
		}
	} else {
		// if sort arg is in url test if it correlates with column header sort arguments
		// the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev)
		if urlSort == normSort {
			// the table is sorted with this header normal
			return svg.RenderHTML("octicon-triangle-up", 16)
		} else if urlSort == revSort {
			// the table is sorted with this header reverse
			return svg.RenderHTML("octicon-triangle-down", 16)
		}
	}
	// the table is NOT sorted with this header
	return ""
}

// IsMultilineCommitMessage checks to see if a commit message contains multiple lines.
func IsMultilineCommitMessage(msg string) bool {
	return strings.Count(strings.TrimSpace(msg), "\n") >= 1
}

// Actioner describes an action
type Actioner interface {
	GetOpType() activities_model.ActionType
	GetActUserName(ctx context.Context) string
	GetRepoUserName(ctx context.Context) string
	GetRepoName(ctx context.Context) string
	GetRepoPath(ctx context.Context) string
	GetRepoLink(ctx context.Context) string
	GetBranch() string
	GetContent() string
	GetCreate() time.Time
	GetIssueInfos() []string
}

// ActionIcon accepts an action operation type and returns an icon class name.
func ActionIcon(opType activities_model.ActionType) string {
	switch opType {
	case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
		return "repo"
	case activities_model.ActionCommitRepo:
		return "git-commit"
	case activities_model.ActionDeleteBranch:
		return "git-branch"
	case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
		return "git-merge"
	case activities_model.ActionCreatePullRequest:
		return "git-pull-request"
	case activities_model.ActionClosePullRequest:
		return "git-pull-request-closed"
	case activities_model.ActionCreateIssue:
		return "issue-opened"
	case activities_model.ActionCloseIssue:
		return "issue-closed"
	case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
		return "issue-reopened"
	case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
		return "comment-discussion"
	case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
		return "mirror"
	case activities_model.ActionApprovePullRequest:
		return "check"
	case activities_model.ActionRejectPullRequest:
		return "file-diff"
	case activities_model.ActionPublishRelease, activities_model.ActionPushTag, activities_model.ActionDeleteTag:
		return "tag"
	case activities_model.ActionPullReviewDismissed:
		return "x"
	default:
		return "question"
	}
}

// ActionContent2Commits converts action content to push commits
func ActionContent2Commits(act Actioner) *repository.PushCommits {
	push := repository.NewPushCommits()

	if act == nil || act.GetContent() == "" {
		return push
	}

	if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
		log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
	}

	if push.Len == 0 {
		push.Len = len(push.Commits)
	}

	return push
}

// MigrationIcon returns a SVG name matching the service an issue/comment was migrated from
func MigrationIcon(hostname string) string {
	switch hostname {
	case "github.com":
		return "octicon-mark-github"
	default:
		return "gitea-git"
	}
}

type remoteAddress struct {
	Address  string
	Username string
	Password string
}

func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
	ret := remoteAddress{}
	remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
	if err != nil {
		log.Error("GetRemoteURL %v", err)
		return ret
	}

	u, err := giturl.Parse(remoteURL)
	if err != nil {
		log.Error("giturl.Parse %v", err)
		return ret
	}

	if u.Scheme != "ssh" && u.Scheme != "file" {
		if u.User != nil {
			ret.Username = u.User.Username()
			ret.Password, _ = u.User.Password()
		}
	}

	// The URL stored in the git repo could contain authentication,
	// erase it, or it will be shown in the UI.
	u.User = nil
	ret.Address = u.String()
	// Why not use m.OriginalURL to set ret.Address?
	// It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository.
	// However, the old code has already stored authentication in m.OriginalURL when updating mirror settings.
	// That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased.
	// Instead of doing this, why not directly use the authentication-erased URL from the Git repository?
	// It should be the same as long as there are no bugs.

	return ret
}

func FilenameIsImage(filename string) bool {
	mimeType := mime.TypeByExtension(filepath.Ext(filename))
	return strings.HasPrefix(mimeType, "image/")
}

func TabSizeClass(ec *editorconfig.Editorconfig, filename string) string {
	if ec != nil {
		def, err := ec.GetDefinitionForFilename(filename)
		if err == nil && def.TabWidth >= 1 && def.TabWidth <= 16 {
			return "tab-size-" + strconv.Itoa(def.TabWidth)
		}
	}
	return "tab-size-4"
}