Files
Atay-Makhzan/routers/web/repo/wiki.go
T

761 lines
21 KiB
Go
Raw Normal View History

2015-11-25 20:10:25 -05:00
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2015-11-25 20:10:25 -05:00
package repo
import (
2021-04-20 06:25:08 +08:00
"bytes"
2025-06-22 18:53:33 +08:00
"html/template"
"io"
"net/http"
"net/url"
2017-02-14 08:13:59 +07:00
"path/filepath"
2015-11-27 01:50:38 -05:00
"strings"
2015-11-27 00:24:24 -05:00
2024-11-24 16:18:57 +08:00
"code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
2021-11-10 03:57:58 +08:00
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2021-06-09 07:33:54 +08:00
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
git_service "code.gitea.io/gitea/services/git"
notify_service "code.gitea.io/gitea/services/notify"
repo_service "code.gitea.io/gitea/services/repository"
wiki_service "code.gitea.io/gitea/services/wiki"
2015-11-25 20:10:25 -05:00
)
const (
tplWikiStart templates.TplName = "repo/wiki/start"
tplWikiView templates.TplName = "repo/wiki/view"
tplWikiRevision templates.TplName = "repo/wiki/revision"
tplWikiNew templates.TplName = "repo/wiki/new"
tplWikiPages templates.TplName = "repo/wiki/pages"
2015-11-25 20:10:25 -05:00
)
// MustEnableWiki check if wiki is enabled, if external then redirect
2016-03-11 11:56:52 -05:00
func MustEnableWiki(ctx *context.Context) {
2021-11-10 03:57:58 +08:00
if !ctx.Repo.CanRead(unit.TypeWiki) &&
!ctx.Repo.CanRead(unit.TypeExternalWiki) {
if log.IsTrace() {
log.Trace("Permission Denied: User %-v cannot read %-v or %-v of repo %-v\n"+
"User in repo has Permissions: %-+v",
2022-03-22 08:03:22 +01:00
ctx.Doer,
2021-11-10 03:57:58 +08:00
unit.TypeWiki,
unit.TypeExternalWiki,
ctx.Repo.Repository,
ctx.Repo.Permission)
}
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2015-12-11 04:55:08 -05:00
return
}
2025-06-22 18:53:33 +08:00
repoUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalWiki)
if err == nil {
2025-06-22 18:53:33 +08:00
ctx.Redirect(repoUnit.ExternalWikiConfig().ExternalWikiURL)
2015-12-11 04:55:08 -05:00
return
2015-12-04 21:30:33 -05:00
}
}
2019-10-02 15:02:04 -05:00
// PageMeta wiki page meta information
2015-11-27 01:50:38 -05:00
type PageMeta struct {
Name string
SubURL string
GitEntryName string
UpdatedUnix timeutil.TimeStamp
2015-11-27 01:50:38 -05:00
}
2015-11-25 20:10:25 -05:00
2017-11-28 01:43:51 -08:00
// findEntryForFile finds the tree entry for a target filepath.
func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
2020-01-28 17:44:08 +08:00
entry, err := commit.GetTreeEntryByPath(target)
if err != nil && !git.IsErrNotExist(err) {
2017-02-14 08:13:59 +07:00
return nil, err
}
2020-01-28 17:44:08 +08:00
if entry != nil {
return entry, nil
2017-02-14 08:13:59 +07:00
}
2020-01-28 17:44:08 +08:00
// Then the unescaped, the shortest alternative
var unescapedTarget string
if unescapedTarget, err = url.QueryUnescape(target); err != nil {
return nil, err
}
2020-01-28 17:44:08 +08:00
return commit.GetTreeEntryByPath(unescapedTarget)
2017-02-14 08:13:59 +07:00
}
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
2025-06-22 18:53:33 +08:00
wikiGitRepo, errGitRepo := gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository.WikiStorageRepo())
if errGitRepo != nil {
ctx.ServerError("OpenRepository", errGitRepo)
return nil, nil, errGitRepo
}
commit, errCommit := wikiGitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch)
if git.IsErrNotExist(errCommit) {
// if the default branch recorded in database is out of sync, then re-sync it
gitRepoDefaultBranch, errBranch := gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository.WikiStorageRepo())
if errBranch != nil {
return wikiGitRepo, nil, errBranch
}
// update the default branch in the database
errDb := repo_model.UpdateRepositoryColsNoAutoTime(ctx, &repo_model.Repository{ID: ctx.Repo.Repository.ID, DefaultWikiBranch: gitRepoDefaultBranch}, "default_wiki_branch")
if errDb != nil {
return wikiGitRepo, nil, errDb
}
ctx.Repo.Repository.DefaultWikiBranch = gitRepoDefaultBranch
// retry to get the commit from the correct default branch
commit, errCommit = wikiGitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch)
2015-11-27 00:24:24 -05:00
}
if errCommit != nil {
return wikiGitRepo, nil, errCommit
2017-02-14 08:13:59 +07:00
}
return wikiGitRepo, commit, nil
2017-02-14 08:13:59 +07:00
}
2017-11-28 01:43:51 -08:00
// wikiContentsByEntry returns the contents of the wiki page referenced by the
// given tree entry. Writes to ctx if an error occurs.
func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
reader, err := entry.Blob().DataAsync()
2017-11-28 01:43:51 -08:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("Blob.Data", err)
2017-11-28 01:43:51 -08:00
return nil
}
defer reader.Close()
content, err := util.ReadWithLimit(reader, 5*1024*1024) // 5MB should be enough for a wiki page
2017-11-28 01:43:51 -08:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("ReadAll", err)
2017-11-28 01:43:51 -08:00
return nil
}
return content
}
2024-08-16 20:40:51 +08:00
// wikiEntryByName returns the entry of a wiki page, along with a boolean
// indicating whether the entry exists. Writes to ctx if an error occurs.
// The last return value indicates whether the file should be returned as a raw file
func wikiEntryByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) (*git.TreeEntry, string, bool, bool) {
isRaw := false
gitFilename := wiki_service.WebPathToGitPath(wikiName)
entry, err := findEntryForFile(commit, gitFilename)
2020-01-28 17:44:08 +08:00
if err != nil && !git.IsErrNotExist(err) {
2018-01-10 22:34:17 +01:00
ctx.ServerError("findEntryForFile", err)
2024-08-16 20:40:51 +08:00
return nil, "", false, false
}
if entry == nil {
// check if the file without ".md" suffix exists
gitFilename := strings.TrimSuffix(gitFilename, ".md")
entry, err = findEntryForFile(commit, gitFilename)
if err != nil && !git.IsErrNotExist(err) {
ctx.ServerError("findEntryForFile", err)
return nil, "", false, false
}
isRaw = true
}
if entry == nil {
return nil, "", true, false
}
return entry, gitFilename, false, isRaw
}
// 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 wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) {
entry, gitFilename, noEntry, _ := wikiEntryByName(ctx, commit, wikiName)
if entry == nil {
2019-07-08 10:20:22 +02:00
return nil, nil, "", true
2017-11-28 01:43:51 -08:00
}
2024-08-16 20:40:51 +08:00
return wikiContentsByEntry(ctx, entry), entry, gitFilename, noEntry
2017-11-28 01:43:51 -08:00
}
2019-07-08 10:20:22 +02:00
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
2025-06-22 18:53:33 +08:00
wikiGitRepo, commit, err := findWikiRepoCommit(ctx)
2017-02-14 08:13:59 +07:00
if err != nil {
2018-12-10 06:45:44 +08:00
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
}
2017-02-14 08:13:59 +07:00
return nil, nil
2015-11-27 01:50:38 -05:00
}
2025-06-22 18:53:33 +08:00
// get the wiki pages list.
2019-07-08 10:20:22 +02:00
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
2015-11-27 01:50:38 -05:00
}
wikiName, err := wiki_service.GitPathToWebPath(entry.Name())
2019-07-08 10:20:22 +02:00
if err != nil {
if repo_model.IsErrWikiInvalidFileName(err) {
2017-11-28 01:43:51 -08:00
continue
}
2019-07-08 10:20:22 +02:00
ctx.ServerError("WikiFilenameToName", err)
return nil, nil
} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
continue
2015-11-27 01:50:38 -05:00
}
_, displayName := wiki_service.WebPathToUserTitle(wikiName)
2019-07-08 10:20:22 +02:00
pages = append(pages, PageMeta{
Name: displayName,
SubURL: wiki_service.WebPathToURLPath(wikiName),
GitEntryName: entry.Name(),
2019-07-08 10:20:22 +02:00
})
2015-11-27 00:24:24 -05:00
}
2019-07-08 10:20:22 +02:00
ctx.Data["Pages"] = pages
2015-11-27 00:24:24 -05:00
// get requested page name
pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
2017-11-28 01:43:51 -08:00
if len(pageName) == 0 {
pageName = "Home"
2015-11-27 00:24:24 -05:00
}
_, displayName := wiki_service.WebPathToUserTitle(pageName)
ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
ctx.Data["old_title"] = displayName
ctx.Data["Title"] = displayName
ctx.Data["title"] = displayName
2015-11-27 00:24:24 -05:00
isSideBar := pageName == "_Sidebar"
isFooter := pageName == "_Footer"
2024-08-16 20:40:51 +08:00
// lookup filename in wiki - get gitTree entry , real filename
entry, pageFilename, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName)
2019-07-08 10:20:22 +02:00
if noEntry {
2021-11-16 18:18:25 +00:00
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
2019-07-08 10:20:22 +02:00
}
2024-08-16 20:40:51 +08:00
if isRaw {
ctx.Redirect(util.URLJoin(ctx.Repo.RepoLink, "wiki/raw", string(pageName)))
}
2019-07-08 10:20:22 +02:00
if entry == nil || ctx.Written() {
2017-02-14 08:13:59 +07:00
return nil, nil
2015-11-27 00:24:24 -05:00
}
2019-07-08 10:20:22 +02:00
2025-06-22 18:53:33 +08:00
// get page content
2024-08-16 20:40:51 +08:00
data := wikiContentsByEntry(ctx, entry)
if ctx.Written() {
return nil, nil
}
2024-11-24 16:18:57 +08:00
rctx := renderhelper.NewRenderContextRepoWiki(ctx, ctx.Repo.Repository)
2021-04-20 06:25:08 +08:00
2025-06-22 18:53:33 +08:00
renderFn := func(data []byte) (escaped *charset.EscapeStatus, output template.HTML, err error) {
buf := &strings.Builder{}
markupRd, markupWr := io.Pipe()
defer markupWr.Close()
done := make(chan struct{})
go func() {
// We allow NBSP here this is rendered
escaped, _ = charset.EscapeControlReader(markupRd, buf, ctx.Locale, charset.RuneNBSP)
2025-06-22 18:53:33 +08:00
output = template.HTML(buf.String())
buf.Reset()
close(done)
}()
err = markdown.Render(rctx, bytes.NewReader(data), markupWr)
_ = markupWr.CloseWithError(err)
<-done
return escaped, output, err
}
2025-06-22 18:53:33 +08:00
ctx.Data["EscapeStatus"], ctx.Data["WikiContentHTML"], err = renderFn(data)
if err != nil {
2021-04-20 06:25:08 +08:00
ctx.ServerError("Render", err)
return nil, nil
}
2023-06-24 03:51:43 +08:00
if rctx.SidebarTocNode != nil {
2025-06-22 18:53:33 +08:00
sb := strings.Builder{}
if err = markdown.SpecializedMarkdown(rctx).Renderer().Render(&sb, nil, rctx.SidebarTocNode); err != nil {
2023-06-24 03:51:43 +08:00
log.Error("Failed to render wiki sidebar TOC: %v", err)
}
2025-06-22 18:53:33 +08:00
ctx.Data["WikiSidebarTocHTML"] = templates.SanitizeHTML(sb.String())
2023-06-24 03:51:43 +08:00
}
if !isSideBar {
2025-06-22 18:53:33 +08:00
sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar")
if ctx.Written() {
return nil, nil
}
ctx.Data["WikiSidebarEscapeStatus"], ctx.Data["WikiSidebarHTML"], err = renderFn(sidebarContent)
if err != nil {
ctx.ServerError("Render", err)
return nil, nil
2021-08-30 21:50:35 +01:00
}
2021-04-20 06:25:08 +08:00
}
if !isFooter {
2025-06-22 18:53:33 +08:00
footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer")
if ctx.Written() {
return nil, nil
}
ctx.Data["WikiFooterEscapeStatus"], ctx.Data["WikiFooterHTML"], err = renderFn(footerContent)
if err != nil {
ctx.ServerError("Render", err)
return nil, nil
2021-08-30 21:50:35 +01:00
}
2021-04-20 06:25:08 +08:00
}
2019-07-08 10:20:22 +02:00
// get commit count - wiki revisions
2025-06-22 18:53:33 +08:00
commitsCount, _ := wikiGitRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
2019-07-08 10:20:22 +02:00
ctx.Data["CommitCount"] = commitsCount
2025-06-22 18:53:33 +08:00
return wikiGitRepo, entry
2019-07-08 10:20:22 +02:00
}
func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
2025-06-22 18:53:33 +08:00
wikiGitRepo, commit, err := findWikiRepoCommit(ctx)
2019-07-08 10:20:22 +02:00
if err != nil {
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
2017-02-14 08:13:59 +07:00
}
2019-07-08 10:20:22 +02:00
return nil, nil
}
2025-06-22 18:53:33 +08:00
// get requested page name
pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
2019-07-08 10:20:22 +02:00
if len(pageName) == 0 {
pageName = "Home"
}
_, displayName := wiki_service.WebPathToUserTitle(pageName)
ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
ctx.Data["old_title"] = displayName
ctx.Data["Title"] = displayName
ctx.Data["title"] = displayName
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
2017-11-28 01:43:51 -08:00
2025-06-22 18:53:33 +08:00
// lookup filename in wiki - get page content, gitTree entry , real filename
_, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
2019-07-08 10:20:22 +02:00
if noEntry {
2021-11-16 18:18:25 +00:00
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
2019-07-08 10:20:22 +02:00
}
if entry == nil || ctx.Written() {
return nil, nil
2015-11-27 01:50:38 -05:00
}
2019-07-08 10:20:22 +02:00
// get commit count - wiki revisions
2025-06-22 18:53:33 +08:00
commitsCount, _ := wikiGitRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
2019-07-08 10:20:22 +02:00
ctx.Data["CommitCount"] = commitsCount
// get page
2025-06-18 03:48:09 +02:00
page := max(ctx.FormInt("page"), 1)
2019-07-08 10:20:22 +02:00
// get Commit Count
2025-06-22 18:53:33 +08:00
commitsHistory, err := wikiGitRepo.CommitsByFileAndRange(
2023-05-08 00:10:53 -07:00
git.CommitsByFileAndRangeOptions{
Revision: ctx.Repo.Repository.DefaultWikiBranch,
2023-05-08 00:10:53 -07:00
File: pageFilename,
Page: page,
})
2019-07-08 10:20:22 +02:00
if err != nil {
2022-08-15 02:22:13 +01:00
ctx.ServerError("CommitsByFileAndRange", err)
2019-07-08 10:20:22 +02:00
return nil, nil
}
ctx.Data["Commits"], err = git_service.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository)
if err != nil {
ctx.ServerError("ConvertFromGitCommit", err)
return nil, nil
}
2019-07-08 10:20:22 +02:00
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
2024-12-30 09:57:38 +08:00
pager.AddParamFromRequest(ctx.Req)
2019-07-08 10:20:22 +02:00
ctx.Data["Page"] = pager
2025-06-22 18:53:33 +08:00
return wikiGitRepo, entry
2015-11-27 01:50:38 -05:00
}
2019-07-08 10:20:22 +02:00
func renderEditPage(ctx *context.Context) {
2025-06-22 18:53:33 +08:00
_, commit, err := findWikiRepoCommit(ctx)
if err != nil {
2019-07-08 10:20:22 +02:00
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
}
return
}
2025-06-22 18:53:33 +08:00
// get requested page name
pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
2019-07-08 10:20:22 +02:00
if len(pageName) == 0 {
pageName = "Home"
}
_, displayName := wiki_service.WebPathToUserTitle(pageName)
ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
ctx.Data["old_title"] = displayName
ctx.Data["Title"] = displayName
ctx.Data["title"] = displayName
2019-07-08 10:20:22 +02:00
2024-08-16 20:40:51 +08:00
// lookup filename in wiki - gitTree entry , real filename
entry, _, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName)
2019-07-08 10:20:22 +02:00
if noEntry {
2021-11-16 18:18:25 +00:00
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
2019-07-08 10:20:22 +02:00
}
2024-08-16 20:40:51 +08:00
if isRaw {
2025-02-17 14:13:17 +08:00
ctx.HTTPError(http.StatusForbidden, "Editing of raw wiki files is not allowed")
2024-08-16 20:40:51 +08:00
}
2019-07-08 10:20:22 +02:00
if entry == nil || ctx.Written() {
return
}
2025-06-22 18:53:33 +08:00
// get wiki page content
2024-08-16 20:40:51 +08:00
data := wikiContentsByEntry(ctx, entry)
if ctx.Written() {
return
}
2025-06-22 18:53:33 +08:00
ctx.Data["WikiEditContent"] = string(data)
2019-07-08 10:20:22 +02:00
}
2021-11-16 18:18:25 +00:00
// WikiPost renders post of wiki page
func WikiPost(ctx *context.Context) {
switch ctx.FormString("action") {
case "_new":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2021-11-16 18:18:25 +00:00
return
}
NewWikiPost(ctx)
return
case "_delete":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2021-11-16 18:18:25 +00:00
return
}
DeleteWikiPagePost(ctx)
return
}
if !ctx.Repo.CanWrite(unit.TypeWiki) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2021-11-16 18:18:25 +00:00
return
}
EditWikiPost(ctx)
}
2017-02-14 08:13:59 +07:00
// Wiki renders single wiki page
2016-03-11 11:56:52 -05:00
func Wiki(ctx *context.Context) {
2021-11-10 03:57:58 +08:00
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
2015-11-27 01:50:38 -05:00
2021-11-16 18:18:25 +00:00
switch ctx.FormString("action") {
case "_pages":
WikiPages(ctx)
return
case "_revision":
WikiRevision(ctx)
return
case "_edit":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2021-11-16 18:18:25 +00:00
return
}
EditWiki(ctx)
return
case "_new":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2021-11-16 18:18:25 +00:00
return
}
NewWiki(ctx)
return
}
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
ctx.Data["Title"] = ctx.Tr("repo.wiki")
ctx.HTML(http.StatusOK, tplWikiStart)
return
}
2025-06-22 18:53:33 +08:00
wikiGitRepo, entry := renderViewPage(ctx)
2021-08-30 21:50:35 +01:00
if ctx.Written() {
return
}
2017-03-20 21:36:19 +08:00
if entry == nil {
ctx.Data["Title"] = ctx.Tr("repo.wiki")
ctx.HTML(http.StatusOK, tplWikiStart)
2017-03-20 21:36:19 +08:00
return
}
2015-11-27 00:24:24 -05:00
2017-11-28 01:43:51 -08:00
wikiPath := entry.Name()
2024-06-18 11:09:20 +08:00
if markup.DetectMarkupTypeByFileName(wikiPath) != markdown.MarkupName {
2017-11-28 01:43:51 -08:00
ext := strings.ToUpper(filepath.Ext(wikiPath))
2025-04-01 12:14:01 +02:00
ctx.Data["FormatWarning"] = ext + " rendering is not supported at the moment. Rendered as Markdown."
2017-02-14 08:13:59 +07:00
}
2015-11-27 00:24:24 -05:00
// Get last change information.
2025-06-22 18:53:33 +08:00
lastCommit, err := wikiGitRepo.GetCommitByPath(wikiPath)
2015-11-27 00:24:24 -05:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("GetCommitByPath", err)
2015-11-27 00:24:24 -05:00
return
}
ctx.Data["Author"] = lastCommit.Author
ctx.HTML(http.StatusOK, tplWikiView)
2015-11-25 20:10:25 -05:00
}
2019-07-08 10:20:22 +02:00
// WikiRevision renders file revision list of wiki page
func WikiRevision(ctx *context.Context) {
2021-11-10 03:57:58 +08:00
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
2019-07-08 10:20:22 +02:00
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
2019-07-08 10:20:22 +02:00
ctx.Data["Title"] = ctx.Tr("repo.wiki")
ctx.HTML(http.StatusOK, tplWikiStart)
2019-07-08 10:20:22 +02:00
return
}
2025-06-22 18:53:33 +08:00
wikiGitRepo, entry := renderRevisionPage(ctx)
2021-08-30 21:50:35 +01:00
if ctx.Written() {
return
}
2019-07-08 10:20:22 +02:00
if entry == nil {
ctx.Data["Title"] = ctx.Tr("repo.wiki")
ctx.HTML(http.StatusOK, tplWikiStart)
2019-07-08 10:20:22 +02:00
return
}
// Get last change information.
wikiPath := entry.Name()
2025-06-22 18:53:33 +08:00
lastCommit, err := wikiGitRepo.GetCommitByPath(wikiPath)
2019-07-08 10:20:22 +02:00
if err != nil {
ctx.ServerError("GetCommitByPath", err)
return
}
ctx.Data["Author"] = lastCommit.Author
ctx.HTML(http.StatusOK, tplWikiRevision)
2019-07-08 10:20:22 +02:00
}
// WikiPages render wiki pages list page
2016-03-11 11:56:52 -05:00
func WikiPages(ctx *context.Context) {
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
2015-11-27 02:16:12 -05:00
ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
return
}
ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
2021-11-10 03:57:58 +08:00
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
2025-06-22 18:53:33 +08:00
_, commit, err := findWikiRepoCommit(ctx)
if err != nil {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
return
}
2024-12-05 23:39:50 -08:00
treePath := "" // To support list sub folders' pages in the future
tree, err := commit.SubTree(treePath)
if err != nil {
ctx.ServerError("SubTree", err)
return
}
allEntries, err := tree.ListEntries()
2021-08-30 21:50:35 +01:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("ListEntries", err)
2015-11-27 02:16:12 -05:00
return
}
allEntries.CustomSort(base.NaturalSortCompare)
2024-12-05 23:39:50 -08:00
entries, _, err := allEntries.GetCommitsInfo(ctx, ctx.Repo.RepoLink, commit, treePath)
2024-12-05 23:39:50 -08:00
if err != nil {
ctx.ServerError("GetCommitsInfo", err)
return
}
2015-11-27 02:16:12 -05:00
pages := make([]PageMeta, 0, len(entries))
2017-11-28 01:43:51 -08:00
for _, entry := range entries {
2024-12-05 23:39:50 -08:00
if !entry.Entry.IsRegular() {
2017-11-28 01:43:51 -08:00
continue
}
2024-12-05 23:39:50 -08:00
wikiName, err := wiki_service.GitPathToWebPath(entry.Entry.Name())
2017-11-28 01:43:51 -08:00
if err != nil {
if repo_model.IsErrWikiInvalidFileName(err) {
continue
}
2018-01-10 22:34:17 +01:00
ctx.ServerError("WikiFilenameToName", err)
2017-11-28 01:43:51 -08:00
return
2015-11-27 02:16:12 -05:00
}
_, displayName := wiki_service.WebPathToUserTitle(wikiName)
2017-11-28 01:43:51 -08:00
pages = append(pages, PageMeta{
Name: displayName,
SubURL: wiki_service.WebPathToURLPath(wikiName),
2024-12-05 23:39:50 -08:00
GitEntryName: entry.Entry.Name(),
UpdatedUnix: timeutil.TimeStamp(entry.Commit.Author.When.Unix()),
2017-11-28 01:43:51 -08:00
})
2015-11-27 02:16:12 -05:00
}
ctx.Data["Pages"] = pages
2015-11-27 00:24:24 -05:00
ctx.HTML(http.StatusOK, tplWikiPages)
2015-11-27 00:24:24 -05:00
}
2017-02-14 08:13:59 +07:00
// WikiRaw outputs raw blob requested by user (image for example)
func WikiRaw(ctx *context.Context) {
2025-06-22 18:53:33 +08:00
_, commit, err := findWikiRepoCommit(ctx)
2021-08-30 21:50:35 +01:00
if err != nil {
if git.IsErrNotExist(err) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2017-02-14 08:13:59 +07:00
return
}
2021-08-30 21:50:35 +01:00
ctx.ServerError("findEntryForfile", err)
return
2017-02-14 08:13:59 +07:00
}
providedWebPath := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
providedGitPath := wiki_service.WebPathToGitPath(providedWebPath)
2017-02-14 08:13:59 +07:00
var entry *git.TreeEntry
if commit != nil {
// Try to find a file with that name
entry, err = findEntryForFile(commit, providedGitPath)
2020-01-28 17:44:08 +08:00
if err != nil && !git.IsErrNotExist(err) {
ctx.ServerError("findFile", err)
return
}
if entry == nil {
// Try to find a wiki page with that name
providedGitPath = strings.TrimSuffix(providedGitPath, ".md")
entry, err = findEntryForFile(commit, providedGitPath)
2020-01-28 17:44:08 +08:00
if err != nil && !git.IsErrNotExist(err) {
ctx.ServerError("findFile", err)
return
}
}
2017-02-14 08:13:59 +07:00
}
if entry != nil {
2025-03-13 07:04:50 +08:00
if err = common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, entry.Blob(), nil); err != nil {
ctx.ServerError("ServeBlob", err)
}
2017-11-28 01:43:51 -08:00
return
}
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2017-02-14 08:13:59 +07:00
}
// NewWiki render wiki create page
2016-03-11 11:56:52 -05:00
func NewWiki(ctx *context.Context) {
2015-11-25 20:10:25 -05:00
ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
2015-11-25 20:10:25 -05:00
ctx.Data["title"] = "Home"
}
2021-12-03 16:28:54 +09:00
if ctx.FormString("title") != "" {
ctx.Data["title"] = ctx.FormString("title")
}
2015-11-25 20:10:25 -05:00
ctx.HTML(http.StatusOK, tplWikiNew)
2015-11-25 20:10:25 -05:00
}
2017-11-28 01:43:51 -08:00
// NewWikiPost response for wiki create request
2021-01-26 23:36:53 +08:00
func NewWikiPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewWikiForm)
2015-11-26 17:33:45 -05:00
ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplWikiNew)
2015-11-26 17:33:45 -05:00
return
}
2019-01-21 12:45:32 +01:00
if util.IsEmptyString(form.Title) {
ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplWikiNew, form)
return
}
wikiName := wiki_service.UserTitleToWebPath("", form.Title)
if len(form.Message) == 0 {
form.Message = ctx.Locale.TrString("repo.editor.add", form.Title)
}
2022-03-22 08:03:22 +01:00
if err := wiki_service.AddWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.Content, form.Message); err != nil {
if repo_model.IsErrWikiReservedName(err) {
2017-11-28 01:43:51 -08:00
ctx.Data["Err_Title"] = true
ctx.RenderWithErr(ctx.Tr("repo.wiki.reserved_page", wikiName), tplWikiNew, &form)
} else if repo_model.IsErrWikiAlreadyExist(err) {
2015-11-27 01:50:38 -05:00
ctx.Data["Err_Title"] = true
ctx.RenderWithErr(ctx.Tr("repo.wiki.page_already_exists"), tplWikiNew, &form)
2015-11-27 01:50:38 -05:00
} else {
2018-01-10 22:34:17 +01:00
ctx.ServerError("AddWikiPage", err)
2015-11-27 01:50:38 -05:00
}
2015-11-26 17:33:45 -05:00
return
}
notify_service.NewWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(wikiName), form.Message)
2022-09-04 21:54:23 +02:00
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.WebPathToURLPath(wikiName))
2015-11-26 17:33:45 -05:00
}
// EditWiki render wiki modify page
2016-03-11 11:56:52 -05:00
func EditWiki(ctx *context.Context) {
2015-11-27 01:50:38 -05:00
ctx.Data["PageIsWikiEdit"] = true
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
2015-11-27 01:50:38 -05:00
ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
return
}
2019-07-08 10:20:22 +02:00
renderEditPage(ctx)
2015-11-27 01:50:38 -05:00
if ctx.Written() {
return
}
ctx.HTML(http.StatusOK, tplWikiNew)
2015-11-27 01:50:38 -05:00
}
2017-11-28 01:43:51 -08:00
// EditWikiPost response for wiki modify request
2021-01-26 23:36:53 +08:00
func EditWikiPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewWikiForm)
2015-11-27 01:50:38 -05:00
ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplWikiNew)
2015-11-27 01:50:38 -05:00
return
}
oldWikiName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
newWikiName := wiki_service.UserTitleToWebPath("", form.Title)
2017-01-21 20:50:51 +08:00
if len(form.Message) == 0 {
form.Message = ctx.Locale.TrString("repo.editor.update", form.Title)
}
2022-03-22 08:03:22 +01:00
if err := wiki_service.EditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, oldWikiName, newWikiName, form.Content, form.Message); err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("EditWikiPage", err)
2015-11-27 01:50:38 -05:00
return
}
notify_service.EditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(newWikiName), form.Message)
2022-09-04 21:54:23 +02:00
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.WebPathToURLPath(newWikiName))
2015-11-25 20:10:25 -05:00
}
2016-03-03 17:06:50 -05:00
// DeleteWikiPagePost delete wiki page
2016-03-11 11:56:52 -05:00
func DeleteWikiPagePost(ctx *context.Context) {
wikiName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
2017-11-28 01:43:51 -08:00
if len(wikiName) == 0 {
wikiName = "Home"
2016-03-03 17:06:50 -05:00
}
2022-03-22 08:03:22 +01:00
if err := wiki_service.DeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName); err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("DeleteWikiPage", err)
2016-03-03 17:06:50 -05:00
return
}
notify_service.DeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(wikiName))
2022-09-04 21:54:23 +02:00
ctx.JSONRedirect(ctx.Repo.RepoLink + "/wiki/")
2016-03-03 17:06:50 -05:00
}