mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-27 01:18:27 +00:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -211,6 +211,40 @@ func RenderCommitMessage( | ||||
| 	return ctx.postProcess(rawHTML) | ||||
| } | ||||
|  | ||||
| var commitMessageSubjectProcessors = []processor{ | ||||
| 	fullIssuePatternProcessor, | ||||
| 	fullSha1PatternProcessor, | ||||
| 	linkProcessor, | ||||
| 	mentionProcessor, | ||||
| 	issueIndexPatternProcessor, | ||||
| 	crossReferenceIssueIndexPatternProcessor, | ||||
| 	sha1CurrentPatternProcessor, | ||||
| } | ||||
|  | ||||
| // RenderCommitMessageSubject will use the same logic as PostProcess and | ||||
| // RenderCommitMessage, but will disable the shortLinkProcessor and | ||||
| // emailAddressProcessor, will add a defaultLinkProcessor if defaultLink is set, | ||||
| // which changes every text node into a link to the passed default link. | ||||
| func RenderCommitMessageSubject( | ||||
| 	rawHTML []byte, | ||||
| 	urlPrefix, defaultLink string, | ||||
| 	metas map[string]string, | ||||
| ) ([]byte, error) { | ||||
| 	ctx := &postProcessCtx{ | ||||
| 		metas:     metas, | ||||
| 		urlPrefix: urlPrefix, | ||||
| 		procs:     commitMessageSubjectProcessors, | ||||
| 	} | ||||
| 	if defaultLink != "" { | ||||
| 		// we don't have to fear data races, because being | ||||
| 		// commitMessageSubjectProcessors of fixed len and cap, every time we | ||||
| 		// append something to it the slice is realloc+copied, so append always | ||||
| 		// generates the slice ex-novo. | ||||
| 		ctx.procs = append(ctx.procs, genDefaultLinkProcessor(defaultLink)) | ||||
| 	} | ||||
| 	return ctx.postProcess(rawHTML) | ||||
| } | ||||
|  | ||||
| // RenderDescriptionHTML will use similar logic as PostProcess, but will | ||||
| // use a single special linkProcessor. | ||||
| func RenderDescriptionHTML( | ||||
| @@ -296,12 +330,17 @@ func (ctx *postProcessCtx) textNode(node *html.Node) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func createLink(href, content string) *html.Node { | ||||
| func createLink(href, content, class string) *html.Node { | ||||
| 	a := &html.Node{ | ||||
| 		Type: html.ElementNode, | ||||
| 		Data: atom.A.String(), | ||||
| 		Attr: []html.Attribute{{Key: "href", Val: href}}, | ||||
| 	} | ||||
|  | ||||
| 	if class != "" { | ||||
| 		a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class}) | ||||
| 	} | ||||
|  | ||||
| 	text := &html.Node{ | ||||
| 		Type: html.TextNode, | ||||
| 		Data: content, | ||||
| @@ -311,12 +350,17 @@ func createLink(href, content string) *html.Node { | ||||
| 	return a | ||||
| } | ||||
|  | ||||
| func createCodeLink(href, content string) *html.Node { | ||||
| func createCodeLink(href, content, class string) *html.Node { | ||||
| 	a := &html.Node{ | ||||
| 		Type: html.ElementNode, | ||||
| 		Data: atom.A.String(), | ||||
| 		Attr: []html.Attribute{{Key: "href", Val: href}}, | ||||
| 	} | ||||
|  | ||||
| 	if class != "" { | ||||
| 		a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class}) | ||||
| 	} | ||||
|  | ||||
| 	text := &html.Node{ | ||||
| 		Type: html.TextNode, | ||||
| 		Data: content, | ||||
| @@ -364,7 +408,7 @@ func mentionProcessor(_ *postProcessCtx, node *html.Node) { | ||||
| 	} | ||||
| 	// Replace the mention with a link to the specified user. | ||||
| 	mention := node.Data[m[2]:m[3]] | ||||
| 	replaceContent(node, m[2], m[3], createLink(util.URLJoin(setting.AppURL, mention[1:]), mention)) | ||||
| 	replaceContent(node, m[2], m[3], createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention")) | ||||
| } | ||||
|  | ||||
| func shortLinkProcessor(ctx *postProcessCtx, node *html.Node) { | ||||
| @@ -541,11 +585,11 @@ func fullIssuePatternProcessor(ctx *postProcessCtx, node *html.Node) { | ||||
| 	if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] { | ||||
| 		// TODO if m[4]:m[5] is not nil, then link is to a comment, | ||||
| 		// and we should indicate that in the text somehow | ||||
| 		replaceContent(node, m[0], m[1], createLink(link, id)) | ||||
| 		replaceContent(node, m[0], m[1], createLink(link, id, "issue")) | ||||
|  | ||||
| 	} else { | ||||
| 		orgRepoID := matchOrg + "/" + matchRepo + id | ||||
| 		replaceContent(node, m[0], m[1], createLink(link, orgRepoID)) | ||||
| 		replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "issue")) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -573,9 +617,9 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) { | ||||
| 		} else { | ||||
| 			ctx.metas["index"] = id[1:] | ||||
| 		} | ||||
| 		link = createLink(com.Expand(ctx.metas["format"], ctx.metas), id) | ||||
| 		link = createLink(com.Expand(ctx.metas["format"], ctx.metas), id, "issue") | ||||
| 	} else { | ||||
| 		link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "issues", id[1:]), id) | ||||
| 		link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "issues", id[1:]), id, "issue") | ||||
| 	} | ||||
| 	replaceContent(node, match[2], match[3], link) | ||||
| } | ||||
| @@ -591,7 +635,7 @@ func crossReferenceIssueIndexPatternProcessor(ctx *postProcessCtx, node *html.No | ||||
| 	repo, issue := parts[0], parts[1] | ||||
|  | ||||
| 	replaceContent(node, m[2], m[3], | ||||
| 		createLink(util.URLJoin(setting.AppURL, repo, "issues", issue), ref)) | ||||
| 		createLink(util.URLJoin(setting.AppURL, repo, "issues", issue), ref, issue)) | ||||
| } | ||||
|  | ||||
| // fullSha1PatternProcessor renders SHA containing URLs | ||||
| @@ -642,7 +686,7 @@ func fullSha1PatternProcessor(ctx *postProcessCtx, node *html.Node) { | ||||
| 		text += " (" + hash + ")" | ||||
| 	} | ||||
|  | ||||
| 	replaceContent(node, start, end, createCodeLink(urlFull, text)) | ||||
| 	replaceContent(node, start, end, createCodeLink(urlFull, text, "commit")) | ||||
| } | ||||
|  | ||||
| // sha1CurrentPatternProcessor renders SHA1 strings to corresponding links that | ||||
| @@ -672,7 +716,7 @@ func sha1CurrentPatternProcessor(ctx *postProcessCtx, node *html.Node) { | ||||
| 	} | ||||
|  | ||||
| 	replaceContent(node, m[2], m[3], | ||||
| 		createCodeLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "commit", hash), base.ShortSha(hash))) | ||||
| 		createCodeLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "commit", hash), base.ShortSha(hash), "commit")) | ||||
| } | ||||
|  | ||||
| // emailAddressProcessor replaces raw email addresses with a mailto: link. | ||||
| @@ -682,7 +726,7 @@ func emailAddressProcessor(ctx *postProcessCtx, node *html.Node) { | ||||
| 		return | ||||
| 	} | ||||
| 	mail := node.Data[m[2]:m[3]] | ||||
| 	replaceContent(node, m[2], m[3], createLink("mailto:"+mail, mail)) | ||||
| 	replaceContent(node, m[2], m[3], createLink("mailto:"+mail, mail, "mailto")) | ||||
| } | ||||
|  | ||||
| // linkProcessor creates links for any HTTP or HTTPS URL not captured by | ||||
| @@ -693,7 +737,7 @@ func linkProcessor(ctx *postProcessCtx, node *html.Node) { | ||||
| 		return | ||||
| 	} | ||||
| 	uri := node.Data[m[0]:m[1]] | ||||
| 	replaceContent(node, m[0], m[1], createLink(uri, uri)) | ||||
| 	replaceContent(node, m[0], m[1], createLink(uri, uri, "link")) | ||||
| } | ||||
|  | ||||
| func genDefaultLinkProcessor(defaultLink string) processor { | ||||
| @@ -707,7 +751,10 @@ func genDefaultLinkProcessor(defaultLink string) processor { | ||||
| 		node.Type = html.ElementNode | ||||
| 		node.Data = "a" | ||||
| 		node.DataAtom = atom.A | ||||
| 		node.Attr = []html.Attribute{{Key: "href", Val: defaultLink}} | ||||
| 		node.Attr = []html.Attribute{ | ||||
| 			{Key: "href", Val: defaultLink}, | ||||
| 			{Key: "class", Val: "default-link"}, | ||||
| 		} | ||||
| 		node.FirstChild, node.LastChild = ch, ch | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -20,18 +20,22 @@ const Repo = "gogits/gogs" | ||||
| const AppSubURL = AppURL + Repo + "/" | ||||
|  | ||||
| // alphanumLink an HTML link to an alphanumeric-style issue | ||||
| func alphanumIssueLink(baseURL string, name string) string { | ||||
| 	return link(util.URLJoin(baseURL, name), name) | ||||
| func alphanumIssueLink(baseURL, class, name string) string { | ||||
| 	return link(util.URLJoin(baseURL, name), class, name) | ||||
| } | ||||
|  | ||||
| // numericLink an HTML to a numeric-style issue | ||||
| func numericIssueLink(baseURL string, index int) string { | ||||
| 	return link(util.URLJoin(baseURL, strconv.Itoa(index)), fmt.Sprintf("#%d", index)) | ||||
| func numericIssueLink(baseURL, class string, index int) string { | ||||
| 	return link(util.URLJoin(baseURL, strconv.Itoa(index)), class, fmt.Sprintf("#%d", index)) | ||||
| } | ||||
|  | ||||
| // link an HTML link | ||||
| func link(href, contents string) string { | ||||
| 	return fmt.Sprintf("<a href=\"%s\">%s</a>", href, contents) | ||||
| func link(href, class, contents string) string { | ||||
| 	if class != "" { | ||||
| 		class = " class=\"" + class + "\"" | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf("<a href=\"%s\"%s>%s</a>", href, class, contents) | ||||
| } | ||||
|  | ||||
| var numericMetas = map[string]string{ | ||||
| @@ -89,13 +93,13 @@ func TestRender_IssueIndexPattern2(t *testing.T) { | ||||
| 	test := func(s, expectedFmt string, indices ...int) { | ||||
| 		links := make([]interface{}, len(indices)) | ||||
| 		for i, index := range indices { | ||||
| 			links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), index) | ||||
| 			links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), "issue", index) | ||||
| 		} | ||||
| 		expectedNil := fmt.Sprintf(expectedFmt, links...) | ||||
| 		testRenderIssueIndexPattern(t, s, expectedNil, &postProcessCtx{metas: localMetas}) | ||||
|  | ||||
| 		for i, index := range indices { | ||||
| 			links[i] = numericIssueLink("https://someurl.com/someUser/someRepo/", index) | ||||
| 			links[i] = numericIssueLink("https://someurl.com/someUser/someRepo/", "issue", index) | ||||
| 		} | ||||
| 		expectedNum := fmt.Sprintf(expectedFmt, links...) | ||||
| 		testRenderIssueIndexPattern(t, s, expectedNum, &postProcessCtx{metas: numericMetas}) | ||||
| @@ -158,7 +162,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) { | ||||
| 	test := func(s, expectedFmt string, names ...string) { | ||||
| 		links := make([]interface{}, len(names)) | ||||
| 		for i, name := range names { | ||||
| 			links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", name) | ||||
| 			links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "issue", name) | ||||
| 		} | ||||
| 		expected := fmt.Sprintf(expectedFmt, links...) | ||||
| 		testRenderIssueIndexPattern(t, s, expected, &postProcessCtx{metas: alphanumericMetas}) | ||||
| @@ -197,17 +201,17 @@ func TestRender_AutoLink(t *testing.T) { | ||||
|  | ||||
| 	// render valid issue URLs | ||||
| 	test(util.URLJoin(setting.AppSubURL, "issues", "3333"), | ||||
| 		numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), 3333)) | ||||
| 		numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), "issue", 3333)) | ||||
|  | ||||
| 	// render valid commit URLs | ||||
| 	tmp := util.URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae") | ||||
| 	test(tmp, "<a href=\""+tmp+"\"><code class=\"nohighlight\">d8a994ef24</code></a>") | ||||
| 	test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24</code></a>") | ||||
| 	tmp += "#diff-2" | ||||
| 	test(tmp, "<a href=\""+tmp+"\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>") | ||||
| 	test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>") | ||||
|  | ||||
| 	// render other commit URLs | ||||
| 	tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2" | ||||
| 	test(tmp, "<a href=\""+tmp+"\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>") | ||||
| 	test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>") | ||||
| } | ||||
|  | ||||
| func TestRender_FullIssueURLs(t *testing.T) { | ||||
| @@ -228,11 +232,11 @@ func TestRender_FullIssueURLs(t *testing.T) { | ||||
| 	test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6", | ||||
| 		"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6") | ||||
| 	test("Look here http://localhost:3000/person/repo/issues/4", | ||||
| 		`Look here <a href="http://localhost:3000/person/repo/issues/4">person/repo#4</a>`) | ||||
| 		`Look here <a href="http://localhost:3000/person/repo/issues/4" class="issue">person/repo#4</a>`) | ||||
| 	test("http://localhost:3000/person/repo/issues/4#issuecomment-1234", | ||||
| 		`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">person/repo#4</a>`) | ||||
| 		`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="issue">person/repo#4</a>`) | ||||
| 	test("http://localhost:3000/gogits/gogs/issues/4", | ||||
| 		`<a href="http://localhost:3000/gogits/gogs/issues/4">#4</a>`) | ||||
| 		`<a href="http://localhost:3000/gogits/gogs/issues/4" class="issue">#4</a>`) | ||||
| } | ||||
|  | ||||
| func TestRegExp_issueNumericPattern(t *testing.T) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user