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

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