* 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 hoveredtags/v1.10.0-rc1
return ctx.postProcess(rawHTML) | 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 | // RenderDescriptionHTML will use similar logic as PostProcess, but will | ||||
// use a single special linkProcessor. | // use a single special linkProcessor. | ||||
func RenderDescriptionHTML( | func RenderDescriptionHTML( | ||||
} | } | ||||
} | } | ||||
func createLink(href, content string) *html.Node { | |||||
func createLink(href, content, class string) *html.Node { | |||||
a := &html.Node{ | a := &html.Node{ | ||||
Type: html.ElementNode, | Type: html.ElementNode, | ||||
Data: atom.A.String(), | Data: atom.A.String(), | ||||
Attr: []html.Attribute{{Key: "href", Val: href}}, | Attr: []html.Attribute{{Key: "href", Val: href}}, | ||||
} | } | ||||
if class != "" { | |||||
a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class}) | |||||
} | |||||
text := &html.Node{ | text := &html.Node{ | ||||
Type: html.TextNode, | Type: html.TextNode, | ||||
Data: content, | Data: content, | ||||
return a | return a | ||||
} | } | ||||
func createCodeLink(href, content string) *html.Node { | |||||
func createCodeLink(href, content, class string) *html.Node { | |||||
a := &html.Node{ | a := &html.Node{ | ||||
Type: html.ElementNode, | Type: html.ElementNode, | ||||
Data: atom.A.String(), | Data: atom.A.String(), | ||||
Attr: []html.Attribute{{Key: "href", Val: href}}, | Attr: []html.Attribute{{Key: "href", Val: href}}, | ||||
} | } | ||||
if class != "" { | |||||
a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class}) | |||||
} | |||||
text := &html.Node{ | text := &html.Node{ | ||||
Type: html.TextNode, | Type: html.TextNode, | ||||
Data: content, | Data: content, | ||||
} | } | ||||
// Replace the mention with a link to the specified user. | // Replace the mention with a link to the specified user. | ||||
mention := node.Data[m[2]:m[3]] | 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) { | func shortLinkProcessor(ctx *postProcessCtx, node *html.Node) { | ||||
if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] { | if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] { | ||||
// TODO if m[4]:m[5] is not nil, then link is to a comment, | // TODO if m[4]:m[5] is not nil, then link is to a comment, | ||||
// and we should indicate that in the text somehow | // 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 { | } else { | ||||
orgRepoID := matchOrg + "/" + matchRepo + id | orgRepoID := matchOrg + "/" + matchRepo + id | ||||
replaceContent(node, m[0], m[1], createLink(link, orgRepoID)) | |||||
replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "issue")) | |||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
ctx.metas["index"] = id[1:] | 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 { | } 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) | replaceContent(node, match[2], match[3], link) | ||||
} | } | ||||
repo, issue := parts[0], parts[1] | repo, issue := parts[0], parts[1] | ||||
replaceContent(node, m[2], m[3], | 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 | // fullSha1PatternProcessor renders SHA containing URLs | ||||
text += " (" + hash + ")" | 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 | // sha1CurrentPatternProcessor renders SHA1 strings to corresponding links that | ||||
} | } | ||||
replaceContent(node, m[2], m[3], | 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. | // emailAddressProcessor replaces raw email addresses with a mailto: link. | ||||
return | return | ||||
} | } | ||||
mail := node.Data[m[2]:m[3]] | 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 | // linkProcessor creates links for any HTTP or HTTPS URL not captured by | ||||
return | return | ||||
} | } | ||||
uri := node.Data[m[0]:m[1]] | 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 { | func genDefaultLinkProcessor(defaultLink string) processor { | ||||
node.Type = html.ElementNode | node.Type = html.ElementNode | ||||
node.Data = "a" | node.Data = "a" | ||||
node.DataAtom = atom.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 | node.FirstChild, node.LastChild = ch, ch | ||||
} | } | ||||
} | } |
const AppSubURL = AppURL + Repo + "/" | const AppSubURL = AppURL + Repo + "/" | ||||
// alphanumLink an HTML link to an alphanumeric-style issue | // 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 | // 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 | // 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{ | var numericMetas = map[string]string{ | ||||
test := func(s, expectedFmt string, indices ...int) { | test := func(s, expectedFmt string, indices ...int) { | ||||
links := make([]interface{}, len(indices)) | links := make([]interface{}, len(indices)) | ||||
for i, index := range 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...) | expectedNil := fmt.Sprintf(expectedFmt, links...) | ||||
testRenderIssueIndexPattern(t, s, expectedNil, &postProcessCtx{metas: localMetas}) | testRenderIssueIndexPattern(t, s, expectedNil, &postProcessCtx{metas: localMetas}) | ||||
for i, index := range indices { | 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...) | expectedNum := fmt.Sprintf(expectedFmt, links...) | ||||
testRenderIssueIndexPattern(t, s, expectedNum, &postProcessCtx{metas: numericMetas}) | testRenderIssueIndexPattern(t, s, expectedNum, &postProcessCtx{metas: numericMetas}) | ||||
test := func(s, expectedFmt string, names ...string) { | test := func(s, expectedFmt string, names ...string) { | ||||
links := make([]interface{}, len(names)) | links := make([]interface{}, len(names)) | ||||
for i, name := range 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...) | expected := fmt.Sprintf(expectedFmt, links...) | ||||
testRenderIssueIndexPattern(t, s, expected, &postProcessCtx{metas: alphanumericMetas}) | testRenderIssueIndexPattern(t, s, expected, &postProcessCtx{metas: alphanumericMetas}) | ||||
// render valid issue URLs | // render valid issue URLs | ||||
test(util.URLJoin(setting.AppSubURL, "issues", "3333"), | 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 | // render valid commit URLs | ||||
tmp := util.URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae") | 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" | 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 | // render other commit URLs | ||||
tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2" | 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) { | func TestRender_FullIssueURLs(t *testing.T) { | ||||
test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6", | 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") | "Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6") | ||||
test("Look here http://localhost:3000/person/repo/issues/4", | 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", | 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", | 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) { | func TestRegExp_issueNumericPattern(t *testing.T) { |
"EscapePound": func(str string) string { | "EscapePound": func(str string) string { | ||||
return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str) | return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str) | ||||
}, | }, | ||||
"PathEscapeSegments": util.PathEscapeSegments, | |||||
"URLJoin": util.URLJoin, | |||||
"RenderCommitMessage": RenderCommitMessage, | |||||
"RenderCommitMessageLink": RenderCommitMessageLink, | |||||
"RenderCommitBody": RenderCommitBody, | |||||
"RenderNote": RenderNote, | |||||
"IsMultilineCommitMessage": IsMultilineCommitMessage, | |||||
"PathEscapeSegments": util.PathEscapeSegments, | |||||
"URLJoin": util.URLJoin, | |||||
"RenderCommitMessage": RenderCommitMessage, | |||||
"RenderCommitMessageLink": RenderCommitMessageLink, | |||||
"RenderCommitMessageLinkSubject": RenderCommitMessageLinkSubject, | |||||
"RenderCommitBody": RenderCommitBody, | |||||
"RenderNote": RenderNote, | |||||
"IsMultilineCommitMessage": IsMultilineCommitMessage, | |||||
"ThemeColorMetaTag": func() string { | "ThemeColorMetaTag": func() string { | ||||
return setting.UI.ThemeColorMetaTag | return setting.UI.ThemeColorMetaTag | ||||
}, | }, | ||||
return template.HTML(msgLines[0]) | return template.HTML(msgLines[0]) | ||||
} | } | ||||
// RenderCommitMessageLinkSubject renders commit message as a XXS-safe link to | |||||
// the provided default url, handling for special links without email to links. | |||||
func RenderCommitMessageLinkSubject(msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML { | |||||
cleanMsg := template.HTMLEscapeString(msg) | |||||
// we can safely assume that it will not return any error, since there | |||||
// shouldn't be any special HTML. | |||||
fullMessage, err := markup.RenderCommitMessageSubject([]byte(cleanMsg), urlPrefix, urlDefault, metas) | |||||
if err != nil { | |||||
log.Error("RenderCommitMessageSubject: %v", err) | |||||
return "" | |||||
} | |||||
msgLines := strings.Split(strings.TrimSpace(string(fullMessage)), "\n") | |||||
if len(msgLines) == 0 { | |||||
return template.HTML("") | |||||
} | |||||
return template.HTML(msgLines[0]) | |||||
} | |||||
// RenderCommitBody extracts the body of a commit message without its title. | // RenderCommitBody extracts the body of a commit message without its title. | ||||
func RenderCommitBody(msg, urlPrefix string, metas map[string]string) template.HTML { | func RenderCommitBody(msg, urlPrefix string, metas map[string]string) template.HTML { | ||||
cleanMsg := template.HTMLEscapeString(msg) | cleanMsg := template.HTMLEscapeString(msg) |
} | } | ||||
.repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400} | .repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400} | ||||
.repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px} | .repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px} | ||||
.repository.file.list #repo-files-table thead .commit-summary a{text-decoration:underline;-webkit-text-decoration-style:dashed;text-decoration-style:dashed} | |||||
.repository.file.list #repo-files-table thead .commit-summary a:hover{-webkit-text-decoration-style:solid;text-decoration-style:solid} | |||||
.repository.file.list #repo-files-table thead .commit-summary a.default-link{text-decoration:none} | |||||
.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} | |||||
.repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777} | .repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777} | ||||
.repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px} | .repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px} | ||||
.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} | .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} | ||||
.stats-table .table-cell.tiny{height:.5em} | .stats-table .table-cell.tiny{height:.5em} | ||||
tbody.commit-list{vertical-align:baseline} | tbody.commit-list{vertical-align:baseline} | ||||
.commit-list .message-wrapper{overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - 50px);display:inline-block;vertical-align:middle} | .commit-list .message-wrapper{overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - 50px);display:inline-block;vertical-align:middle} | ||||
.commit-list .commit-summary a{text-decoration:underline;-webkit-text-decoration-style:dashed;text-decoration-style:dashed} | |||||
.commit-list .commit-summary a:hover{-webkit-text-decoration-style:solid;text-decoration-style:solid} | |||||
.commit-list .commit-summary a.default-link{text-decoration:none} | |||||
.commit-list .commit-summary a.default-link:hover{text-decoration:underline;-webkit-text-decoration-style:solid;text-decoration-style:solid} | |||||
.commit-list .commit-status-link{display:inline-block;vertical-align:middle} | .commit-list .commit-status-link{display:inline-block;vertical-align:middle} | ||||
.commit-body{white-space:pre-wrap} | .commit-body{white-space:pre-wrap} | ||||
.git-notes.top{text-align:left} | .git-notes.top{text-align:left} |
.ui.avatar { | .ui.avatar { | ||||
margin-bottom: 5px; | margin-bottom: 5px; | ||||
} | } | ||||
.commit-summary a { | |||||
text-decoration: underline; | |||||
text-decoration-style: dashed; | |||||
&:hover { | |||||
text-decoration-style: solid; | |||||
} | |||||
&.default-link { | |||||
text-decoration: none; | |||||
&:hover { | |||||
text-decoration: underline; | |||||
text-decoration-style: solid; | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
tbody { | tbody { | ||||
vertical-align: middle; | vertical-align: middle; | ||||
} | } | ||||
.commit-list .commit-summary a { | |||||
text-decoration: underline; | |||||
text-decoration-style: dashed; | |||||
&:hover { | |||||
text-decoration-style: solid; | |||||
} | |||||
&.default-link { | |||||
text-decoration: none; | |||||
&:hover { | |||||
text-decoration: underline; | |||||
text-decoration-style: solid; | |||||
} | |||||
} | |||||
} | |||||
.commit-list .commit-status-link { | .commit-list .commit-status-link { | ||||
display: inline-block; | display: inline-block; | ||||
vertical-align: middle; | vertical-align: middle; |
</td> | </td> | ||||
<td class="message"> | <td class="message"> | ||||
<span class="message-wrapper"> | <span class="message-wrapper"> | ||||
<span class="commit-summary has-emoji{{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessage .Message $.RepoLink $.Repository.ComposeMetas}}</span> | |||||
{{ $commitLink:= printf "%s/%s/%s/commit/%s" AppSubUrl $.Username $.Reponame .ID }} | |||||
<span class="commit-summary has-emoji{{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject .Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span> | |||||
</span> | </span> | ||||
{{if IsMultilineCommitMessage .Message}} | {{if IsMultilineCommitMessage .Message}} | ||||
<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> | <button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> |
{{end}} | {{end}} | ||||
</a> | </a> | ||||
{{template "repo/commit_status" .LatestCommitStatus}} | {{template "repo/commit_status" .LatestCommitStatus}} | ||||
<span class="grey has-emoji commit-summary" title="{{.LatestCommit.Summary}}">{{RenderCommitMessage .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}} | |||||
{{ $commitLink:= printf "%s/commit/%s" .RepoLink .LatestCommit.ID }} | |||||
<span class="grey has-emoji commit-summary" title="{{.LatestCommit.Summary}}">{{RenderCommitMessageLinkSubject .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}} | |||||
{{if IsMultilineCommitMessage .LatestCommit.Message}} | {{if IsMultilineCommitMessage .LatestCommit.Message}} | ||||
<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> | <button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> | ||||
<pre class="commit-body" style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> | <pre class="commit-body" style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> |