gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package repo
  5. import (
  6. "bytes"
  7. "html/template"
  8. "io"
  9. "net/http"
  10. "net/url"
  11. "path/filepath"
  12. "strings"
  13. "code.gitea.io/gitea/models/renderhelper"
  14. repo_model "code.gitea.io/gitea/models/repo"
  15. "code.gitea.io/gitea/models/unit"
  16. "code.gitea.io/gitea/modules/base"
  17. "code.gitea.io/gitea/modules/charset"
  18. "code.gitea.io/gitea/modules/git"
  19. "code.gitea.io/gitea/modules/gitrepo"
  20. "code.gitea.io/gitea/modules/log"
  21. "code.gitea.io/gitea/modules/markup"
  22. "code.gitea.io/gitea/modules/markup/markdown"
  23. "code.gitea.io/gitea/modules/setting"
  24. "code.gitea.io/gitea/modules/templates"
  25. "code.gitea.io/gitea/modules/timeutil"
  26. "code.gitea.io/gitea/modules/util"
  27. "code.gitea.io/gitea/modules/web"
  28. "code.gitea.io/gitea/routers/common"
  29. "code.gitea.io/gitea/services/context"
  30. "code.gitea.io/gitea/services/forms"
  31. git_service "code.gitea.io/gitea/services/git"
  32. notify_service "code.gitea.io/gitea/services/notify"
  33. repo_service "code.gitea.io/gitea/services/repository"
  34. wiki_service "code.gitea.io/gitea/services/wiki"
  35. )
  36. const (
  37. tplWikiStart templates.TplName = "repo/wiki/start"
  38. tplWikiView templates.TplName = "repo/wiki/view"
  39. tplWikiRevision templates.TplName = "repo/wiki/revision"
  40. tplWikiNew templates.TplName = "repo/wiki/new"
  41. tplWikiPages templates.TplName = "repo/wiki/pages"
  42. )
  43. // MustEnableWiki check if wiki is enabled, if external then redirect
  44. func MustEnableWiki(ctx *context.Context) {
  45. if !ctx.Repo.CanRead(unit.TypeWiki) &&
  46. !ctx.Repo.CanRead(unit.TypeExternalWiki) {
  47. if log.IsTrace() {
  48. log.Trace("Permission Denied: User %-v cannot read %-v or %-v of repo %-v\n"+
  49. "User in repo has Permissions: %-+v",
  50. ctx.Doer,
  51. unit.TypeWiki,
  52. unit.TypeExternalWiki,
  53. ctx.Repo.Repository,
  54. ctx.Repo.Permission)
  55. }
  56. ctx.NotFound(nil)
  57. return
  58. }
  59. repoUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalWiki)
  60. if err == nil {
  61. ctx.Redirect(repoUnit.ExternalWikiConfig().ExternalWikiURL)
  62. return
  63. }
  64. }
  65. // PageMeta wiki page meta information
  66. type PageMeta struct {
  67. Name string
  68. SubURL string
  69. GitEntryName string
  70. UpdatedUnix timeutil.TimeStamp
  71. }
  72. // findEntryForFile finds the tree entry for a target filepath.
  73. func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
  74. entry, err := commit.GetTreeEntryByPath(target)
  75. if err != nil && !git.IsErrNotExist(err) {
  76. return nil, err
  77. }
  78. if entry != nil {
  79. return entry, nil
  80. }
  81. // Then the unescaped, the shortest alternative
  82. var unescapedTarget string
  83. if unescapedTarget, err = url.QueryUnescape(target); err != nil {
  84. return nil, err
  85. }
  86. return commit.GetTreeEntryByPath(unescapedTarget)
  87. }
  88. func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
  89. wikiGitRepo, errGitRepo := gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository.WikiStorageRepo())
  90. if errGitRepo != nil {
  91. ctx.ServerError("OpenRepository", errGitRepo)
  92. return nil, nil, errGitRepo
  93. }
  94. commit, errCommit := wikiGitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch)
  95. if git.IsErrNotExist(errCommit) {
  96. // if the default branch recorded in database is out of sync, then re-sync it
  97. gitRepoDefaultBranch, errBranch := gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository.WikiStorageRepo())
  98. if errBranch != nil {
  99. return wikiGitRepo, nil, errBranch
  100. }
  101. // update the default branch in the database
  102. errDb := repo_model.UpdateRepositoryColsNoAutoTime(ctx, &repo_model.Repository{ID: ctx.Repo.Repository.ID, DefaultWikiBranch: gitRepoDefaultBranch}, "default_wiki_branch")
  103. if errDb != nil {
  104. return wikiGitRepo, nil, errDb
  105. }
  106. ctx.Repo.Repository.DefaultWikiBranch = gitRepoDefaultBranch
  107. // retry to get the commit from the correct default branch
  108. commit, errCommit = wikiGitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch)
  109. }
  110. if errCommit != nil {
  111. return wikiGitRepo, nil, errCommit
  112. }
  113. return wikiGitRepo, commit, nil
  114. }
  115. // wikiContentsByEntry returns the contents of the wiki page referenced by the
  116. // given tree entry. Writes to ctx if an error occurs.
  117. func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
  118. reader, err := entry.Blob().DataAsync()
  119. if err != nil {
  120. ctx.ServerError("Blob.Data", err)
  121. return nil
  122. }
  123. defer reader.Close()
  124. content, err := io.ReadAll(reader)
  125. if err != nil {
  126. ctx.ServerError("ReadAll", err)
  127. return nil
  128. }
  129. return content
  130. }
  131. // wikiEntryByName returns the entry of a wiki page, along with a boolean
  132. // indicating whether the entry exists. Writes to ctx if an error occurs.
  133. // The last return value indicates whether the file should be returned as a raw file
  134. func wikiEntryByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) (*git.TreeEntry, string, bool, bool) {
  135. isRaw := false
  136. gitFilename := wiki_service.WebPathToGitPath(wikiName)
  137. entry, err := findEntryForFile(commit, gitFilename)
  138. if err != nil && !git.IsErrNotExist(err) {
  139. ctx.ServerError("findEntryForFile", err)
  140. return nil, "", false, false
  141. }
  142. if entry == nil {
  143. // check if the file without ".md" suffix exists
  144. gitFilename := strings.TrimSuffix(gitFilename, ".md")
  145. entry, err = findEntryForFile(commit, gitFilename)
  146. if err != nil && !git.IsErrNotExist(err) {
  147. ctx.ServerError("findEntryForFile", err)
  148. return nil, "", false, false
  149. }
  150. isRaw = true
  151. }
  152. if entry == nil {
  153. return nil, "", true, false
  154. }
  155. return entry, gitFilename, false, isRaw
  156. }
  157. // wikiContentsByName returns the contents of a wiki page, along with a boolean
  158. // indicating whether the page exists. Writes to ctx if an error occurs.
  159. func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) {
  160. entry, gitFilename, noEntry, _ := wikiEntryByName(ctx, commit, wikiName)
  161. if entry == nil {
  162. return nil, nil, "", true
  163. }
  164. return wikiContentsByEntry(ctx, entry), entry, gitFilename, noEntry
  165. }
  166. func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
  167. wikiGitRepo, commit, err := findWikiRepoCommit(ctx)
  168. if err != nil {
  169. if !git.IsErrNotExist(err) {
  170. ctx.ServerError("GetBranchCommit", err)
  171. }
  172. return nil, nil
  173. }
  174. // get the wiki pages list.
  175. entries, err := commit.ListEntries()
  176. if err != nil {
  177. ctx.ServerError("ListEntries", err)
  178. return nil, nil
  179. }
  180. pages := make([]PageMeta, 0, len(entries))
  181. for _, entry := range entries {
  182. if !entry.IsRegular() {
  183. continue
  184. }
  185. wikiName, err := wiki_service.GitPathToWebPath(entry.Name())
  186. if err != nil {
  187. if repo_model.IsErrWikiInvalidFileName(err) {
  188. continue
  189. }
  190. ctx.ServerError("WikiFilenameToName", err)
  191. return nil, nil
  192. } else if wikiName == "_Sidebar" || wikiName == "_Footer" {
  193. continue
  194. }
  195. _, displayName := wiki_service.WebPathToUserTitle(wikiName)
  196. pages = append(pages, PageMeta{
  197. Name: displayName,
  198. SubURL: wiki_service.WebPathToURLPath(wikiName),
  199. GitEntryName: entry.Name(),
  200. })
  201. }
  202. ctx.Data["Pages"] = pages
  203. // get requested page name
  204. pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
  205. if len(pageName) == 0 {
  206. pageName = "Home"
  207. }
  208. _, displayName := wiki_service.WebPathToUserTitle(pageName)
  209. ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
  210. ctx.Data["old_title"] = displayName
  211. ctx.Data["Title"] = displayName
  212. ctx.Data["title"] = displayName
  213. isSideBar := pageName == "_Sidebar"
  214. isFooter := pageName == "_Footer"
  215. // lookup filename in wiki - get gitTree entry , real filename
  216. entry, pageFilename, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName)
  217. if noEntry {
  218. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
  219. }
  220. if isRaw {
  221. ctx.Redirect(util.URLJoin(ctx.Repo.RepoLink, "wiki/raw", string(pageName)))
  222. }
  223. if entry == nil || ctx.Written() {
  224. return nil, nil
  225. }
  226. // get page content
  227. data := wikiContentsByEntry(ctx, entry)
  228. if ctx.Written() {
  229. return nil, nil
  230. }
  231. rctx := renderhelper.NewRenderContextRepoWiki(ctx, ctx.Repo.Repository)
  232. renderFn := func(data []byte) (escaped *charset.EscapeStatus, output template.HTML, err error) {
  233. buf := &strings.Builder{}
  234. markupRd, markupWr := io.Pipe()
  235. defer markupWr.Close()
  236. done := make(chan struct{})
  237. go func() {
  238. // We allow NBSP here this is rendered
  239. escaped, _ = charset.EscapeControlReader(markupRd, buf, ctx.Locale, charset.RuneNBSP)
  240. output = template.HTML(buf.String())
  241. buf.Reset()
  242. close(done)
  243. }()
  244. err = markdown.Render(rctx, bytes.NewReader(data), markupWr)
  245. _ = markupWr.CloseWithError(err)
  246. <-done
  247. return escaped, output, err
  248. }
  249. ctx.Data["EscapeStatus"], ctx.Data["WikiContentHTML"], err = renderFn(data)
  250. if err != nil {
  251. ctx.ServerError("Render", err)
  252. return nil, nil
  253. }
  254. if rctx.SidebarTocNode != nil {
  255. sb := strings.Builder{}
  256. if err = markdown.SpecializedMarkdown(rctx).Renderer().Render(&sb, nil, rctx.SidebarTocNode); err != nil {
  257. log.Error("Failed to render wiki sidebar TOC: %v", err)
  258. }
  259. ctx.Data["WikiSidebarTocHTML"] = templates.SanitizeHTML(sb.String())
  260. }
  261. if !isSideBar {
  262. sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar")
  263. if ctx.Written() {
  264. return nil, nil
  265. }
  266. ctx.Data["WikiSidebarEscapeStatus"], ctx.Data["WikiSidebarHTML"], err = renderFn(sidebarContent)
  267. if err != nil {
  268. ctx.ServerError("Render", err)
  269. return nil, nil
  270. }
  271. }
  272. if !isFooter {
  273. footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer")
  274. if ctx.Written() {
  275. return nil, nil
  276. }
  277. ctx.Data["WikiFooterEscapeStatus"], ctx.Data["WikiFooterHTML"], err = renderFn(footerContent)
  278. if err != nil {
  279. ctx.ServerError("Render", err)
  280. return nil, nil
  281. }
  282. }
  283. // get commit count - wiki revisions
  284. commitsCount, _ := wikiGitRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
  285. ctx.Data["CommitCount"] = commitsCount
  286. return wikiGitRepo, entry
  287. }
  288. func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
  289. wikiGitRepo, commit, err := findWikiRepoCommit(ctx)
  290. if err != nil {
  291. if !git.IsErrNotExist(err) {
  292. ctx.ServerError("GetBranchCommit", err)
  293. }
  294. return nil, nil
  295. }
  296. // get requested page name
  297. pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
  298. if len(pageName) == 0 {
  299. pageName = "Home"
  300. }
  301. _, displayName := wiki_service.WebPathToUserTitle(pageName)
  302. ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
  303. ctx.Data["old_title"] = displayName
  304. ctx.Data["Title"] = displayName
  305. ctx.Data["title"] = displayName
  306. ctx.Data["Username"] = ctx.Repo.Owner.Name
  307. ctx.Data["Reponame"] = ctx.Repo.Repository.Name
  308. // lookup filename in wiki - get page content, gitTree entry , real filename
  309. _, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
  310. if noEntry {
  311. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
  312. }
  313. if entry == nil || ctx.Written() {
  314. return nil, nil
  315. }
  316. // get commit count - wiki revisions
  317. commitsCount, _ := wikiGitRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
  318. ctx.Data["CommitCount"] = commitsCount
  319. // get page
  320. page := max(ctx.FormInt("page"), 1)
  321. // get Commit Count
  322. commitsHistory, err := wikiGitRepo.CommitsByFileAndRange(
  323. git.CommitsByFileAndRangeOptions{
  324. Revision: ctx.Repo.Repository.DefaultWikiBranch,
  325. File: pageFilename,
  326. Page: page,
  327. })
  328. if err != nil {
  329. ctx.ServerError("CommitsByFileAndRange", err)
  330. return nil, nil
  331. }
  332. ctx.Data["Commits"], err = git_service.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository)
  333. if err != nil {
  334. ctx.ServerError("ConvertFromGitCommit", err)
  335. return nil, nil
  336. }
  337. pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
  338. pager.AddParamFromRequest(ctx.Req)
  339. ctx.Data["Page"] = pager
  340. return wikiGitRepo, entry
  341. }
  342. func renderEditPage(ctx *context.Context) {
  343. _, commit, err := findWikiRepoCommit(ctx)
  344. if err != nil {
  345. if !git.IsErrNotExist(err) {
  346. ctx.ServerError("GetBranchCommit", err)
  347. }
  348. return
  349. }
  350. // get requested page name
  351. pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
  352. if len(pageName) == 0 {
  353. pageName = "Home"
  354. }
  355. _, displayName := wiki_service.WebPathToUserTitle(pageName)
  356. ctx.Data["PageURL"] = wiki_service.WebPathToURLPath(pageName)
  357. ctx.Data["old_title"] = displayName
  358. ctx.Data["Title"] = displayName
  359. ctx.Data["title"] = displayName
  360. // lookup filename in wiki - gitTree entry , real filename
  361. entry, _, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName)
  362. if noEntry {
  363. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
  364. }
  365. if isRaw {
  366. ctx.HTTPError(http.StatusForbidden, "Editing of raw wiki files is not allowed")
  367. }
  368. if entry == nil || ctx.Written() {
  369. return
  370. }
  371. // get wiki page content
  372. data := wikiContentsByEntry(ctx, entry)
  373. if ctx.Written() {
  374. return
  375. }
  376. ctx.Data["WikiEditContent"] = string(data)
  377. }
  378. // WikiPost renders post of wiki page
  379. func WikiPost(ctx *context.Context) {
  380. switch ctx.FormString("action") {
  381. case "_new":
  382. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  383. ctx.NotFound(nil)
  384. return
  385. }
  386. NewWikiPost(ctx)
  387. return
  388. case "_delete":
  389. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  390. ctx.NotFound(nil)
  391. return
  392. }
  393. DeleteWikiPagePost(ctx)
  394. return
  395. }
  396. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  397. ctx.NotFound(nil)
  398. return
  399. }
  400. EditWikiPost(ctx)
  401. }
  402. // Wiki renders single wiki page
  403. func Wiki(ctx *context.Context) {
  404. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
  405. switch ctx.FormString("action") {
  406. case "_pages":
  407. WikiPages(ctx)
  408. return
  409. case "_revision":
  410. WikiRevision(ctx)
  411. return
  412. case "_edit":
  413. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  414. ctx.NotFound(nil)
  415. return
  416. }
  417. EditWiki(ctx)
  418. return
  419. case "_new":
  420. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  421. ctx.NotFound(nil)
  422. return
  423. }
  424. NewWiki(ctx)
  425. return
  426. }
  427. if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
  428. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  429. ctx.HTML(http.StatusOK, tplWikiStart)
  430. return
  431. }
  432. wikiGitRepo, entry := renderViewPage(ctx)
  433. if ctx.Written() {
  434. return
  435. }
  436. if entry == nil {
  437. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  438. ctx.HTML(http.StatusOK, tplWikiStart)
  439. return
  440. }
  441. wikiPath := entry.Name()
  442. if markup.DetectMarkupTypeByFileName(wikiPath) != markdown.MarkupName {
  443. ext := strings.ToUpper(filepath.Ext(wikiPath))
  444. ctx.Data["FormatWarning"] = ext + " rendering is not supported at the moment. Rendered as Markdown."
  445. }
  446. // Get last change information.
  447. lastCommit, err := wikiGitRepo.GetCommitByPath(wikiPath)
  448. if err != nil {
  449. ctx.ServerError("GetCommitByPath", err)
  450. return
  451. }
  452. ctx.Data["Author"] = lastCommit.Author
  453. ctx.HTML(http.StatusOK, tplWikiView)
  454. }
  455. // WikiRevision renders file revision list of wiki page
  456. func WikiRevision(ctx *context.Context) {
  457. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
  458. if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
  459. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  460. ctx.HTML(http.StatusOK, tplWikiStart)
  461. return
  462. }
  463. wikiGitRepo, entry := renderRevisionPage(ctx)
  464. if ctx.Written() {
  465. return
  466. }
  467. if entry == nil {
  468. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  469. ctx.HTML(http.StatusOK, tplWikiStart)
  470. return
  471. }
  472. // Get last change information.
  473. wikiPath := entry.Name()
  474. lastCommit, err := wikiGitRepo.GetCommitByPath(wikiPath)
  475. if err != nil {
  476. ctx.ServerError("GetCommitByPath", err)
  477. return
  478. }
  479. ctx.Data["Author"] = lastCommit.Author
  480. ctx.HTML(http.StatusOK, tplWikiRevision)
  481. }
  482. // WikiPages render wiki pages list page
  483. func WikiPages(ctx *context.Context) {
  484. if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
  485. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  486. return
  487. }
  488. ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
  489. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
  490. _, commit, err := findWikiRepoCommit(ctx)
  491. if err != nil {
  492. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  493. return
  494. }
  495. treePath := "" // To support list sub folders' pages in the future
  496. tree, err := commit.SubTree(treePath)
  497. if err != nil {
  498. ctx.ServerError("SubTree", err)
  499. return
  500. }
  501. allEntries, err := tree.ListEntries()
  502. if err != nil {
  503. ctx.ServerError("ListEntries", err)
  504. return
  505. }
  506. allEntries.CustomSort(base.NaturalSortLess)
  507. entries, _, err := allEntries.GetCommitsInfo(ctx, ctx.Repo.RepoLink, commit, treePath)
  508. if err != nil {
  509. ctx.ServerError("GetCommitsInfo", err)
  510. return
  511. }
  512. pages := make([]PageMeta, 0, len(entries))
  513. for _, entry := range entries {
  514. if !entry.Entry.IsRegular() {
  515. continue
  516. }
  517. wikiName, err := wiki_service.GitPathToWebPath(entry.Entry.Name())
  518. if err != nil {
  519. if repo_model.IsErrWikiInvalidFileName(err) {
  520. continue
  521. }
  522. ctx.ServerError("WikiFilenameToName", err)
  523. return
  524. }
  525. _, displayName := wiki_service.WebPathToUserTitle(wikiName)
  526. pages = append(pages, PageMeta{
  527. Name: displayName,
  528. SubURL: wiki_service.WebPathToURLPath(wikiName),
  529. GitEntryName: entry.Entry.Name(),
  530. UpdatedUnix: timeutil.TimeStamp(entry.Commit.Author.When.Unix()),
  531. })
  532. }
  533. ctx.Data["Pages"] = pages
  534. ctx.HTML(http.StatusOK, tplWikiPages)
  535. }
  536. // WikiRaw outputs raw blob requested by user (image for example)
  537. func WikiRaw(ctx *context.Context) {
  538. _, commit, err := findWikiRepoCommit(ctx)
  539. if err != nil {
  540. if git.IsErrNotExist(err) {
  541. ctx.NotFound(nil)
  542. return
  543. }
  544. ctx.ServerError("findEntryForfile", err)
  545. return
  546. }
  547. providedWebPath := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
  548. providedGitPath := wiki_service.WebPathToGitPath(providedWebPath)
  549. var entry *git.TreeEntry
  550. if commit != nil {
  551. // Try to find a file with that name
  552. entry, err = findEntryForFile(commit, providedGitPath)
  553. if err != nil && !git.IsErrNotExist(err) {
  554. ctx.ServerError("findFile", err)
  555. return
  556. }
  557. if entry == nil {
  558. // Try to find a wiki page with that name
  559. providedGitPath = strings.TrimSuffix(providedGitPath, ".md")
  560. entry, err = findEntryForFile(commit, providedGitPath)
  561. if err != nil && !git.IsErrNotExist(err) {
  562. ctx.ServerError("findFile", err)
  563. return
  564. }
  565. }
  566. }
  567. if entry != nil {
  568. if err = common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, entry.Blob(), nil); err != nil {
  569. ctx.ServerError("ServeBlob", err)
  570. }
  571. return
  572. }
  573. ctx.NotFound(nil)
  574. }
  575. // NewWiki render wiki create page
  576. func NewWiki(ctx *context.Context) {
  577. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  578. if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
  579. ctx.Data["title"] = "Home"
  580. }
  581. if ctx.FormString("title") != "" {
  582. ctx.Data["title"] = ctx.FormString("title")
  583. }
  584. ctx.HTML(http.StatusOK, tplWikiNew)
  585. }
  586. // NewWikiPost response for wiki create request
  587. func NewWikiPost(ctx *context.Context) {
  588. form := web.GetForm(ctx).(*forms.NewWikiForm)
  589. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  590. if ctx.HasError() {
  591. ctx.HTML(http.StatusOK, tplWikiNew)
  592. return
  593. }
  594. if util.IsEmptyString(form.Title) {
  595. ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplWikiNew, form)
  596. return
  597. }
  598. wikiName := wiki_service.UserTitleToWebPath("", form.Title)
  599. if len(form.Message) == 0 {
  600. form.Message = ctx.Locale.TrString("repo.editor.add", form.Title)
  601. }
  602. if err := wiki_service.AddWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.Content, form.Message); err != nil {
  603. if repo_model.IsErrWikiReservedName(err) {
  604. ctx.Data["Err_Title"] = true
  605. ctx.RenderWithErr(ctx.Tr("repo.wiki.reserved_page", wikiName), tplWikiNew, &form)
  606. } else if repo_model.IsErrWikiAlreadyExist(err) {
  607. ctx.Data["Err_Title"] = true
  608. ctx.RenderWithErr(ctx.Tr("repo.wiki.page_already_exists"), tplWikiNew, &form)
  609. } else {
  610. ctx.ServerError("AddWikiPage", err)
  611. }
  612. return
  613. }
  614. notify_service.NewWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(wikiName), form.Message)
  615. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.WebPathToURLPath(wikiName))
  616. }
  617. // EditWiki render wiki modify page
  618. func EditWiki(ctx *context.Context) {
  619. ctx.Data["PageIsWikiEdit"] = true
  620. if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
  621. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  622. return
  623. }
  624. renderEditPage(ctx)
  625. if ctx.Written() {
  626. return
  627. }
  628. ctx.HTML(http.StatusOK, tplWikiNew)
  629. }
  630. // EditWikiPost response for wiki modify request
  631. func EditWikiPost(ctx *context.Context) {
  632. form := web.GetForm(ctx).(*forms.NewWikiForm)
  633. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  634. if ctx.HasError() {
  635. ctx.HTML(http.StatusOK, tplWikiNew)
  636. return
  637. }
  638. oldWikiName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
  639. newWikiName := wiki_service.UserTitleToWebPath("", form.Title)
  640. if len(form.Message) == 0 {
  641. form.Message = ctx.Locale.TrString("repo.editor.update", form.Title)
  642. }
  643. if err := wiki_service.EditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, oldWikiName, newWikiName, form.Content, form.Message); err != nil {
  644. ctx.ServerError("EditWikiPage", err)
  645. return
  646. }
  647. notify_service.EditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(newWikiName), form.Message)
  648. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.WebPathToURLPath(newWikiName))
  649. }
  650. // DeleteWikiPagePost delete wiki page
  651. func DeleteWikiPagePost(ctx *context.Context) {
  652. wikiName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
  653. if len(wikiName) == 0 {
  654. wikiName = "Home"
  655. }
  656. if err := wiki_service.DeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName); err != nil {
  657. ctx.ServerError("DeleteWikiPage", err)
  658. return
  659. }
  660. notify_service.DeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(wikiName))
  661. ctx.JSONRedirect(ctx.Repo.RepoLink + "/wiki/")
  662. }