From dbf1d06ff6eac46f0fbe62ec2fd33605f1f9c618 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Fri, 12 Apr 2024 22:30:20 +0000 Subject: [PATCH] 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 Reviewed-by: Gusted Co-authored-by: Mai-Lapyst Co-committed-by: Mai-Lapyst (cherry picked from commit 1d1c0131bb1bbd117d5aaac48d7dd32f4638c648) --- modules/markup/file_preview.go | 60 +++++++++++++++++++++++----------- modules/markup/html.go | 2 +- modules/markup/html_test.go | 42 ++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 22 deletions(-) diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 95c94e0c14..a37007d75b 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -27,10 +27,9 @@ var filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([ type FilePreview struct { fileContent []template.HTML + title template.HTML subTitle template.HTML lineOffset int - urlFull string - filePath string start int end int isTruncated bool @@ -54,39 +53,65 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca 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 - if !strings.HasPrefix(preview.urlFull, setting.AppURL+setting.AppSubURL) { + if !strings.HasPrefix(urlFull, setting.AppURL+setting.AppSubURL) { return nil } projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/") 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]] preview.start = m[0] preview.end = m[1] projPathSegments := strings.Split(projPath, "/") + ownerName := projPathSegments[len(projPathSegments)-2] + repoName := projPathSegments[len(projPathSegments)-1] + var language string fileBlob, err := DefaultProcessorHelper.GetRepoFileBlob( ctx.Ctx, - projPathSegments[len(projPathSegments)-2], - projPathSegments[len(projPathSegments)-1], - commitSha, preview.filePath, + ownerName, + repoName, + commitSha, filePath, &language, ) if err != 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(" – ") + } + + 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, "-") 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 { 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(), Attr: []html.Attribute{{Key: "class", Val: "header"}}, } - afilepath := &html.Node{ + + ptitle := &html.Node{ Type: html.ElementNode, - Data: atom.A.String(), - Attr: []html.Attribute{ - {Key: "href", Val: p.urlFull}, - {Key: "class", Val: "muted"}, - }, + Data: atom.Div.String(), } - afilepath.AppendChild(&html.Node{ - Type: html.TextNode, - Data: p.filePath, + ptitle.AppendChild(&html.Node{ + Type: html.RawNode, + Data: string(p.title), }) - header.AppendChild(afilepath) + header.AppendChild(ptitle) psubtitle := &html.Node{ Type: html.ElementNode, diff --git a/modules/markup/html.go b/modules/markup/html.go index 020ba9c631..d19eb37013 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -1056,7 +1056,7 @@ func comparePatternProcessor(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 } if DefaultProcessorHelper.GetRepoFileBlob == nil { diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 61a3edd6b3..2c5ed8c7b8 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -705,11 +705,11 @@ func TestRender_FilePreview(t *testing.T) { sha := "190d9492934af498c3f669d6a2431dc5459e5b20" 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{ Ctx: git.DefaultContext, RelativePath: ".md", - Metas: localMetas, + Metas: metas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -720,7 +720,9 @@ func TestRender_FilePreview(t *testing.T) { `

`+ `
`+ `
`+ + `
`+ `path/to/file.go`+ + `
`+ ``+ `Lines 2 to 3 in 190d949`+ ``+ @@ -741,5 +743,41 @@ func TestRender_FilePreview(t *testing.T) { `
`+ `
`+ `

`, + localMetas, + ) + + test( + commitFilePreview, + `

`+ + `
`+ + `
`+ + `
`+ + `gogits/gogs – `+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in gogits/gogs@190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + map[string]string{ + "user": "gogits", + "repo": "gogs2", + }, ) }