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 17 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  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. "net/url"
  10. "path/filepath"
  11. "strings"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/auth"
  14. "code.gitea.io/gitea/modules/base"
  15. "code.gitea.io/gitea/modules/context"
  16. "code.gitea.io/gitea/modules/git"
  17. "code.gitea.io/gitea/modules/log"
  18. "code.gitea.io/gitea/modules/markup"
  19. "code.gitea.io/gitea/modules/markup/markdown"
  20. "code.gitea.io/gitea/modules/timeutil"
  21. "code.gitea.io/gitea/modules/util"
  22. wiki_service "code.gitea.io/gitea/services/wiki"
  23. )
  24. const (
  25. tplWikiStart base.TplName = "repo/wiki/start"
  26. tplWikiView base.TplName = "repo/wiki/view"
  27. tplWikiRevision base.TplName = "repo/wiki/revision"
  28. tplWikiNew base.TplName = "repo/wiki/new"
  29. tplWikiPages base.TplName = "repo/wiki/pages"
  30. )
  31. // MustEnableWiki check if wiki is enabled, if external then redirect
  32. func MustEnableWiki(ctx *context.Context) {
  33. if !ctx.Repo.CanRead(models.UnitTypeWiki) &&
  34. !ctx.Repo.CanRead(models.UnitTypeExternalWiki) {
  35. if log.IsTrace() {
  36. log.Trace("Permission Denied: User %-v cannot read %-v or %-v of repo %-v\n"+
  37. "User in repo has Permissions: %-+v",
  38. ctx.User,
  39. models.UnitTypeWiki,
  40. models.UnitTypeExternalWiki,
  41. ctx.Repo.Repository,
  42. ctx.Repo.Permission)
  43. }
  44. ctx.NotFound("MustEnableWiki", nil)
  45. return
  46. }
  47. unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalWiki)
  48. if err == nil {
  49. ctx.Redirect(unit.ExternalWikiConfig().ExternalWikiURL)
  50. return
  51. }
  52. }
  53. // PageMeta wiki page meta information
  54. type PageMeta struct {
  55. Name string
  56. SubURL string
  57. UpdatedUnix timeutil.TimeStamp
  58. }
  59. // findEntryForFile finds the tree entry for a target filepath.
  60. func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
  61. entries, err := commit.ListEntries()
  62. if err != nil {
  63. return nil, err
  64. }
  65. // The longest name should be checked first
  66. for _, entry := range entries {
  67. if entry.IsRegular() && entry.Name() == target {
  68. return entry, nil
  69. }
  70. }
  71. // Then the unescaped, shortest alternative
  72. var unescapedTarget string
  73. if unescapedTarget, err = url.QueryUnescape(target); err != nil {
  74. return nil, err
  75. }
  76. for _, entry := range entries {
  77. if entry.IsRegular() && entry.Name() == unescapedTarget {
  78. return entry, nil
  79. }
  80. }
  81. return nil, nil
  82. }
  83. func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
  84. wikiRepo, err := git.OpenRepository(ctx.Repo.Repository.WikiPath())
  85. if err != nil {
  86. ctx.ServerError("OpenRepository", err)
  87. return nil, nil, err
  88. }
  89. commit, err := wikiRepo.GetBranchCommit("master")
  90. if err != nil {
  91. return wikiRepo, nil, err
  92. }
  93. return wikiRepo, commit, nil
  94. }
  95. // wikiContentsByEntry returns the contents of the wiki page referenced by the
  96. // given tree entry. Writes to ctx if an error occurs.
  97. func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
  98. reader, err := entry.Blob().DataAsync()
  99. if err != nil {
  100. ctx.ServerError("Blob.Data", err)
  101. return nil
  102. }
  103. defer reader.Close()
  104. content, err := ioutil.ReadAll(reader)
  105. if err != nil {
  106. ctx.ServerError("ReadAll", err)
  107. return nil
  108. }
  109. return content
  110. }
  111. // wikiContentsByName returns the contents of a wiki page, along with a boolean
  112. // indicating whether the page exists. Writes to ctx if an error occurs.
  113. func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
  114. var entry *git.TreeEntry
  115. var err error
  116. pageFilename := wiki_service.NameToFilename(wikiName)
  117. if entry, err = findEntryForFile(commit, pageFilename); err != nil {
  118. ctx.ServerError("findEntryForFile", err)
  119. return nil, nil, "", false
  120. } else if entry == nil {
  121. return nil, nil, "", true
  122. }
  123. return wikiContentsByEntry(ctx, entry), entry, pageFilename, false
  124. }
  125. func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
  126. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  127. if err != nil {
  128. if !git.IsErrNotExist(err) {
  129. ctx.ServerError("GetBranchCommit", err)
  130. }
  131. return nil, nil
  132. }
  133. // Get page list.
  134. entries, err := commit.ListEntries()
  135. if err != nil {
  136. if wikiRepo != nil {
  137. wikiRepo.Close()
  138. }
  139. ctx.ServerError("ListEntries", err)
  140. return nil, nil
  141. }
  142. pages := make([]PageMeta, 0, len(entries))
  143. for _, entry := range entries {
  144. if !entry.IsRegular() {
  145. continue
  146. }
  147. wikiName, err := wiki_service.FilenameToName(entry.Name())
  148. if err != nil {
  149. if models.IsErrWikiInvalidFileName(err) {
  150. continue
  151. }
  152. if wikiRepo != nil {
  153. wikiRepo.Close()
  154. }
  155. ctx.ServerError("WikiFilenameToName", err)
  156. return nil, nil
  157. } else if wikiName == "_Sidebar" || wikiName == "_Footer" {
  158. continue
  159. }
  160. pages = append(pages, PageMeta{
  161. Name: wikiName,
  162. SubURL: wiki_service.NameToSubURL(wikiName),
  163. })
  164. }
  165. ctx.Data["Pages"] = pages
  166. // get requested pagename
  167. pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
  168. if len(pageName) == 0 {
  169. pageName = "Home"
  170. }
  171. ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName)
  172. ctx.Data["old_title"] = pageName
  173. ctx.Data["Title"] = pageName
  174. ctx.Data["title"] = pageName
  175. ctx.Data["RequireHighlightJS"] = true
  176. //lookup filename in wiki - get filecontent, gitTree entry , real filename
  177. data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
  178. if noEntry {
  179. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
  180. }
  181. if entry == nil || ctx.Written() {
  182. if wikiRepo != nil {
  183. wikiRepo.Close()
  184. }
  185. return nil, nil
  186. }
  187. sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar")
  188. if ctx.Written() {
  189. if wikiRepo != nil {
  190. wikiRepo.Close()
  191. }
  192. return nil, nil
  193. }
  194. footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer")
  195. if ctx.Written() {
  196. if wikiRepo != nil {
  197. wikiRepo.Close()
  198. }
  199. return nil, nil
  200. }
  201. metas := ctx.Repo.Repository.ComposeMetas()
  202. ctx.Data["content"] = markdown.RenderWiki(data, ctx.Repo.RepoLink, metas)
  203. ctx.Data["sidebarPresent"] = sidebarContent != nil
  204. ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas)
  205. ctx.Data["footerPresent"] = footerContent != nil
  206. ctx.Data["footerContent"] = markdown.RenderWiki(footerContent, ctx.Repo.RepoLink, metas)
  207. // get commit count - wiki revisions
  208. commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
  209. ctx.Data["CommitCount"] = commitsCount
  210. return wikiRepo, entry
  211. }
  212. func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
  213. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  214. if err != nil {
  215. if wikiRepo != nil {
  216. wikiRepo.Close()
  217. }
  218. if !git.IsErrNotExist(err) {
  219. ctx.ServerError("GetBranchCommit", err)
  220. }
  221. return nil, nil
  222. }
  223. // get requested pagename
  224. pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
  225. if len(pageName) == 0 {
  226. pageName = "Home"
  227. }
  228. ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName)
  229. ctx.Data["old_title"] = pageName
  230. ctx.Data["Title"] = pageName
  231. ctx.Data["title"] = pageName
  232. ctx.Data["RequireHighlightJS"] = true
  233. //lookup filename in wiki - get filecontent, gitTree entry , real filename
  234. data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
  235. if noEntry {
  236. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
  237. }
  238. if entry == nil || ctx.Written() {
  239. if wikiRepo != nil {
  240. wikiRepo.Close()
  241. }
  242. return nil, nil
  243. }
  244. ctx.Data["content"] = string(data)
  245. ctx.Data["sidebarPresent"] = false
  246. ctx.Data["sidebarContent"] = ""
  247. ctx.Data["footerPresent"] = false
  248. ctx.Data["footerContent"] = ""
  249. // get commit count - wiki revisions
  250. commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
  251. ctx.Data["CommitCount"] = commitsCount
  252. // get page
  253. page := ctx.QueryInt("page")
  254. if page <= 1 {
  255. page = 1
  256. }
  257. // get Commit Count
  258. commitsHistory, err := wikiRepo.CommitsByFileAndRangeNoFollow("master", pageFilename, page)
  259. if err != nil {
  260. if wikiRepo != nil {
  261. wikiRepo.Close()
  262. }
  263. ctx.ServerError("CommitsByFileAndRangeNoFollow", err)
  264. return nil, nil
  265. }
  266. commitsHistory = models.ValidateCommitsWithEmails(commitsHistory)
  267. commitsHistory = models.ParseCommitsWithSignature(commitsHistory)
  268. ctx.Data["Commits"] = commitsHistory
  269. pager := context.NewPagination(int(commitsCount), git.CommitsRangeSize, page, 5)
  270. pager.SetDefaultParams(ctx)
  271. ctx.Data["Page"] = pager
  272. return wikiRepo, entry
  273. }
  274. func renderEditPage(ctx *context.Context) {
  275. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  276. if err != nil {
  277. if wikiRepo != nil {
  278. wikiRepo.Close()
  279. }
  280. if !git.IsErrNotExist(err) {
  281. ctx.ServerError("GetBranchCommit", err)
  282. }
  283. return
  284. }
  285. defer func() {
  286. if wikiRepo != nil {
  287. wikiRepo.Close()
  288. }
  289. }()
  290. // get requested pagename
  291. pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
  292. if len(pageName) == 0 {
  293. pageName = "Home"
  294. }
  295. ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName)
  296. ctx.Data["old_title"] = pageName
  297. ctx.Data["Title"] = pageName
  298. ctx.Data["title"] = pageName
  299. ctx.Data["RequireHighlightJS"] = true
  300. //lookup filename in wiki - get filecontent, gitTree entry , real filename
  301. data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
  302. if noEntry {
  303. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
  304. }
  305. if entry == nil || ctx.Written() {
  306. return
  307. }
  308. ctx.Data["content"] = string(data)
  309. ctx.Data["sidebarPresent"] = false
  310. ctx.Data["sidebarContent"] = ""
  311. ctx.Data["footerPresent"] = false
  312. ctx.Data["footerContent"] = ""
  313. }
  314. // Wiki renders single wiki page
  315. func Wiki(ctx *context.Context) {
  316. ctx.Data["PageIsWiki"] = true
  317. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
  318. if !ctx.Repo.Repository.HasWiki() {
  319. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  320. ctx.HTML(200, tplWikiStart)
  321. return
  322. }
  323. wikiRepo, entry := renderViewPage(ctx)
  324. if ctx.Written() {
  325. if wikiRepo != nil {
  326. wikiRepo.Close()
  327. }
  328. return
  329. }
  330. defer func() {
  331. if wikiRepo != nil {
  332. wikiRepo.Close()
  333. }
  334. }()
  335. if entry == nil {
  336. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  337. ctx.HTML(200, tplWikiStart)
  338. return
  339. }
  340. wikiPath := entry.Name()
  341. if markup.Type(wikiPath) != markdown.MarkupName {
  342. ext := strings.ToUpper(filepath.Ext(wikiPath))
  343. ctx.Data["FormatWarning"] = fmt.Sprintf("%s rendering is not supported at the moment. Rendered as Markdown.", ext)
  344. }
  345. // Get last change information.
  346. lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
  347. if err != nil {
  348. ctx.ServerError("GetCommitByPath", err)
  349. return
  350. }
  351. ctx.Data["Author"] = lastCommit.Author
  352. ctx.HTML(200, tplWikiView)
  353. }
  354. // WikiRevision renders file revision list of wiki page
  355. func WikiRevision(ctx *context.Context) {
  356. ctx.Data["PageIsWiki"] = true
  357. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
  358. if !ctx.Repo.Repository.HasWiki() {
  359. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  360. ctx.HTML(200, tplWikiStart)
  361. return
  362. }
  363. wikiRepo, entry := renderRevisionPage(ctx)
  364. if ctx.Written() {
  365. if wikiRepo != nil {
  366. wikiRepo.Close()
  367. }
  368. return
  369. }
  370. defer func() {
  371. if wikiRepo != nil {
  372. wikiRepo.Close()
  373. }
  374. }()
  375. if entry == nil {
  376. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  377. ctx.HTML(200, tplWikiStart)
  378. return
  379. }
  380. // Get last change information.
  381. wikiPath := entry.Name()
  382. lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
  383. if err != nil {
  384. ctx.ServerError("GetCommitByPath", err)
  385. return
  386. }
  387. ctx.Data["Author"] = lastCommit.Author
  388. ctx.HTML(200, tplWikiRevision)
  389. }
  390. // WikiPages render wiki pages list page
  391. func WikiPages(ctx *context.Context) {
  392. if !ctx.Repo.Repository.HasWiki() {
  393. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  394. return
  395. }
  396. ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
  397. ctx.Data["PageIsWiki"] = true
  398. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
  399. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  400. if err != nil {
  401. if wikiRepo != nil {
  402. wikiRepo.Close()
  403. }
  404. return
  405. }
  406. entries, err := commit.ListEntries()
  407. if err != nil {
  408. if wikiRepo != nil {
  409. wikiRepo.Close()
  410. }
  411. ctx.ServerError("ListEntries", err)
  412. return
  413. }
  414. pages := make([]PageMeta, 0, len(entries))
  415. for _, entry := range entries {
  416. if !entry.IsRegular() {
  417. continue
  418. }
  419. c, err := wikiRepo.GetCommitByPath(entry.Name())
  420. if err != nil {
  421. if wikiRepo != nil {
  422. wikiRepo.Close()
  423. }
  424. ctx.ServerError("GetCommit", err)
  425. return
  426. }
  427. wikiName, err := wiki_service.FilenameToName(entry.Name())
  428. if err != nil {
  429. if models.IsErrWikiInvalidFileName(err) {
  430. continue
  431. }
  432. if wikiRepo != nil {
  433. wikiRepo.Close()
  434. }
  435. ctx.ServerError("WikiFilenameToName", err)
  436. return
  437. }
  438. pages = append(pages, PageMeta{
  439. Name: wikiName,
  440. SubURL: wiki_service.NameToSubURL(wikiName),
  441. UpdatedUnix: timeutil.TimeStamp(c.Author.When.Unix()),
  442. })
  443. }
  444. ctx.Data["Pages"] = pages
  445. defer func() {
  446. if wikiRepo != nil {
  447. wikiRepo.Close()
  448. }
  449. }()
  450. ctx.HTML(200, tplWikiPages)
  451. }
  452. // WikiRaw outputs raw blob requested by user (image for example)
  453. func WikiRaw(ctx *context.Context) {
  454. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  455. if err != nil {
  456. if wikiRepo != nil {
  457. return
  458. }
  459. }
  460. providedPath := ctx.Params("*")
  461. var entry *git.TreeEntry
  462. if commit != nil {
  463. // Try to find a file with that name
  464. entry, err = findEntryForFile(commit, providedPath)
  465. if err != nil {
  466. ctx.ServerError("findFile", err)
  467. return
  468. }
  469. if entry == nil {
  470. // Try to find a wiki page with that name
  471. if strings.HasSuffix(providedPath, ".md") {
  472. providedPath = providedPath[:len(providedPath)-3]
  473. }
  474. wikiPath := wiki_service.NameToFilename(providedPath)
  475. entry, err = findEntryForFile(commit, wikiPath)
  476. if err != nil {
  477. ctx.ServerError("findFile", err)
  478. return
  479. }
  480. }
  481. }
  482. if entry != nil {
  483. if err = ServeBlob(ctx, entry.Blob()); err != nil {
  484. ctx.ServerError("ServeBlob", err)
  485. }
  486. return
  487. }
  488. ctx.NotFound("findEntryForFile", nil)
  489. }
  490. // NewWiki render wiki create page
  491. func NewWiki(ctx *context.Context) {
  492. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  493. ctx.Data["PageIsWiki"] = true
  494. ctx.Data["RequireSimpleMDE"] = true
  495. if !ctx.Repo.Repository.HasWiki() {
  496. ctx.Data["title"] = "Home"
  497. }
  498. ctx.HTML(200, tplWikiNew)
  499. }
  500. // NewWikiPost response for wiki create request
  501. func NewWikiPost(ctx *context.Context, form auth.NewWikiForm) {
  502. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  503. ctx.Data["PageIsWiki"] = true
  504. ctx.Data["RequireSimpleMDE"] = true
  505. if ctx.HasError() {
  506. ctx.HTML(200, tplWikiNew)
  507. return
  508. }
  509. if util.IsEmptyString(form.Title) {
  510. ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplWikiNew, form)
  511. return
  512. }
  513. wikiName := wiki_service.NormalizeWikiName(form.Title)
  514. if err := wiki_service.AddWikiPage(ctx.User, ctx.Repo.Repository, wikiName, form.Content, form.Message); err != nil {
  515. if models.IsErrWikiReservedName(err) {
  516. ctx.Data["Err_Title"] = true
  517. ctx.RenderWithErr(ctx.Tr("repo.wiki.reserved_page", wikiName), tplWikiNew, &form)
  518. } else if models.IsErrWikiAlreadyExist(err) {
  519. ctx.Data["Err_Title"] = true
  520. ctx.RenderWithErr(ctx.Tr("repo.wiki.page_already_exists"), tplWikiNew, &form)
  521. } else {
  522. ctx.ServerError("AddWikiPage", err)
  523. }
  524. return
  525. }
  526. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(wikiName))
  527. }
  528. // EditWiki render wiki modify page
  529. func EditWiki(ctx *context.Context) {
  530. ctx.Data["PageIsWiki"] = true
  531. ctx.Data["PageIsWikiEdit"] = true
  532. ctx.Data["RequireSimpleMDE"] = true
  533. if !ctx.Repo.Repository.HasWiki() {
  534. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  535. return
  536. }
  537. renderEditPage(ctx)
  538. if ctx.Written() {
  539. return
  540. }
  541. ctx.HTML(200, tplWikiNew)
  542. }
  543. // EditWikiPost response for wiki modify request
  544. func EditWikiPost(ctx *context.Context, form auth.NewWikiForm) {
  545. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  546. ctx.Data["PageIsWiki"] = true
  547. ctx.Data["RequireSimpleMDE"] = true
  548. if ctx.HasError() {
  549. ctx.HTML(200, tplWikiNew)
  550. return
  551. }
  552. oldWikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
  553. newWikiName := wiki_service.NormalizeWikiName(form.Title)
  554. if err := wiki_service.EditWikiPage(ctx.User, ctx.Repo.Repository, oldWikiName, newWikiName, form.Content, form.Message); err != nil {
  555. ctx.ServerError("EditWikiPage", err)
  556. return
  557. }
  558. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(newWikiName))
  559. }
  560. // DeleteWikiPagePost delete wiki page
  561. func DeleteWikiPagePost(ctx *context.Context) {
  562. wikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
  563. if len(wikiName) == 0 {
  564. wikiName = "Home"
  565. }
  566. if err := wiki_service.DeleteWikiPage(ctx.User, ctx.Repo.Repository, wikiName); err != nil {
  567. ctx.ServerError("DeleteWikiPage", err)
  568. return
  569. }
  570. ctx.JSON(200, map[string]interface{}{
  571. "redirect": ctx.Repo.RepoLink + "/wiki/",
  572. })
  573. }