In Wiki pages, short-links created to local Wiki files were always expanded as regular Wiki Links. In particular, if a link wanted to point to a file that Gitea doesn't know how to render (e.g, a .zip file), a user following the link would be silently redirected to the Wiki's home page. This change makes short-links* in Wiki pages be expanded to raw wiki links, so these local wiki files may be accessed without manually accessing their URL. * only short-links ending in a file extension that isn't renderable are affected. Closes #27121. Signed-off-by: Rafael Girão <rafael.s.girao@tecnico.ulisboa.pt> Co-authored-by: silverwind <me@silverwind.io>tags/v1.22.0-rc1
@@ -709,7 +709,8 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { | |||
name += tail | |||
image := false | |||
switch ext := filepath.Ext(link); ext { | |||
ext := filepath.Ext(link) | |||
switch ext { | |||
// fast path: empty string, ignore | |||
case "": | |||
// leave image as false | |||
@@ -767,11 +768,26 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { | |||
} | |||
} else { | |||
if !absoluteLink { | |||
var base string | |||
if ctx.IsWiki { | |||
link = util.URLJoin(ctx.Links.WikiLink(), link) | |||
switch ext { | |||
case "": | |||
// no file extension, create a regular wiki link | |||
base = ctx.Links.WikiLink() | |||
default: | |||
// we have a file extension: | |||
// return a regular wiki link if it's a renderable file (extension), | |||
// raw link otherwise | |||
if Type(link) != "" { | |||
base = ctx.Links.WikiLink() | |||
} else { | |||
base = ctx.Links.WikiRawLink() | |||
} | |||
} | |||
} else { | |||
link = util.URLJoin(ctx.Links.SrcLink(), link) | |||
base = ctx.Links.SrcLink() | |||
} | |||
link = util.URLJoin(base, link) | |||
} | |||
childNode.Type = html.TextNode | |||
childNode.Data = name |
@@ -427,6 +427,10 @@ func TestRender_ShortLinks(t *testing.T) { | |||
otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg") | |||
encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg") | |||
notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg") | |||
renderableFileURL := util.URLJoin(tree, "markdown_file.md") | |||
renderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "markdown_file.md") | |||
unrenderableFileURL := util.URLJoin(tree, "file.zip") | |||
unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "file.zip") | |||
favicon := "http://google.com/favicon.ico" | |||
test( | |||
@@ -481,6 +485,14 @@ func TestRender_ShortLinks(t *testing.T) { | |||
"[[Link]] [[Other Link]] [[Link?]]", | |||
`<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a> <a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`, | |||
`<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a> <a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`) | |||
test( | |||
"[[markdown_file.md]]", | |||
`<p><a href="`+renderableFileURL+`" rel="nofollow">markdown_file.md</a></p>`, | |||
`<p><a href="`+renderableFileURLWiki+`" rel="nofollow">markdown_file.md</a></p>`) | |||
test( | |||
"[[file.zip]]", | |||
`<p><a href="`+unrenderableFileURL+`" rel="nofollow">file.zip</a></p>`, | |||
`<p><a href="`+unrenderableFileURLWiki+`" rel="nofollow">file.zip</a></p>`) | |||
test( | |||
"[[Link #.jpg]]", | |||
`<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`, |
@@ -653,9 +653,9 @@ space</p> | |||
Expected: `<p>space @mention-user<br/> | |||
/just/a/path.bin<br/> | |||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/> | |||
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image"/></a><br/> | |||
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image"/></a><br/> | |||
@@ -711,9 +711,9 @@ space</p> | |||
Expected: `<p>space @mention-user<br/> | |||
/just/a/path.bin<br/> | |||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/> | |||
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://gitea.io/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://gitea.io/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image"/></a><br/> | |||
<a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image"/></a><br/> | |||
@@ -769,9 +769,9 @@ space</p> | |||
Expected: `<p>space @mention-user<br/> | |||
/just/a/path.bin<br/> | |||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/> | |||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/> | |||
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> | |||
@@ -829,9 +829,9 @@ space</p> | |||
Expected: `<p>space @mention-user<br/> | |||
/just/a/path.bin<br/> | |||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/> | |||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/> | |||
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> | |||
@@ -889,9 +889,9 @@ space</p> | |||
Expected: `<p>space @mention-user<br/> | |||
/just/a/path.bin<br/> | |||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/> | |||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/> | |||
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> | |||
@@ -951,9 +951,9 @@ space</p> | |||
Expected: `<p>space @mention-user<br/> | |||
/just/a/path.bin<br/> | |||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/> | |||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/> | |||
<a href="https://example.com" rel="nofollow">remote link</a><br/> | |||
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/> | |||
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> |
@@ -4,6 +4,8 @@ | |||
package markdown | |||
import ( | |||
"path/filepath" | |||
"code.gitea.io/gitea/modules/markup" | |||
giteautil "code.gitea.io/gitea/modules/util" | |||
@@ -18,7 +20,16 @@ func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link, r | |||
if !isAnchorFragment && !markup.IsFullURLBytes(link) { | |||
base := ctx.Links.Base | |||
if ctx.IsWiki { | |||
base = ctx.Links.WikiLink() | |||
if filepath.Ext(string(link)) == "" { | |||
// This link doesn't have a file extension - assume a regular wiki link | |||
base = ctx.Links.WikiLink() | |||
} else if markup.Type(string(link)) != "" { | |||
// If it's a file type we can render, use a regular wiki link | |||
base = ctx.Links.WikiLink() | |||
} else { | |||
// Otherwise, use a raw link instead | |||
base = ctx.Links.WikiRawLink() | |||
} | |||
} else if ctx.Links.HasBranchInfo() { | |||
base = ctx.Links.SrcLink() | |||
} |
@@ -200,12 +200,13 @@ func TestDeleteWikiPagePost(t *testing.T) { | |||
func TestWikiRaw(t *testing.T) { | |||
for filepath, filetype := range map[string]string{ | |||
"jpeg.jpg": "image/jpeg", | |||
"images/jpeg.jpg": "image/jpeg", | |||
"Page With Spaced Name": "text/plain; charset=utf-8", | |||
"Page-With-Spaced-Name": "text/plain; charset=utf-8", | |||
"Page With Spaced Name.md": "", // there is no "Page With Spaced Name.md" in repo | |||
"Page-With-Spaced-Name.md": "text/plain; charset=utf-8", | |||
"jpeg.jpg": "image/jpeg", | |||
"images/jpeg.jpg": "image/jpeg", | |||
"files/Non-Renderable-File.zip": "application/octet-stream", | |||
"Page With Spaced Name": "text/plain; charset=utf-8", | |||
"Page-With-Spaced-Name": "text/plain; charset=utf-8", | |||
"Page With Spaced Name.md": "", // there is no "Page With Spaced Name.md" in repo | |||
"Page-With-Spaced-Name.md": "text/plain; charset=utf-8", | |||
} { | |||
unittest.PrepareTestEnv(t) | |||
@@ -0,0 +1 @@ | |||
x��KnΔ D³ζμ£±ΑvσEQ²Κ~nΠ@3Fςπ‰r”\,d��^”T¥�SµΟ�Gj|�ΕK+DάΚΰΆ $2`ε4ΗΙY«”Y”u{‡Xho\�ΰ‚u4E;k- P4�Q^H³84ΐπlk.ό†iγ_©ό|gώV�v¨Γ=Μ�ό�|Ξ-U—q8Ϊ;—ZJµθ,ύ�Nnτ_�Υ”κ0Ωaμ3Ύηύ�Ώ TΠmΔ·μqγ1mΔΨ�b½ςµµ£^Ηρ�ΪzΊ΅/οω�α_Φ5ΥzR'-'Ι~ιtl½ |
@@ -1 +1 @@ | |||
0dca5bd9b5d7ef937710e056f575e86c0184ba85 | |||
a5bbc0fd39a696feabed2d4cccaf05abbcaf3b02 |
@@ -45,6 +45,7 @@ func TestRepoCloneWiki(t *testing.T) { | |||
assertFileExist(t, filepath.Join(dstPath, "Page-With-Image.md")) | |||
assertFileExist(t, filepath.Join(dstPath, "Page-With-Spaced-Name.md")) | |||
assertFileExist(t, filepath.Join(dstPath, "images")) | |||
assertFileExist(t, filepath.Join(dstPath, "files/Non-Renderable-File.zip")) | |||
assertFileExist(t, filepath.Join(dstPath, "jpeg.jpg")) | |||
}) | |||
}) |