Make wiki title supports dashes and improve wiki name related features (#24143)
Close #7570 1. Clearly define the wiki path behaviors, see `services/wiki/wiki_path.go` and tests 2. Keep compatibility with old contents 3. Allow to use dashes in titles, eg: "2000-01-02 Meeting record" 4. Add a "Pages" link in the dropdown, otherwise users can't go to the Pages page easily. 5. Add a "View original git file" link in the Pages list, even if some file names are broken, users still have a chance to edit or remove it, without cloning the wiki repo to local. 6. Fix 500 error when the name contains prefix spaces. This PR also introduces the ability to support sub-directories, but it can't be done at the moment due to there are a lot of legacy wiki data, which use "%2F" in file names.   Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
parent
738f2af527
commit
b39a5bbbd6
13 changed files with 400 additions and 256 deletions
|
@ -68,9 +68,10 @@ func MustEnableWiki(ctx *context.Context) {
|
|||
|
||||
// PageMeta wiki page meta information
|
||||
type PageMeta struct {
|
||||
Name string
|
||||
SubURL string
|
||||
UpdatedUnix timeutil.TimeStamp
|
||||
Name string
|
||||
SubURL string
|
||||
GitEntryName string
|
||||
UpdatedUnix timeutil.TimeStamp
|
||||
}
|
||||
|
||||
// findEntryForFile finds the tree entry for a target filepath.
|
||||
|
@ -83,7 +84,7 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error)
|
|||
return entry, nil
|
||||
}
|
||||
|
||||
// Then the unescaped, shortest alternative
|
||||
// Then the unescaped, the shortest alternative
|
||||
var unescapedTarget string
|
||||
if unescapedTarget, err = url.QueryUnescape(target); err != nil {
|
||||
return nil, err
|
||||
|
@ -124,16 +125,16 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
|
|||
|
||||
// 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 string) ([]byte, *git.TreeEntry, string, bool) {
|
||||
pageFilename := wiki_service.NameToFilename(wikiName)
|
||||
entry, err := findEntryForFile(commit, pageFilename)
|
||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) {
|
||||
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, nil, "", true
|
||||
}
|
||||
return wikiContentsByEntry(ctx, entry), entry, pageFilename, false
|
||||
return wikiContentsByEntry(ctx, entry), entry, gitFilename, false
|
||||
}
|
||||
|
||||
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
|
@ -162,7 +163,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
|||
if !entry.IsRegular() {
|
||||
continue
|
||||
}
|
||||
wikiName, err := wiki_service.FilenameToName(entry.Name())
|
||||
wikiName, err := wiki_service.GitPathToWebPath(entry.Name())
|
||||
if err != nil {
|
||||
if repo_model.IsErrWikiInvalidFileName(err) {
|
||||
continue
|
||||
|
@ -175,22 +176,26 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
|||
} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
|
||||
continue
|
||||
}
|
||||
_, displayName := wiki_service.WebPathToUserTitle(wikiName)
|
||||
pages = append(pages, PageMeta{
|
||||
Name: wikiName,
|
||||
SubURL: wiki_service.NameToSubURL(wikiName),
|
||||
Name: displayName,
|
||||
SubURL: wiki_service.WebPathToURLPath(wikiName),
|
||||
GitEntryName: entry.Name(),
|
||||
})
|
||||
}
|
||||
ctx.Data["Pages"] = pages
|
||||
|
||||
// get requested pagename
|
||||
pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
|
||||
// get requested page name
|
||||
pageName := wiki_service.WebPathFromRequest(ctx.Params("*"))
|
||||
if len(pageName) == 0 {
|
||||
pageName = "Home"
|
||||
}
|
||||
ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName)
|
||||
ctx.Data["old_title"] = pageName
|
||||
ctx.Data["Title"] = pageName
|
||||
ctx.Data["title"] = pageName
|
||||
|
||||
_, displayName := wiki_service.WebPathToUserTitle(pageName)
|
||||
ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
|
||||
ctx.Data["old_title"] = displayName
|
||||
ctx.Data["Title"] = displayName
|
||||
ctx.Data["title"] = displayName
|
||||
|
||||
isSideBar := pageName == "_Sidebar"
|
||||
isFooter := pageName == "_Footer"
|
||||
|
@ -328,14 +333,17 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
|||
}
|
||||
|
||||
// get requested pagename
|
||||
pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
|
||||
pageName := wiki_service.WebPathFromRequest(ctx.Params("*"))
|
||||
if len(pageName) == 0 {
|
||||
pageName = "Home"
|
||||
}
|
||||
ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName)
|
||||
ctx.Data["old_title"] = pageName
|
||||
ctx.Data["Title"] = pageName
|
||||
ctx.Data["title"] = pageName
|
||||
|
||||
_, displayName := wiki_service.WebPathToUserTitle(pageName)
|
||||
ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
|
||||
ctx.Data["old_title"] = displayName
|
||||
ctx.Data["Title"] = displayName
|
||||
ctx.Data["title"] = displayName
|
||||
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||
|
||||
|
@ -403,14 +411,16 @@ func renderEditPage(ctx *context.Context) {
|
|||
}()
|
||||
|
||||
// get requested pagename
|
||||
pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
|
||||
pageName := wiki_service.WebPathFromRequest(ctx.Params("*"))
|
||||
if len(pageName) == 0 {
|
||||
pageName = "Home"
|
||||
}
|
||||
ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName)
|
||||
ctx.Data["old_title"] = pageName
|
||||
ctx.Data["Title"] = pageName
|
||||
ctx.Data["title"] = pageName
|
||||
|
||||
_, displayName := wiki_service.WebPathToUserTitle(pageName)
|
||||
ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
|
||||
ctx.Data["old_title"] = displayName
|
||||
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)
|
||||
|
@ -594,7 +604,7 @@ func WikiPages(ctx *context.Context) {
|
|||
ctx.ServerError("GetCommit", err)
|
||||
return
|
||||
}
|
||||
wikiName, err := wiki_service.FilenameToName(entry.Name())
|
||||
wikiName, err := wiki_service.GitPathToWebPath(entry.Name())
|
||||
if err != nil {
|
||||
if repo_model.IsErrWikiInvalidFileName(err) {
|
||||
continue
|
||||
|
@ -602,10 +612,12 @@ func WikiPages(ctx *context.Context) {
|
|||
ctx.ServerError("WikiFilenameToName", err)
|
||||
return
|
||||
}
|
||||
_, displayName := wiki_service.WebPathToUserTitle(wikiName)
|
||||
pages = append(pages, PageMeta{
|
||||
Name: wikiName,
|
||||
SubURL: wiki_service.NameToSubURL(wikiName),
|
||||
UpdatedUnix: timeutil.TimeStamp(c.Author.When.Unix()),
|
||||
Name: displayName,
|
||||
SubURL: wiki_service.WebPathToURLPath(wikiName),
|
||||
GitEntryName: entry.Name(),
|
||||
UpdatedUnix: timeutil.TimeStamp(c.Author.When.Unix()),
|
||||
})
|
||||
}
|
||||
ctx.Data["Pages"] = pages
|
||||
|
@ -631,12 +643,12 @@ func WikiRaw(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
providedPath := ctx.Params("*")
|
||||
|
||||
providedWebPath := wiki_service.WebPathFromRequest(ctx.Params("*"))
|
||||
providedGitPath := wiki_service.WebPathToGitPath(providedWebPath)
|
||||
var entry *git.TreeEntry
|
||||
if commit != nil {
|
||||
// Try to find a file with that name
|
||||
entry, err = findEntryForFile(commit, providedPath)
|
||||
entry, err = findEntryForFile(commit, providedGitPath)
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
ctx.ServerError("findFile", err)
|
||||
return
|
||||
|
@ -644,10 +656,8 @@ func WikiRaw(ctx *context.Context) {
|
|||
|
||||
if entry == nil {
|
||||
// Try to find a wiki page with that name
|
||||
providedPath = strings.TrimSuffix(providedPath, ".md")
|
||||
|
||||
wikiPath := wiki_service.NameToFilename(providedPath)
|
||||
entry, err = findEntryForFile(commit, wikiPath)
|
||||
providedGitPath = strings.TrimSuffix(providedGitPath, ".md")
|
||||
entry, err = findEntryForFile(commit, providedGitPath)
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
ctx.ServerError("findFile", err)
|
||||
return
|
||||
|
@ -694,7 +704,7 @@ func NewWikiPost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
wikiName := wiki_service.NormalizeWikiName(form.Title)
|
||||
wikiName := wiki_service.UserTitleToWebPath("", form.Title)
|
||||
|
||||
if len(form.Message) == 0 {
|
||||
form.Message = ctx.Tr("repo.editor.add", form.Title)
|
||||
|
@ -713,9 +723,9 @@ func NewWikiPost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
notification.NotifyNewWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.Message)
|
||||
notification.NotifyNewWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(wikiName), form.Message)
|
||||
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(wikiName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.WebPathToURLPath(wikiName))
|
||||
}
|
||||
|
||||
// EditWiki render wiki modify page
|
||||
|
@ -745,8 +755,8 @@ func EditWikiPost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
oldWikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
|
||||
newWikiName := wiki_service.NormalizeWikiName(form.Title)
|
||||
oldWikiName := wiki_service.WebPathFromRequest(ctx.Params("*"))
|
||||
newWikiName := wiki_service.UserTitleToWebPath("", form.Title)
|
||||
|
||||
if len(form.Message) == 0 {
|
||||
form.Message = ctx.Tr("repo.editor.update", form.Title)
|
||||
|
@ -757,14 +767,14 @@ func EditWikiPost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
notification.NotifyEditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, newWikiName, form.Message)
|
||||
notification.NotifyEditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(newWikiName), form.Message)
|
||||
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(newWikiName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.WebPathToURLPath(newWikiName))
|
||||
}
|
||||
|
||||
// DeleteWikiPagePost delete wiki page
|
||||
func DeleteWikiPagePost(ctx *context.Context) {
|
||||
wikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
|
||||
wikiName := wiki_service.WebPathFromRequest(ctx.Params("*"))
|
||||
if len(wikiName) == 0 {
|
||||
wikiName = "Home"
|
||||
}
|
||||
|
@ -774,7 +784,7 @@ func DeleteWikiPagePost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
notification.NotifyDeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName)
|
||||
notification.NotifyDeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(wikiName))
|
||||
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"redirect": ctx.Repo.RepoLink + "/wiki/",
|
||||
|
|
|
@ -6,6 +6,7 @@ package repo
|
|||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
|
@ -24,7 +25,7 @@ const (
|
|||
message = "Wiki commit message for unit tests"
|
||||
)
|
||||
|
||||
func wikiEntry(t *testing.T, repo *repo_model.Repository, wikiName string) *git.TreeEntry {
|
||||
func wikiEntry(t *testing.T, repo *repo_model.Repository, wikiName wiki_service.WebPath) *git.TreeEntry {
|
||||
wikiRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath())
|
||||
assert.NoError(t, err)
|
||||
defer wikiRepo.Close()
|
||||
|
@ -33,14 +34,14 @@ func wikiEntry(t *testing.T, repo *repo_model.Repository, wikiName string) *git.
|
|||
entries, err := commit.ListEntries()
|
||||
assert.NoError(t, err)
|
||||
for _, entry := range entries {
|
||||
if entry.Name() == wiki_service.NameToFilename(wikiName) {
|
||||
if entry.Name() == wiki_service.WebPathToGitPath(wikiName) {
|
||||
return entry
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func wikiContent(t *testing.T, repo *repo_model.Repository, wikiName string) string {
|
||||
func wikiContent(t *testing.T, repo *repo_model.Repository, wikiName wiki_service.WebPath) string {
|
||||
entry := wikiEntry(t, repo, wikiName)
|
||||
if !assert.NotNil(t, entry) {
|
||||
return ""
|
||||
|
@ -53,11 +54,11 @@ func wikiContent(t *testing.T, repo *repo_model.Repository, wikiName string) str
|
|||
return string(bytes)
|
||||
}
|
||||
|
||||
func assertWikiExists(t *testing.T, repo *repo_model.Repository, wikiName string) {
|
||||
func assertWikiExists(t *testing.T, repo *repo_model.Repository, wikiName wiki_service.WebPath) {
|
||||
assert.NotNil(t, wikiEntry(t, repo, wikiName))
|
||||
}
|
||||
|
||||
func assertWikiNotExists(t *testing.T, repo *repo_model.Repository, wikiName string) {
|
||||
func assertWikiNotExists(t *testing.T, repo *repo_model.Repository, wikiName wiki_service.WebPath) {
|
||||
assert.Nil(t, wikiEntry(t, repo, wikiName))
|
||||
}
|
||||
|
||||
|
@ -124,8 +125,8 @@ func TestNewWikiPost(t *testing.T) {
|
|||
})
|
||||
NewWikiPost(ctx)
|
||||
assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
|
||||
assertWikiExists(t, ctx.Repo.Repository, title)
|
||||
assert.Equal(t, wikiContent(t, ctx.Repo.Repository, title), content)
|
||||
assertWikiExists(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title))
|
||||
assert.Equal(t, wikiContent(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title)), content)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,8 +177,8 @@ func TestEditWikiPost(t *testing.T) {
|
|||
})
|
||||
EditWikiPost(ctx)
|
||||
assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
|
||||
assertWikiExists(t, ctx.Repo.Repository, title)
|
||||
assert.Equal(t, wikiContent(t, ctx.Repo.Repository, title), content)
|
||||
assertWikiExists(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title))
|
||||
assert.Equal(t, wikiContent(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title)), content)
|
||||
if title != "Home" {
|
||||
assertWikiNotExists(t, ctx.Repo.Repository, "Home")
|
||||
}
|
||||
|
@ -201,17 +202,21 @@ func TestWikiRaw(t *testing.T) {
|
|||
"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": "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)
|
||||
|
||||
ctx := test.MockContext(t, "user2/repo1/wiki/raw/"+filepath)
|
||||
ctx := test.MockContext(t, "user2/repo1/wiki/raw/"+url.PathEscape(filepath))
|
||||
ctx.SetParams("*", filepath)
|
||||
test.LoadUser(t, ctx, 2)
|
||||
test.LoadRepo(t, ctx, 1)
|
||||
WikiRaw(ctx)
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
assert.EqualValues(t, filetype, ctx.Resp.Header().Get("Content-Type"))
|
||||
if filetype == "" {
|
||||
assert.EqualValues(t, http.StatusNotFound, ctx.Resp.Status(), "filepath: %s", filepath)
|
||||
} else {
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status(), "filepath: %s", filepath)
|
||||
assert.EqualValues(t, filetype, ctx.Resp.Header().Get("Content-Type"), "filepath: %s", filepath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue