From 632551069a96ba4e0617d36ce6486d912c826577 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 11 Jun 2019 12:04:47 +0200 Subject: [PATCH 01/37] add toc to wikipages and rendered md files see go-gitea#822 Signed-off-by: Michael Gnehr --- public/js/index.js | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/public/js/index.js b/public/js/index.js index 28023e1061bbf..05a7def3cc9ed 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3034,3 +3034,95 @@ function onOAuthLoginClick() { oauthNav.show(); },5000); } +(function() { + // html listings ---------------------------------------------------- + let openedLists, listEopen; + // close list + const _closeList = function (count) { + let out = ''; + if(count == false || count == 0 || count == 'undefined' || typeof(count) == 'undefined' ) { + count = openedLists.length; + } else { + count = Math.min(count, openedLists.length); + } + while (count > 0){ + out += '' + openedLists.pop(); + listEopen = true; + count--; + } + return out; + }; + // open list + const _openList = function () { + let out = ''); + listEopen = false; + return out; + }; + // handle list element + // create valid html list + const __list = function (line, level, id) { + let out = ''; + let diff = level - openedLists.length; + if(diff > 0) { //open new level + out += _openList(); + out += __list(line,level, id); + } else if(diff < 0 ) { + out += _closeList(-diff); + out += __list(line, level, id); + } else { // only add list element + out += ((listEopen)?'':'') + '
  • ' + line + ''; + listEopen = true; + } + return out; + }; + /** + * find headlines and create list ---------------------------------- + * @param target Element target container where toc should be created + */ + const create_toc_inside = function(target) { + let rm; + if(target != null) { + if( (rm = target.querySelector('.auto-toc-wrapper')) != null ) { + rm.parentNode.removeChild(rm); + } + openedLists = []; listEopen = false; + // get content and create html + const elms = target.querySelectorAll('h1,h2,h3,h4,h5'); + let html = ''; + for(let i = 0; i < elms.length; i++){ + let l = elms[i].tagName.substr(1); //level + let t = elms[i].innerText.trim().trim(''); //text + let id = elms[i].id; + // create html + if(t.length > 0 && l >= 1) { + html += __list( t, l, id); + } else { + html += _closeList(0) + l; + } + } + html += _closeList(0); + //create elements + let d = document.createElement('div'); + d.id = 'auto-toc'; + d.className = 'anchor-wrap'; + d.innerHTML = '

    Table of Contents

    '; + let d2 = document.createElement('div'); + d2.className = 'auto-toc-container'; + d2.innerHTML = html; + d2.insertBefore(d, d2.firstChild); + let c = document.createElement('div'); + c.className = 'auto-toc-wrapper'; + c.appendChild(d2); + //inject toc + target.insertBefore(c, target.firstChild); + //set style + c.style.cssText = "float:right;background:#fff;padding:0 0 7px 20px;position:relative;z-index:1"; + d2.style.cssText = "padding:7px;border:1px solid #333;border-radius:5px"; + } + }; + // create toc ---------------------------------- + create_toc_inside(document.querySelector('.file-view.markdown')); // md + create_toc_inside(document.querySelector('.segment.markdown')); // wiki pages +})(); + From 0a9fb89f497c3d893a6127fabd0e60bf98315593 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 11 Jun 2019 13:15:02 +0200 Subject: [PATCH 02/37] move style settings from js to css files Signed-off-by: Michael Gnehr --- public/css/index.css | 2 ++ public/css/theme-arc-green.css | 2 ++ public/js/index.js | 3 --- public/less/_markdown.less | 14 ++++++++++++++ public/less/themes/arc-green.less | 8 ++++++++ 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index d192f43d1529a..a0841137d6a84 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -287,6 +287,8 @@ 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} +.markdown:not(code) .auto-toc-container{padding:7px;border:1px solid #d4d4d5;border-radius:5px} .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 dd2b13542a4de..21aa71c160f59 100644 --- a/public/css/theme-arc-green.css +++ b/public/css/theme-arc-green.css @@ -93,6 +93,8 @@ footer{background:#2e323e;border-top:1px solid #313131} .hljs,.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#9daccc} .markdown:not(code) .highlight pre,.markdown:not(code) pre{background-color:#2a2e3a;border:1px solid #404552} .markdown:not(code) table tr:nth-child(2n){background-color:#474d61} +.markdown:not(code) .auto-toc-wrapper{background:#353945} +.markdown:not(code) .auto-toc-container{background:#2a2e3a} .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 05a7def3cc9ed..df7dba690573e 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3116,9 +3116,6 @@ function onOAuthLoginClick() { c.appendChild(d2); //inject toc target.insertBefore(c, target.firstChild); - //set style - c.style.cssText = "float:right;background:#fff;padding:0 0 7px 20px;position:relative;z-index:1"; - d2.style.cssText = "padding:7px;border:1px solid #333;border-radius:5px"; } }; // create toc ---------------------------------- diff --git a/public/less/_markdown.less b/public/less/_markdown.less index af46d6a3b21cf..dafde53a008b5 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -496,4 +496,18 @@ ul.ui.list ul { padding-left: 2em; } + + .auto-toc-wrapper { + position: relative; + padding: 0 0 7px 20px; + float: right; + background: #ffffff; + z-index: 1; + } + + .auto-toc-container { + padding: 7px; + border: 1px solid #d4d4d5; + border-radius: 5px; + } } diff --git a/public/less/themes/arc-green.less b/public/less/themes/arc-green.less index 6d13bb2e5217e..a79968f84455b 100644 --- a/public/less/themes/arc-green.less +++ b/public/less/themes/arc-green.less @@ -504,6 +504,14 @@ a.ui.basic.green.label:hover { background-color: #474d61; } +.markdown:not(code) .auto-toc-wrapper { + background: #353945; +} + +.markdown:not(code) .auto-toc-container { + background: #2a2e3a; +} + .ui.dropdown .menu { background: #2c303a; } From 0e08aa9f7e7bd56b0b4ecb241f44414814f0284f Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 11 Jun 2019 13:29:43 +0200 Subject: [PATCH 03/37] fix dark theme border color Signed-off-by: Michael Gnehr --- public/css/theme-arc-green.css | 2 +- public/less/themes/arc-green.less | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/public/css/theme-arc-green.css b/public/css/theme-arc-green.css index 21aa71c160f59..c3f50589a234d 100644 --- a/public/css/theme-arc-green.css +++ b/public/css/theme-arc-green.css @@ -94,7 +94,7 @@ footer{background:#2e323e;border-top:1px solid #313131} .markdown:not(code) .highlight pre,.markdown:not(code) pre{background-color:#2a2e3a;border:1px solid #404552} .markdown:not(code) table tr:nth-child(2n){background-color:#474d61} .markdown:not(code) .auto-toc-wrapper{background:#353945} -.markdown:not(code) .auto-toc-container{background:#2a2e3a} +.markdown:not(code) .auto-toc-container{background:#2a2e3a;border-color:#404552} .ui.dropdown .menu{background:#2c303a} .ui.dropdown .menu>.message:not(.ui){color:#636363} .ui.input{color:#dbdbdb} diff --git a/public/less/themes/arc-green.less b/public/less/themes/arc-green.less index a79968f84455b..ad28d4c186578 100644 --- a/public/less/themes/arc-green.less +++ b/public/less/themes/arc-green.less @@ -510,6 +510,7 @@ a.ui.basic.green.label:hover { .markdown:not(code) .auto-toc-container { background: #2a2e3a; + border-color: #404552; } .ui.dropdown .menu { From 994bf331e3fa5f5d6f35b4cd270112aa633565d5 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Wed, 12 Jun 2019 16:30:04 +0200 Subject: [PATCH 04/37] change padding on TOC headlines Signed-off-by: Michael Gnehr --- public/css/index.css | 1 + public/less/_markdown.less | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/public/css/index.css b/public/css/index.css index a0841137d6a84..eb2c78da5d666 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -289,6 +289,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} .markdown:not(code) .auto-toc-wrapper{position:relative;padding:0 0 7px 20px;float:right;background:#fff;z-index:1} .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} .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/less/_markdown.less b/public/less/_markdown.less index dafde53a008b5..44a3cc80ef3df 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -509,5 +509,10 @@ padding: 7px; border: 1px solid #d4d4d5; border-radius: 5px; + + h2 { + padding: 0.3em; + font-size: 1.65em; + } } } From f60056bdbb206c4773d14aea303ad0791fdccaa9 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Wed, 12 Jun 2019 18:12:18 +0200 Subject: [PATCH 05/37] add md and wikifile TOC settings to repository options Signed-off-by: Michael Gnehr --- custom/conf/app.ini.sample | 6 +++ .../doc/advanced/config-cheat-sheet.en-us.md | 6 +++ models/migrations/migrations.go | 2 + models/migrations/v88.go | 40 +++++++++++++++++++ models/repo.go | 6 +++ modules/auth/repo_form.go | 3 ++ modules/setting/repository.go | 6 +++ options/locale/locale_de-DE.ini | 4 ++ options/locale/locale_en-US.ini | 4 ++ routers/repo/setting.go | 18 +++++++++ templates/repo/settings/options.tmpl | 25 ++++++++++++ 11 files changed, 120 insertions(+) create mode 100644 models/migrations/v88.go diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index a674984a2584e..010862d8a4e01 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -39,6 +39,12 @@ ACCESS_CONTROL_ALLOW_ORIGIN = USE_COMPAT_SSH_URI = false ; Close issues as long as a commit on any branch marks it as fixed DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false +; 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 markdown files (does not alter file), enabled: overwrites DEFAULT_TOC_MARKDOWN_BY_FLAG +DEFAULT_TOC_MARKDOWN_ALWAYS = false +; Create Table of Contents on renderes markdown files if line '%%TOC%%' is present +DEFAULT_TOC_MARKDOWN_BY_FLAG = true [repository.editor] ; List of file extensions for which lines should be wrapped in the CodeMirror editor 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 ecc196c86e136..f1a2289c64780 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -66,6 +66,12 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. default is not to present. **WARNING**: This maybe harmful to you website if you do not give it a right value. - `DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH`: **false**: Close an issue if a commit on a non default branch marks it as closed. +- `DEFAULT_TOC_WIKI_FILE`: **true**: Create Table of Contents based on headlines on wiki page (does + not alter file) +- `DEFAULT_TOC_MARKDOWN_ALWAYS`: **false** : Create Table of Contents on all rendered markdown files + (does not alter file), enabled: overwrites `TOC_MARKDOWN_BY_FLAG` +- `DEFAULT_TOC_MARKDOWN_BY_FLAG`: **true** : Create Table of Contents on renderes markdown files if + line '%%TOC%%' is present ### Repository - Pull Request (`repository.pull-request`) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index b95a74c3621db..c5ad549c5d0bc 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -229,6 +229,8 @@ var migrations = []Migration{ NewMigration("add http method to webhook", addHTTPMethodToWebhook), // v87 -> v88 NewMigration("add avatar field to repository", addAvatarFieldToRepository), + // v88 -> v89 + NewMigration("add toc on wiki and markedown", addCanTocOnWikiAndMarkdown), } // Migrate database to current version diff --git a/models/migrations/v88.go b/models/migrations/v88.go new file mode 100644 index 0000000000000..efb3403969ebe --- /dev/null +++ b/models/migrations/v88.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"` + TocMarkdownAlways bool `xorm:"NOT NULL DEFAULT false"` + TocMarkdownByFlag 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.Repository.DefaultTocWikiFile); err != nil { + return err + } + + if _, err := x.Exec("UPDATE repository SET toc_markdown_always = ?", + setting.Repository.DefaultTocMarkdownAlways); err != nil { + return err + } + + if _, err := x.Exec("UPDATE repository SET toc_markdown_by_flag = ?", + setting.Repository.DefaultTocMarkdownByFlag); err != nil { + return err + } + return nil +} diff --git a/models/repo.go b/models/repo.go index d5eca3d22502e..2b535225b1226 100644 --- a/models/repo.go +++ b/models/repo.go @@ -170,6 +170,9 @@ type Repository struct { IndexerStatus *RepoIndexerStatus `xorm:"-"` IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"` CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"` + TocWikiFile bool `xorm:"NOT NULL DEFAULT true"` + TocMarkdownAlways bool `xorm:"NOT NULL DEFAULT false"` + TocMarkdownByFlag bool `xorm:"NOT NULL DEFAULT true"` Topics []string `xorm:"TEXT JSON"` // Avatar: ID(10-20)-md5(32) - must fit into 64 symbols @@ -1371,6 +1374,9 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err IsPrivate: opts.IsPrivate, IsFsckEnabled: !opts.IsMirror, CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, + TocWikiFile: setting.Repository.DefaultTocWikiFile, + TocMarkdownAlways: setting.Repository.DefaultTocMarkdownAlways, + TocMarkdownByFlag: setting.Repository.DefaultTocMarkdownByFlag, } sess := x.NewSession() diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 0333c3c92614a..f9302c2b24b08 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -125,6 +125,9 @@ type RepoSettingForm struct { AllowOnlyContributorsToTrackTime bool EnableIssueDependencies bool IsArchived bool + TocWikiFile bool + TocMarkdownAlways bool + TocMarkdownByFlag bool // Admin settings EnableHealthCheck bool diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 98e3d6e82624d..52a90e30f41e7 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -34,6 +34,9 @@ var ( AccessControlAllowOrigin string UseCompatSSHURI bool DefaultCloseIssuesViaCommitsInAnyBranch bool + DefaultTocWikiFile bool + DefaultTocMarkdownAlways bool + DefaultTocMarkdownByFlag bool // Repository editor settings Editor struct { @@ -76,6 +79,9 @@ var ( AccessControlAllowOrigin: "", UseCompatSSHURI: false, DefaultCloseIssuesViaCommitsInAnyBranch: false, + DefaultTocWikiFile: true, + DefaultTocMarkdownAlways: false, + DefaultTocMarkdownByFlag: true, // Repository editor settings Editor: struct { diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 7176b23f56040..6966535f5b9a4 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1147,6 +1147,10 @@ settings.pulls.allow_squash_commits=Mergen von Commits durch Squash aktivieren settings.admin_settings=Administratoreinstellungen settings.admin_enable_health_check=Repository-Health-Checks aktivieren (git fsck) settings.admin_enable_close_issues_via_commit_in_any_branch=Einen Issue mit einem Commit auf einem nicht-Standard-Branch schließen +settings.toc.toc = Inhaltsverzeichnis +settings.toc.toc_wiki_file = Automatisches Inhaltsverzeichnis auf Wikiseiten rendern. +settings.toc.toc_markdown_always = Automatisches Inhaltsverzeichnis auf allen Markdown Seiten rendern. +settings.toc.toc_markdown_by_flag = Automatisches Inhaltsverzeichnis auf Markdown Seiten rendern, wenn diese das Flag '%%TOC%%' enthalten. settings.danger_zone=Gefahrenzone settings.new_owner_has_same_repo=Der neue Eigentümer hat bereits ein Repository mit dem gleichen Namen. Bitte wähle einen anderen Namen. settings.convert=In ein normales Repository umwandeln diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 71c76fd9b6ada..f398228ad3acd 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1149,6 +1149,10 @@ 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.toc_wiki_file = Enable autogenerated TOC on wikipages (based on headlines) (does not alter files) +settings.toc.toc_markdown_always = Enable autogenerated TOC on all markdown files (based on headlines) (does not alter files) +settings.toc.toc_markdown_by_flag = Enable autogenerated TOC on markdown files which contain '%%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/routers/repo/setting.go b/routers/repo/setting.go index 767cdacde0195..741c39e807b82 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -294,6 +294,24 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { ctx.ServerError("UpdateRepositoryUnits", err) return } + + if repo.TocWikiFile != form.TocWikiFile { + repo.TocWikiFile = form.TocWikiFile + } + + if repo.TocMarkdownAlways != form.TocMarkdownAlways { + repo.TocMarkdownAlways = form.TocMarkdownAlways + } + + if repo.TocMarkdownByFlag != form.TocMarkdownByFlag { + repo.TocMarkdownByFlag = form.TocMarkdownByFlag + } + + if err := models.UpdateRepository(repo, false); err != nil { + ctx.ServerError("UpdateRepository", err) + return + } + log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index c6d715acbee88..dd6fc4b9e074f 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -262,6 +262,31 @@ {{end}} +
    + +
    +
    + +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    From a9dce7f38cc9230174296ce71e730b87ba93be75 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Wed, 12 Jun 2019 21:33:36 +0200 Subject: [PATCH 06/37] handle options on template and update js Signed-off-by: Michael Gnehr --- public/js/index.js | 103 ++++++++++++++++++++++++---------- templates/repo/view_file.tmpl | 2 +- templates/repo/wiki/view.tmpl | 2 +- 3 files changed, 74 insertions(+), 33 deletions(-) diff --git a/public/js/index.js b/public/js/index.js index df7dba690573e..ca5f0017b0a08 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3076,6 +3076,42 @@ function onOAuthLoginClick() { } return out; }; + /** + * find headlines and create list ---------------------------------- + * @param target Element target container where toc should look for headlines + * @return Element toc wrapper - div container + */ + const get_toc_inside = function (target) { + openedLists = []; listEopen = false; + // get content and create html + const elms = target.querySelectorAll('h1,h2,h3,h4,h5'); + let html = ''; + for(let i = 0; i < elms.length; i++){ + let l = elms[i].tagName.substr(1); //level + let t = elms[i].innerText.trim().trim(''); //text + let id = elms[i].id; + // create html + if(t.length > 0 && l >= 1) { + html += __list( t, l, id); + } else { + html += _closeList(0) + l; + } + } + html += _closeList(0); + //create elements + let d = document.createElement('div'); + d.id = 'auto-toc'; + d.className = 'anchor-wrap'; + d.innerHTML = '

    '+((typeof(target.dataset.toc) == 'string' && target.dataset.toc != '')?target.dataset.toc:'Table of Contents')+'

    '; + let d2 = document.createElement('div'); + d2.className = 'auto-toc-container'; + d2.innerHTML = html; + d2.insertBefore(d, d2.firstChild); + let c = document.createElement('div'); + c.className = 'auto-toc-wrapper'; + c.appendChild(d2); + return c; + }; /** * find headlines and create list ---------------------------------- * @param target Element target container where toc should be created @@ -3086,40 +3122,45 @@ function onOAuthLoginClick() { if( (rm = target.querySelector('.auto-toc-wrapper')) != null ) { rm.parentNode.removeChild(rm); } - openedLists = []; listEopen = false; - // get content and create html - const elms = target.querySelectorAll('h1,h2,h3,h4,h5'); - let html = ''; - for(let i = 0; i < elms.length; i++){ - let l = elms[i].tagName.substr(1); //level - let t = elms[i].innerText.trim().trim(''); //text - let id = elms[i].id; - // create html - if(t.length > 0 && l >= 1) { - html += __list( t, l, id); - } else { - html += _closeList(0) + l; - } + //remove optional toc flag + let ps = target.querySelectorAll('p'); + //look for toc keywoard + for (let i = 0; i < ps.length; i++) { + if (ps[i].textContent.trim() == '%%TOC%%') { + ps[i].parentNode.removeChild(ps[i]); + } } - html += _closeList(0); - //create elements - let d = document.createElement('div'); - d.id = 'auto-toc'; - d.className = 'anchor-wrap'; - d.innerHTML = '

    Table of Contents

    '; - let d2 = document.createElement('div'); - d2.className = 'auto-toc-container'; - d2.innerHTML = html; - d2.insertBefore(d, d2.firstChild); - let c = document.createElement('div'); - c.className = 'auto-toc-wrapper'; - c.appendChild(d2); //inject toc - target.insertBefore(c, target.firstChild); + target.insertBefore(get_toc_inside(target), target.firstChild); + } + }; + /** + * search for %%TOC%% inside document if found create toc -------------------------- + * @param target Element target container where toc should be created + */ + const detect_toc_flag = function(target) { + if(target != null) { + let ps = target.querySelectorAll('p'); + let found = false; + //look for toc keywoard + for (let i = 0; i < ps.length; i++) { + if (ps[i].textContent.trim() == '%%TOC%%') { + found = ps[i]; + break; + } + } + if(found !== false) { + //remove toc keywoard + found.parentNode.removeChild(found); + //create toc + create_toc_inside(target); + } } }; // create toc ---------------------------------- - create_toc_inside(document.querySelector('.file-view.markdown')); // md - create_toc_inside(document.querySelector('.segment.markdown')); // wiki pages + addEventListener("load", function(){ + create_toc_inside(document.querySelector('.file-view.markdown.auto-toc')); // md + detect_toc_flag( document.querySelector('.file-view.markdown.auto-toc-by-flag')); // md by %%TOC%% flag + create_toc_inside(document.querySelector('.segment.markdown.auto-toc')); // wiki pages + }); })(); - diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 6445ee7b9e58d..56734e6030da2 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -45,7 +45,7 @@
    -
    +
    {{if .IsMarkup}} {{if .FileContent}}{{.FileContent | Safe}}{{end}} {{else if .IsRenderedHTML}} diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index dd2de2a041380..d276d7acfb08b 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -79,7 +79,7 @@
    {{end}}
    -
    +
    {{.content | Str2html}}
    {{if .sidebarPresent}} From e971230c01e967485d6af4095e31a812a180a0cd Mon Sep 17 00:00:00 2001 From: Cherrg Date: Thu, 13 Jun 2019 02:45:07 +0200 Subject: [PATCH 07/37] Update index.js --- public/js/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/index.js b/public/js/index.js index ca5f0017b0a08..11cdd2e5c810b 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3094,7 +3094,7 @@ function onOAuthLoginClick() { if(t.length > 0 && l >= 1) { html += __list( t, l, id); } else { - html += _closeList(0) + l; + html += _closeList(0); } } html += _closeList(0); From a889262a4c1bf9d618173eb8b9dc3a3a64bd6c81 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Thu, 13 Jun 2019 03:47:14 +0200 Subject: [PATCH 08/37] add float clear element + reduce js call stack: function -> inline function Signed-off-by: Michael Gnehr --- public/css/index.css | 1 + public/js/index.js | 75 +++++++++++++++++++------------------- public/less/_markdown.less | 4 ++ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index eb2c78da5d666..654e2f085a90d 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -290,6 +290,7 @@ footer .ui.left,footer .ui.right{line-height:40px} .markdown:not(code) .auto-toc-wrapper{position:relative;padding:0 0 7px 20px;float:right;background:#fff;z-index:1} .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} .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/js/index.js b/public/js/index.js index 11cdd2e5c810b..653f79136f3fe 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3076,42 +3076,6 @@ function onOAuthLoginClick() { } return out; }; - /** - * find headlines and create list ---------------------------------- - * @param target Element target container where toc should look for headlines - * @return Element toc wrapper - div container - */ - const get_toc_inside = function (target) { - openedLists = []; listEopen = false; - // get content and create html - const elms = target.querySelectorAll('h1,h2,h3,h4,h5'); - let html = ''; - for(let i = 0; i < elms.length; i++){ - let l = elms[i].tagName.substr(1); //level - let t = elms[i].innerText.trim().trim(''); //text - let id = elms[i].id; - // create html - if(t.length > 0 && l >= 1) { - html += __list( t, l, id); - } else { - html += _closeList(0); - } - } - html += _closeList(0); - //create elements - let d = document.createElement('div'); - d.id = 'auto-toc'; - d.className = 'anchor-wrap'; - d.innerHTML = '

    '+((typeof(target.dataset.toc) == 'string' && target.dataset.toc != '')?target.dataset.toc:'Table of Contents')+'

    '; - let d2 = document.createElement('div'); - d2.className = 'auto-toc-container'; - d2.innerHTML = html; - d2.insertBefore(d, d2.firstChild); - let c = document.createElement('div'); - c.className = 'auto-toc-wrapper'; - c.appendChild(d2); - return c; - }; /** * find headlines and create list ---------------------------------- * @param target Element target container where toc should be created @@ -3130,8 +3094,43 @@ function onOAuthLoginClick() { ps[i].parentNode.removeChild(ps[i]); } } - //inject toc - target.insertBefore(get_toc_inside(target), target.firstChild); + openedLists = []; listEopen = false; + // get content and create html + const elms = target.querySelectorAll('h1,h2,h3,h4,h5'); + if (elms.length > 0) { + let html = ''; + for(let i = 0; i < elms.length; i++){ + let l = elms[i].tagName.substr(1); //level + let t = elms[i].innerText.trim().trim(''); //text + let id = elms[i].id; + // create html + if(t.length > 0 && l >= 1) { + html += __list(t, l, id); + } else { + html += _closeList(0); + } + } + html += _closeList(0); + //create elements + let d = document.createElement('div'); + d.id = 'auto-toc'; + d.className = 'anchor-wrap'; + d.innerHTML = '

    '+((typeof(target.dataset.toc) == 'string' && target.dataset.toc != '')?target.dataset.toc:'Table of Contents')+'

    '; + let d2 = document.createElement('div'); + d2.className = 'auto-toc-container'; + d2.innerHTML = html; + d2.insertBefore(d, d2.firstChild); + let c = document.createElement('div'); + c.className = 'auto-toc-wrapper'; + c.appendChild(d2); + //inject toc + target.insertBefore(c, target.firstChild); + if( (rm = document.querySelector('.auto-toc-clear')) != null ) { + rm.parentNode.removeChild(rm); + } + let a = document.createElement('div'); a.className = 'auto-toc-clear'; + target.appendChild(a); + } } }; /** diff --git a/public/less/_markdown.less b/public/less/_markdown.less index 44a3cc80ef3df..f12b70b4fc860 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -515,4 +515,8 @@ font-size: 1.65em; } } + + .auto-toc-clear { + clear: both; + } } From 77fb62127e6e350db28a68fae624f0c61f5fbe25 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Thu, 13 Jun 2019 04:48:19 +0200 Subject: [PATCH 09/37] move TOC settings in app.ini from section 'repository' to 'markdown' Signed-off-by: Michael Gnehr --- custom/conf/app.ini.sample | 12 ++++++------ .../doc/advanced/config-cheat-sheet.en-us.md | 12 ++++++------ models/migrations/v88.go | 6 +++--- models/repo.go | 6 +++--- modules/setting/repository.go | 6 ------ modules/setting/setting.go | 16 +++++++++++----- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index 010862d8a4e01..bb7eac824a40b 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -39,12 +39,6 @@ ACCESS_CONTROL_ALLOW_ORIGIN = USE_COMPAT_SSH_URI = false ; Close issues as long as a commit on any branch marks it as fixed DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false -; 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 markdown files (does not alter file), enabled: overwrites DEFAULT_TOC_MARKDOWN_BY_FLAG -DEFAULT_TOC_MARKDOWN_ALWAYS = false -; Create Table of Contents on renderes markdown files if line '%%TOC%%' is present -DEFAULT_TOC_MARKDOWN_BY_FLAG = true [repository.editor] ; List of file extensions for which lines should be wrapped in the CodeMirror editor @@ -151,6 +145,12 @@ 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 Contents based on headlines on wiki page (does not alter file) +DEFAULT_TOC_WIKI_FILE = true +; Create Table of Contents on all rendered markdown files (does not alter file), enabled: overwrites DEFAULT_TOC_MARKDOWN_BY_FLAG +DEFAULT_TOC_MARKDOWN_ALWAYS = false +; Create Table of Contents on renderes markdown files if line '%%TOC%%' is present +DEFAULT_TOC_MARKDOWN_BY_FLAG = true [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 f1a2289c64780..76d923a4a929f 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -66,12 +66,6 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. default is not to present. **WARNING**: This maybe harmful to you website if you do not give it a right value. - `DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH`: **false**: Close an issue if a commit on a non default branch marks it as closed. -- `DEFAULT_TOC_WIKI_FILE`: **true**: Create Table of Contents based on headlines on wiki page (does - not alter file) -- `DEFAULT_TOC_MARKDOWN_ALWAYS`: **false** : Create Table of Contents on all rendered markdown files - (does not alter file), enabled: overwrites `TOC_MARKDOWN_BY_FLAG` -- `DEFAULT_TOC_MARKDOWN_BY_FLAG`: **true** : Create Table of Contents on renderes markdown files if - line '%%TOC%%' is present ### Repository - Pull Request (`repository.pull-request`) @@ -113,6 +107,12 @@ 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_FILE`: **true**: Create Table of Contents based on headlines on wiki page (does + not alter file) +- `DEFAULT_TOC_MARKDOWN_ALWAYS`: **false** : Create Table of Contents on all rendered markdown files + (does not alter file), enabled: overwrites `TOC_MARKDOWN_BY_FLAG` +- `DEFAULT_TOC_MARKDOWN_BY_FLAG`: **true** : Create Table of Contents on renderes markdown files if + line '%%TOC%%' is present ## Server (`server`) diff --git a/models/migrations/v88.go b/models/migrations/v88.go index efb3403969ebe..a7228d224a82d 100644 --- a/models/migrations/v88.go +++ b/models/migrations/v88.go @@ -23,17 +23,17 @@ func addCanTocOnWikiAndMarkdown(x *xorm.Engine) error { } if _, err := x.Exec("UPDATE repository SET toc_wiki_file = ?", - setting.Repository.DefaultTocWikiFile); err != nil { + setting.Markdown.DefaultTocWikiFile); err != nil { return err } if _, err := x.Exec("UPDATE repository SET toc_markdown_always = ?", - setting.Repository.DefaultTocMarkdownAlways); err != nil { + setting.Markdown.DefaultTocMarkdownAlways); err != nil { return err } if _, err := x.Exec("UPDATE repository SET toc_markdown_by_flag = ?", - setting.Repository.DefaultTocMarkdownByFlag); err != nil { + setting.Markdown.DefaultTocMarkdownByFlag); err != nil { return err } return nil diff --git a/models/repo.go b/models/repo.go index 2b535225b1226..a73074c52043b 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1374,9 +1374,9 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err IsPrivate: opts.IsPrivate, IsFsckEnabled: !opts.IsMirror, CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, - TocWikiFile: setting.Repository.DefaultTocWikiFile, - TocMarkdownAlways: setting.Repository.DefaultTocMarkdownAlways, - TocMarkdownByFlag: setting.Repository.DefaultTocMarkdownByFlag, + TocWikiFile: setting.Markdown.DefaultTocWikiFile, + TocMarkdownAlways: setting.Markdown.DefaultTocMarkdownAlways, + TocMarkdownByFlag: setting.Markdown.DefaultTocMarkdownByFlag, } sess := x.NewSession() diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 52a90e30f41e7..98e3d6e82624d 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -34,9 +34,6 @@ var ( AccessControlAllowOrigin string UseCompatSSHURI bool DefaultCloseIssuesViaCommitsInAnyBranch bool - DefaultTocWikiFile bool - DefaultTocMarkdownAlways bool - DefaultTocMarkdownByFlag bool // Repository editor settings Editor struct { @@ -79,9 +76,6 @@ var ( AccessControlAllowOrigin: "", UseCompatSSHURI: false, DefaultCloseIssuesViaCommitsInAnyBranch: false, - DefaultTocWikiFile: true, - DefaultTocMarkdownAlways: false, - DefaultTocMarkdownByFlag: true, // Repository editor settings Editor: struct { diff --git a/modules/setting/setting.go b/modules/setting/setting.go index ff53e9a3757f1..87ac2eb72b980 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -236,12 +236,18 @@ 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 + DefaultTocWikiFile bool + DefaultTocMarkdownAlways bool + DefaultTocMarkdownByFlag bool }{ - EnableHardLineBreak: false, - FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), + EnableHardLineBreak: false, + FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), + DefaultTocWikiFile: true, + DefaultTocMarkdownAlways: false, + DefaultTocMarkdownByFlag: true, } // Admin settings From 06238aa35571b51963ca61356b4aadafda25d562 Mon Sep 17 00:00:00 2001 From: Cherrg Date: Thu, 13 Jun 2019 13:02:38 +0200 Subject: [PATCH 10/37] remove german translation --- options/locale/locale_de-DE.ini | 4 ---- 1 file changed, 4 deletions(-) diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 6966535f5b9a4..7176b23f56040 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1147,10 +1147,6 @@ settings.pulls.allow_squash_commits=Mergen von Commits durch Squash aktivieren settings.admin_settings=Administratoreinstellungen settings.admin_enable_health_check=Repository-Health-Checks aktivieren (git fsck) settings.admin_enable_close_issues_via_commit_in_any_branch=Einen Issue mit einem Commit auf einem nicht-Standard-Branch schließen -settings.toc.toc = Inhaltsverzeichnis -settings.toc.toc_wiki_file = Automatisches Inhaltsverzeichnis auf Wikiseiten rendern. -settings.toc.toc_markdown_always = Automatisches Inhaltsverzeichnis auf allen Markdown Seiten rendern. -settings.toc.toc_markdown_by_flag = Automatisches Inhaltsverzeichnis auf Markdown Seiten rendern, wenn diese das Flag '%%TOC%%' enthalten. settings.danger_zone=Gefahrenzone settings.new_owner_has_same_repo=Der neue Eigentümer hat bereits ein Repository mit dem gleichen Namen. Bitte wähle einen anderen Namen. settings.convert=In ein normales Repository umwandeln From c486aca2c8b9f4f000325c73d5153dfeb26d026a Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Thu, 13 Jun 2019 17:00:54 +0200 Subject: [PATCH 11/37] add configuration flag to set TOC extensions Signed-off-by: Michael Gnehr --- custom/conf/app.ini.sample | 3 ++ .../doc/advanced/config-cheat-sheet.en-us.md | 3 ++ modules/setting/setting.go | 24 +++++++------ routers/repo/view.go | 35 +++++++++++++++++++ templates/repo/view_file.tmpl | 2 +- 5 files changed, 55 insertions(+), 12 deletions(-) diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index bb7eac824a40b..372f362601f85 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -151,6 +151,9 @@ DEFAULT_TOC_WIKI_FILE = true DEFAULT_TOC_MARKDOWN_ALWAYS = false ; Create Table of Contents on renderes markdown files if line '%%TOC%%' is present DEFAULT_TOC_MARKDOWN_BY_FLAG = true +; List of markup file extensions that TOC should be created on, set to not interfere with external renderers +; Separate the extensions with a comma. To ignore file extension check, just put a comma +TOC_MARKDOWN_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 76d923a4a929f..1d346d6ff8082 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -113,6 +113,9 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. (does not alter file), enabled: overwrites `TOC_MARKDOWN_BY_FLAG` - `DEFAULT_TOC_MARKDOWN_BY_FLAG`: **true** : Create Table of Contents on renderes markdown files if line '%%TOC%%' is present +- `TOC_MARKDOWN_FILE_EXTENSIONS`: **.md,.markdown,.mdown,.mkd,.org** : List of markup file extensions + that TOC should be created on, set to not interfere with external renderers. + Separate the extensions with a comma. To ignore file extension check, just put a comma. ## Server (`server`) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 87ac2eb72b980..b9975cf5ef8dd 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -236,18 +236,20 @@ var ( // Markdown settings Markdown = struct { - EnableHardLineBreak bool - CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` - FileExtensions []string - DefaultTocWikiFile bool - DefaultTocMarkdownAlways bool - DefaultTocMarkdownByFlag bool + EnableHardLineBreak bool + CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` + FileExtensions []string + DefaultTocWikiFile bool + DefaultTocMarkdownAlways bool + DefaultTocMarkdownByFlag bool + TocMarkdownFileExtensions []string }{ - EnableHardLineBreak: false, - FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), - DefaultTocWikiFile: true, - DefaultTocMarkdownAlways: false, - DefaultTocMarkdownByFlag: true, + EnableHardLineBreak: false, + FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), + DefaultTocWikiFile: true, + DefaultTocMarkdownAlways: false, + DefaultTocMarkdownByFlag: true, + TocMarkdownFileExtensions: strings.Split(".md,.markdown,.mdown,.mkd,.org", ","), } // Admin settings diff --git a/routers/repo/view.go b/routers/repo/view.go index 3483a53a0d31f..1aba934d80a2b 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -12,6 +12,7 @@ import ( gotemplate "html/template" "io/ioutil" "path" + "path/filepath" "strings" "code.gitea.io/gitea/models" @@ -163,6 +164,23 @@ func renderDirectory(ctx *context.Context, treeLink string) { buf = templates.ToUTF8WithFallback(append(buf, d...)) if markup.Type(readmeFile.Name()) != "" { + // Check if extension matches TOC file list + tocExts := setting.Markdown.TocMarkdownFileExtensions + isTocMarkup := false + if len(tocExts) == 0 || len(tocExts) == 1 && tocExts[0] == "" || len(tocExts) == 2 && tocExts[0] == "" && tocExts[1] == "" { + isTocMarkup = true + } else { + fileExt := strings.ToLower(filepath.Ext(readmeFile.Name())) + if fileExt != "" { + for _, tExt := range tocExts { + if tExt == fileExt { + isTocMarkup = true + break + } + } + } + } + ctx.Data["IsTocMarkup"] = isTocMarkup ctx.Data["IsMarkup"] = true ctx.Data["FileContent"] = string(markup.Render(readmeFile.Name(), buf, treeLink, ctx.Repo.Repository.ComposeMetas())) } else { @@ -283,6 +301,23 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st readmeExist := markup.IsReadmeFile(blob.Name()) ctx.Data["ReadmeExist"] = readmeExist if markup.Type(blob.Name()) != "" { + // Check if extension matches TOC file list + tocExts := setting.Markdown.TocMarkdownFileExtensions + isTocMarkup := false + if len(tocExts) == 0 || len(tocExts) == 1 && tocExts[0] == "" || len(tocExts) == 2 && tocExts[0] == "" && tocExts[1] == "" { + isTocMarkup = true + } else { + fileExt := strings.ToLower(filepath.Ext(blob.Name())) + if fileExt != "" { + for _, tExt := range tocExts { + if tExt == fileExt { + isTocMarkup = true + break + } + } + } + } + ctx.Data["IsTocMarkup"] = isTocMarkup ctx.Data["IsMarkup"] = true ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas())) } else if readmeExist { diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 56734e6030da2..d89f9e4cdc236 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -45,7 +45,7 @@
    -
    +
    {{if .IsMarkup}} {{if .FileContent}}{{.FileContent | Safe}}{{end}} {{else if .IsRenderedHTML}} From 588fcca7c18e9dc2da9ad0d6e9d48195e2af5fd2 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Thu, 13 Jun 2019 18:30:27 +0200 Subject: [PATCH 12/37] move toc beside markdown container on large screens >1760px Signed-off-by: Michael Gnehr --- public/css/index.css | 6 ++++-- public/less/_markdown.less | 9 ++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index 654e2f085a90d..e0104fecaea1a 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -207,7 +207,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} @@ -288,9 +288,11 @@ footer .ui.left,footer .ui.right{line-height:40px} .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%} +} .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} +.markdown:not(code) .auto-toc-clear{clear:both;margin-bottom:-20px!important} .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/less/_markdown.less b/public/less/_markdown.less index f12b70b4fc860..5571433554920 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -1,5 +1,4 @@ .markdown:not(code) { - overflow: hidden; font-size: 16px; line-height: 1.6 !important; word-wrap: break-word; @@ -503,6 +502,13 @@ float: right; background: #ffffff; z-index: 1; + @media only screen and (min-width:1760px) { + right: -31%; + width: 26%; + padding: 0; + margin-top: -3em !important; + margin-left: -26%; + } } .auto-toc-container { @@ -518,5 +524,6 @@ .auto-toc-clear { clear: both; + margin-bottom: -20px !important; } } From 841640962659f4568859579d46375ac6498dd597 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Thu, 13 Jun 2019 23:06:15 +0200 Subject: [PATCH 13/37] add page toc configuration option and db migrate (TocWikiTree) + ui + language Signed-off-by: Michael Gnehr --- custom/conf/app.ini.sample | 2 ++ .../doc/advanced/config-cheat-sheet.en-us.md | 1 + models/migrations/migrations.go | 2 ++ models/migrations/v89.go | 28 +++++++++++++++++++ models/repo.go | 2 ++ modules/auth/repo_form.go | 1 + modules/setting/setting.go | 2 ++ options/locale/locale_en-US.ini | 2 ++ routers/repo/setting.go | 5 ++++ templates/repo/settings/options.tmpl | 6 ++++ templates/repo/wiki/view.tmpl | 2 +- 11 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 models/migrations/v89.go diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index 372f362601f85..929c5fe07a39b 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -145,6 +145,8 @@ 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 markdown files (does not alter file), enabled: overwrites DEFAULT_TOC_MARKDOWN_BY_FLAG 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 1d346d6ff8082..533dca07daa4e 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,7 @@ 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_MARKDOWN_ALWAYS`: **false** : Create Table of Contents on all rendered markdown files diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 3db6fe4f7eee0..4459a1e0c9bac 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -231,6 +231,8 @@ var migrations = []Migration{ NewMigration("add avatar field to repository", addAvatarFieldToRepository), // v88 -> v89 NewMigration("add toc on wiki and markedown", addCanTocOnWikiAndMarkdown), + // v89 -> v90 + 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..7879c8527b17f --- /dev/null +++ b/models/migrations/v89.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 ed21931587d90..7d12469f1277d 100644 --- a/models/repo.go +++ b/models/repo.go @@ -169,6 +169,7 @@ 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"` TocMarkdownAlways bool `xorm:"NOT NULL DEFAULT false"` TocMarkdownByFlag bool `xorm:"NOT NULL DEFAULT true"` @@ -1367,6 +1368,7 @@ 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, TocMarkdownAlways: setting.Markdown.DefaultTocMarkdownAlways, TocMarkdownByFlag: setting.Markdown.DefaultTocMarkdownByFlag, diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index f9302c2b24b08..ad448ca2f73cc 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -125,6 +125,7 @@ type RepoSettingForm struct { AllowOnlyContributorsToTrackTime bool EnableIssueDependencies bool IsArchived bool + TocWikiTree bool TocWikiFile bool TocMarkdownAlways bool TocMarkdownByFlag bool diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 44b66200a3a0d..6423b6afbcd77 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -239,6 +239,7 @@ var ( EnableHardLineBreak bool CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` FileExtensions []string + DefaultTocWikiTree bool DefaultTocWikiFile bool DefaultTocMarkdownAlways bool DefaultTocMarkdownByFlag bool @@ -246,6 +247,7 @@ var ( }{ EnableHardLineBreak: false, FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), + DefaultTocWikiTree: true, DefaultTocWikiFile: true, DefaultTocMarkdownAlways: false, DefaultTocMarkdownByFlag: true, diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index f398228ad3acd..620c13ae07d4e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1150,6 +1150,8 @@ 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_markdown_always = Enable autogenerated TOC on all markdown files (based on headlines) (does not alter files) settings.toc.toc_markdown_by_flag = Enable autogenerated TOC on markdown files which contain '%%TOC%%' (based on headlines) (does not alter files) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index fadf1b9211400..985fb7a131160 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -295,6 +295,11 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } + // update toc settings + if repo.TocWikiTree != form.TocWikiTree { + repo.TocWikiTree = form.TocWikiTree + } + if repo.TocWikiFile != form.TocWikiFile { repo.TocWikiFile = form.TocWikiFile } diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index dd6fc4b9e074f..c4e3a94bbddcf 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -267,6 +267,12 @@
    +
    +
    + + +
    +
    diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index d276d7acfb08b..b5d16ad013411 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -79,7 +79,7 @@
    {{end}}
    -
    +
    {{.content | Str2html}}
    {{if .sidebarPresent}} From 8914972d01ce61e6213ffa11456d86b592d96982 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Thu, 13 Jun 2019 23:07:17 +0200 Subject: [PATCH 14/37] add js + css to render page toc Signed-off-by: Michael Gnehr --- public/css/index.css | 7 ++ public/css/theme-arc-green.css | 2 +- public/js/index.js | 140 +++++++++++++++++++++++++++--- public/less/_markdown.less | 29 +++++++ public/less/themes/arc-green.less | 1 + 5 files changed, 165 insertions(+), 14 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index e0104fecaea1a..76e02295e7534 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -290,6 +290,13 @@ footer .ui.left,footer .ui.right{line-height:40px} .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%} } +.markdown:not(code) .page-toc-wrapper{position:relative;padding:0 20px 7px 0;float:left;background:#fff;z-index:1} +@media only screen and (min-width:1760px){.markdown:not(code) .page-toc-wrapper{left:-31%;width:26%;padding:0;margin-top:-3em!important;margin-right:-26%} +} +@media only screen and (max-width:1759px){.markdown:not(code) .page-toc-wrapper{display:none} +} +.markdown:not(code) .page-toc-wrapper a.selected{font-weight:700;font-style:italic} +.markdown:not(code) .page-toc-wrapper span.error{color:#f22} .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} diff --git a/public/css/theme-arc-green.css b/public/css/theme-arc-green.css index c3f50589a234d..ee6fb7f965d53 100644 --- a/public/css/theme-arc-green.css +++ b/public/css/theme-arc-green.css @@ -93,7 +93,7 @@ footer{background:#2e323e;border-top:1px solid #313131} .hljs,.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#9daccc} .markdown:not(code) .highlight pre,.markdown:not(code) pre{background-color:#2a2e3a;border:1px solid #404552} .markdown:not(code) table tr:nth-child(2n){background-color:#474d61} -.markdown:not(code) .auto-toc-wrapper{background:#353945} +.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} .ui.dropdown .menu{background:#2c303a} .ui.dropdown .menu>.message:not(.ui){color:#636363} diff --git a/public/js/index.js b/public/js/index.js index 653f79136f3fe..f7f52d9de469d 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3076,10 +3076,33 @@ function onOAuthLoginClick() { } return out; }; + // handle wiki page list element + // create valid html list + const __page_list = function (line, level, link, selected) { + let out = ''; + let diff = level - openedLists.length; + if(diff > 0) { //open new level + out += _openList(); + out += __page_list(line,level, link, selected); + } else if(diff < 0 ) { + out += _closeList(-diff); + out += __page_list(line, level, link, selected); + } else { // only add list element + if (link === null){ + out += ((listEopen)?'
  • ':'') + '
  • ' + line + ''; + } else if (link === false) { + out += ((listEopen)?'
  • ':'') + '
  • ' + line + ''; + } else { + out += ((listEopen)?'
  • ':'') + '
  • ' + line + ''; + } + listEopen = true; + } + return out; + }; /** - * find headlines and create list ---------------------------------- - * @param target Element target container where toc should be created - */ + * find headlines and create list ---------------------------------- + * @param target Element target container where toc should be created + */ const create_toc_inside = function(target) { let rm; if(target != null) { @@ -3090,9 +3113,9 @@ function onOAuthLoginClick() { let ps = target.querySelectorAll('p'); //look for toc keywoard for (let i = 0; i < ps.length; i++) { - if (ps[i].textContent.trim() == '%%TOC%%') { - ps[i].parentNode.removeChild(ps[i]); - } + if (ps[i].textContent.trim() == '%%TOC%%') { + ps[i].parentNode.removeChild(ps[i]); + } } openedLists = []; listEopen = false; // get content and create html @@ -3134,19 +3157,19 @@ function onOAuthLoginClick() { } }; /** - * search for %%TOC%% inside document if found create toc -------------------------- - * @param target Element target container where toc should be created - */ + * search for %%TOC%% inside document if found create toc -------------------------- + * @param target Element target container where toc should be created + */ const detect_toc_flag = function(target) { if(target != null) { let ps = target.querySelectorAll('p'); let found = false; //look for toc keywoard for (let i = 0; i < ps.length; i++) { - if (ps[i].textContent.trim() == '%%TOC%%') { - found = ps[i]; - break; - } + if (ps[i].textContent.trim() == '%%TOC%%') { + found = ps[i]; + break; + } } if(found !== false) { //remove toc keywoard @@ -3156,10 +3179,101 @@ function onOAuthLoginClick() { } } }; + // wiki page toc + const wiki_page_toc = function(target, data) { + let rm; + if(target != null && data != null) { + //remove page toc + if( (rm = document.querySelector('.page-toc-wrapper')) != null ) { + rm.parentNode.removeChild(rm); + } + if (data.length > 0) { + //get base path + let dl = [], tl = []; + let l = ''; + let ll = 0; + for(let i = 0; i < data.length; i++){ + if (i == 0) { + ll = data[i].dataset.url.indexOf('wiki', 0) + 5; + l = data[i].dataset.url.substr(0, ll); + } + let o = {li: data[i].dataset.url.substr(ll), t: '', l: 0, s: false, p: '', al:true}; + o.l = (o.li.match(/(\/|%2F)/g) || []).length + 1; + let lisplit = o.li.split(/%2F|\//g); + o.t = lisplit.pop(); + o.p = lisplit.join('%2F'); + tl.push(o.li); + if (data[i].className.indexOf('selected') != -1) o.s = true; + //add missing objects without link + dl.push(o); + } + const sortArr = function(a, b){ + // Compare the 2 dates + if(a.li < b.li) return -1; + if(a.li > b.li) return 1; + return 0; + }; + dl.sort(sortArr); + // fill missing elements + for(let i = 0; i < dl.length; i++){ + if (dl[i].p != ''){ + let pathsplit = dl[i].p.split('%2F'); + for(let j = 0; j < pathsplit.length; j++){ + let p = pathsplit.slice(0, j + 1).join('%2F'); + if (p != '' && tl.indexOf(p) == -1){ + let o = {li: p, t: '', l: (p.match(/(\/|%2F)/g) || []).length + 1, s: false, p: '', al:false}; + let lisplit = o.li.split(/%2F|\//g); + o.t = lisplit.pop(); + tl.push(p); + //add missing objects without link + dl.push(o); + } + } + } + } + dl.sort(sortArr); + //create list + openedLists = []; listEopen = false; + // get content and create html + let html = ''; + let last_link = ''; + for(let i = 0; i < dl.length; i++){ + // create html + if(dl[i].t.length > 0 && dl[i].l >= 1) { + html += __page_list( dl[i].t, dl[i].l, (last_link !== l+dl[i].li)?(dl[i].al? l+dl[i].li : null): false, dl[i].s); + } else { + html += _closeList(0); + } + last_link = l+dl[i].li; + } + html += _closeList(0); + //create elements + let d = document.createElement('div'); + d.id = 'auto-page-toc'; + d.className = 'anchor-wrap'; + d.innerHTML = '

    '+((typeof(target.dataset.pagetoc) == 'string' && target.dataset.pagetoc != '')?target.dataset.pagetoc:'Table of Pages')+'

    '; + let d2 = document.createElement('div'); + d2.className = 'auto-toc-container'; + d2.innerHTML = html; + d2.insertBefore(d, d2.firstChild); + let c = document.createElement('div'); + c.className = 'page-toc-wrapper'; + c.appendChild(d2); + //inject page toc + target.insertBefore(c, target.firstChild); + if( (rm = document.querySelector('.auto-toc-clear')) != null ) { + rm.parentNode.removeChild(rm); + } + let a = document.createElement('div'); a.className = 'auto-toc-clear'; + target.appendChild(a); + } + } + }; // create toc ---------------------------------- addEventListener("load", function(){ create_toc_inside(document.querySelector('.file-view.markdown.auto-toc')); // md detect_toc_flag( document.querySelector('.file-view.markdown.auto-toc-by-flag')); // md by %%TOC%% flag create_toc_inside(document.querySelector('.segment.markdown.auto-toc')); // wiki pages + wiki_page_toc(document.querySelector('.segment.markdown.page-toc'), document.querySelectorAll('.wiki .choose.page .menu .item')); // wiki pages toc }); })(); diff --git a/public/less/_markdown.less b/public/less/_markdown.less index 5571433554920..5f0295f36ab4e 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -511,6 +511,35 @@ } } + .page-toc-wrapper { + position: relative; + padding: 0 20px 7px 0; + float: left; + background: #ffffff; + z-index: 1; + + @media only screen and (min-width:1760px) { + left: -31%; + width: 26%; + padding: 0; + margin-top: -3em !important; + margin-right: -26%; + } + + @media only screen and (max-width:1759px) { + display: none; + } + + a.selected { + font-weight: bold; + font-style: italic; + } + + span.error { + color: #ff2222; + } + } + .auto-toc-container { padding: 7px; border: 1px solid #d4d4d5; diff --git a/public/less/themes/arc-green.less b/public/less/themes/arc-green.less index ad28d4c186578..5c18227466467 100644 --- a/public/less/themes/arc-green.less +++ b/public/less/themes/arc-green.less @@ -504,6 +504,7 @@ a.ui.basic.green.label:hover { background-color: #474d61; } +.markdown:not(code) .page-toc-wrapper, .markdown:not(code) .auto-toc-wrapper { background: #353945; } From 9f1c89be4e4681df3eaa043adc7c1b3d429eeb27 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Fri, 14 Jun 2019 01:38:17 +0200 Subject: [PATCH 15/37] add directory structure to wiki pages Signed-off-by: Michael Gnehr --- models/wiki.go | 63 ++++++++++++++++++++++++++++++++++++++++++-- routers/repo/wiki.go | 33 +++++++++++++++++------ 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index 9ae3386333298..50898a2a71bd0 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -9,6 +9,7 @@ import ( "net/url" "os" "path/filepath" + "regexp" "strings" "code.gitea.io/gitea/modules/git" @@ -39,6 +40,16 @@ func WikiNameToFilename(name string) string { return url.QueryEscape(name) + ".md" } +// WikiNameToFilename converts a wiki name to its corresponding filename - keep direcory paths. +func WikiNameToPathFilename(name string) string { + // remove path up + re1 := regexp.MustCompile(`(\.\.\/)`) + name = re1.ReplaceAllString(name, "") + // trim whitespace and / + re2 := regexp.MustCompile(`(?m)^([\s\/]*)(([^\/]+|[^\/]+\/[^\/]+)*)([\s\/]*)$`) + return re2.ReplaceAllString(name, "$2") + ".md" +} + // WikiFilenameToName converts a wiki filename to its corresponding page name. func WikiFilenameToName(filename string) (string, error) { if !strings.HasSuffix(filename, ".md") { @@ -149,6 +160,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 +173,16 @@ 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} + } + } } else { oldWikiPath := WikiNameToFilename(oldWikiName) filesInIndex, err := gitRepo.LsFiles(oldWikiPath) @@ -181,6 +204,26 @@ 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 + } + } } // FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here @@ -191,7 +234,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 +334,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/routers/repo/wiki.go b/routers/repo/wiki.go index 43149c034061b..3fb29d90366fc 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -62,7 +62,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 } @@ -126,12 +126,11 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi } return nil, nil } - // Get page list. if isViewPage { - 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)) @@ -170,12 +169,20 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi pageFilename := models.WikiNameToFilename(pageName) var entry *git.TreeEntry + // old wiki pages are all inside git base directory if entry, err = findEntryForFile(commit, pageFilename); err != nil { ctx.ServerError("findEntryForFile", err) return nil, nil } else if entry == nil { - ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages") - return nil, nil + // lookup file inside directory structure + pageFilename = models.WikiNameToPathFilename(pageName) + if entry, err = findEntryForFile(commit, pageFilename); err != nil { + ctx.ServerError("findEntryForFile", err) + return nil, nil + } else if entry == nil { + ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages") + return nil, nil + } } data := wikiContentsByEntry(ctx, entry) if ctx.Written() { @@ -263,16 +270,26 @@ func WikiPages(ctx *context.Context) { return } - entries, err := commit.ListEntries() + entries, err := commit.Tree.ListEntriesRecursive() + fmt.Printf("%v", entries) 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) From e91b8c5e942a2d1fdde434d7fd1c5e1987be9f75 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Fri, 14 Jun 2019 15:35:00 +0200 Subject: [PATCH 16/37] rename configuration variables 'markdown' to 'markup' --- custom/conf/app.ini.sample | 14 +++++----- .../doc/advanced/config-cheat-sheet.en-us.md | 9 +++---- models/migrations/v88.go | 14 +++++----- models/repo.go | 8 +++--- modules/auth/repo_form.go | 4 +-- modules/setting/setting.go | 26 +++++++++---------- options/locale/locale_en-US.ini | 4 +-- routers/repo/setting.go | 8 +++--- routers/repo/view.go | 4 +-- templates/repo/settings/options.tmpl | 8 +++--- templates/repo/view_file.tmpl | 2 +- 11 files changed, 49 insertions(+), 52 deletions(-) diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index 372f362601f85..13d46b342fbc7 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -147,13 +147,13 @@ CUSTOM_URL_SCHEMES = FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd ; 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 markdown files (does not alter file), enabled: overwrites DEFAULT_TOC_MARKDOWN_BY_FLAG -DEFAULT_TOC_MARKDOWN_ALWAYS = false -; Create Table of Contents on renderes markdown files if line '%%TOC%%' is present -DEFAULT_TOC_MARKDOWN_BY_FLAG = true -; List of markup file extensions that TOC should be created on, set to not interfere with external renderers -; Separate the extensions with a comma. To ignore file extension check, just put a comma -TOC_MARKDOWN_FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd,.org +; 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 1d346d6ff8082..f9ae6afbc0e3f 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -109,12 +109,9 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. - `ENABLE_HARD_LINE_BREAK`: **false**: Enable Markdown's hard line break extension. - `DEFAULT_TOC_WIKI_FILE`: **true**: Create Table of Contents based on headlines on wiki page (does not alter file) -- `DEFAULT_TOC_MARKDOWN_ALWAYS`: **false** : Create Table of Contents on all rendered markdown files - (does not alter file), enabled: overwrites `TOC_MARKDOWN_BY_FLAG` -- `DEFAULT_TOC_MARKDOWN_BY_FLAG`: **true** : Create Table of Contents on renderes markdown files if - line '%%TOC%%' is present -- `TOC_MARKDOWN_FILE_EXTENSIONS`: **.md,.markdown,.mdown,.mkd,.org** : List of markup file extensions - that TOC should be created on, set to not interfere with external renderers. +- `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/v88.go b/models/migrations/v88.go index a7228d224a82d..52295efeb8979 100644 --- a/models/migrations/v88.go +++ b/models/migrations/v88.go @@ -13,9 +13,9 @@ import ( func addCanTocOnWikiAndMarkdown(x *xorm.Engine) error { type Repository struct { - TocWikiFile bool `xorm:"NOT NULL DEFAULT true"` - TocMarkdownAlways bool `xorm:"NOT NULL DEFAULT false"` - TocMarkdownByFlag 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"` } if err := x.Sync2(new(Repository)); err != nil { @@ -27,13 +27,13 @@ func addCanTocOnWikiAndMarkdown(x *xorm.Engine) error { return err } - if _, err := x.Exec("UPDATE repository SET toc_markdown_always = ?", - setting.Markdown.DefaultTocMarkdownAlways); err != nil { + 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_markdown_by_flag = ?", - setting.Markdown.DefaultTocMarkdownByFlag); err != nil { + if _, err := x.Exec("UPDATE repository SET toc_markup_by_flag = ?", + setting.Markdown.DefaultTocMarkupByFlag); err != nil { return err } return nil diff --git a/models/repo.go b/models/repo.go index ed21931587d90..8008be1ff344f 100644 --- a/models/repo.go +++ b/models/repo.go @@ -170,8 +170,8 @@ type Repository struct { IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"` CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"` TocWikiFile bool `xorm:"NOT NULL DEFAULT true"` - TocMarkdownAlways bool `xorm:"NOT NULL DEFAULT false"` - TocMarkdownByFlag 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 @@ -1368,8 +1368,8 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err IsFsckEnabled: !opts.IsMirror, CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, TocWikiFile: setting.Markdown.DefaultTocWikiFile, - TocMarkdownAlways: setting.Markdown.DefaultTocMarkdownAlways, - TocMarkdownByFlag: setting.Markdown.DefaultTocMarkdownByFlag, + TocMarkupAlways: setting.Markdown.DefaultTocMarkupAlways, + TocMarkupByFlag: setting.Markdown.DefaultTocMarkupByFlag, } sess := x.NewSession() diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index f9302c2b24b08..0d45e267e7a6c 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -126,8 +126,8 @@ type RepoSettingForm struct { EnableIssueDependencies bool IsArchived bool TocWikiFile bool - TocMarkdownAlways bool - TocMarkdownByFlag bool + TocMarkupAlways bool + TocMarkupByFlag bool // Admin settings EnableHealthCheck bool diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 44b66200a3a0d..b19ee2e4e22c5 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -236,20 +236,20 @@ var ( // Markdown settings Markdown = struct { - EnableHardLineBreak bool - CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` - FileExtensions []string - DefaultTocWikiFile bool - DefaultTocMarkdownAlways bool - DefaultTocMarkdownByFlag bool - TocMarkdownFileExtensions []string + EnableHardLineBreak bool + CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` + FileExtensions []string + DefaultTocWikiFile bool + DefaultTocMarkupAlways bool + DefaultTocMarkupByFlag bool + TocMarkupFileExtensions []string }{ - EnableHardLineBreak: false, - FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), - DefaultTocWikiFile: true, - DefaultTocMarkdownAlways: false, - DefaultTocMarkdownByFlag: true, - TocMarkdownFileExtensions: strings.Split(".md,.markdown,.mdown,.mkd,.org", ","), + EnableHardLineBreak: false, + FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), + 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 f398228ad3acd..1114ffc348298 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1151,8 +1151,8 @@ 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.toc_wiki_file = Enable autogenerated TOC on wikipages (based on headlines) (does not alter files) -settings.toc.toc_markdown_always = Enable autogenerated TOC on all markdown files (based on headlines) (does not alter files) -settings.toc.toc_markdown_by_flag = Enable autogenerated TOC on markdown files which contain '%%TOC%%' (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/routers/repo/setting.go b/routers/repo/setting.go index fadf1b9211400..8a2157059774a 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -299,12 +299,12 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { repo.TocWikiFile = form.TocWikiFile } - if repo.TocMarkdownAlways != form.TocMarkdownAlways { - repo.TocMarkdownAlways = form.TocMarkdownAlways + if repo.TocMarkupAlways != form.TocMarkupAlways { + repo.TocMarkupAlways = form.TocMarkupAlways } - if repo.TocMarkdownByFlag != form.TocMarkdownByFlag { - repo.TocMarkdownByFlag = form.TocMarkdownByFlag + if repo.TocMarkupByFlag != form.TocMarkupByFlag { + repo.TocMarkupByFlag = form.TocMarkupByFlag } if err := models.UpdateRepository(repo, false); err != nil { diff --git a/routers/repo/view.go b/routers/repo/view.go index 4b17375a7c698..b75ce5d3dec1e 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -165,7 +165,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { if markup.Type(readmeFile.Name()) != "" { // Check if extension matches TOC file list - tocExts := setting.Markdown.TocMarkdownFileExtensions + tocExts := setting.Markdown.TocMarkupFileExtensions isTocMarkup := false if len(tocExts) == 0 || len(tocExts) == 1 && tocExts[0] == "" || len(tocExts) == 2 && tocExts[0] == "" && tocExts[1] == "" { isTocMarkup = true @@ -302,7 +302,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["ReadmeExist"] = readmeExist if markup.Type(blob.Name()) != "" { // Check if extension matches TOC file list - tocExts := setting.Markdown.TocMarkdownFileExtensions + tocExts := setting.Markdown.TocMarkupFileExtensions isTocMarkup := false if len(tocExts) == 0 || len(tocExts) == 1 && tocExts[0] == "" || len(tocExts) == 2 && tocExts[0] == "" && tocExts[1] == "" { isTocMarkup = true diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index dd6fc4b9e074f..d941ce3814792 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -275,14 +275,14 @@
    - - + +
    - - + +
    diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index d89f9e4cdc236..db4e8824e125c 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -45,7 +45,7 @@
    -
    +
    {{if .IsMarkup}} {{if .FileContent}}{{.FileContent | Safe}}{{end}} {{else if .IsRenderedHTML}} From 03fb9d8837d6b221b8bdda5318bddbc101f908f6 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 16 Jun 2019 16:08:17 +0200 Subject: [PATCH 17/37] change wiki 'delete' route to '_delete' like other wiki routes + add reseverd keyword for delete route Signed-off-by: Michael Gnehr --- models/wiki.go | 2 +- routers/repo/wiki_test.go | 2 +- routers/routes/routes.go | 2 +- templates/repo/wiki/view.tmpl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index 50898a2a71bd0..d7820329f3d5c 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -20,7 +20,7 @@ import ( ) var ( - reservedWikiNames = []string{"_pages", "_new", "_edit", "raw"} + reservedWikiNames = []string{"_pages", "_new", "_edit", "_delete", "raw"} wikiWorkingPool = sync.NewExclusivePool() ) diff --git a/routers/repo/wiki_test.go b/routers/repo/wiki_test.go index 4687d24f6b28d..6e82bc0365fe5 100644 --- a/routers/repo/wiki_test.go +++ b/routers/repo/wiki_test.go @@ -179,7 +179,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 744088a9d7b1e..ceac67a02b66e 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -810,7 +810,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 b5d16ad013411..2b8dcdff038cc 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -67,7 +67,7 @@ {{end}}
    From 138cc94e089f6fe00305c7a70c98cb2e4d5b06ba Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 16 Jun 2019 16:20:31 +0200 Subject: [PATCH 18/37] add abort button to wiki `_edit/_new` Signed-off-by: Michael Gnehr --- options/locale/locale_en-US.ini | 1 + templates/repo/wiki/new.tmpl | 1 + 2 files changed, 2 insertions(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 01e239cee963a..cb969fc8d58f9 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1028,6 +1028,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? diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index bf6c24220bdde..7fdcdb87959ce 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -23,6 +23,7 @@
    + {{.i18n.Tr "repo.wiki.abort_edit_page_button"}} From 3779af69a730290947e396594406aac1b4895876 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 16 Jun 2019 16:28:15 +0200 Subject: [PATCH 19/37] fix relative image paths on wiki Signed-off-by: Michael Gnehr --- models/wiki.go | 9 +++++++++ modules/markup/markdown/markdown.go | 3 --- routers/repo/wiki.go | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index d7820329f3d5c..5e339ab0978bb 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -15,6 +15,7 @@ import ( "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" ) @@ -50,6 +51,14 @@ func WikiNameToPathFilename(name string) string { return re2.ReplaceAllString(name, "$2") + ".md" } +// 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) { if !strings.HasSuffix(filename, ".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/routers/repo/wiki.go b/routers/repo/wiki.go index 3fb29d90366fc..f8158dda19d91 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -201,7 +201,7 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi } 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"] = sidebarPresent ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas) ctx.Data["footerPresent"] = footerPresent From 9d50e7cc937550df7058e57a54da40909b5c35db Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 16 Jun 2019 17:14:53 +0200 Subject: [PATCH 20/37] add path ':page' to '_new' wiki page route this will create a new page on same parent directory as currently shown page Signed-off-by: Michael Gnehr --- routers/repo/wiki.go | 7 +++++++ routers/routes/routes.go | 1 + templates/repo/wiki/new.tmpl | 2 +- templates/repo/wiki/view.tmpl | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index f8158dda19d91..ebe77a2af7e9f 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -367,6 +367,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 := models.NormalizeWikiName(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) diff --git a/routers/routes/routes.go b/routers/routes/routes.go index ceac67a02b66e..afd53a361836d 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -808,6 +808,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 7fdcdb87959ce..d5d76523e5c9b 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -7,7 +7,7 @@ {{.i18n.Tr "repo.wiki.new_page"}} {{if .PageIsWikiEdit}} {{end}}
    diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index 2b8dcdff038cc..5d3de135639c0 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -66,7 +66,7 @@ {{if and .CanWriteWiki (not .Repository.IsMirror)}} {{end}} From ce56067ad37f9b5194bc9e423e3b78cf5681fd86 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 16 Jun 2019 21:53:02 +0200 Subject: [PATCH 21/37] FIX bug mentiond in js/index.js:1138 wiki render request was triggered when return back to edit mode Signed-off-by: Michael Gnehr --- public/js/index.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/public/js/index.js b/public/js/index.js index f7f52d9de469d..47baaa8d9ba65 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1136,18 +1136,20 @@ 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 = '
    ' + data + '
    '; - emojify.run($('.editor-preview')[0]); - } - ); + let $toolbar = $(preview).closest('.CodeMirror-wrap').prev(); + if ($toolbar.hasClass('disabled-for-preview')) { + $.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); return "Loading..."; From 0f4d6ed365b66449212b0a706bb2a096eea0eb6c Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 16 Jun 2019 21:54:13 +0200 Subject: [PATCH 22/37] fix broken image links on wiki edit page Signed-off-by: Michael Gnehr --- public/js/index.js | 3 ++- templates/repo/wiki/new.tmpl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/js/index.js b/public/js/index.js index 47baaa8d9ba65..906ab84931fb9 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1141,7 +1141,8 @@ function initWikiForm() { $.post($editArea.data('url'), { "_csrf": csrf, "mode": "gfm", - "context": $editArea.data('context'), + "wiki": true, + "context": decodeURIComponent($editArea.data('context')), "text": plainText }, function (data) { diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index d5d76523e5c9b..3e7dff9c3e401 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -17,7 +17,7 @@
    - +
    From d9883dec5cf27af0009a92fd01e541b4ec76d322 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Mon, 17 Jun 2019 00:33:08 +0200 Subject: [PATCH 23/37] add wiki page toc for normal/small and mobile screens + add ui elements, script and css + automatic resize (js) content cotainer, if toc size is larger than rendered markup Signed-off-by: Michael Gnehr --- public/css/index.css | 21 +++++--- public/css/theme-arc-green.css | 1 + public/js/index.js | 30 +++++++++-- public/less/_markdown.less | 85 ++++++++++++++++++++++++------- public/less/themes/arc-green.less | 5 ++ 5 files changed, 111 insertions(+), 31 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index 76e02295e7534..6aab92c513a8f 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -290,16 +290,23 @@ footer .ui.left,footer .ui.right{line-height:40px} .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%} } -.markdown:not(code) .page-toc-wrapper{position:relative;padding:0 20px 7px 0;float:left;background:#fff;z-index:1} -@media only screen and (min-width:1760px){.markdown:not(code) .page-toc-wrapper{left:-31%;width:26%;padding:0;margin-top:-3em!important;margin-right:-26%} -} -@media only screen and (max-width:1759px){.markdown:not(code) .page-toc-wrapper{display:none} -} -.markdown:not(code) .page-toc-wrapper a.selected{font-weight:700;font-style:italic} -.markdown:not(code) .page-toc-wrapper span.error{color:#f22} .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{font-weight:700;font-style:italic} +.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 ee6fb7f965d53..ecb5163f66336 100644 --- a/public/css/theme-arc-green.css +++ b/public/css/theme-arc-green.css @@ -95,6 +95,7 @@ footer{background:#2e323e;border-top:1px solid #313131} .markdown:not(code) table tr:nth-child(2n){background-color:#474d61} .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 906ab84931fb9..0bb73bd82eade 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3254,7 +3254,7 @@ function onOAuthLoginClick() { let d = document.createElement('div'); d.id = 'auto-page-toc'; d.className = 'anchor-wrap'; - d.innerHTML = '

    '+((typeof(target.dataset.pagetoc) == 'string' && target.dataset.pagetoc != '')?target.dataset.pagetoc:'Table of Pages')+'

    '; + d.innerHTML = '

    '+((typeof(target.dataset.pagetoc) == 'string' && target.dataset.pagetoc != '')?target.dataset.pagetoc:'Table of Pages')+'

    '; let d2 = document.createElement('div'); d2.className = 'auto-toc-container'; d2.innerHTML = html; @@ -3264,11 +3264,31 @@ function onOAuthLoginClick() { c.appendChild(d2); //inject page toc target.insertBefore(c, target.firstChild); - if( (rm = document.querySelector('.auto-toc-clear')) != null ) { - rm.parentNode.removeChild(rm); + if( (rm = document.querySelector('.auto-toc-clear')) == null ) { + let a = document.createElement('div'); a.className = 'auto-toc-clear'; + target.appendChild(a); + } + // small screen toggle + if( (rm = document.querySelector('.page-toc-label')) == null ) { + let a = document.createElement('label'); + a.className = 'page-toc-wiki-label ui basic button'; a.htmlFor = 'page-toc-wiki-cb'; + a.title = ((typeof(target.dataset.pagetoc) == 'string' && target.dataset.pagetoc != '')?target.dataset.pagetoc:'Table of Pages'); + a.innerHTML = ''; + let ltarget = document.querySelector('.repository.wiki .ui.container .ui.dividing.header .ui.stackable.grid').firstElementChild; + ltarget.insertBefore(a, ltarget.firstChild); + let cb = document.createElement('input'); + cb.type = 'checkbox'; cb.id = 'page-toc-wiki-cb'; cb.hidden = true; + target.insertBefore(cb, target.firstChild); + // may update height if toc size > container + cb.addEventListener("click", function(){ + if (cb.checked && target.offsetHeight < c.offsetHeight) { + target.style.minHeight = c.offsetHeight + 'px'; + } else { + target.style.removeProperty('min-height'); + } + return false; + }); } - let a = document.createElement('div'); a.className = 'auto-toc-clear'; - target.appendChild(a); } } }; diff --git a/public/less/_markdown.less b/public/less/_markdown.less index 5f0295f36ab4e..6771577ddacb1 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -511,14 +511,31 @@ } } + .auto-toc-container { + padding: 7px; + border: 1px solid #d4d4d5; + border-radius: 5px; + + h2 { + padding: 0.3em; + font-size: 1.65em; + } + } + + .auto-toc-clear { + clear: both; + margin-bottom: -20px !important; + } + .page-toc-wrapper { - position: relative; - padding: 0 20px 7px 0; - float: left; - background: #ffffff; z-index: 1; + background: #ffffff; + position: relative; + display: none; @media only screen and (min-width:1760px) { + display: block; + float: left; left: -31%; width: 26%; padding: 0; @@ -526,10 +543,6 @@ margin-right: -26%; } - @media only screen and (max-width:1759px) { - display: none; - } - a.selected { font-weight: bold; font-style: italic; @@ -538,21 +551,55 @@ span.error { color: #ff2222; } + + .page-toc-close { + display: none; + } } - .auto-toc-container { - padding: 7px; - border: 1px solid #d4d4d5; - border-radius: 5px; + #page-toc-wiki-cb { + display: none; + } - h2 { - padding: 0.3em; - font-size: 1.65em; + @media only screen and (max-width:1759px) { + #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: #ffffff; + margin: -3em -3em 3em !important; + z-index: 5; } - } - .auto-toc-clear { - clear: both; - margin-bottom: -20px !important; + .page-toc-wrapper .page-toc-close { + display: inline-block; + float: right; + margin-top: -0.6em !important; + margin-right: -0.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) { + display: block; + } + + i { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } +} diff --git a/public/less/themes/arc-green.less b/public/less/themes/arc-green.less index 5c18227466467..b5acc78e93d9f 100644 --- a/public/less/themes/arc-green.less +++ b/public/less/themes/arc-green.less @@ -514,6 +514,11 @@ a.ui.basic.green.label:hover { border-color: #404552; } +.markdown:not(code) #page-toc-wiki-cb:checked + .page-toc-wrapper { + border-color: #d4d4d5; + background: #383c4a; +} + .ui.dropdown .menu { background: #2c303a; } From 8dd08af927fcc5c0d7e8957ecc07fbdb3089954e Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Mon, 17 Jun 2019 02:04:20 +0200 Subject: [PATCH 24/37] set fixed width on small mobile screens Signed-off-by: Michael Gnehr --- public/css/index.css | 2 ++ public/less/_markdown.less | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/public/css/index.css b/public/css/index.css index e0104fecaea1a..1e46472aa6a70 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -290,6 +290,8 @@ footer .ui.left,footer .ui.right{line-height:40px} .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} diff --git a/public/less/_markdown.less b/public/less/_markdown.less index 5571433554920..f824d2e17f869 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -509,6 +509,14 @@ margin-top: -3em !important; margin-left: -26%; } + + @media only screen and (max-width:479px) { + float: none; + width: 100%; + padding: 0; + margin-bottom: 1em; + } + } .auto-toc-container { From 3a2936a27deeb8d595d6bf4e482ad67f3f87679c Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Mon, 17 Jun 2019 02:29:50 +0200 Subject: [PATCH 25/37] fix documentation style (make revive failed) Signed-off-by: Michael Gnehr --- models/wiki.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index 5e339ab0978bb..7a673990de639 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -41,7 +41,7 @@ func WikiNameToFilename(name string) string { return url.QueryEscape(name) + ".md" } -// WikiNameToFilename converts a wiki name to its corresponding filename - keep direcory paths. +// WikiNameToPathFilename converts a wiki name to its corresponding filename, keep directory paths. func WikiNameToPathFilename(name string) string { // remove path up re1 := regexp.MustCompile(`(\.\.\/)`) @@ -51,8 +51,7 @@ func WikiNameToPathFilename(name string) string { return re2.ReplaceAllString(name, "$2") + ".md" } -// Get raw file path inside wiki -// removes last path element and returns +// 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] From aadb11a0ecad6b33d83ff0ffe732c871de6aef7f Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Mon, 17 Jun 2019 02:29:50 +0200 Subject: [PATCH 26/37] fix documentation style and tests (make revive failed) (make unit-test-coverage failed) Signed-off-by: Michael Gnehr From 96d5ab6005ae1e8e6336c9519090c4d67edfb30a Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Mon, 17 Jun 2019 22:31:58 +0200 Subject: [PATCH 27/37] support all letters inside directory Now the render function look up files by exact .md filename match. So its possible to always access misspelled wikipages, which may be pushed via git. The fallback for old plain file structure is only kept (and used) to support old links inside existing wikipages. So there is no priority on rendering old or new structure, as it is decided by link. The new/edit function now always renames files to gitea wiki structure, if a file is saved. This keep mostly all letters, as it is possible by gitrepo too. As NormalizeWikiName (exchanges spaces with '-') only breaks file access, this is removed from rendering. It is now done inside model. Signed-off-by: Michael Gnehr --- models/wiki.go | 70 ++++++++++++++++++++++++++++++++++---------- routers/repo/wiki.go | 35 +++++++++++++--------- 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index 5e339ab0978bb..d281d3506678e 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -30,9 +30,18 @@ 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 / + re2 := regexp.MustCompile(`(?m)^([\s\/]*)(([^\/]+|[^\/]+\/[^\/]+)*)([\s\/]*)$`) + name = re2.ReplaceAllString(name, "$2") + 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. @@ -41,18 +50,37 @@ func WikiNameToFilename(name string) string { return url.QueryEscape(name) + ".md" } -// WikiNameToFilename converts a wiki name to its corresponding filename - keep direcory paths. +// WikiNameToPathFilename converts a wiki name to its corresponding filename, keep directory paths. func WikiNameToPathFilename(name string) string { - // remove path up - re1 := regexp.MustCompile(`(\.\.\/)`) - name = re1.ReplaceAllString(name, "") - // trim whitespace and / - re2 := regexp.MustCompile(`(?m)^([\s\/]*)(([^\/]+|[^\/]+\/[^\/]+)*)([\s\/]*)$`) - return re2.ReplaceAllString(name, "$2") + ".md" + 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 / . + name = strings.TrimPrefix(name, ".git") // trim prefix .git + 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 } -// Get raw file path inside wiki -// removes last path element and returns +// 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] @@ -60,16 +88,16 @@ func WikiNameToRawPrefix(repositoryName string, wikiPage string) string { } // 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 "","", err } - return NormalizeWikiName(unescaped), nil + return unescaped, basename, nil } // WikiCloneLink returns clone URLs of repository wiki. @@ -192,6 +220,16 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con 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) @@ -235,6 +273,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)) diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index ebe77a2af7e9f..a99ee6fdf0e09 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -138,25 +138,25 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi 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" { + } else if wikiUnescapedName == "_Sidebar" || wikiUnescapedName == "_Footer" { continue } pages = append(pages, PageMeta{ - Name: wikiName, + Name: wikiUnescapedName, SubURL: models.WikiNameToSubURL(wikiName), }) } ctx.Data["Pages"] = pages } - pageName := models.NormalizeWikiName(ctx.Params(":page")) + pageName := ctx.Params(":page") if len(pageName) == 0 { pageName = "Home" } @@ -167,15 +167,16 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi ctx.Data["title"] = pageName ctx.Data["RequireHighlightJS"] = true - pageFilename := models.WikiNameToFilename(pageName) + // check new file structure with directories + pageFilename := models.WikiNameToPathFilename(pageName) var entry *git.TreeEntry // old wiki pages are all inside git base directory if entry, err = findEntryForFile(commit, pageFilename); err != nil { ctx.ServerError("findEntryForFile", err) return nil, nil } else if entry == nil { - // lookup file inside directory structure - pageFilename = models.WikiNameToPathFilename(pageName) + // check old file structure without directories + pageFilename = models.WikiNameToFilename(pageName) if entry, err = findEntryForFile(commit, pageFilename); err != nil { ctx.ServerError("findEntryForFile", err) return nil, nil @@ -295,7 +296,7 @@ LoopPages: 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 @@ -304,7 +305,7 @@ LoopPages: return } pages = append(pages, PageMeta{ - Name: wikiName, + Name: wikiUnescapedName, SubURL: models.WikiNameToSubURL(wikiName), UpdatedUnix: util.TimeStamp(c.Author.When.Unix()), }) @@ -369,7 +370,7 @@ func NewWiki(ctx *context.Context) { ctx.Data["title"] = "Home" } else if len(ctx.Params(":page")) != 0 { // create files on same subdiretory like current file - wikiName := models.NormalizeWikiName(ctx.Params(":page")) + wikiName := ctx.Params(":page") // remove current filename a := strings.Split(wikiName, "/") a[len(a)-1] = "New Page" @@ -395,7 +396,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 @@ -442,20 +443,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" } From 8f90a0d08635afbf9bfb16f630440535e5c20082 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Mon, 17 Jun 2019 22:32:44 +0200 Subject: [PATCH 28/37] fix broken new/edit form url Signed-off-by: Michael Gnehr --- templates/repo/wiki/new.tmpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index 3e7dff9c3e401..2b8aa477208e7 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -11,7 +11,11 @@
    {{end}} + {{if .PageIsWikiEdit}}
    + {{else}} + + {{end}} {{.CsrfTokenHtml}}
    From e315aa4d25d6d8b7d8bd46193059564c9ba2495a Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Mon, 17 Jun 2019 22:33:35 +0200 Subject: [PATCH 29/37] update css + js for toc generation to handle new link styles Signed-off-by: Michael Gnehr --- public/css/index.css | 4 +-- public/js/index.js | 65 +++++++++++++++++++++++++++----------- public/less/_markdown.less | 4 ++- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index a5e502553024e..6d473773f147e 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -298,8 +298,8 @@ footer .ui.left,footer .ui.right{line-height:40px} .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{font-weight:700;font-style:italic} -.markdown:not(code) .page-toc-wrapper span.error{color:#f22} +.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} diff --git a/public/js/index.js b/public/js/index.js index 0bb73bd82eade..d6e667b701c17 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3081,23 +3081,31 @@ function onOAuthLoginClick() { }; // handle wiki page list element // create valid html list - const __page_list = function (line, level, link, selected) { + const __page_list = function (line, level, link, selected, error) { let out = ''; let diff = level - openedLists.length; if(diff > 0) { //open new level out += _openList(); - out += __page_list(line,level, link, selected); + out += __page_list(line,level, link, selected, error); } else if(diff < 0 ) { out += _closeList(-diff); - out += __page_list(line, level, link, selected); + out += __page_list(line, level, link, selected, error); } else { // only add list element - if (link === null){ - out += ((listEopen)?'
  • ':'') + '
  • ' + line + ''; - } else if (link === false) { - out += ((listEopen)?'
  • ':'') + '
  • ' + line + ''; - } else { - out += ((listEopen)?'
  • ':'') + '
  • ' + line + ''; + let t = (link === null)? 'span' : 'a'; + let ref = (link === null)? '':' href="' + link + '"'; + let cl = '' + (error === true? ' error': '') + (selected?' selected':''); + let l = line; + try{ + l = decodeURIComponent(l); + try { + l = decodeURIComponent(l); + } catch (e) { + l = decodeURIComponent(line); + } + } catch (e) { + l = line; } + out += ((listEopen)?'
  • ':'') + '
  • <'+t+' class="'+cl+'"'+ref+'>' + escapeHtml(l) + ''; listEopen = true; } return out; @@ -3182,6 +3190,17 @@ function onOAuthLoginClick() { } } }; + const escapeHtml = function (text) { + var map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.replace(/[&<>"']/g, function(m) { return map[m]; }); + } + // wiki page toc // wiki page toc const wiki_page_toc = function(target, data) { let rm; @@ -3197,12 +3216,12 @@ function onOAuthLoginClick() { let ll = 0; for(let i = 0; i < data.length; i++){ if (i == 0) { - ll = data[i].dataset.url.indexOf('wiki', 0) + 5; + ll = data[i].dataset.url.indexOf('/wiki/', 0) + 6; l = data[i].dataset.url.substr(0, ll); } let o = {li: data[i].dataset.url.substr(ll), t: '', l: 0, s: false, p: '', al:true}; - o.l = (o.li.match(/(\/|%2F)/g) || []).length + 1; - let lisplit = o.li.split(/%2F|\//g); + o.l = (o.li.match(/(\/|%2F|%252F)/g) || []).length + 1; + let lisplit = o.li.split(/%252F|%2F|\//g); o.t = lisplit.pop(); o.p = lisplit.join('%2F'); tl.push(o.li); @@ -3210,10 +3229,20 @@ function onOAuthLoginClick() { //add missing objects without link dl.push(o); } + // compare paths const sortArr = function(a, b){ - // Compare the 2 dates - if(a.li < b.li) return -1; - if(a.li > b.li) return 1; + let _a = decodeURIComponent(a.li).toLowerCase().split('/'); + let _b = decodeURIComponent(b.li).toLowerCase().split('/'); + // split and compare path components + for (let i = 0; i < Math.max(_a.length, _b.length); i++ ) { + // end if one path is shorter + if (i >= _a.length) return -1; + if (i >= _b.length) return 1; + // compare string components + if(_a[i] < _b[i]) return -1; + if(_a[i] > _b[i]) return 1; + } + // equals return 0; }; dl.sort(sortArr); @@ -3224,8 +3253,8 @@ function onOAuthLoginClick() { for(let j = 0; j < pathsplit.length; j++){ let p = pathsplit.slice(0, j + 1).join('%2F'); if (p != '' && tl.indexOf(p) == -1){ - let o = {li: p, t: '', l: (p.match(/(\/|%2F)/g) || []).length + 1, s: false, p: '', al:false}; - let lisplit = o.li.split(/%2F|\//g); + let o = {li: p, t: '', l: (p.match(/(\/|%2F|%252F)/g) || []).length + 1, s: false, p: '', al:false}; + let lisplit = o.li.split(/%252F|%2F|\//g); o.t = lisplit.pop(); tl.push(p); //add missing objects without link @@ -3243,7 +3272,7 @@ function onOAuthLoginClick() { for(let i = 0; i < dl.length; i++){ // create html if(dl[i].t.length > 0 && dl[i].l >= 1) { - html += __page_list( dl[i].t, dl[i].l, (last_link !== l+dl[i].li)?(dl[i].al? l+dl[i].li : null): false, dl[i].s); + html += __page_list( dl[i].t, dl[i].l, (dl[i].al? l+dl[i].li : null), dl[i].s, (last_link !== l+dl[i].li && decodeURIComponent(last_link) !== l+dl[i].li && last_link !== decodeURIComponent(l+dl[i].li))? false : true ); } else { html += _closeList(0); } diff --git a/public/less/_markdown.less b/public/less/_markdown.less index cccf9d041eeb0..4e91a09ff495d 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -551,11 +551,13 @@ margin-right: -26%; } - a.selected { + a.selected, + span.selected { font-weight: bold; font-style: italic; } + a.error, span.error { color: #ff2222; } From 3e1092a358d3fedbb8e9c7f16d988348d03f2bcb Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 18 Jun 2019 01:28:02 +0200 Subject: [PATCH 30/37] go fmt Signed-off-by: Michael Gnehr --- models/wiki.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index d281d3506678e..f1fff8523a132 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -52,7 +52,7 @@ func WikiNameToFilename(name string) string { // WikiNameToPathFilename converts a wiki name to its corresponding filename, keep directory paths. func WikiNameToPathFilename(name string) string { - var restore = [1][2] string{ + var restore = [1][2]string{ {`(\.\.\/)`, ""}, // remove path up } for _, kv := range restore { @@ -67,11 +67,11 @@ func WikiNameToPathFilename(name string) string { // 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 % + 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]) @@ -90,7 +90,7 @@ func WikiNameToRawPrefix(repositoryName string, wikiPage string) string { // WikiFilenameToName converts a wiki filename to its corresponding page name. 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) From 048ee8f59b8116360bfa6252b9af9729a6bda18d Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 18 Jun 2019 01:28:51 +0200 Subject: [PATCH 31/37] replace some regex with go trim functions Signed-off-by: Michael Gnehr --- models/wiki.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index f1fff8523a132..2498ead5240c3 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -36,8 +36,7 @@ func WikiNameToSubURL(name string) string { re1 := regexp.MustCompile(`(\.\.\/)`) name = re1.ReplaceAllString(name, "") // trim whitespace and / - re2 := regexp.MustCompile(`(?m)^([\s\/]*)(([^\/]+|[^\/]+\/[^\/]+)*)([\s\/]*)$`) - name = re2.ReplaceAllString(name, "$2") + name = strings.Trim(name, "\n\r\t /") name = url.QueryEscape(name) //restore spaces re3 := regexp.MustCompile(`(?m)(%20|\+)`) @@ -60,7 +59,6 @@ func WikiNameToPathFilename(name string) string { name = loopRe.ReplaceAllString(name, kv[1]) } name = strings.Trim(name, "\n\r\t ./") // trim whitespace and / . - name = strings.TrimPrefix(name, ".git") // trim prefix .git return name + ".md" } From c3d89b0955e14316ecb971721332f967257dd22e Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 18 Jun 2019 01:29:32 +0200 Subject: [PATCH 32/37] fix internal server error if wiki contains badescaping%% Signed-off-by: Michael Gnehr --- models/wiki.go | 2 +- routers/repo/wiki.go | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/models/wiki.go b/models/wiki.go index 2498ead5240c3..800bea8b73c72 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -93,7 +93,7 @@ func WikiFilenameToName(filename string) (string, string, error) { basename := filename[:len(filename)-3] unescaped, err := url.QueryUnescape(basename) if err != nil { - return "","", err + return basename, basename, err } return unescaped, basename, nil } diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index a99ee6fdf0e09..026dce17ef004 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" @@ -143,8 +144,10 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi if models.IsErrWikiInvalidFileName(err) { continue } - ctx.ServerError("WikiFilenameToName", err) - return nil, nil + if _, ok := err.(url.EscapeError); !ok { + ctx.ServerError("WikiFilenameToName", err) + return nil, nil + } } else if wikiUnescapedName == "_Sidebar" || wikiUnescapedName == "_Footer" { continue } @@ -301,8 +304,10 @@ LoopPages: 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: wikiUnescapedName, From 90de3d684e5ccea46cbf7a536c79901c4c1caf8f Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Tue, 18 Jun 2019 01:42:04 +0200 Subject: [PATCH 33/37] add missing tests for model/wiki and routers/repo/wiki and fix existing ones, broken by with this PR Signed-off-by: Michael Gnehr --- models/wiki_test.go | 100 ++++++++++++++++++++++++++++++-------- routers/repo/wiki_test.go | 9 ++-- 2 files changed, 87 insertions(+), 22 deletions(-) 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/routers/repo/wiki_test.go b/routers/repo/wiki_test.go index 6e82bc0365fe5..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) { From 22b82ffb43c8a2e84d76ad97f3a6ef1ec6ae5879 Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Fri, 28 Jun 2019 00:40:18 +0200 Subject: [PATCH 34/37] fix css lint errors Signed-off-by: Michael Gnehr --- public/less/_markdown.less | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/less/_markdown.less b/public/less/_markdown.less index 4cf378d1d418a..3f5920bc6d901 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -499,7 +499,8 @@ float: right; background: #ffffff; z-index: 1; - @media only screen and (min-width:1760px) { + + @media only screen and (min-width: 1760px) { right: -31%; width: 26%; padding: 0; @@ -507,7 +508,7 @@ margin-left: -26%; } - @media only screen and (max-width:479px) { + @media only screen and (max-width: 479px) { float: none; width: 100%; padding: 0; From 0607c0a2892e9f06f6b44bc2958833356e20d16e Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 7 Jul 2019 00:49:22 +0200 Subject: [PATCH 35/37] rename migration file Signed-off-by: Michael Gnehr --- models/migrations/{v88.go => v89.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename models/migrations/{v88.go => v89.go} (100%) diff --git a/models/migrations/v88.go b/models/migrations/v89.go similarity index 100% rename from models/migrations/v88.go rename to models/migrations/v89.go From ca0ebe1fd25380dd77e4b39507abd1ef5506e3de Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 7 Jul 2019 00:54:31 +0200 Subject: [PATCH 36/37] rename migration file Signed-off-by: Michael Gnehr --- models/migrations/{v89.go => v90.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename models/migrations/{v89.go => v90.go} (100%) diff --git a/models/migrations/v89.go b/models/migrations/v90.go similarity index 100% rename from models/migrations/v89.go rename to models/migrations/v90.go From 83e2f6eb0b46f82a61de73784cdde337fcbab53a Mon Sep 17 00:00:00 2001 From: Michael Gnehr Date: Sun, 7 Jul 2019 00:56:06 +0200 Subject: [PATCH 37/37] fix migration number comment Signed-off-by: Michael Gnehr --- models/migrations/migrations.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index fa5455bbbb45f..1fefa176b9c02 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -234,7 +234,7 @@ var migrations = []Migration{ NewMigration("add commit status context field to commit_status", addCommitStatusContext), // v89 -> v90 NewMigration("add toc on wiki and markedown", addCanTocOnWikiAndMarkdown), - // v89 -> v90 + // v90 -> v91 NewMigration("add pagetoc to wiki", addCanWikiPageToc), }