Fix inline permalinks across repo; closes #2965 (#3042)

This PR fixes the possible ambiguity of rendered inline permalinks across repos by adding it as a suffix to the title element if the permalink refers to a file not inside the current repository. Closes #2965

![grafik](/attachments/e70e37b8-24c7-4f7b-ab52-92f1e8dfb009)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3042
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Mai-Lapyst <mai-lapyst@noreply.codeberg.org>
Co-committed-by: Mai-Lapyst <mai-lapyst@noreply.codeberg.org>
This commit is contained in:
Mai-Lapyst 2024-04-12 22:30:20 +00:00 committed by Earl Warren
parent 828ae39c22
commit 1d1c0131bb
3 changed files with 82 additions and 22 deletions

View file

@ -27,10 +27,9 @@ var filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([
type FilePreview struct { type FilePreview struct {
fileContent []template.HTML fileContent []template.HTML
title template.HTML
subTitle template.HTML subTitle template.HTML
lineOffset int lineOffset int
urlFull string
filePath string
start int start int
end int end int
isTruncated bool isTruncated bool
@ -54,39 +53,65 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca
return nil return nil
} }
preview.urlFull = node.Data[m[0]:m[1]] urlFull := node.Data[m[0]:m[1]]
// Ensure that we only use links to local repositories // Ensure that we only use links to local repositories
if !strings.HasPrefix(preview.urlFull, setting.AppURL+setting.AppSubURL) { if !strings.HasPrefix(urlFull, setting.AppURL+setting.AppSubURL) {
return nil return nil
} }
projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/") projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/")
commitSha := node.Data[m[4]:m[5]] commitSha := node.Data[m[4]:m[5]]
preview.filePath = node.Data[m[6]:m[7]] filePath := node.Data[m[6]:m[7]]
hash := node.Data[m[8]:m[9]] hash := node.Data[m[8]:m[9]]
preview.start = m[0] preview.start = m[0]
preview.end = m[1] preview.end = m[1]
projPathSegments := strings.Split(projPath, "/") projPathSegments := strings.Split(projPath, "/")
ownerName := projPathSegments[len(projPathSegments)-2]
repoName := projPathSegments[len(projPathSegments)-1]
var language string var language string
fileBlob, err := DefaultProcessorHelper.GetRepoFileBlob( fileBlob, err := DefaultProcessorHelper.GetRepoFileBlob(
ctx.Ctx, ctx.Ctx,
projPathSegments[len(projPathSegments)-2], ownerName,
projPathSegments[len(projPathSegments)-1], repoName,
commitSha, preview.filePath, commitSha, filePath,
&language, &language,
) )
if err != nil { if err != nil {
return nil return nil
} }
titleBuffer := new(bytes.Buffer)
isExternRef := ownerName != ctx.Metas["user"] || repoName != ctx.Metas["repo"]
if isExternRef {
err = html.Render(titleBuffer, createLink(node.Data[m[0]:m[3]], ownerName+"/"+repoName, ""))
if err != nil {
log.Error("failed to render repoLink: %v", err)
}
titleBuffer.WriteString(" &ndash; ")
}
err = html.Render(titleBuffer, createLink(urlFull, filePath, "muted"))
if err != nil {
log.Error("failed to render filepathLink: %v", err)
}
preview.title = template.HTML(titleBuffer.String())
lineSpecs := strings.Split(hash, "-") lineSpecs := strings.Split(hash, "-")
commitLinkBuffer := new(bytes.Buffer) commitLinkBuffer := new(bytes.Buffer)
err = html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) commitLinkText := commitSha[0:7]
if isExternRef {
commitLinkText = ownerName + "/" + repoName + "@" + commitLinkText
}
err = html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitLinkText, "text black"))
if err != nil { if err != nil {
log.Error("failed to render commitLink: %v", err) log.Error("failed to render commitLink: %v", err)
} }
@ -272,19 +297,16 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node {
Data: atom.Div.String(), Data: atom.Div.String(),
Attr: []html.Attribute{{Key: "class", Val: "header"}}, Attr: []html.Attribute{{Key: "class", Val: "header"}},
} }
afilepath := &html.Node{
ptitle := &html.Node{
Type: html.ElementNode, Type: html.ElementNode,
Data: atom.A.String(), Data: atom.Div.String(),
Attr: []html.Attribute{
{Key: "href", Val: p.urlFull},
{Key: "class", Val: "muted"},
},
} }
afilepath.AppendChild(&html.Node{ ptitle.AppendChild(&html.Node{
Type: html.TextNode, Type: html.RawNode,
Data: p.filePath, Data: string(p.title),
}) })
header.AppendChild(afilepath) header.AppendChild(ptitle)
psubtitle := &html.Node{ psubtitle := &html.Node{
Type: html.ElementNode, Type: html.ElementNode,

View file

@ -1056,7 +1056,7 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
} }
func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil { if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" {
return return
} }
if DefaultProcessorHelper.GetRepoFileBlob == nil { if DefaultProcessorHelper.GetRepoFileBlob == nil {

View file

@ -705,11 +705,11 @@ func TestRender_FilePreview(t *testing.T) {
sha := "190d9492934af498c3f669d6a2431dc5459e5b20" sha := "190d9492934af498c3f669d6a2431dc5459e5b20"
commitFilePreview := util.URLJoin(markup.TestRepoURL, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3" commitFilePreview := util.URLJoin(markup.TestRepoURL, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3"
test := func(input, expected string) { test := func(input, expected string, metas map[string]string) {
buffer, err := markup.RenderString(&markup.RenderContext{ buffer, err := markup.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext, Ctx: git.DefaultContext,
RelativePath: ".md", RelativePath: ".md",
Metas: localMetas, Metas: metas,
}, input) }, input)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
@ -720,7 +720,9 @@ func TestRender_FilePreview(t *testing.T) {
`<p></p>`+ `<p></p>`+
`<div class="file-preview-box">`+ `<div class="file-preview-box">`+
`<div class="header">`+ `<div class="header">`+
`<div>`+
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
`</div>`+
`<span class="text small grey">`+ `<span class="text small grey">`+
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
`</span>`+ `</span>`+
@ -741,5 +743,41 @@ func TestRender_FilePreview(t *testing.T) {
`</div>`+ `</div>`+
`</div>`+ `</div>`+
`<p></p>`, `<p></p>`,
localMetas,
)
test(
commitFilePreview,
`<p></p>`+
`<div class="file-preview-box">`+
`<div class="header">`+
`<div>`+
`<a href="http://localhost:3000/gogits/gogs/" rel="nofollow">gogits/gogs</a> `+
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
`</div>`+
`<span class="text small grey">`+
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">gogits/gogs@190d949</a>`+
`</span>`+
`</div>`+
`<div class="ui table">`+
`<table class="file-preview">`+
`<tbody>`+
`<tr>`+
`<td class="lines-num"><span data-line-number="2"></span></td>`+
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
`</tr>`+
`<tr>`+
`<td class="lines-num"><span data-line-number="3"></span></td>`+
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
`</tr>`+
`</tbody>`+
`</table>`+
`</div>`+
`</div>`+
`<p></p>`,
map[string]string{
"user": "gogits",
"repo": "gogs2",
},
) )
} }