diff options
-rw-r--r-- | options/locale/locale_en-US.ini | 3 | ||||
-rw-r--r-- | public/css/index.css | 5 | ||||
-rw-r--r-- | public/less/_markdown.less | 32 | ||||
-rw-r--r-- | routers/repo/wiki.go | 252 | ||||
-rw-r--r-- | routers/routes/routes.go | 1 | ||||
-rw-r--r-- | templates/repo/wiki/revision.tmpl | 104 | ||||
-rw-r--r-- | templates/repo/wiki/view.tmpl | 1 |
7 files changed, 334 insertions, 64 deletions
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0c83a7aef1..6ef1277c62 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1034,6 +1034,9 @@ wiki.save_page = Save Page wiki.last_commit_info = %s edited this page %s wiki.edit_page_button = Edit wiki.new_page_button = New Page +wiki.file_revision = Page Revision +wiki.wiki_page_revisions = Wiki Page Revisions +wiki.back_to_wiki = Back to wiki page wiki.delete_page_button = Delete Page wiki.delete_page_notice_1 = Deleting the wiki page '%s' cannot be undone. Continue? wiki.page_already_exists = A wiki page with the same name already exists. diff --git a/public/css/index.css b/public/css/index.css index 9039409f14..b948766b41 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -290,6 +290,11 @@ 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} +.repository.wiki.revisions .ui.container>.ui.stackable.grid{flex-direction:row-reverse} +.repository.wiki.revisions .ui.container>.ui.stackable.grid>.header{margin-top:0} +.repository.wiki.revisions .ui.container>.ui.stackable.grid>.header .sub.header{padding-left:52px} +.file-revisions-btn{display:block;float:left;margin-bottom:2px!important;padding:11px!important;margin-right:10px!important} +.file-revisions-btn i{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} .home .logo{max-width:220px} @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 e971248f6f..1dcc2caf94 100644 --- a/public/less/_markdown.less +++ b/public/less/_markdown.less @@ -494,3 +494,35 @@ padding-left: 2em; } } + +.repository.wiki.revisions { + .ui.container > .ui.stackable.grid { + -ms-flex-direction: row-reverse; + flex-direction: row-reverse; + + > .header { + margin-top: 0; + + .sub.header { + padding-left: 52px; + } + } + } +} + +.file-revisions-btn { + display: block; + float: left; + margin-bottom: 2px !important; + padding: 11px !important; + margin-right: 10px !important; + + 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/routers/repo/wiki.go b/routers/repo/wiki.go index 43149c0340..0fdf853630 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -23,10 +23,11 @@ import ( ) const ( - tplWikiStart base.TplName = "repo/wiki/start" - tplWikiView base.TplName = "repo/wiki/view" - tplWikiNew base.TplName = "repo/wiki/new" - tplWikiPages base.TplName = "repo/wiki/pages" + tplWikiStart base.TplName = "repo/wiki/start" + tplWikiView base.TplName = "repo/wiki/view" + tplWikiRevision base.TplName = "repo/wiki/revision" + tplWikiNew base.TplName = "repo/wiki/new" + tplWikiPages base.TplName = "repo/wiki/pages" ) // MustEnableWiki check if wiki is enabled, if external then redirect @@ -107,18 +108,20 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte { // wikiContentsByName returns the contents of a wiki page, along with a boolean // indicating whether the page exists. Writes to ctx if an error occurs. -func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, bool) { - entry, err := findEntryForFile(commit, models.WikiNameToFilename(wikiName)) - if err != nil { +func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) { + var entry *git.TreeEntry + var err error + pageFilename := models.WikiNameToFilename(wikiName) + if entry, err = findEntryForFile(commit, pageFilename); err != nil { ctx.ServerError("findEntryForFile", err) - return nil, false + return nil, nil, "", false } else if entry == nil { - return nil, false + return nil, nil, "", true } - return wikiContentsByEntry(ctx, entry), true + return wikiContentsByEntry(ctx, entry), entry, pageFilename, false } -func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *git.TreeEntry) { +func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { wikiRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { if !git.IsErrNotExist(err) { @@ -128,88 +131,176 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi } // Get page list. - if isViewPage { - entries, err := commit.ListEntries() - if err != nil { - ctx.ServerError("ListEntries", err) - return nil, nil + entries, err := commit.ListEntries() + if err != nil { + ctx.ServerError("ListEntries", err) + return nil, nil + } + pages := make([]PageMeta, 0, len(entries)) + for _, entry := range entries { + if !entry.IsRegular() { + continue } - pages := make([]PageMeta, 0, len(entries)) - for _, entry := range entries { - if !entry.IsRegular() { - continue - } - 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" { + wikiName, err := models.WikiFilenameToName(entry.Name()) + if err != nil { + if models.IsErrWikiInvalidFileName(err) { continue } - pages = append(pages, PageMeta{ - Name: wikiName, - SubURL: models.WikiNameToSubURL(wikiName), - }) + ctx.ServerError("WikiFilenameToName", err) + return nil, nil + } else if wikiName == "_Sidebar" || wikiName == "_Footer" { + continue } - ctx.Data["Pages"] = pages + pages = append(pages, PageMeta{ + Name: wikiName, + SubURL: models.WikiNameToSubURL(wikiName), + }) } + ctx.Data["Pages"] = pages + // get requested pagename pageName := models.NormalizeWikiName(ctx.Params(":page")) if len(pageName) == 0 { pageName = "Home" } ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName) - ctx.Data["old_title"] = pageName ctx.Data["Title"] = pageName ctx.Data["title"] = pageName ctx.Data["RequireHighlightJS"] = true - pageFilename := models.WikiNameToFilename(pageName) - var entry *git.TreeEntry - if entry, err = findEntryForFile(commit, pageFilename); err != nil { - ctx.ServerError("findEntryForFile", err) - return nil, nil - } else if entry == nil { + //lookup filename in wiki - get filecontent, gitTree entry , real filename + data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) + if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages") + } + if entry == nil || ctx.Written() { return nil, nil } - data := wikiContentsByEntry(ctx, entry) + + sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar") if ctx.Written() { return nil, nil } - if isViewPage { - sidebarContent, sidebarPresent := wikiContentsByName(ctx, commit, "_Sidebar") - if ctx.Written() { - return nil, nil - } + footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer") + if ctx.Written() { + return nil, nil + } - footerContent, footerPresent := wikiContentsByName(ctx, commit, "_Footer") - if ctx.Written() { - return nil, nil + metas := ctx.Repo.Repository.ComposeMetas() + ctx.Data["content"] = markdown.RenderWiki(data, ctx.Repo.RepoLink, metas) + ctx.Data["sidebarPresent"] = sidebarContent != nil + ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas) + ctx.Data["footerPresent"] = footerContent != nil + ctx.Data["footerContent"] = markdown.RenderWiki(footerContent, ctx.Repo.RepoLink, metas) + + // get commit count - wiki revisions + commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename) + ctx.Data["CommitCount"] = commitsCount + + return wikiRepo, entry +} + +func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { + wikiRepo, commit, err := findWikiRepoCommit(ctx) + if err != nil { + if !git.IsErrNotExist(err) { + ctx.ServerError("GetBranchCommit", err) } + return nil, nil + } + + // get requested pagename + pageName := models.NormalizeWikiName(ctx.Params(":page")) + if len(pageName) == 0 { + pageName = "Home" + } + ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName) + ctx.Data["old_title"] = pageName + ctx.Data["Title"] = pageName + ctx.Data["title"] = pageName + ctx.Data["RequireHighlightJS"] = true - metas := ctx.Repo.Repository.ComposeMetas() - ctx.Data["content"] = markdown.RenderWiki(data, ctx.Repo.RepoLink, metas) - ctx.Data["sidebarPresent"] = sidebarPresent - ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas) - ctx.Data["footerPresent"] = footerPresent - ctx.Data["footerContent"] = markdown.RenderWiki(footerContent, ctx.Repo.RepoLink, metas) - } else { - ctx.Data["content"] = string(data) - ctx.Data["sidebarPresent"] = false - ctx.Data["sidebarContent"] = "" - ctx.Data["footerPresent"] = false - ctx.Data["footerContent"] = "" + //lookup filename in wiki - get filecontent, gitTree entry , real filename + data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) + if noEntry { + ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages") + } + if entry == nil || ctx.Written() { + return nil, nil } + ctx.Data["content"] = string(data) + ctx.Data["sidebarPresent"] = false + ctx.Data["sidebarContent"] = "" + ctx.Data["footerPresent"] = false + ctx.Data["footerContent"] = "" + + // get commit count - wiki revisions + commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename) + ctx.Data["CommitCount"] = commitsCount + + // get page + page := ctx.QueryInt("page") + if page <= 1 { + page = 1 + } + + // get Commit Count + commitsHistory, err := wikiRepo.CommitsByFileAndRange("master", pageFilename, page) + if err != nil { + ctx.ServerError("CommitsByFileAndRange", err) + return nil, nil + } + commitsHistory = models.ValidateCommitsWithEmails(commitsHistory) + commitsHistory = models.ParseCommitsWithSignature(commitsHistory) + + ctx.Data["Commits"] = commitsHistory + + pager := context.NewPagination(int(commitsCount), git.CommitsRangeSize, page, 5) + pager.SetDefaultParams(ctx) + ctx.Data["Page"] = pager + return wikiRepo, entry } +func renderEditPage(ctx *context.Context) { + _, commit, err := findWikiRepoCommit(ctx) + if err != nil { + if !git.IsErrNotExist(err) { + ctx.ServerError("GetBranchCommit", err) + } + return + } + + // get requested pagename + pageName := models.NormalizeWikiName(ctx.Params(":page")) + if len(pageName) == 0 { + pageName = "Home" + } + ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName) + ctx.Data["old_title"] = pageName + ctx.Data["Title"] = pageName + ctx.Data["title"] = pageName + ctx.Data["RequireHighlightJS"] = true + + //lookup filename in wiki - get filecontent, gitTree entry , real filename + data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName) + if noEntry { + ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages") + } + if entry == nil || ctx.Written() { + return + } + + ctx.Data["content"] = string(data) + ctx.Data["sidebarPresent"] = false + ctx.Data["sidebarContent"] = "" + ctx.Data["footerPresent"] = false + ctx.Data["footerContent"] = "" +} + // Wiki renders single wiki page func Wiki(ctx *context.Context) { ctx.Data["PageIsWiki"] = true @@ -221,7 +312,7 @@ func Wiki(ctx *context.Context) { return } - wikiRepo, entry := renderWikiPage(ctx, true) + wikiRepo, entry := renderViewPage(ctx) if ctx.Written() { return } @@ -247,6 +338,39 @@ func Wiki(ctx *context.Context) { ctx.HTML(200, tplWikiView) } +// WikiRevision renders file revision list of wiki page +func WikiRevision(ctx *context.Context) { + ctx.Data["PageIsWiki"] = true + ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived + + if !ctx.Repo.Repository.HasWiki() { + ctx.Data["Title"] = ctx.Tr("repo.wiki") + ctx.HTML(200, tplWikiStart) + return + } + + wikiRepo, entry := renderRevisionPage(ctx) + if ctx.Written() { + return + } + if entry == nil { + ctx.Data["Title"] = ctx.Tr("repo.wiki") + ctx.HTML(200, tplWikiStart) + return + } + + // Get last change information. + wikiPath := entry.Name() + lastCommit, err := wikiRepo.GetCommitByPath(wikiPath) + if err != nil { + ctx.ServerError("GetCommitByPath", err) + return + } + ctx.Data["Author"] = lastCommit.Author + + ctx.HTML(200, tplWikiRevision) +} + // WikiPages render wiki pages list page func WikiPages(ctx *context.Context) { if !ctx.Repo.Repository.HasWiki() { @@ -399,7 +523,7 @@ func EditWiki(ctx *context.Context) { return } - renderWikiPage(ctx, false) + renderEditPage(ctx) if ctx.Written() { return } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 744088a9d7..ec57e8f5fd 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -804,6 +804,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/wiki", func() { m.Get("/?:page", repo.Wiki) m.Get("/_pages", repo.WikiPages) + m.Get("/:page/_revision", repo.WikiRevision) m.Group("", func() { m.Combo("/_new").Get(repo.NewWiki). diff --git a/templates/repo/wiki/revision.tmpl b/templates/repo/wiki/revision.tmpl new file mode 100644 index 0000000000..a64c386edc --- /dev/null +++ b/templates/repo/wiki/revision.tmpl @@ -0,0 +1,104 @@ +{{template "base/head" .}} +<div class="repository wiki revisions"> + {{template "repo/header" .}} + {{ $title := .title}} + <div class="ui container"> + <div class="ui stackable grid"> + <div class="ui eight wide column text right"> + <div class="ui action small input" id="clone-panel"> + {{if not $.DisableHTTP}} + <button class="ui basic clone button" id="repo-clone-https" data-link="{{.WikiCloneLink.HTTPS}}"> + {{if UseHTTPS}}HTTPS{{else}}HTTP{{end}} + </button> + {{end}} + {{if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}} + <button class="ui basic clone button" id="repo-clone-ssh" data-link="{{.WikiCloneLink.SSH}}"> + SSH + </button> + {{end}} + {{if not $.DisableHTTP}} + <input id="repo-clone-url" value="{{$.WikiCloneLink.HTTPS}}" readonly> + {{else if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}} + <input id="repo-clone-url" value="{{$.WikiCloneLink.SSH}}" readonly> + {{end}} + {{if or ((not $.DisableHTTP) (and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)))}} + <button class="ui basic icon button poping up clipboard" id="clipboard-btn" data-original="{{.i18n.Tr "repo.copy_link"}}" data-success="{{.i18n.Tr "repo.copy_link_success"}}" data-error="{{.i18n.Tr "repo.copy_link_error"}}" data-content="{{.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-target="#repo-clone-url"> + <i class="octicon octicon-clippy"></i> + </button> + {{end}} + </div> + </div> + <div class="ui header eight wide column"> + <a class="file-revisions-btn ui basic button" title="{{.i18n.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}" ><span>{{.revision}}</span> <i class="fa fa-fw fa-file-text-o"></i></a> + {{$title}} + <div class="ui sub header"> + {{$timeSince := TimeSince .Author.When $.Lang}} + {{.i18n.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}} + </div> + </div> + </div> + <h2 class="ui top header">{{.i18n.Tr "repo.wiki.wiki_page_revisions"}}</h2> + <div class="ui" style="margin-top: 1rem;"> + <h4 class="ui top attached header"> + <div class="ui stackable grid"> + <div class="sixteen wide column"> + {{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} + </div> + </div> + </h4> + + {{if and .Commits (gt .CommitCount 0)}} + <div class="ui attached table segment"> + <table class="ui very basic striped fixed table single line" id="commits-table"> + <thead> + <tr> + <th class="eight wide">{{.i18n.Tr "repo.commits.author"}}</th> + <th class="four wide sha">SHA1</th> + <th class="four wide">{{.i18n.Tr "repo.commits.date"}}</th> + </tr> + </thead> + <tbody class="commit-list"> + {{ $r:= List .Commits}} + {{range $r}} + <tr> + <td class="author"> + {{if .User}} + {{if .User.FullName}} + <img class="ui avatar image" src="{{.User.RelAvatarLink}}" alt=""/> <a href="{{AppSubUrl}}/{{.User.Name}}">{{.User.FullName}}</a> + {{else}} + <img class="ui avatar image" src="{{.User.RelAvatarLink}}" alt=""/> <a href="{{AppSubUrl}}/{{.User.Name}}">{{.Author.Name}}</a> + {{end}} + {{else}} + <img class="ui avatar image" src="{{AvatarLink .Author.Email}}" alt=""/> {{.Author.Name}} + {{end}} + </td> + <td class="sha"> + <label rel="nofollow" class="ui sha label {{if .Signature}} isSigned {{if .Verification.Verified }} isVerified {{end}}{{end}}"> + {{ShortSha .ID.String}} + {{if .Signature}} + <div class="ui detail icon button"> + {{if .Verification.Verified}} + <i title="{{.Verification.Reason}}" class="lock green icon"></i> + {{else}} + <i title="{{$.i18n.Tr .Verification.Reason}}" class="unlock icon"></i> + {{end}} + </div> + {{end}} + </label> + </td> + <td class="grey text">{{TimeSince .Author.When $.Lang}}</td> + </tr> + {{end}} + </tbody> + </table> + </div> + {{end}} + + {{template "base/paginate" .}} + + </div> + </div> +</div> + + +{{template "base/footer" .}} diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index dd2de2a041..f775ac9429 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -56,6 +56,7 @@ <div class="ui dividing header"> <div class="ui stackable grid"> <div class="eight wide column"> + <a class="file-revisions-btn ui basic button" title="{{.i18n.Tr "repo.wiki.file_revision"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}/_revision" ><span>{{.CommitCount}}</span> <i class="fa fa-fw fa-history"></i></a> {{$title}} <div class="ui sub header"> {{$timeSince := TimeSince .Author.When $.Lang}} |