You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

wiki.go 15 kB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
6 years ago
10 years ago
10 years ago
10 years ago
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
6 years ago
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
6 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
6 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "fmt"
  8. "io/ioutil"
  9. "path/filepath"
  10. "strings"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/modules/auth"
  13. "code.gitea.io/gitea/modules/base"
  14. "code.gitea.io/gitea/modules/context"
  15. "code.gitea.io/gitea/modules/git"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/markup"
  18. "code.gitea.io/gitea/modules/markup/markdown"
  19. "code.gitea.io/gitea/modules/timeutil"
  20. "code.gitea.io/gitea/modules/util"
  21. )
  22. const (
  23. tplWikiStart base.TplName = "repo/wiki/start"
  24. tplWikiView base.TplName = "repo/wiki/view"
  25. tplWikiRevision base.TplName = "repo/wiki/revision"
  26. tplWikiNew base.TplName = "repo/wiki/new"
  27. tplWikiPages base.TplName = "repo/wiki/pages"
  28. )
  29. // MustEnableWiki check if wiki is enabled, if external then redirect
  30. func MustEnableWiki(ctx *context.Context) {
  31. if !ctx.Repo.CanRead(models.UnitTypeWiki) &&
  32. !ctx.Repo.CanRead(models.UnitTypeExternalWiki) {
  33. if log.IsTrace() {
  34. log.Trace("Permission Denied: User %-v cannot read %-v or %-v of repo %-v\n"+
  35. "User in repo has Permissions: %-+v",
  36. ctx.User,
  37. models.UnitTypeWiki,
  38. models.UnitTypeExternalWiki,
  39. ctx.Repo.Repository,
  40. ctx.Repo.Permission)
  41. }
  42. ctx.NotFound("MustEnableWiki", nil)
  43. return
  44. }
  45. unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalWiki)
  46. if err == nil {
  47. ctx.Redirect(unit.ExternalWikiConfig().ExternalWikiURL)
  48. return
  49. }
  50. }
  51. // PageMeta wiki page meat information
  52. type PageMeta struct {
  53. Name string
  54. SubURL string
  55. UpdatedUnix timeutil.TimeStamp
  56. }
  57. // findEntryForFile finds the tree entry for a target filepath.
  58. func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
  59. entries, err := commit.ListEntries()
  60. if err != nil {
  61. return nil, err
  62. }
  63. for _, entry := range entries {
  64. if entry.IsRegular() && entry.Name() == target {
  65. return entry, nil
  66. }
  67. }
  68. return nil, nil
  69. }
  70. func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
  71. wikiRepo, err := git.OpenRepository(ctx.Repo.Repository.WikiPath())
  72. if err != nil {
  73. ctx.ServerError("OpenRepository", err)
  74. return nil, nil, err
  75. }
  76. commit, err := wikiRepo.GetBranchCommit("master")
  77. if err != nil {
  78. return wikiRepo, nil, err
  79. }
  80. return wikiRepo, commit, nil
  81. }
  82. // wikiContentsByEntry returns the contents of the wiki page referenced by the
  83. // given tree entry. Writes to ctx if an error occurs.
  84. func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
  85. reader, err := entry.Blob().DataAsync()
  86. if err != nil {
  87. ctx.ServerError("Blob.Data", err)
  88. return nil
  89. }
  90. defer reader.Close()
  91. content, err := ioutil.ReadAll(reader)
  92. if err != nil {
  93. ctx.ServerError("ReadAll", err)
  94. return nil
  95. }
  96. return content
  97. }
  98. // wikiContentsByName returns the contents of a wiki page, along with a boolean
  99. // indicating whether the page exists. Writes to ctx if an error occurs.
  100. func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
  101. var entry *git.TreeEntry
  102. var err error
  103. pageFilename := models.WikiNameToFilename(wikiName)
  104. if entry, err = findEntryForFile(commit, pageFilename); err != nil {
  105. ctx.ServerError("findEntryForFile", err)
  106. return nil, nil, "", false
  107. } else if entry == nil {
  108. return nil, nil, "", true
  109. }
  110. return wikiContentsByEntry(ctx, entry), entry, pageFilename, false
  111. }
  112. func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
  113. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  114. if err != nil {
  115. if !git.IsErrNotExist(err) {
  116. ctx.ServerError("GetBranchCommit", err)
  117. }
  118. return nil, nil
  119. }
  120. // Get page list.
  121. entries, err := commit.ListEntries()
  122. if err != nil {
  123. ctx.ServerError("ListEntries", err)
  124. return nil, nil
  125. }
  126. pages := make([]PageMeta, 0, len(entries))
  127. for _, entry := range entries {
  128. if !entry.IsRegular() {
  129. continue
  130. }
  131. wikiName, err := models.WikiFilenameToName(entry.Name())
  132. if err != nil {
  133. if models.IsErrWikiInvalidFileName(err) {
  134. continue
  135. }
  136. ctx.ServerError("WikiFilenameToName", err)
  137. return nil, nil
  138. } else if wikiName == "_Sidebar" || wikiName == "_Footer" {
  139. continue
  140. }
  141. pages = append(pages, PageMeta{
  142. Name: wikiName,
  143. SubURL: models.WikiNameToSubURL(wikiName),
  144. })
  145. }
  146. ctx.Data["Pages"] = pages
  147. // get requested pagename
  148. pageName := models.NormalizeWikiName(ctx.Params(":page"))
  149. if len(pageName) == 0 {
  150. pageName = "Home"
  151. }
  152. ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName)
  153. ctx.Data["old_title"] = pageName
  154. ctx.Data["Title"] = pageName
  155. ctx.Data["title"] = pageName
  156. ctx.Data["RequireHighlightJS"] = true
  157. //lookup filename in wiki - get filecontent, gitTree entry , real filename
  158. data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
  159. if noEntry {
  160. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
  161. }
  162. if entry == nil || ctx.Written() {
  163. return nil, nil
  164. }
  165. sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar")
  166. if ctx.Written() {
  167. return nil, nil
  168. }
  169. footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer")
  170. if ctx.Written() {
  171. return nil, nil
  172. }
  173. metas := ctx.Repo.Repository.ComposeMetas()
  174. ctx.Data["content"] = markdown.RenderWiki(data, ctx.Repo.RepoLink, metas)
  175. ctx.Data["sidebarPresent"] = sidebarContent != nil
  176. ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas)
  177. ctx.Data["footerPresent"] = footerContent != nil
  178. ctx.Data["footerContent"] = markdown.RenderWiki(footerContent, ctx.Repo.RepoLink, metas)
  179. // get commit count - wiki revisions
  180. commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
  181. ctx.Data["CommitCount"] = commitsCount
  182. return wikiRepo, entry
  183. }
  184. func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
  185. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  186. if err != nil {
  187. if !git.IsErrNotExist(err) {
  188. ctx.ServerError("GetBranchCommit", err)
  189. }
  190. return nil, nil
  191. }
  192. // get requested pagename
  193. pageName := models.NormalizeWikiName(ctx.Params(":page"))
  194. if len(pageName) == 0 {
  195. pageName = "Home"
  196. }
  197. ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName)
  198. ctx.Data["old_title"] = pageName
  199. ctx.Data["Title"] = pageName
  200. ctx.Data["title"] = pageName
  201. ctx.Data["RequireHighlightJS"] = true
  202. //lookup filename in wiki - get filecontent, gitTree entry , real filename
  203. data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
  204. if noEntry {
  205. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
  206. }
  207. if entry == nil || ctx.Written() {
  208. return nil, nil
  209. }
  210. ctx.Data["content"] = string(data)
  211. ctx.Data["sidebarPresent"] = false
  212. ctx.Data["sidebarContent"] = ""
  213. ctx.Data["footerPresent"] = false
  214. ctx.Data["footerContent"] = ""
  215. // get commit count - wiki revisions
  216. commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
  217. ctx.Data["CommitCount"] = commitsCount
  218. // get page
  219. page := ctx.QueryInt("page")
  220. if page <= 1 {
  221. page = 1
  222. }
  223. // get Commit Count
  224. commitsHistory, err := wikiRepo.CommitsByFileAndRangeNoFollow("master", pageFilename, page)
  225. if err != nil {
  226. ctx.ServerError("CommitsByFileAndRangeNoFollow", err)
  227. return nil, nil
  228. }
  229. commitsHistory = models.ValidateCommitsWithEmails(commitsHistory)
  230. commitsHistory = models.ParseCommitsWithSignature(commitsHistory)
  231. ctx.Data["Commits"] = commitsHistory
  232. pager := context.NewPagination(int(commitsCount), git.CommitsRangeSize, page, 5)
  233. pager.SetDefaultParams(ctx)
  234. ctx.Data["Page"] = pager
  235. return wikiRepo, entry
  236. }
  237. func renderEditPage(ctx *context.Context) {
  238. _, commit, err := findWikiRepoCommit(ctx)
  239. if err != nil {
  240. if !git.IsErrNotExist(err) {
  241. ctx.ServerError("GetBranchCommit", err)
  242. }
  243. return
  244. }
  245. // get requested pagename
  246. pageName := models.NormalizeWikiName(ctx.Params(":page"))
  247. if len(pageName) == 0 {
  248. pageName = "Home"
  249. }
  250. ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName)
  251. ctx.Data["old_title"] = pageName
  252. ctx.Data["Title"] = pageName
  253. ctx.Data["title"] = pageName
  254. ctx.Data["RequireHighlightJS"] = true
  255. //lookup filename in wiki - get filecontent, gitTree entry , real filename
  256. data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
  257. if noEntry {
  258. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
  259. }
  260. if entry == nil || ctx.Written() {
  261. return
  262. }
  263. ctx.Data["content"] = string(data)
  264. ctx.Data["sidebarPresent"] = false
  265. ctx.Data["sidebarContent"] = ""
  266. ctx.Data["footerPresent"] = false
  267. ctx.Data["footerContent"] = ""
  268. }
  269. // Wiki renders single wiki page
  270. func Wiki(ctx *context.Context) {
  271. ctx.Data["PageIsWiki"] = true
  272. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
  273. if !ctx.Repo.Repository.HasWiki() {
  274. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  275. ctx.HTML(200, tplWikiStart)
  276. return
  277. }
  278. wikiRepo, entry := renderViewPage(ctx)
  279. if ctx.Written() {
  280. return
  281. }
  282. if entry == nil {
  283. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  284. ctx.HTML(200, tplWikiStart)
  285. return
  286. }
  287. wikiPath := entry.Name()
  288. if markup.Type(wikiPath) != markdown.MarkupName {
  289. ext := strings.ToUpper(filepath.Ext(wikiPath))
  290. ctx.Data["FormatWarning"] = fmt.Sprintf("%s rendering is not supported at the moment. Rendered as Markdown.", ext)
  291. }
  292. // Get last change information.
  293. lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
  294. if err != nil {
  295. ctx.ServerError("GetCommitByPath", err)
  296. return
  297. }
  298. ctx.Data["Author"] = lastCommit.Author
  299. ctx.HTML(200, tplWikiView)
  300. }
  301. // WikiRevision renders file revision list of wiki page
  302. func WikiRevision(ctx *context.Context) {
  303. ctx.Data["PageIsWiki"] = true
  304. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
  305. if !ctx.Repo.Repository.HasWiki() {
  306. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  307. ctx.HTML(200, tplWikiStart)
  308. return
  309. }
  310. wikiRepo, entry := renderRevisionPage(ctx)
  311. if ctx.Written() {
  312. return
  313. }
  314. if entry == nil {
  315. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  316. ctx.HTML(200, tplWikiStart)
  317. return
  318. }
  319. // Get last change information.
  320. wikiPath := entry.Name()
  321. lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
  322. if err != nil {
  323. ctx.ServerError("GetCommitByPath", err)
  324. return
  325. }
  326. ctx.Data["Author"] = lastCommit.Author
  327. ctx.HTML(200, tplWikiRevision)
  328. }
  329. // WikiPages render wiki pages list page
  330. func WikiPages(ctx *context.Context) {
  331. if !ctx.Repo.Repository.HasWiki() {
  332. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  333. return
  334. }
  335. ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
  336. ctx.Data["PageIsWiki"] = true
  337. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
  338. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  339. if err != nil {
  340. return
  341. }
  342. entries, err := commit.ListEntries()
  343. if err != nil {
  344. ctx.ServerError("ListEntries", err)
  345. return
  346. }
  347. pages := make([]PageMeta, 0, len(entries))
  348. for _, entry := range entries {
  349. if !entry.IsRegular() {
  350. continue
  351. }
  352. c, err := wikiRepo.GetCommitByPath(entry.Name())
  353. if err != nil {
  354. ctx.ServerError("GetCommit", err)
  355. return
  356. }
  357. wikiName, err := models.WikiFilenameToName(entry.Name())
  358. if err != nil {
  359. if models.IsErrWikiInvalidFileName(err) {
  360. continue
  361. }
  362. ctx.ServerError("WikiFilenameToName", err)
  363. return
  364. }
  365. pages = append(pages, PageMeta{
  366. Name: wikiName,
  367. SubURL: models.WikiNameToSubURL(wikiName),
  368. UpdatedUnix: timeutil.TimeStamp(c.Author.When.Unix()),
  369. })
  370. }
  371. ctx.Data["Pages"] = pages
  372. ctx.HTML(200, tplWikiPages)
  373. }
  374. // WikiRaw outputs raw blob requested by user (image for example)
  375. func WikiRaw(ctx *context.Context) {
  376. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  377. if err != nil {
  378. if wikiRepo != nil {
  379. return
  380. }
  381. }
  382. providedPath := ctx.Params("*")
  383. var entry *git.TreeEntry
  384. if commit != nil {
  385. // Try to find a file with that name
  386. entry, err = findEntryForFile(commit, providedPath)
  387. if err != nil {
  388. ctx.ServerError("findFile", err)
  389. return
  390. }
  391. if entry == nil {
  392. // Try to find a wiki page with that name
  393. if strings.HasSuffix(providedPath, ".md") {
  394. providedPath = providedPath[:len(providedPath)-3]
  395. }
  396. wikiPath := models.WikiNameToFilename(providedPath)
  397. entry, err = findEntryForFile(commit, wikiPath)
  398. if err != nil {
  399. ctx.ServerError("findFile", err)
  400. return
  401. }
  402. }
  403. }
  404. if entry != nil {
  405. if err = ServeBlob(ctx, entry.Blob()); err != nil {
  406. ctx.ServerError("ServeBlob", err)
  407. }
  408. return
  409. }
  410. ctx.NotFound("findEntryForFile", nil)
  411. }
  412. // NewWiki render wiki create page
  413. func NewWiki(ctx *context.Context) {
  414. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  415. ctx.Data["PageIsWiki"] = true
  416. ctx.Data["RequireSimpleMDE"] = true
  417. if !ctx.Repo.Repository.HasWiki() {
  418. ctx.Data["title"] = "Home"
  419. }
  420. ctx.HTML(200, tplWikiNew)
  421. }
  422. // NewWikiPost response for wiki create request
  423. func NewWikiPost(ctx *context.Context, form auth.NewWikiForm) {
  424. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  425. ctx.Data["PageIsWiki"] = true
  426. ctx.Data["RequireSimpleMDE"] = true
  427. if ctx.HasError() {
  428. ctx.HTML(200, tplWikiNew)
  429. return
  430. }
  431. if util.IsEmptyString(form.Title) {
  432. ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplWikiNew, form)
  433. return
  434. }
  435. wikiName := models.NormalizeWikiName(form.Title)
  436. if err := ctx.Repo.Repository.AddWikiPage(ctx.User, wikiName, form.Content, form.Message); err != nil {
  437. if models.IsErrWikiReservedName(err) {
  438. ctx.Data["Err_Title"] = true
  439. ctx.RenderWithErr(ctx.Tr("repo.wiki.reserved_page", wikiName), tplWikiNew, &form)
  440. } else if models.IsErrWikiAlreadyExist(err) {
  441. ctx.Data["Err_Title"] = true
  442. ctx.RenderWithErr(ctx.Tr("repo.wiki.page_already_exists"), tplWikiNew, &form)
  443. } else {
  444. ctx.ServerError("AddWikiPage", err)
  445. }
  446. return
  447. }
  448. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + models.WikiNameToSubURL(wikiName))
  449. }
  450. // EditWiki render wiki modify page
  451. func EditWiki(ctx *context.Context) {
  452. ctx.Data["PageIsWiki"] = true
  453. ctx.Data["PageIsWikiEdit"] = true
  454. ctx.Data["RequireSimpleMDE"] = true
  455. if !ctx.Repo.Repository.HasWiki() {
  456. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  457. return
  458. }
  459. renderEditPage(ctx)
  460. if ctx.Written() {
  461. return
  462. }
  463. ctx.HTML(200, tplWikiNew)
  464. }
  465. // EditWikiPost response for wiki modify request
  466. func EditWikiPost(ctx *context.Context, form auth.NewWikiForm) {
  467. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  468. ctx.Data["PageIsWiki"] = true
  469. ctx.Data["RequireSimpleMDE"] = true
  470. if ctx.HasError() {
  471. ctx.HTML(200, tplWikiNew)
  472. return
  473. }
  474. oldWikiName := models.NormalizeWikiName(ctx.Params(":page"))
  475. newWikiName := models.NormalizeWikiName(form.Title)
  476. if err := ctx.Repo.Repository.EditWikiPage(ctx.User, oldWikiName, newWikiName, form.Content, form.Message); err != nil {
  477. ctx.ServerError("EditWikiPage", err)
  478. return
  479. }
  480. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + models.WikiNameToSubURL(newWikiName))
  481. }
  482. // DeleteWikiPagePost delete wiki page
  483. func DeleteWikiPagePost(ctx *context.Context) {
  484. wikiName := models.NormalizeWikiName(ctx.Params(":page"))
  485. if len(wikiName) == 0 {
  486. wikiName = "Home"
  487. }
  488. if err := ctx.Repo.Repository.DeleteWikiPage(ctx.User, wikiName); err != nil {
  489. ctx.ServerError("DeleteWikiPage", err)
  490. return
  491. }
  492. ctx.JSON(200, map[string]interface{}{
  493. "redirect": ctx.Repo.RepoLink + "/wiki/",
  494. })
  495. }