From eff8e1958187be7d85a8194689218e3c3e73e187 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 9 Jul 2019 01:21:11 +0200 Subject: [PATCH 1/3] wiki make delete wiki keywords start with _ + add _delete to reserved keywords Signed-off-by: Michael Gnehr --- models/wiki.go | 2 +- routers/routes/routes.go | 2 +- templates/repo/wiki/view.tmpl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index 9ae3386333298..fcefb43d64d13 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -19,7 +19,7 @@ import ( ) var ( - reservedWikiNames = []string{"_pages", "_new", "_edit", "raw"} + reservedWikiNames = []string{"_pages", "_new", "_edit", "_delete", "raw"} wikiWorkingPool = sync.NewExclusivePool() ) diff --git a/routers/routes/routes.go b/routers/routes/routes.go index ec57e8f5fdac7..2315fbc055ae1 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -811,7 +811,7 @@ func RegisterRoutes(m *macaron.Macaron) { Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost) m.Combo("/:page/_edit").Get(repo.EditWiki). Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) - m.Post("/:page/delete", repo.DeleteWikiPagePost) + m.Post("/:page/_delete", repo.DeleteWikiPagePost) }, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter) }, repo.MustEnableWiki, context.RepoRef()) diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index f775ac9429200..4a16e5526cd90 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -68,7 +68,7 @@ {{end}} From 9b34073b667a70f89a6ed91add1f810c534c8ce3 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 9 Jul 2019 01:22:46 +0200 Subject: [PATCH 2/3] multi directory support + tests see #7225 for more details Signed-off-by: Michael Gnehr --- models/wiki.go | 147 ++++++++++++++++++++++------ models/wiki_test.go | 100 +++++++++++++++---- modules/markup/markdown/markdown.go | 3 - options/locale/locale_en-US.ini | 1 + public/css/index.css | 2 +- public/js/index.js | 41 +++++--- routers/repo/wiki.go | 89 ++++++++++++----- routers/repo/wiki_test.go | 11 ++- routers/routes/routes.go | 1 + templates/repo/wiki/new.tmpl | 9 +- templates/repo/wiki/view.tmpl | 2 +- 11 files changed, 305 insertions(+), 101 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index fcefb43d64d13..7456faf374a55 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -9,11 +9,14 @@ import ( "net/url" "os" "path/filepath" + "reflect" + "regexp" "strings" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/sync" + "code.gitea.io/gitea/modules/util" "github.com/Unknwon/com" ) @@ -28,9 +31,17 @@ func NormalizeWikiName(name string) string { return strings.Replace(name, "-", " ", -1) } -// WikiNameToSubURL converts a wiki name to its corresponding sub-URL. +// WikiNameToSubURL converts a wiki name to its corresponding sub-URL. This will escape dangerous letters. func WikiNameToSubURL(name string) string { - return url.QueryEscape(strings.Replace(name, " ", "-", -1)) + // remove path up + re1 := regexp.MustCompile(`(\.\.\/)`) + name = re1.ReplaceAllString(name, "") + // trim whitespace and / + name = strings.Trim(name, "\n\r\t /") + name = url.QueryEscape(name) + //restore spaces + re3 := regexp.MustCompile(`(?m)(%20|\+)`) + return re3.ReplaceAllString(name, "%20") } // WikiNameToFilename converts a wiki name to its corresponding filename. @@ -39,17 +50,53 @@ func WikiNameToFilename(name string) string { return url.QueryEscape(name) + ".md" } +// WikiNameToPathFilename converts a wiki name to its corresponding filename, keep directory paths. +func WikiNameToPathFilename(name string) string { + var restore = [1][2]string{ + {`(\.\.\/)`, ""}, // remove path up + } + for _, kv := range restore { + loopRe := regexp.MustCompile(kv[0]) + name = loopRe.ReplaceAllString(name, kv[1]) + } + name = strings.Trim(name, "\n\r\t ./") // trim whitespace and / . + return name + ".md" +} + +// FilenameToPathFilename converts a wiki filename to filename with filepath. +func FilenameToPathFilename(name string) string { + // restore spaces and slashes + var restore = [4][2]string{ + {`(?m)%2F`, "/"}, //recover slashes / + {`(?m)(%20|\+)`, " "}, //restore spaces + {`(?m)(%25)`, "%"}, //restore % + {`(?m)(%26)`, "&"}, //restore & + } + for _, kv := range restore { + loopRe := regexp.MustCompile(kv[0]) + name = loopRe.ReplaceAllString(name, kv[1]) + } + return name +} + +// WikiNameToRawPrefix Get raw file path inside wiki, removes last path element and returns +func WikiNameToRawPrefix(repositoryName string, wikiPage string) string { + a := strings.Split(wikiPage, "/") + a = a[:len(a)-1] + return util.URLJoin(repositoryName, "wiki", "raw", strings.Join(a, "/")) +} + // WikiFilenameToName converts a wiki filename to its corresponding page name. -func WikiFilenameToName(filename string) (string, error) { +func WikiFilenameToName(filename string) (string, string, error) { if !strings.HasSuffix(filename, ".md") { - return "", ErrWikiInvalidFileName{filename} + return "", "", ErrWikiInvalidFileName{filename} } basename := filename[:len(filename)-3] unescaped, err := url.QueryUnescape(basename) if err != nil { - return "", err + return basename, basename, err } - return NormalizeWikiName(unescaped), nil + return unescaped, basename, nil } // WikiCloneLink returns clone URLs of repository wiki. @@ -97,6 +144,21 @@ func nameAllowed(name string) error { return nil } +// checkNewWikiFilename check filename or file exists inside repository +func checkNewWikiFilename(repo *git.Repository, name string) (bool, error) { + filesInIndex, err := repo.LsFiles(name) + if err != nil { + log.Error("%v", err) + return false, err + } + for _, file := range filesInIndex { + if file == name { + return true, ErrWikiAlreadyExist{name} + } + } + return false, nil +} + // updateWikiPage adds a new page to the repository wiki. func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, content, message string, isNew bool) (err error) { if err = nameAllowed(newWikiName); err != nil { @@ -136,6 +198,9 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con } gitRepo, err := git.OpenRepository(basePath) + + fmt.Println(reflect.TypeOf(gitRepo)) + if err != nil { log.Error("Unable to open temporary repository: %s (%v)", basePath, err) return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) @@ -149,33 +214,41 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con } newWikiPath := WikiNameToFilename(newWikiName) + newWikiDirPath := WikiNameToPathFilename(newWikiName) + if isNew { - filesInIndex, err := gitRepo.LsFiles(newWikiPath) - if err != nil { - log.Error("%v", err) + // check file already exists - plain structure + if _, err := checkNewWikiFilename(gitRepo, newWikiPath); err != nil { return err } - for _, file := range filesInIndex { - if file == newWikiPath { - return ErrWikiAlreadyExist{newWikiPath} - } + + // check file already exists - directory structure + if _, err := checkNewWikiFilename(gitRepo, newWikiDirPath); err != nil { + return err } } else { + var found bool + + // check file already exists - plain structure oldWikiPath := WikiNameToFilename(oldWikiName) - filesInIndex, err := gitRepo.LsFiles(oldWikiPath) - if err != nil { - log.Error("%v", err) + if found, err = checkNewWikiFilename(gitRepo, oldWikiPath); err != nil && !found { return err } - found := false - for _, file := range filesInIndex { - if file == oldWikiPath { - found = true - break + if found { + err := gitRepo.RemoveFilesFromIndex(oldWikiPath) + if err != nil { + log.Error("%v", err) + return err } } + + // check file already exists - directory structure + oldWikiDirPath := WikiNameToPathFilename(oldWikiName) + if found, err = checkNewWikiFilename(gitRepo, oldWikiDirPath); err != nil && !found { + return err + } if found { - err := gitRepo.RemoveFilesFromIndex(oldWikiPath) + err := gitRepo.RemoveFilesFromIndex(oldWikiDirPath) if err != nil { log.Error("%v", err) return err @@ -183,6 +256,8 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con } } + newWikiDirPath = FilenameToPathFilename(newWikiDirPath) + // FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here objectHash, err := gitRepo.HashObject(strings.NewReader(content)) @@ -191,7 +266,7 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con return err } - if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil { + if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiDirPath); err != nil { log.Error("%v", err) return err } @@ -276,14 +351,12 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error) return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err) } + var found bool + + // check file exists - plain structure wikiPath := WikiNameToFilename(wikiName) - filesInIndex, err := gitRepo.LsFiles(wikiPath) - found := false - for _, file := range filesInIndex { - if file == wikiPath { - found = true - break - } + if found, err = checkNewWikiFilename(gitRepo, wikiName); err != nil && !found { + return err } if found { err := gitRepo.RemoveFilesFromIndex(wikiPath) @@ -291,7 +364,19 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error) return err } } else { - return os.ErrNotExist + // check file exists - plain structure + wikiDirPath := WikiNameToPathFilename(wikiName) + if found, err = checkNewWikiFilename(gitRepo, wikiDirPath); err != nil && !found { + return err + } + if found { + err := gitRepo.RemoveFilesFromIndex(wikiDirPath) + if err != nil { + return err + } + } else { + return os.ErrNotExist + } } // FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here diff --git a/models/wiki_test.go b/models/wiki_test.go index 991a3d95b9036..a9e4610de1548 100644 --- a/models/wiki_test.go +++ b/models/wiki_test.go @@ -6,6 +6,7 @@ package models import ( "path/filepath" + "regexp" "testing" "code.gitea.io/gitea/modules/git" @@ -30,6 +31,22 @@ func TestNormalizeWikiName(t *testing.T) { } } +func TestWikiNameToSubURL(t *testing.T) { + type test struct { + Expected string + WikiName string + } + for _, test := range []test{ + {"wiki%2Fpath", "wiki/../path/../../"}, + {"wiki%2Fpath", " wiki/path ////// "}, + {"wiki-name", "wiki-name"}, + {"name%20with%2Fslash", "name with/slash"}, + {"name%20with%25percent", "name with%percent"}, + } { + assert.Equal(t, test.Expected, WikiNameToSubURL(test.WikiName)) + } +} + func TestWikiNameToFilename(t *testing.T) { type test struct { Expected string @@ -40,48 +57,88 @@ func TestWikiNameToFilename(t *testing.T) { {"wiki-name.md", "wiki-name"}, {"name-with%2Fslash.md", "name with/slash"}, {"name-with%25percent.md", "name with%percent"}, + {"wiki-name-with%2Fslash.md", "wiki name with/slash"}, + {"%24%24%24%25%25%25%5E%5E%26%26%21%40%23%24%28%29%2C.%3C%3E.md", "$$$%%%^^&&!@#$(),.<>"}, } { assert.Equal(t, test.Expected, WikiNameToFilename(test.WikiName)) } } -func TestWikiNameToSubURL(t *testing.T) { +func TestWikiNameToPathFilename(t *testing.T) { type test struct { Expected string WikiName string } for _, test := range []test{ - {"wiki-name", "wiki name"}, - {"wiki-name", "wiki-name"}, - {"name-with%2Fslash", "name with/slash"}, - {"name-with%25percent", "name with%percent"}, + {"wiki name.md", "wiki name"}, + {"wiki-name.md", "wiki-name"}, + {"name with/slash.md", "name with/slash"}, + {"name with/slash.md", "name with/../slash"}, + {"name with%percent.md", "name with%percent"}, + {"git/config.md", ".git/config "}, } { - assert.Equal(t, test.Expected, WikiNameToSubURL(test.WikiName)) + assert.Equal(t, test.Expected, WikiNameToPathFilename(test.WikiName)) } } -func TestWikiFilenameToName(t *testing.T) { +func TestFilenameToPathFilename(t *testing.T) { type test struct { Expected string Filename string } for _, test := range []test{ - {"hello world", "hello-world.md"}, - {"symbols/?*", "symbols%2F%3F%2A.md"}, + {"wiki/name.md", "wiki%2Fname.md"}, + {"wiki name path", "wiki%20name+path"}, + {"name with/slash", "name with/slash"}, + {"name with&and", "name with%2526and"}, + {"name with%percent", "name with%percent"}, + {"&&&&", "%26%26%26%26"}, } { - name, err := WikiFilenameToName(test.Filename) + assert.Equal(t, test.Expected, FilenameToPathFilename(test.Filename)) + } +} + +func TestWikiNameToRawPrefix(t *testing.T) { + type test struct { + RepoName string + WikiPage string + Expected string + } + for _, test := range []test{ + {"/repo1/name", "wiki/path", "/repo1/name/wiki/raw/wiki"}, + {"/repo2/name", "wiki/path/subdir", "/repo2/name/wiki/raw/wiki/path"}, + } { + assert.Equal(t, test.Expected, WikiNameToRawPrefix(test.RepoName, test.WikiPage)) + } +} + +func TestWikiFilenameToName(t *testing.T) { + type test struct { + Expected1 string + Expected2 string + Filename string + } + for _, test := range []test{ + {"hello world", "hello world", "hello world.md"}, + {"hello-world", "hello-world", "hello-world.md"}, + {"symbols/?*", "symbols%2F%3F%2A", "symbols%2F%3F%2A.md"}, + {"wiki-name-with/slash", "wiki-name-with%2Fslash", "wiki-name-with%2Fslash.md"}, + {"$$$%%%^^&&!@#$(),.<>", "%24%24%24%25%25%25%5E%5E%26%26%21%40%23%24%28%29%2C.%3C%3E", "%24%24%24%25%25%25%5E%5E%26%26%21%40%23%24%28%29%2C.%3C%3E.md"}, + } { + unescaped, basename, err := WikiFilenameToName(test.Filename) assert.NoError(t, err) - assert.Equal(t, test.Expected, name) + assert.Equal(t, test.Expected1, unescaped) + assert.Equal(t, test.Expected2, basename) } for _, badFilename := range []string{ "nofileextension", "wrongfileextension.txt", } { - _, err := WikiFilenameToName(badFilename) + _, _, err := WikiFilenameToName(badFilename) assert.Error(t, err) assert.True(t, IsErrWikiInvalidFileName(err)) } - _, err := WikiFilenameToName("badescaping%%.md") + _, _, err := WikiFilenameToName("badescaping%%.md") assert.Error(t, err) assert.False(t, IsErrWikiInvalidFileName(err)) } @@ -96,9 +153,9 @@ func TestWikiNameToFilenameToName(t *testing.T) { "$$$%%%^^&&!@#$(),.<>", } { filename := WikiNameToFilename(name) - resultName, err := WikiFilenameToName(filename) + resultName, _, err := WikiFilenameToName(filename) assert.NoError(t, err) - assert.Equal(t, NormalizeWikiName(name), resultName) + assert.Equal(t, NormalizeWikiName(name), NormalizeWikiName(resultName)) } } @@ -163,10 +220,12 @@ func TestRepository_AddWikiPage(t *testing.T) { assert.NoError(t, err) masterTree, err := gitRepo.GetTree("master") assert.NoError(t, err) - wikiPath := WikiNameToFilename(wikiName) + wikiPath := WikiNameToPathFilename(wikiName) entry, err := masterTree.GetTreeEntryByPath(wikiPath) + re := regexp.MustCompile(`(?m)(.*)(\/)([^\/]*)$`) + assert.NoError(t, err) - assert.Equal(t, wikiPath, entry.Name(), "%s not addded correctly", wikiName) + assert.Equal(t, re.ReplaceAllString(wikiPath, "$3"), entry.Name(), "%s not addded correctly", wikiName) }) } @@ -205,10 +264,13 @@ func TestRepository_EditWikiPage(t *testing.T) { assert.NoError(t, err) masterTree, err := gitRepo.GetTree("master") assert.NoError(t, err) - wikiPath := WikiNameToFilename(newWikiName) + re := regexp.MustCompile(`(?m)(.*)(\/)([^\/]*)$`) + + wikiPath := WikiNameToPathFilename(newWikiName) + entry, err := masterTree.GetTreeEntryByPath(wikiPath) assert.NoError(t, err) - assert.Equal(t, wikiPath, entry.Name(), "%s not editted correctly", newWikiName) + assert.Equal(t, re.ReplaceAllString(wikiPath, "$3"), entry.Name(), "%s not editted correctly", newWikiName) if newWikiName != "Home" { _, err := masterTree.GetTreeEntryByPath("Home.md") diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index aab951c60f8c2..9cd04f35a21a7 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -103,9 +103,6 @@ func (r *Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) { // Image defines how images should be processed to produce corresponding HTML elements. func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { prefix := r.URLPrefix - if r.IsWiki { - prefix = util.URLJoin(prefix, "wiki", "raw") - } prefix = strings.Replace(prefix, "/src/", "/media/", 1) if len(link) > 0 && !markup.IsLink(link) { lnk := string(link) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6ef1277c62543..92e5b6aaf9009 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1033,6 +1033,7 @@ wiki.default_commit_message = Write a note about this page update (optional). wiki.save_page = Save Page wiki.last_commit_info = %s edited this page %s wiki.edit_page_button = Edit +wiki.abort_edit_page_button = Abort wiki.new_page_button = New Page wiki.file_revision = Page Revision wiki.wiki_page_revisions = Wiki Page Revisions diff --git a/public/css/index.css b/public/css/index.css index b948766b416b1..a260c8c706a17 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -292,7 +292,7 @@ footer .ui.left,footer .ui.right{line-height:40px} .markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em} .repository.wiki.revisions .ui.container>.ui.stackable.grid{flex-direction:row-reverse} .repository.wiki.revisions .ui.container>.ui.stackable.grid>.header{margin-top:0} -.repository.wiki.revisions .ui.container>.ui.stackable.grid>.header .sub.header{padding-left:52px} +.repository.wiki.revisions .ui.container>.ui.stackable.grid>.header .sub.header{padding-left:52px;word-break:break-word} .file-revisions-btn{display:block;float:left;margin-bottom:2px!important;padding:11px!important;margin-right:10px!important} .file-revisions-btn i{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} .home .logo{max-width:220px} diff --git a/public/js/index.js b/public/js/index.js index b4ce8c78b6126..95c3aa6d85e3c 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1126,28 +1126,37 @@ function initTeamSettings() { } function initWikiForm() { - var $editArea = $('.repository.wiki textarea#edit_area'); + let $editArea = $('.repository.wiki textarea#edit_area'); if ($editArea.length > 0) { - var simplemde = new SimpleMDE({ + let simplemde = new SimpleMDE({ autoDownloadFontAwesome: false, element: $editArea[0], forceSync: true, previewRender: function (plainText, preview) { // Async method setTimeout(function () { - // FIXME: still send render request when return back to edit mode - $.post($editArea.data('url'), { - "_csrf": csrf, - "mode": "gfm", - "context": $editArea.data('context'), - "text": plainText - }, - function (data) { - preview.innerHTML = '
' + data + '
'; - emojify.run($('.editor-preview')[0]); - } - ); - }, 0); - + let $toolbar = $(preview).closest('.CodeMirror-wrap').prev(); + if (/(editor-preview-active-side)/.test(preview.className) || $toolbar.length > 0 && $toolbar.hasClass('disabled-for-preview') ) { + const render = function () { + $.post($editArea.data('url'), { + "_csrf": csrf, + "mode": "gfm", + "wiki": true, + "context": decodeURIComponent($editArea.data('context')), + "text": plainText + }, + function (data) { + preview.innerHTML = '
' + data + '
'; + emojify.run($('.editor-preview')[0]); + // run highlighting on preview + $(preview).find('pre code').each(function (_, e) { + hljs.highlightBlock(e); + }); + } + ); + }; + render(); + } + }, 3); // in some cases the ui need to be updated before this return "Loading..."; }, renderingConfig: { diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index 0fdf8536307e7..69ef4aab20c1f 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -8,6 +8,7 @@ package repo import ( "fmt" "io/ioutil" + "net/url" "path/filepath" "strings" @@ -63,7 +64,7 @@ type PageMeta struct { // findEntryForFile finds the tree entry for a target filepath. func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) { - entries, err := commit.ListEntries() + entries, err := commit.ListEntriesRecursive() if err != nil { return nil, err } @@ -111,12 +112,20 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte { func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) { var entry *git.TreeEntry var err error - pageFilename := models.WikiNameToFilename(wikiName) + // check new file structure with directories + pageFilename := models.WikiNameToPathFilename(wikiName) if entry, err = findEntryForFile(commit, pageFilename); err != nil { ctx.ServerError("findEntryForFile", err) return nil, nil, "", false } else if entry == nil { - return nil, nil, "", true + // check old file structure without directories + pageFilename = models.WikiNameToFilename(wikiName) + if entry, err = findEntryForFile(commit, pageFilename); err != nil { + ctx.ServerError("findEntryForFile", err) + return nil, nil, "", false + } else if entry == nil { + return nil, nil, "", true + } } return wikiContentsByEntry(ctx, entry), entry, pageFilename, false } @@ -131,9 +140,9 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { } // Get page list. - entries, err := commit.ListEntries() + entries, err := commit.Tree.ListEntriesRecursive() if err != nil { - ctx.ServerError("ListEntries", err) + ctx.ServerError("ListEntriesRecursive", err) return nil, nil } pages := make([]PageMeta, 0, len(entries)) @@ -141,25 +150,27 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { if !entry.IsRegular() { continue } - wikiName, err := models.WikiFilenameToName(entry.Name()) + wikiUnescapedName, wikiName, err := models.WikiFilenameToName(entry.Name()) if err != nil { if models.IsErrWikiInvalidFileName(err) { continue } - ctx.ServerError("WikiFilenameToName", err) - return nil, nil - } else if wikiName == "_Sidebar" || wikiName == "_Footer" { + if _, ok := err.(url.EscapeError); !ok { + ctx.ServerError("WikiFilenameToName", err) + return nil, nil + } + } else if wikiUnescapedName == "_Sidebar" || wikiUnescapedName == "_Footer" { continue } pages = append(pages, PageMeta{ - Name: wikiName, + Name: wikiUnescapedName, SubURL: models.WikiNameToSubURL(wikiName), }) } ctx.Data["Pages"] = pages // get requested pagename - pageName := models.NormalizeWikiName(ctx.Params(":page")) + pageName := ctx.Params(":page") if len(pageName) == 0 { pageName = "Home" } @@ -189,7 +200,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { } metas := ctx.Repo.Repository.ComposeMetas() - ctx.Data["content"] = markdown.RenderWiki(data, ctx.Repo.RepoLink, metas) + ctx.Data["content"] = markdown.RenderWiki(data, models.WikiNameToRawPrefix(ctx.Repo.RepoLink, pageFilename), metas) ctx.Data["sidebarPresent"] = sidebarContent != nil ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas) ctx.Data["footerPresent"] = footerContent != nil @@ -212,7 +223,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) } // get requested pagename - pageName := models.NormalizeWikiName(ctx.Params(":page")) + pageName := ctx.Params(":page") if len(pageName) == 0 { pageName = "Home" } @@ -275,7 +286,7 @@ func renderEditPage(ctx *context.Context) { } // get requested pagename - pageName := models.NormalizeWikiName(ctx.Params(":page")) + pageName := ctx.Params(":page") if len(pageName) == 0 { pageName = "Home" } @@ -387,31 +398,42 @@ func WikiPages(ctx *context.Context) { return } - entries, err := commit.ListEntries() + entries, err := commit.Tree.ListEntriesRecursive() if err != nil { - ctx.ServerError("ListEntries", err) + ctx.ServerError("ListEntriesRecursive", err) return } pages := make([]PageMeta, 0, len(entries)) +LoopPages: for _, entry := range entries { + blacklistWikifiles := strings.Split(".gitignore,.git", ",") + for _, b := range blacklistWikifiles { + if entry.Name() == b || strings.HasSuffix(entry.Name(), b) { + continue LoopPages + } + } + if !entry.IsRegular() { continue } + c, err := wikiRepo.GetCommitByPath(entry.Name()) if err != nil { ctx.ServerError("GetCommit", err) return } - wikiName, err := models.WikiFilenameToName(entry.Name()) + wikiUnescapedName, wikiName, err := models.WikiFilenameToName(entry.Name()) if err != nil { if models.IsErrWikiInvalidFileName(err) { continue } - ctx.ServerError("WikiFilenameToName", err) - return + if _, ok := err.(url.EscapeError); !ok { + ctx.ServerError("WikiFilenameToName", err) + return + } } pages = append(pages, PageMeta{ - Name: wikiName, + Name: wikiUnescapedName, SubURL: models.WikiNameToSubURL(wikiName), UpdatedUnix: util.TimeStamp(c.Author.When.Unix()), }) @@ -474,6 +496,13 @@ func NewWiki(ctx *context.Context) { if !ctx.Repo.Repository.HasWiki() { ctx.Data["title"] = "Home" + } else if len(ctx.Params(":page")) != 0 { + // create files on same subdiretory like current file + wikiName := ctx.Params(":page") + // remove current filename + a := strings.Split(wikiName, "/") + a[len(a)-1] = "New Page" + ctx.Data["title"] = strings.Join(a, "/") } ctx.HTML(200, tplWikiNew) @@ -495,7 +524,7 @@ func NewWikiPost(ctx *context.Context, form auth.NewWikiForm) { return } - wikiName := models.NormalizeWikiName(form.Title) + wikiName := form.Title if err := ctx.Repo.Repository.AddWikiPage(ctx.User, wikiName, form.Content, form.Message); err != nil { if models.IsErrWikiReservedName(err) { ctx.Data["Err_Title"] = true @@ -509,6 +538,12 @@ func NewWikiPost(ctx *context.Context, form auth.NewWikiForm) { return } + // redirect to may changed filename + wikiName = models.FilenameToPathFilename(models.WikiNameToPathFilename(wikiName)) + if strings.HasSuffix(wikiName, ".md") { + wikiName = wikiName[:len(wikiName)-3] + } + ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + models.WikiNameToSubURL(wikiName)) } @@ -542,20 +577,26 @@ func EditWikiPost(ctx *context.Context, form auth.NewWikiForm) { return } - oldWikiName := models.NormalizeWikiName(ctx.Params(":page")) - newWikiName := models.NormalizeWikiName(form.Title) + oldWikiName := ctx.Params(":page") + newWikiName := form.Title if err := ctx.Repo.Repository.EditWikiPage(ctx.User, oldWikiName, newWikiName, form.Content, form.Message); err != nil { ctx.ServerError("EditWikiPage", err) return } + // redirect to may changed filename + newWikiName = models.FilenameToPathFilename(models.WikiNameToPathFilename(newWikiName)) + if strings.HasSuffix(newWikiName, ".md") { + newWikiName = newWikiName[:len(newWikiName)-3] + } + ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + models.WikiNameToSubURL(newWikiName)) } // DeleteWikiPagePost delete wiki page func DeleteWikiPagePost(ctx *context.Context) { - wikiName := models.NormalizeWikiName(ctx.Params(":page")) + wikiName := ctx.Params(":page") if len(wikiName) == 0 { wikiName = "Home" } diff --git a/routers/repo/wiki_test.go b/routers/repo/wiki_test.go index 4687d24f6b28d..ecda2c9fc8b5d 100644 --- a/routers/repo/wiki_test.go +++ b/routers/repo/wiki_test.go @@ -25,12 +25,15 @@ func wikiEntry(t *testing.T, repo *models.Repository, wikiName string) *git.Tree assert.NoError(t, err) commit, err := wikiRepo.GetBranchCommit("master") assert.NoError(t, err) - entries, err := commit.ListEntries() + entries, err := commit.ListEntriesRecursive() assert.NoError(t, err) for _, entry := range entries { if entry.Name() == models.WikiNameToFilename(wikiName) { return entry } + if entry.Name() == models.WikiNameToPathFilename(wikiName) { + return entry + } } return nil } @@ -78,7 +81,7 @@ func TestWiki(t *testing.T) { Wiki(ctx) 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"}, ctx.Data["Pages"]) + assertPagesMetas(t, []string{"Home", "Page-With-Image", "Page-With-Spaced-Name"}, ctx.Data["Pages"]) } func TestWikiPages(t *testing.T) { @@ -88,7 +91,7 @@ func TestWikiPages(t *testing.T) { test.LoadRepo(t, ctx, 1) WikiPages(ctx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) - assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name"}, ctx.Data["Pages"]) + assertPagesMetas(t, []string{"Home", "Page-With-Image", "Page-With-Spaced-Name"}, ctx.Data["Pages"]) } func TestNewWiki(t *testing.T) { @@ -179,7 +182,7 @@ func TestEditWikiPost(t *testing.T) { func TestDeleteWikiPagePost(t *testing.T) { models.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1/wiki/Home/delete") + ctx := test.MockContext(t, "user2/repo1/wiki/Home/_delete") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) DeleteWikiPagePost(ctx) diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 2315fbc055ae1..d6c91b6f19c98 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -809,6 +809,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("", func() { m.Combo("/_new").Get(repo.NewWiki). Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost) + m.Get("/:page/_new/", repo.NewWiki) m.Combo("/:page/_edit").Get(repo.EditWiki). Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) m.Post("/:page/_delete", repo.DeleteWikiPagePost) diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index bf6c24220bdde..2b8aa477208e7 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -7,22 +7,27 @@ {{.i18n.Tr "repo.wiki.new_page"}} {{if .PageIsWikiEdit}} {{end}} + {{if .PageIsWikiEdit}}
+ {{else}} + + {{end}} {{.CsrfTokenHtml}}
- +
+ {{.i18n.Tr "repo.wiki.abort_edit_page_button"}} diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index 4a16e5526cd90..9e09a85aa9c7d 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -67,7 +67,7 @@ {{if and .CanWriteWiki (not .Repository.IsMirror)}} {{end}} From d10e0fd9c3ea48f82da11d182526b5af9a6db014 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 9 Jul 2019 01:24:11 +0200 Subject: [PATCH 3/3] update css file Signed-off-by: Michael Gnehr --- public/css/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/css/index.css b/public/css/index.css index a260c8c706a17..b948766b416b1 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -292,7 +292,7 @@ footer .ui.left,footer .ui.right{line-height:40px} .markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em} .repository.wiki.revisions .ui.container>.ui.stackable.grid{flex-direction:row-reverse} .repository.wiki.revisions .ui.container>.ui.stackable.grid>.header{margin-top:0} -.repository.wiki.revisions .ui.container>.ui.stackable.grid>.header .sub.header{padding-left:52px;word-break:break-word} +.repository.wiki.revisions .ui.container>.ui.stackable.grid>.header .sub.header{padding-left:52px} .file-revisions-btn{display:block;float:left;margin-bottom:2px!important;padding:11px!important;margin-right:10px!important} .file-revisions-btn i{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} .home .logo{max-width:220px}