Skip to content

Commit 7e17424

Browse files
jaqralafriks
jaqra
authored andcommitted
Make link last commit massages in repository home page and commit tables (#8006)
* Make link last commit massages in repository home page and commit tables * Use RenderCommitMessageLink instead surround with a * deleted __debug_bin file * Exclude email to link from latest commit title * Exclude email processor from commit table Co-Authored-By: mrsdizzie <info@mrsdizzie.com> * Add class parameter to a html element creator functions. Make links underline dashed that are not commit * fix tests * Show dashed underline when also not hovered
1 parent 7eacdcf commit 7e17424

File tree

7 files changed

+154
-38
lines changed

7 files changed

+154
-38
lines changed

modules/markup/html.go

+60-13
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,40 @@ func RenderCommitMessage(
211211
return ctx.postProcess(rawHTML)
212212
}
213213

214+
var commitMessageSubjectProcessors = []processor{
215+
fullIssuePatternProcessor,
216+
fullSha1PatternProcessor,
217+
linkProcessor,
218+
mentionProcessor,
219+
issueIndexPatternProcessor,
220+
crossReferenceIssueIndexPatternProcessor,
221+
sha1CurrentPatternProcessor,
222+
}
223+
224+
// RenderCommitMessageSubject will use the same logic as PostProcess and
225+
// RenderCommitMessage, but will disable the shortLinkProcessor and
226+
// emailAddressProcessor, will add a defaultLinkProcessor if defaultLink is set,
227+
// which changes every text node into a link to the passed default link.
228+
func RenderCommitMessageSubject(
229+
rawHTML []byte,
230+
urlPrefix, defaultLink string,
231+
metas map[string]string,
232+
) ([]byte, error) {
233+
ctx := &postProcessCtx{
234+
metas: metas,
235+
urlPrefix: urlPrefix,
236+
procs: commitMessageSubjectProcessors,
237+
}
238+
if defaultLink != "" {
239+
// we don't have to fear data races, because being
240+
// commitMessageSubjectProcessors of fixed len and cap, every time we
241+
// append something to it the slice is realloc+copied, so append always
242+
// generates the slice ex-novo.
243+
ctx.procs = append(ctx.procs, genDefaultLinkProcessor(defaultLink))
244+
}
245+
return ctx.postProcess(rawHTML)
246+
}
247+
214248
// RenderDescriptionHTML will use similar logic as PostProcess, but will
215249
// use a single special linkProcessor.
216250
func RenderDescriptionHTML(
@@ -296,12 +330,17 @@ func (ctx *postProcessCtx) textNode(node *html.Node) {
296330
}
297331
}
298332

299-
func createLink(href, content string) *html.Node {
333+
func createLink(href, content, class string) *html.Node {
300334
a := &html.Node{
301335
Type: html.ElementNode,
302336
Data: atom.A.String(),
303337
Attr: []html.Attribute{{Key: "href", Val: href}},
304338
}
339+
340+
if class != "" {
341+
a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class})
342+
}
343+
305344
text := &html.Node{
306345
Type: html.TextNode,
307346
Data: content,
@@ -311,12 +350,17 @@ func createLink(href, content string) *html.Node {
311350
return a
312351
}
313352

314-
func createCodeLink(href, content string) *html.Node {
353+
func createCodeLink(href, content, class string) *html.Node {
315354
a := &html.Node{
316355
Type: html.ElementNode,
317356
Data: atom.A.String(),
318357
Attr: []html.Attribute{{Key: "href", Val: href}},
319358
}
359+
360+
if class != "" {
361+
a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class})
362+
}
363+
320364
text := &html.Node{
321365
Type: html.TextNode,
322366
Data: content,
@@ -364,7 +408,7 @@ func mentionProcessor(_ *postProcessCtx, node *html.Node) {
364408
}
365409
// Replace the mention with a link to the specified user.
366410
mention := node.Data[m[2]:m[3]]
367-
replaceContent(node, m[2], m[3], createLink(util.URLJoin(setting.AppURL, mention[1:]), mention))
411+
replaceContent(node, m[2], m[3], createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention"))
368412
}
369413

370414
func shortLinkProcessor(ctx *postProcessCtx, node *html.Node) {
@@ -541,11 +585,11 @@ func fullIssuePatternProcessor(ctx *postProcessCtx, node *html.Node) {
541585
if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] {
542586
// TODO if m[4]:m[5] is not nil, then link is to a comment,
543587
// and we should indicate that in the text somehow
544-
replaceContent(node, m[0], m[1], createLink(link, id))
588+
replaceContent(node, m[0], m[1], createLink(link, id, "issue"))
545589

546590
} else {
547591
orgRepoID := matchOrg + "/" + matchRepo + id
548-
replaceContent(node, m[0], m[1], createLink(link, orgRepoID))
592+
replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "issue"))
549593
}
550594
}
551595

@@ -573,9 +617,9 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
573617
} else {
574618
ctx.metas["index"] = id[1:]
575619
}
576-
link = createLink(com.Expand(ctx.metas["format"], ctx.metas), id)
620+
link = createLink(com.Expand(ctx.metas["format"], ctx.metas), id, "issue")
577621
} else {
578-
link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "issues", id[1:]), id)
622+
link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "issues", id[1:]), id, "issue")
579623
}
580624
replaceContent(node, match[2], match[3], link)
581625
}
@@ -591,7 +635,7 @@ func crossReferenceIssueIndexPatternProcessor(ctx *postProcessCtx, node *html.No
591635
repo, issue := parts[0], parts[1]
592636

593637
replaceContent(node, m[2], m[3],
594-
createLink(util.URLJoin(setting.AppURL, repo, "issues", issue), ref))
638+
createLink(util.URLJoin(setting.AppURL, repo, "issues", issue), ref, issue))
595639
}
596640

597641
// fullSha1PatternProcessor renders SHA containing URLs
@@ -642,7 +686,7 @@ func fullSha1PatternProcessor(ctx *postProcessCtx, node *html.Node) {
642686
text += " (" + hash + ")"
643687
}
644688

645-
replaceContent(node, start, end, createCodeLink(urlFull, text))
689+
replaceContent(node, start, end, createCodeLink(urlFull, text, "commit"))
646690
}
647691

648692
// sha1CurrentPatternProcessor renders SHA1 strings to corresponding links that
@@ -672,7 +716,7 @@ func sha1CurrentPatternProcessor(ctx *postProcessCtx, node *html.Node) {
672716
}
673717

674718
replaceContent(node, m[2], m[3],
675-
createCodeLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "commit", hash), base.ShortSha(hash)))
719+
createCodeLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "commit", hash), base.ShortSha(hash), "commit"))
676720
}
677721

678722
// emailAddressProcessor replaces raw email addresses with a mailto: link.
@@ -682,7 +726,7 @@ func emailAddressProcessor(ctx *postProcessCtx, node *html.Node) {
682726
return
683727
}
684728
mail := node.Data[m[2]:m[3]]
685-
replaceContent(node, m[2], m[3], createLink("mailto:"+mail, mail))
729+
replaceContent(node, m[2], m[3], createLink("mailto:"+mail, mail, "mailto"))
686730
}
687731

688732
// linkProcessor creates links for any HTTP or HTTPS URL not captured by
@@ -693,7 +737,7 @@ func linkProcessor(ctx *postProcessCtx, node *html.Node) {
693737
return
694738
}
695739
uri := node.Data[m[0]:m[1]]
696-
replaceContent(node, m[0], m[1], createLink(uri, uri))
740+
replaceContent(node, m[0], m[1], createLink(uri, uri, "link"))
697741
}
698742

699743
func genDefaultLinkProcessor(defaultLink string) processor {
@@ -707,7 +751,10 @@ func genDefaultLinkProcessor(defaultLink string) processor {
707751
node.Type = html.ElementNode
708752
node.Data = "a"
709753
node.DataAtom = atom.A
710-
node.Attr = []html.Attribute{{Key: "href", Val: defaultLink}}
754+
node.Attr = []html.Attribute{
755+
{Key: "href", Val: defaultLink},
756+
{Key: "class", Val: "default-link"},
757+
}
711758
node.FirstChild, node.LastChild = ch, ch
712759
}
713760
}

modules/markup/html_internal_test.go

+20-16
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,22 @@ const Repo = "gogits/gogs"
2020
const AppSubURL = AppURL + Repo + "/"
2121

2222
// alphanumLink an HTML link to an alphanumeric-style issue
23-
func alphanumIssueLink(baseURL string, name string) string {
24-
return link(util.URLJoin(baseURL, name), name)
23+
func alphanumIssueLink(baseURL, class, name string) string {
24+
return link(util.URLJoin(baseURL, name), class, name)
2525
}
2626

2727
// numericLink an HTML to a numeric-style issue
28-
func numericIssueLink(baseURL string, index int) string {
29-
return link(util.URLJoin(baseURL, strconv.Itoa(index)), fmt.Sprintf("#%d", index))
28+
func numericIssueLink(baseURL, class string, index int) string {
29+
return link(util.URLJoin(baseURL, strconv.Itoa(index)), class, fmt.Sprintf("#%d", index))
3030
}
3131

3232
// link an HTML link
33-
func link(href, contents string) string {
34-
return fmt.Sprintf("<a href=\"%s\">%s</a>", href, contents)
33+
func link(href, class, contents string) string {
34+
if class != "" {
35+
class = " class=\"" + class + "\""
36+
}
37+
38+
return fmt.Sprintf("<a href=\"%s\"%s>%s</a>", href, class, contents)
3539
}
3640

3741
var numericMetas = map[string]string{
@@ -89,13 +93,13 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
8993
test := func(s, expectedFmt string, indices ...int) {
9094
links := make([]interface{}, len(indices))
9195
for i, index := range indices {
92-
links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), index)
96+
links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), "issue", index)
9397
}
9498
expectedNil := fmt.Sprintf(expectedFmt, links...)
9599
testRenderIssueIndexPattern(t, s, expectedNil, &postProcessCtx{metas: localMetas})
96100

97101
for i, index := range indices {
98-
links[i] = numericIssueLink("https://someurl.com/someUser/someRepo/", index)
102+
links[i] = numericIssueLink("https://someurl.com/someUser/someRepo/", "issue", index)
99103
}
100104
expectedNum := fmt.Sprintf(expectedFmt, links...)
101105
testRenderIssueIndexPattern(t, s, expectedNum, &postProcessCtx{metas: numericMetas})
@@ -158,7 +162,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
158162
test := func(s, expectedFmt string, names ...string) {
159163
links := make([]interface{}, len(names))
160164
for i, name := range names {
161-
links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", name)
165+
links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "issue", name)
162166
}
163167
expected := fmt.Sprintf(expectedFmt, links...)
164168
testRenderIssueIndexPattern(t, s, expected, &postProcessCtx{metas: alphanumericMetas})
@@ -197,17 +201,17 @@ func TestRender_AutoLink(t *testing.T) {
197201

198202
// render valid issue URLs
199203
test(util.URLJoin(setting.AppSubURL, "issues", "3333"),
200-
numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), 3333))
204+
numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), "issue", 3333))
201205

202206
// render valid commit URLs
203207
tmp := util.URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
204-
test(tmp, "<a href=\""+tmp+"\"><code class=\"nohighlight\">d8a994ef24</code></a>")
208+
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24</code></a>")
205209
tmp += "#diff-2"
206-
test(tmp, "<a href=\""+tmp+"\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
210+
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
207211

208212
// render other commit URLs
209213
tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2"
210-
test(tmp, "<a href=\""+tmp+"\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
214+
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
211215
}
212216

213217
func TestRender_FullIssueURLs(t *testing.T) {
@@ -228,11 +232,11 @@ func TestRender_FullIssueURLs(t *testing.T) {
228232
test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
229233
"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")
230234
test("Look here http://localhost:3000/person/repo/issues/4",
231-
`Look here <a href="http://localhost:3000/person/repo/issues/4">person/repo#4</a>`)
235+
`Look here <a href="http://localhost:3000/person/repo/issues/4" class="issue">person/repo#4</a>`)
232236
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
233-
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">person/repo#4</a>`)
237+
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="issue">person/repo#4</a>`)
234238
test("http://localhost:3000/gogits/gogs/issues/4",
235-
`<a href="http://localhost:3000/gogits/gogs/issues/4">#4</a>`)
239+
`<a href="http://localhost:3000/gogits/gogs/issues/4" class="issue">#4</a>`)
236240
}
237241

238242
func TestRegExp_issueNumericPattern(t *testing.T) {

modules/templates/helper.go

+26-7
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,14 @@ func NewFuncMap() []template.FuncMap {
118118
"EscapePound": func(str string) string {
119119
return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str)
120120
},
121-
"PathEscapeSegments": util.PathEscapeSegments,
122-
"URLJoin": util.URLJoin,
123-
"RenderCommitMessage": RenderCommitMessage,
124-
"RenderCommitMessageLink": RenderCommitMessageLink,
125-
"RenderCommitBody": RenderCommitBody,
126-
"RenderNote": RenderNote,
127-
"IsMultilineCommitMessage": IsMultilineCommitMessage,
121+
"PathEscapeSegments": util.PathEscapeSegments,
122+
"URLJoin": util.URLJoin,
123+
"RenderCommitMessage": RenderCommitMessage,
124+
"RenderCommitMessageLink": RenderCommitMessageLink,
125+
"RenderCommitMessageLinkSubject": RenderCommitMessageLinkSubject,
126+
"RenderCommitBody": RenderCommitBody,
127+
"RenderNote": RenderNote,
128+
"IsMultilineCommitMessage": IsMultilineCommitMessage,
128129
"ThemeColorMetaTag": func() string {
129130
return setting.UI.ThemeColorMetaTag
130131
},
@@ -322,6 +323,24 @@ func RenderCommitMessageLink(msg, urlPrefix, urlDefault string, metas map[string
322323
return template.HTML(msgLines[0])
323324
}
324325

326+
// RenderCommitMessageLinkSubject renders commit message as a XXS-safe link to
327+
// the provided default url, handling for special links without email to links.
328+
func RenderCommitMessageLinkSubject(msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
329+
cleanMsg := template.HTMLEscapeString(msg)
330+
// we can safely assume that it will not return any error, since there
331+
// shouldn't be any special HTML.
332+
fullMessage, err := markup.RenderCommitMessageSubject([]byte(cleanMsg), urlPrefix, urlDefault, metas)
333+
if err != nil {
334+
log.Error("RenderCommitMessageSubject: %v", err)
335+
return ""
336+
}
337+
msgLines := strings.Split(strings.TrimSpace(string(fullMessage)), "\n")
338+
if len(msgLines) == 0 {
339+
return template.HTML("")
340+
}
341+
return template.HTML(msgLines[0])
342+
}
343+
325344
// RenderCommitBody extracts the body of a commit message without its title.
326345
func RenderCommitBody(msg, urlPrefix string, metas map[string]string) template.HTML {
327346
cleanMsg := template.HTMLEscapeString(msg)

public/css/index.css

+8
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ footer .ui.left,footer .ui.right{line-height:40px}
466466
}
467467
.repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400}
468468
.repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px}
469+
.repository.file.list #repo-files-table thead .commit-summary a{text-decoration:underline;-webkit-text-decoration-style:dashed;text-decoration-style:dashed}
470+
.repository.file.list #repo-files-table thead .commit-summary a:hover{-webkit-text-decoration-style:solid;text-decoration-style:solid}
471+
.repository.file.list #repo-files-table thead .commit-summary a.default-link{text-decoration:none}
472+
.repository.file.list #repo-files-table thead .commit-summary a.default-link:hover{text-decoration:underline;-webkit-text-decoration-style:solid;text-decoration-style:solid}
469473
.repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777}
470474
.repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px}
471475
.repository.file.list #repo-files-table tbody .octicon.octicon-file-directory,.repository.file.list #repo-files-table tbody .octicon.octicon-file-submodule,.repository.file.list #repo-files-table tbody .octicon.octicon-file-symlink-directory{color:#1e70bf}
@@ -829,6 +833,10 @@ footer .ui.left,footer .ui.right{line-height:40px}
829833
.stats-table .table-cell.tiny{height:.5em}
830834
tbody.commit-list{vertical-align:baseline}
831835
.commit-list .message-wrapper{overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - 50px);display:inline-block;vertical-align:middle}
836+
.commit-list .commit-summary a{text-decoration:underline;-webkit-text-decoration-style:dashed;text-decoration-style:dashed}
837+
.commit-list .commit-summary a:hover{-webkit-text-decoration-style:solid;text-decoration-style:solid}
838+
.commit-list .commit-summary a.default-link{text-decoration:none}
839+
.commit-list .commit-summary a.default-link:hover{text-decoration:underline;-webkit-text-decoration-style:solid;text-decoration-style:solid}
832840
.commit-list .commit-status-link{display:inline-block;vertical-align:middle}
833841
.commit-body{white-space:pre-wrap}
834842
.git-notes.top{text-align:left}

public/less/_repository.less

+36
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,24 @@
277277
.ui.avatar {
278278
margin-bottom: 5px;
279279
}
280+
281+
.commit-summary a {
282+
text-decoration: underline;
283+
text-decoration-style: dashed;
284+
285+
&:hover {
286+
text-decoration-style: solid;
287+
}
288+
289+
&.default-link {
290+
text-decoration: none;
291+
292+
&:hover {
293+
text-decoration: underline;
294+
text-decoration-style: solid;
295+
}
296+
}
297+
}
280298
}
281299

282300
tbody {
@@ -2188,6 +2206,24 @@ tbody.commit-list {
21882206
vertical-align: middle;
21892207
}
21902208

2209+
.commit-list .commit-summary a {
2210+
text-decoration: underline;
2211+
text-decoration-style: dashed;
2212+
2213+
&:hover {
2214+
text-decoration-style: solid;
2215+
}
2216+
2217+
&.default-link {
2218+
text-decoration: none;
2219+
2220+
&:hover {
2221+
text-decoration: underline;
2222+
text-decoration-style: solid;
2223+
}
2224+
}
2225+
}
2226+
21912227
.commit-list .commit-status-link {
21922228
display: inline-block;
21932229
vertical-align: middle;

templates/repo/commits_table.tmpl

+2-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
</td>
7272
<td class="message">
7373
<span class="message-wrapper">
74-
<span class="commit-summary has-emoji{{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessage .Message $.RepoLink $.Repository.ComposeMetas}}</span>
74+
{{ $commitLink:= printf "%s/%s/%s/commit/%s" AppSubUrl $.Username $.Reponame .ID }}
75+
<span class="commit-summary has-emoji{{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject .Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span>
7576
</span>
7677
{{if IsMultilineCommitMessage .Message}}
7778
<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button>

0 commit comments

Comments
 (0)