Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

full directory support for wiki #7390

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 117 additions & 32 deletions models/wiki.go
Original file line number Diff line number Diff line change
@@ -9,17 +9,20 @@ 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"
)

var (
reservedWikiNames = []string{"_pages", "_new", "_edit", "raw"}
reservedWikiNames = []string{"_pages", "_new", "_edit", "_delete", "raw"}
wikiWorkingPool = sync.NewExclusivePool()
)

@@ -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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed


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,40 +214,50 @@ 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
}
}
}

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,22 +351,32 @@ 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)
if err != nil {
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
100 changes: 81 additions & 19 deletions models/wiki_test.go
Original file line number Diff line number Diff line change
@@ -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")
3 changes: 0 additions & 3 deletions modules/markup/markdown/markdown.go
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
@@ -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
41 changes: 25 additions & 16 deletions public/js/index.js
Original file line number Diff line number Diff line change
@@ -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 = '<div class="markdown ui segment">' + data + '</div>';
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 = '<div class="markdown ui segment">' + data + '</div>';
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: {
89 changes: 65 additions & 24 deletions routers/repo/wiki.go
Original file line number Diff line number Diff line change
@@ -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,35 +140,37 @@ 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))
for _, entry := range entries {
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"
}
11 changes: 7 additions & 4 deletions routers/repo/wiki_test.go
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 2 additions & 1 deletion routers/routes/routes.go
Original file line number Diff line number Diff line change
@@ -809,9 +809,10 @@ 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)
m.Post("/:page/_delete", repo.DeleteWikiPagePost)
}, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter)
}, repo.MustEnableWiki, context.RepoRef())

9 changes: 7 additions & 2 deletions templates/repo/wiki/new.tmpl
Original file line number Diff line number Diff line change
@@ -7,22 +7,27 @@
{{.i18n.Tr "repo.wiki.new_page"}}
{{if .PageIsWikiEdit}}
<div class="ui right">
<a class="ui green small button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>
<a class="ui green small button" href="{{.RepoLink}}/wiki/{{.PageURL}}/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>
</div>
{{end}}
</div>
{{if .PageIsWikiEdit}}
<form class="ui form" action="{{.Link}}" method="post">
{{else}}
<form class="ui form" action="{{.RepoLink}}/wiki/_new" method="post">
{{end}}
{{.CsrfTokenHtml}}
<div class="field {{if .Err_Title}}error{{end}}">
<input name="title" value="{{.title}}" autofocus required>
</div>
<div class="field">
<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/wiki" required>{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea>
<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/wiki/raw/{{.PageURL}}/../" required>{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea>
</div>
<div class="field">
<input name="message" placeholder="{{.i18n.Tr "repo.wiki.default_commit_message"}}">
</div>
<div class="text right">
<a class="ui small red button" href="{{.RepoLink}}/wiki/{{.PageURL}}">{{.i18n.Tr "repo.wiki.abort_edit_page_button"}}</a>
<button class="ui green button">
{{.i18n.Tr "repo.wiki.save_page"}}
</button>
4 changes: 2 additions & 2 deletions templates/repo/wiki/view.tmpl
Original file line number Diff line number Diff line change
@@ -67,8 +67,8 @@
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
<div class="ui right">
<a class="ui small button" href="{{.RepoLink}}/wiki/{{.PageURL}}/_edit">{{.i18n.Tr "repo.wiki.edit_page_button"}}</a>
<a class="ui green small button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>
<a class="ui red small button delete-button" href="" data-url="{{.RepoLink}}/wiki/{{.PageURL}}/delete" data-id="{{.PageURL}}">{{.i18n.Tr "repo.wiki.delete_page_button"}}</a>
<a class="ui green small button" href="{{.RepoLink}}/wiki/{{.PageURL}}/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>
<a class="ui red small button delete-button" href="" data-url="{{.RepoLink}}/wiki/{{.PageURL}}/_delete" data-id="{{.PageURL}}">{{.i18n.Tr "repo.wiki.delete_page_button"}}</a>
</div>
{{end}}
</div>