diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index a674984a2584e..79f41f9b1bb56 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -145,6 +145,17 @@ CUSTOM_URL_SCHEMES = ; List of file extensions that should be rendered/edited as Markdown ; Separate the extensions with a comma. To render files without any extension as markdown, just put a comma FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd +; Create 'Table of Pages' on internal wiki pages +DEFAULT_TOC_WIKI_TREE = true +; Create Table of Contents based on headlines on wiki page (does not alter file) +DEFAULT_TOC_WIKI_FILE = true +; Create 'Table of Contents' on all rendered markup files. This does not alter files. Enabled: overwrites DEFAULT_TOC_MARKUP_BY_FLAG +DEFAULT_TOC_MARKUP_ALWAYS = false +; Create 'Table of Contents' on rendered markup files if line '%%TOC%%' is present. +DEFAULT_TOC_MARKUP_BY_FLAG = true +; List of markup file extensions that TOC should be created on, set to not interfere with external markup renderer. +; Separate the extensions with a comma. To ignore file extension check, just put a comma. +TOC_MARKUP_FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd,.org [server] ; The protocol the server listens on. One of 'http', 'https', 'unix' or 'fcgi'. diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 11dbfc5d095ec..8ee9663c7cbaf 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -107,6 +107,13 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. ## Markdown (`markdown`) - `ENABLE_HARD_LINE_BREAK`: **false**: Enable Markdown's hard line break extension. +- `DEFAULT_TOC_WIKI_TREE`: **true**: Create 'Table of Pages' on internal wiki pages. +- `DEFAULT_TOC_WIKI_FILE`: **true**: Create Table of Contents based on headlines on wiki page (does + not alter file) +- `DEFAULT_TOC_MARKUP_ALWAYS`: **false** : Create 'Table of Contents' on all rendered markup files. This does not alter files. Enabled: overwrites `DEFAULT_TOC_MARKUP_BY_FLAG` +- `DEFAULT_TOC_MARKUP_BY_FLAG`: **true** : Create 'Table of Contents' on rendered markup files if line `%%TOC%%` is present. +- `TOC_MARKUP_FILE_EXTENSIONS`: **.md,.markdown,.mdown,.mkd,.org** : List of markup file extensions that TOC should be created on, set to not interfere with external markup renderer. + Separate the extensions with a comma. To ignore file extension check, just put a comma. ## Server (`server`) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index ef43b0453b092..1fefa176b9c02 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -232,6 +232,10 @@ var migrations = []Migration{ NewMigration("add avatar field to repository", addAvatarFieldToRepository), // v88 -> v89 NewMigration("add commit status context field to commit_status", addCommitStatusContext), + // v89 -> v90 + NewMigration("add toc on wiki and markedown", addCanTocOnWikiAndMarkdown), + // v90 -> v91 + NewMigration("add pagetoc to wiki", addCanWikiPageToc), } // Migrate database to current version diff --git a/models/migrations/v89.go b/models/migrations/v89.go new file mode 100644 index 0000000000000..52295efeb8979 --- /dev/null +++ b/models/migrations/v89.go @@ -0,0 +1,40 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "code.gitea.io/gitea/modules/setting" + + "github.com/go-xorm/xorm" +) + +func addCanTocOnWikiAndMarkdown(x *xorm.Engine) error { + + type Repository struct { + TocWikiFile bool `xorm:"NOT NULL DEFAULT true"` + TocMarkupAlways bool `xorm:"NOT NULL DEFAULT false"` + TocMarkupByFlag bool `xorm:"NOT NULL DEFAULT true"` + } + + if err := x.Sync2(new(Repository)); err != nil { + return err + } + + if _, err := x.Exec("UPDATE repository SET toc_wiki_file = ?", + setting.Markdown.DefaultTocWikiFile); err != nil { + return err + } + + if _, err := x.Exec("UPDATE repository SET toc_markup_always = ?", + setting.Markdown.DefaultTocMarkupAlways); err != nil { + return err + } + + if _, err := x.Exec("UPDATE repository SET toc_markup_by_flag = ?", + setting.Markdown.DefaultTocMarkupByFlag); err != nil { + return err + } + return nil +} diff --git a/models/migrations/v90.go b/models/migrations/v90.go new file mode 100644 index 0000000000000..7879c8527b17f --- /dev/null +++ b/models/migrations/v90.go @@ -0,0 +1,28 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "code.gitea.io/gitea/modules/setting" + + "github.com/go-xorm/xorm" +) + +func addCanWikiPageToc(x *xorm.Engine) error { + + type Repository struct { + TocWikiTree bool `xorm:"NOT NULL DEFAULT true"` + } + + if err := x.Sync2(new(Repository)); err != nil { + return err + } + + if _, err := x.Exec("UPDATE repository SET toc_wiki_tree = ?", + setting.Markdown.DefaultTocWikiTree); err != nil { + return err + } + return nil +} diff --git a/models/repo.go b/models/repo.go index 59ce18fa88e9e..47e343b7b8253 100644 --- a/models/repo.go +++ b/models/repo.go @@ -169,6 +169,10 @@ type Repository struct { IndexerStatus *RepoIndexerStatus `xorm:"-"` IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"` CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"` + TocWikiTree bool `xorm:"NOT NULL DEFAULT true"` + TocWikiFile bool `xorm:"NOT NULL DEFAULT true"` + TocMarkupAlways bool `xorm:"NOT NULL DEFAULT false"` + TocMarkupByFlag bool `xorm:"NOT NULL DEFAULT true"` Topics []string `xorm:"TEXT JSON"` // Avatar: ID(10-20)-md5(32) - must fit into 64 symbols @@ -1361,6 +1365,10 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err IsPrivate: opts.IsPrivate, IsFsckEnabled: !opts.IsMirror, CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, + TocWikiTree: setting.Markdown.DefaultTocWikiTree, + TocWikiFile: setting.Markdown.DefaultTocWikiFile, + TocMarkupAlways: setting.Markdown.DefaultTocMarkupAlways, + TocMarkupByFlag: setting.Markdown.DefaultTocMarkupByFlag, } sess := x.NewSession() diff --git a/models/wiki.go b/models/wiki.go index 9ae3386333298..800bea8b73c72 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -9,17 +9,19 @@ import ( "net/url" "os" "path/filepath" + "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 +30,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 +49,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. @@ -149,6 +195,8 @@ 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 { @@ -160,6 +208,26 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con return ErrWikiAlreadyExist{newWikiPath} } } + filesInIndex, err = gitRepo.LsFiles(newWikiDirPath) + if err != nil { + log.Error("%v", err) + return err + } + for _, file := range filesInIndex { + if file == newWikiDirPath { + return ErrWikiAlreadyExist{newWikiDirPath} + } + } + filesInIndex, err = gitRepo.LsFiles(FilenameToPathFilename(newWikiDirPath)) + if err != nil { + log.Error("%v", err) + return err + } + for _, file := range filesInIndex { + if file == newWikiDirPath { + return ErrWikiAlreadyExist{newWikiDirPath} + } + } } else { oldWikiPath := WikiNameToFilename(oldWikiName) filesInIndex, err := gitRepo.LsFiles(oldWikiPath) @@ -181,8 +249,30 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con return err } } + oldWikiDirPath := WikiNameToPathFilename(oldWikiName) + filesInIndex, err = gitRepo.LsFiles(oldWikiDirPath) + if err != nil { + log.Error("%v", err) + return err + } + found = false + for _, file := range filesInIndex { + if file == oldWikiDirPath { + found = true + break + } + } + if found { + 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 +281,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 } @@ -291,7 +381,23 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error) return err } } else { - return os.ErrNotExist + wikiDirPath := WikiNameToPathFilename(wikiName) + filesInIndex, err = gitRepo.LsFiles(wikiDirPath) + found = false + for _, file := range filesInIndex { + if file == wikiDirPath { + found = true + break + } + } + 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/auth/repo_form.go b/modules/auth/repo_form.go index 0333c3c92614a..1e7ef1c6ac077 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -125,6 +125,10 @@ type RepoSettingForm struct { AllowOnlyContributorsToTrackTime bool EnableIssueDependencies bool IsArchived bool + TocWikiTree bool + TocWikiFile bool + TocMarkupAlways bool + TocMarkupByFlag bool // Admin settings EnableHealthCheck bool 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/modules/setting/setting.go b/modules/setting/setting.go index 43a61aa7faeed..8fbb3e3196b54 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -236,12 +236,22 @@ var ( // Markdown settings Markdown = struct { - EnableHardLineBreak bool - CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` - FileExtensions []string + EnableHardLineBreak bool + CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` + FileExtensions []string + DefaultTocWikiTree bool + DefaultTocWikiFile bool + DefaultTocMarkupAlways bool + DefaultTocMarkupByFlag bool + TocMarkupFileExtensions []string }{ - EnableHardLineBreak: false, - FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), + EnableHardLineBreak: false, + FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), + DefaultTocWikiTree: true, + DefaultTocWikiFile: true, + DefaultTocMarkupAlways: false, + DefaultTocMarkupByFlag: true, + TocMarkupFileExtensions: strings.Split(".md,.markdown,.mdown,.mkd,.org", ","), } // Admin settings diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 223df91fda469..e13a1f4100449 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1031,6 +1031,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.delete_page_button = Delete Page wiki.delete_page_notice_1 = Deleting the wiki page '%s' cannot be undone. Continue? @@ -1152,6 +1153,12 @@ settings.pulls.allow_squash_commits = Enable Squashing to Merge Commits settings.admin_settings = Administrator Settings settings.admin_enable_health_check = Enable Repository Health Checks (git fsck) settings.admin_enable_close_issues_via_commit_in_any_branch = Close an issue via a commit made in a non default branch +settings.toc.toc = Table of Contents +settings.toc.pagetoc = Table of Pages +settings.toc.toc_wiki_tree = Enable additional wiki page tree +settings.toc.toc_wiki_file = Enable autogenerated TOC on wikipages (based on headlines) (does not alter files) +settings.toc.toc_markup_always = Enable autogenerated TOC on all markup files (based on headlines) (does not alter files) +settings.toc.toc_markup_by_flag = Enable autogenerated TOC on markup files which contain keyword '%%TOC%%' (based on headlines) (does not alter files) settings.danger_zone = Danger Zone settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name. settings.convert = Convert to Regular Repository diff --git a/public/css/index.css b/public/css/index.css index 437605e1d31f0..8010267db1faf 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -208,7 +208,7 @@ footer .ui.left,footer .ui.right{line-height:40px} .ui.tabular.menu .item{color:rgba(0,0,0,.5)} .ui.tabular.menu .item:hover{color:rgba(0,0,0,.8)} .ui.tabular.menu .item.active{color:rgba(0,0,0,.9)} -.markdown:not(code){overflow:hidden;font-size:16px;line-height:1.6!important;word-wrap:break-word} +.markdown:not(code){font-size:16px;line-height:1.6!important;word-wrap:break-word} .markdown:not(code).ui.segment{padding:3em} .markdown:not(code).file-view{padding:2em 2em 2em!important} .markdown:not(code)>:first-child{margin-top:0!important} @@ -287,6 +287,28 @@ footer .ui.left,footer .ui.right{line-height:40px} .markdown:not(code) .csv-data tr{border-top:0} .markdown:not(code) .csv-data th{font-weight:700;background:#f8f8f8;border-top:0} .markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em} +.markdown:not(code) .auto-toc-wrapper{position:relative;padding:0 0 7px 20px;float:right;background:#fff;z-index:1} +@media only screen and (min-width:1760px){.markdown:not(code) .auto-toc-wrapper{right:-31%;width:26%;padding:0;margin-top:-3em!important;margin-left:-26%} +} +@media only screen and (max-width:479px){.markdown:not(code) .auto-toc-wrapper{float:none;width:100%;padding:0;margin-bottom:1em} +} +.markdown:not(code) .auto-toc-container{padding:7px;border:1px solid #d4d4d5;border-radius:5px} +.markdown:not(code) .auto-toc-container h2{padding:.3em;font-size:1.65em} +.markdown:not(code) .auto-toc-clear{clear:both;margin-bottom:-20px!important} +.markdown:not(code) .page-toc-wrapper{z-index:1;background:#fff;position:relative;display:none} +@media only screen and (min-width:1760px){.markdown:not(code) .page-toc-wrapper{display:block;float:left;left:-31%;width:26%;padding:0;margin-top:-3em!important;margin-right:-26%} +} +.markdown:not(code) .page-toc-wrapper a.selected,.markdown:not(code) .page-toc-wrapper span.selected{font-weight:700;font-style:italic} +.markdown:not(code) .page-toc-wrapper a.error,.markdown:not(code) .page-toc-wrapper span.error{color:#f22} +.markdown:not(code) .page-toc-wrapper .page-toc-close{display:none} +.markdown:not(code) #page-toc-wiki-cb{display:none} +@media only screen and (max-width:1759px){.markdown:not(code) #page-toc-wiki-cb:checked+.page-toc-wrapper{display:block;position:absolute;width:100%;max-width:420px;border:1px solid #404552;border-radius:5px;padding:20px;background:#fff;margin:-3em -3em 3em!important;z-index:5} +.markdown:not(code) .page-toc-wrapper .page-toc-close{display:inline-block;float:right;margin-top:-.6em!important;margin-right:-.2em!important} +} +label.ui.basic.button.page-toc-wiki-label{display:none;float:left;padding:11px!important;margin-right:10px!important} +@media only screen and (max-width:1759px){label.ui.basic.button.page-toc-wiki-label{display:block} +} +label.ui.basic.button.page-toc-wiki-label 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} @media only screen and (max-width:767px){.home .hero h1{font-size:3.5em} .home .hero h2{font-size:2em} diff --git a/public/css/theme-arc-green.css b/public/css/theme-arc-green.css index b48e22b41e1c5..05dbc8d62b1e9 100644 --- a/public/css/theme-arc-green.css +++ b/public/css/theme-arc-green.css @@ -98,6 +98,9 @@ footer{background:#2e323e;border-top:1px solid #313131} .repository.file.editor.edit,.repository.wiki.new .CodeMirror{border-right:1px solid rgba(187,187,187,.6);border-left:1px solid rgba(187,187,187,.6);border-bottom:1px solid rgba(187,187,187,.6)} .repository.file.editor.edit .editor-preview,.repository.file.editor.edit .editor-preview-side,.repository.wiki.new .CodeMirror .editor-preview,.repository.wiki.new .CodeMirror .editor-preview-side{background:#353945} .repository.file.editor.edit .editor-preview .markdown:not(code).ui.segment,.repository.file.editor.edit .editor-preview-side .markdown:not(code).ui.segment,.repository.wiki.new .CodeMirror .editor-preview .markdown:not(code).ui.segment,.repository.wiki.new .CodeMirror .editor-preview-side .markdown:not(code).ui.segment{border-width:0} +.markdown:not(code) .auto-toc-wrapper,.markdown:not(code) .page-toc-wrapper{background:#353945} +.markdown:not(code) .auto-toc-container{background:#2a2e3a;border-color:#404552} +.markdown:not(code) #page-toc-wiki-cb:checked+.page-toc-wrapper{border-color:#d4d4d5;background:#383c4a} .ui.dropdown .menu{background:#2c303a} .ui.dropdown .menu>.message:not(.ui){color:#636363} .ui.input{color:#dbdbdb} diff --git a/public/js/index.js b/public/js/index.js index 22e4f9d419b2c..05b9ebd44f4f7 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1134,18 +1134,21 @@ function initWikiForm() { 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 = '