mirror of https://github.com/go-gitea/gitea.git
Fix raw wiki links (#31825)
Fix #31395 This regression is introduced by #30273. To find out how GitHub handles this case, I did [some tests](https://github.com/go-gitea/gitea/issues/31395#issuecomment-2278929115). I use redirect in this PR instead of checking if the corresponding `.md` file exists when rendering the link because GitHub also uses redirect. With this PR, there is no need to resolve the raw wiki link when rendering a wiki page. If a wiki link points to a raw file, access will be redirected to the raw link.
This commit is contained in:
parent
e4f850bf63
commit
2010fbe060
|
@ -4,8 +4,6 @@
|
|||
package markup
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
|
@ -14,13 +12,9 @@ func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (resu
|
|||
if !isAnchorFragment && !IsFullURLString(link) {
|
||||
linkBase := ctx.Links.Base
|
||||
if ctx.IsWiki {
|
||||
if ext := path.Ext(link); ext == "" || ext == ".-" {
|
||||
linkBase = ctx.Links.WikiLink() // the link is for a wiki page
|
||||
} else if DetectMarkupTypeByFileName(link) != "" {
|
||||
linkBase = ctx.Links.WikiLink() // the link is renderable as a wiki page
|
||||
} else {
|
||||
linkBase = ctx.Links.WikiRawLink() // otherwise, use a raw link instead to view&download medias
|
||||
}
|
||||
// no need to check if the link should be resolved as a wiki link or a wiki raw link
|
||||
// just use wiki link here and it will be redirected to a wiki raw link if necessary
|
||||
linkBase = ctx.Links.WikiLink()
|
||||
} else if ctx.Links.BranchPath != "" || ctx.Links.TreePath != "" {
|
||||
// if there is no BranchPath, then the link will be something like "/owner/repo/src/{the-file-path}"
|
||||
// and then this link will be handled by the "legacy-ref" code and be redirected to the default branch like "/owner/repo/src/branch/main/{the-file-path}"
|
||||
|
|
|
@ -437,7 +437,7 @@ func TestRender_ShortLinks(t *testing.T) {
|
|||
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")
|
||||
unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "file.zip")
|
||||
favicon := "http://google.com/favicon.ico"
|
||||
|
||||
test(
|
||||
|
|
|
@ -672,9 +672,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/raw/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/wiki/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/>
|
||||
|
@ -730,9 +730,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/raw/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://gitea.io/wiki/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/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://gitea.io/wiki/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/>
|
||||
|
@ -788,9 +788,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/raw/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/relative/path/wiki/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/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/relative/path/wiki/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/>
|
||||
|
@ -848,9 +848,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/raw/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/relative/path/wiki/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/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/relative/path/wiki/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/>
|
||||
|
@ -908,9 +908,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/raw/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/relative/path/wiki/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/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/relative/path/wiki/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/>
|
||||
|
@ -970,9 +970,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/raw/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/relative/path/wiki/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/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="/relative/path/wiki/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/>
|
||||
|
|
|
@ -138,18 +138,41 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
|
|||
return content
|
||||
}
|
||||
|
||||
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
||||
// indicating whether the page exists. Writes to ctx if an error occurs.
|
||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) {
|
||||
// wikiEntryByName returns the entry of a wiki page, along with a boolean
|
||||
// indicating whether the entry exists. Writes to ctx if an error occurs.
|
||||
// The last return value indicates whether the file should be returned as a raw file
|
||||
func wikiEntryByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) (*git.TreeEntry, string, bool, bool) {
|
||||
isRaw := false
|
||||
gitFilename := wiki_service.WebPathToGitPath(wikiName)
|
||||
entry, err := findEntryForFile(commit, gitFilename)
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
ctx.ServerError("findEntryForFile", err)
|
||||
return nil, nil, "", false
|
||||
} else if entry == nil {
|
||||
return nil, "", false, false
|
||||
}
|
||||
if entry == nil {
|
||||
// check if the file without ".md" suffix exists
|
||||
gitFilename := strings.TrimSuffix(gitFilename, ".md")
|
||||
entry, err = findEntryForFile(commit, gitFilename)
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
ctx.ServerError("findEntryForFile", err)
|
||||
return nil, "", false, false
|
||||
}
|
||||
isRaw = true
|
||||
}
|
||||
if entry == nil {
|
||||
return nil, "", true, false
|
||||
}
|
||||
return entry, gitFilename, false, isRaw
|
||||
}
|
||||
|
||||
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
||||
// indicating whether the page exists. Writes to ctx if an error occurs.
|
||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) {
|
||||
entry, gitFilename, noEntry, _ := wikiEntryByName(ctx, commit, wikiName)
|
||||
if entry == nil {
|
||||
return nil, nil, "", true
|
||||
}
|
||||
return wikiContentsByEntry(ctx, entry), entry, gitFilename, false
|
||||
return wikiContentsByEntry(ctx, entry), entry, gitFilename, noEntry
|
||||
}
|
||||
|
||||
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
|
@ -215,11 +238,14 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
|||
isSideBar := pageName == "_Sidebar"
|
||||
isFooter := pageName == "_Footer"
|
||||
|
||||
// lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
|
||||
// lookup filename in wiki - get gitTree entry , real filename
|
||||
entry, pageFilename, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName)
|
||||
if noEntry {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
|
||||
}
|
||||
if isRaw {
|
||||
ctx.Redirect(util.URLJoin(ctx.Repo.RepoLink, "wiki/raw", string(pageName)))
|
||||
}
|
||||
if entry == nil || ctx.Written() {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
|
@ -227,6 +253,15 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// get filecontent
|
||||
data := wikiContentsByEntry(ctx, entry)
|
||||
if ctx.Written() {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var sidebarContent []byte
|
||||
if !isSideBar {
|
||||
sidebarContent, _, _, _ = wikiContentsByName(ctx, commit, "_Sidebar")
|
||||
|
@ -442,15 +477,24 @@ func renderEditPage(ctx *context.Context) {
|
|||
ctx.Data["Title"] = displayName
|
||||
ctx.Data["title"] = displayName
|
||||
|
||||
// lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
|
||||
// lookup filename in wiki - gitTree entry , real filename
|
||||
entry, _, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName)
|
||||
if noEntry {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
|
||||
}
|
||||
if isRaw {
|
||||
ctx.Error(http.StatusForbidden, "Editing of raw wiki files is not allowed")
|
||||
}
|
||||
if entry == nil || ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
// get filecontent
|
||||
data := wikiContentsByEntry(ctx, entry)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["content"] = string(data)
|
||||
ctx.Data["sidebarPresent"] = false
|
||||
ctx.Data["sidebarContent"] = ""
|
||||
|
|
|
@ -87,6 +87,13 @@ func TestWiki(t *testing.T) {
|
|||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
assert.EqualValues(t, "Home", ctx.Data["Title"])
|
||||
assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"])
|
||||
|
||||
ctx, _ = contexttest.MockContext(t, "user2/repo1/jpeg.jpg")
|
||||
ctx.SetPathParam("*", "jpeg.jpg")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
Wiki(ctx)
|
||||
assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
|
||||
assert.Equal(t, "/user2/repo1/wiki/raw/jpeg.jpg", ctx.Resp.Header().Get("Location"))
|
||||
}
|
||||
|
||||
func TestWikiPages(t *testing.T) {
|
||||
|
@ -160,6 +167,13 @@ func TestEditWiki(t *testing.T) {
|
|||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
assert.EqualValues(t, "Home", ctx.Data["Title"])
|
||||
assert.Equal(t, wikiContent(t, ctx.Repo.Repository, "Home"), ctx.Data["content"])
|
||||
|
||||
ctx, _ = contexttest.MockContext(t, "user2/repo1/wiki/jpeg.jpg?action=_edit")
|
||||
ctx.SetPathParam("*", "jpeg.jpg")
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
EditWiki(ctx)
|
||||
assert.EqualValues(t, http.StatusForbidden, ctx.Resp.Status())
|
||||
}
|
||||
|
||||
func TestEditWikiPost(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue