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

wiki.go 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  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. "bytes"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "net/url"
  12. "path/filepath"
  13. "strings"
  14. "code.gitea.io/gitea/models"
  15. "code.gitea.io/gitea/models/unit"
  16. "code.gitea.io/gitea/modules/base"
  17. "code.gitea.io/gitea/modules/context"
  18. "code.gitea.io/gitea/modules/git"
  19. "code.gitea.io/gitea/modules/log"
  20. "code.gitea.io/gitea/modules/markup"
  21. "code.gitea.io/gitea/modules/markup/markdown"
  22. "code.gitea.io/gitea/modules/setting"
  23. "code.gitea.io/gitea/modules/timeutil"
  24. "code.gitea.io/gitea/modules/util"
  25. "code.gitea.io/gitea/modules/web"
  26. "code.gitea.io/gitea/routers/common"
  27. "code.gitea.io/gitea/services/forms"
  28. wiki_service "code.gitea.io/gitea/services/wiki"
  29. )
  30. const (
  31. tplWikiStart base.TplName = "repo/wiki/start"
  32. tplWikiView base.TplName = "repo/wiki/view"
  33. tplWikiRevision base.TplName = "repo/wiki/revision"
  34. tplWikiNew base.TplName = "repo/wiki/new"
  35. tplWikiPages base.TplName = "repo/wiki/pages"
  36. )
  37. // MustEnableWiki check if wiki is enabled, if external then redirect
  38. func MustEnableWiki(ctx *context.Context) {
  39. if !ctx.Repo.CanRead(unit.TypeWiki) &&
  40. !ctx.Repo.CanRead(unit.TypeExternalWiki) {
  41. if log.IsTrace() {
  42. log.Trace("Permission Denied: User %-v cannot read %-v or %-v of repo %-v\n"+
  43. "User in repo has Permissions: %-+v",
  44. ctx.User,
  45. unit.TypeWiki,
  46. unit.TypeExternalWiki,
  47. ctx.Repo.Repository,
  48. ctx.Repo.Permission)
  49. }
  50. ctx.NotFound("MustEnableWiki", nil)
  51. return
  52. }
  53. unit, err := ctx.Repo.Repository.GetUnit(unit.TypeExternalWiki)
  54. if err == nil {
  55. ctx.Redirect(unit.ExternalWikiConfig().ExternalWikiURL)
  56. return
  57. }
  58. }
  59. // PageMeta wiki page meta information
  60. type PageMeta struct {
  61. Name string
  62. SubURL string
  63. UpdatedUnix timeutil.TimeStamp
  64. }
  65. // findEntryForFile finds the tree entry for a target filepath.
  66. func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
  67. entry, err := commit.GetTreeEntryByPath(target)
  68. if err != nil && !git.IsErrNotExist(err) {
  69. return nil, err
  70. }
  71. if entry != nil {
  72. return entry, nil
  73. }
  74. // Then the unescaped, shortest alternative
  75. var unescapedTarget string
  76. if unescapedTarget, err = url.QueryUnescape(target); err != nil {
  77. return nil, err
  78. }
  79. return commit.GetTreeEntryByPath(unescapedTarget)
  80. }
  81. func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
  82. wikiRepo, err := git.OpenRepository(ctx.Repo.Repository.WikiPath())
  83. if err != nil {
  84. ctx.ServerError("OpenRepository", err)
  85. return nil, nil, err
  86. }
  87. commit, err := wikiRepo.GetBranchCommit("master")
  88. if err != nil {
  89. return wikiRepo, nil, err
  90. }
  91. return wikiRepo, commit, nil
  92. }
  93. // wikiContentsByEntry returns the contents of the wiki page referenced by the
  94. // given tree entry. Writes to ctx if an error occurs.
  95. func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
  96. reader, err := entry.Blob().DataAsync()
  97. if err != nil {
  98. ctx.ServerError("Blob.Data", err)
  99. return nil
  100. }
  101. defer reader.Close()
  102. content, err := io.ReadAll(reader)
  103. if err != nil {
  104. ctx.ServerError("ReadAll", err)
  105. return nil
  106. }
  107. return content
  108. }
  109. // wikiContentsByName returns the contents of a wiki page, along with a boolean
  110. // indicating whether the page exists. Writes to ctx if an error occurs.
  111. func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
  112. pageFilename := wiki_service.NameToFilename(wikiName)
  113. entry, err := findEntryForFile(commit, pageFilename)
  114. if err != nil && !git.IsErrNotExist(err) {
  115. ctx.ServerError("findEntryForFile", err)
  116. return nil, nil, "", false
  117. } else if entry == nil {
  118. return nil, nil, "", true
  119. }
  120. return wikiContentsByEntry(ctx, entry), entry, pageFilename, false
  121. }
  122. func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
  123. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  124. if err != nil {
  125. if wikiRepo != nil {
  126. wikiRepo.Close()
  127. }
  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("*"))
  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/?action=_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. var rctx = &markup.RenderContext{
  202. URLPrefix: ctx.Repo.RepoLink,
  203. Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
  204. IsWiki: true,
  205. }
  206. var buf strings.Builder
  207. if err := markdown.Render(rctx, bytes.NewReader(data), &buf); err != nil {
  208. if wikiRepo != nil {
  209. wikiRepo.Close()
  210. }
  211. ctx.ServerError("Render", err)
  212. return nil, nil
  213. }
  214. ctx.Data["content"] = buf.String()
  215. buf.Reset()
  216. if err := markdown.Render(rctx, bytes.NewReader(sidebarContent), &buf); err != nil {
  217. if wikiRepo != nil {
  218. wikiRepo.Close()
  219. }
  220. ctx.ServerError("Render", err)
  221. return nil, nil
  222. }
  223. ctx.Data["sidebarPresent"] = sidebarContent != nil
  224. ctx.Data["sidebarContent"] = buf.String()
  225. buf.Reset()
  226. if err := markdown.Render(rctx, bytes.NewReader(footerContent), &buf); err != nil {
  227. if wikiRepo != nil {
  228. wikiRepo.Close()
  229. }
  230. ctx.ServerError("Render", err)
  231. return nil, nil
  232. }
  233. ctx.Data["footerPresent"] = footerContent != nil
  234. ctx.Data["footerContent"] = buf.String()
  235. // get commit count - wiki revisions
  236. commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
  237. ctx.Data["CommitCount"] = commitsCount
  238. return wikiRepo, entry
  239. }
  240. func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
  241. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  242. if err != nil {
  243. if wikiRepo != nil {
  244. wikiRepo.Close()
  245. }
  246. if !git.IsErrNotExist(err) {
  247. ctx.ServerError("GetBranchCommit", err)
  248. }
  249. return nil, nil
  250. }
  251. // get requested pagename
  252. pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
  253. if len(pageName) == 0 {
  254. pageName = "Home"
  255. }
  256. ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName)
  257. ctx.Data["old_title"] = pageName
  258. ctx.Data["Title"] = pageName
  259. ctx.Data["title"] = pageName
  260. ctx.Data["RequireHighlightJS"] = true
  261. ctx.Data["Username"] = ctx.Repo.Owner.Name
  262. ctx.Data["Reponame"] = ctx.Repo.Repository.Name
  263. //lookup filename in wiki - get filecontent, gitTree entry , real filename
  264. data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
  265. if noEntry {
  266. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
  267. }
  268. if entry == nil || ctx.Written() {
  269. if wikiRepo != nil {
  270. wikiRepo.Close()
  271. }
  272. return nil, nil
  273. }
  274. ctx.Data["content"] = string(data)
  275. ctx.Data["sidebarPresent"] = false
  276. ctx.Data["sidebarContent"] = ""
  277. ctx.Data["footerPresent"] = false
  278. ctx.Data["footerContent"] = ""
  279. // get commit count - wiki revisions
  280. commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
  281. ctx.Data["CommitCount"] = commitsCount
  282. // get page
  283. page := ctx.FormInt("page")
  284. if page <= 1 {
  285. page = 1
  286. }
  287. // get Commit Count
  288. commitsHistory, err := wikiRepo.CommitsByFileAndRangeNoFollow("master", pageFilename, page)
  289. if err != nil {
  290. if wikiRepo != nil {
  291. wikiRepo.Close()
  292. }
  293. ctx.ServerError("CommitsByFileAndRangeNoFollow", err)
  294. return nil, nil
  295. }
  296. ctx.Data["Commits"] = models.ConvertFromGitCommit(commitsHistory, ctx.Repo.Repository)
  297. pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
  298. pager.SetDefaultParams(ctx)
  299. ctx.Data["Page"] = pager
  300. return wikiRepo, entry
  301. }
  302. func renderEditPage(ctx *context.Context) {
  303. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  304. if err != nil {
  305. if wikiRepo != nil {
  306. wikiRepo.Close()
  307. }
  308. if !git.IsErrNotExist(err) {
  309. ctx.ServerError("GetBranchCommit", err)
  310. }
  311. return
  312. }
  313. defer func() {
  314. if wikiRepo != nil {
  315. wikiRepo.Close()
  316. }
  317. }()
  318. // get requested pagename
  319. pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
  320. if len(pageName) == 0 {
  321. pageName = "Home"
  322. }
  323. ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName)
  324. ctx.Data["old_title"] = pageName
  325. ctx.Data["Title"] = pageName
  326. ctx.Data["title"] = pageName
  327. ctx.Data["RequireHighlightJS"] = true
  328. //lookup filename in wiki - get filecontent, gitTree entry , real filename
  329. data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
  330. if noEntry {
  331. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
  332. }
  333. if entry == nil || ctx.Written() {
  334. return
  335. }
  336. ctx.Data["content"] = string(data)
  337. ctx.Data["sidebarPresent"] = false
  338. ctx.Data["sidebarContent"] = ""
  339. ctx.Data["footerPresent"] = false
  340. ctx.Data["footerContent"] = ""
  341. }
  342. // WikiPost renders post of wiki page
  343. func WikiPost(ctx *context.Context) {
  344. switch ctx.FormString("action") {
  345. case "_new":
  346. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  347. ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
  348. return
  349. }
  350. NewWikiPost(ctx)
  351. return
  352. case "_delete":
  353. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  354. ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
  355. return
  356. }
  357. DeleteWikiPagePost(ctx)
  358. return
  359. }
  360. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  361. ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
  362. return
  363. }
  364. EditWikiPost(ctx)
  365. }
  366. // Wiki renders single wiki page
  367. func Wiki(ctx *context.Context) {
  368. ctx.Data["PageIsWiki"] = true
  369. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
  370. switch ctx.FormString("action") {
  371. case "_pages":
  372. WikiPages(ctx)
  373. return
  374. case "_revision":
  375. WikiRevision(ctx)
  376. return
  377. case "_edit":
  378. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  379. ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
  380. return
  381. }
  382. EditWiki(ctx)
  383. return
  384. case "_new":
  385. if !ctx.Repo.CanWrite(unit.TypeWiki) {
  386. ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
  387. return
  388. }
  389. NewWiki(ctx)
  390. return
  391. }
  392. if !ctx.Repo.Repository.HasWiki() {
  393. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  394. ctx.HTML(http.StatusOK, tplWikiStart)
  395. return
  396. }
  397. wikiRepo, entry := renderViewPage(ctx)
  398. defer func() {
  399. if wikiRepo != nil {
  400. wikiRepo.Close()
  401. }
  402. }()
  403. if ctx.Written() {
  404. return
  405. }
  406. if entry == nil {
  407. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  408. ctx.HTML(http.StatusOK, tplWikiStart)
  409. return
  410. }
  411. wikiPath := entry.Name()
  412. if markup.Type(wikiPath) != markdown.MarkupName {
  413. ext := strings.ToUpper(filepath.Ext(wikiPath))
  414. ctx.Data["FormatWarning"] = fmt.Sprintf("%s rendering is not supported at the moment. Rendered as Markdown.", ext)
  415. }
  416. // Get last change information.
  417. lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
  418. if err != nil {
  419. ctx.ServerError("GetCommitByPath", err)
  420. return
  421. }
  422. ctx.Data["Author"] = lastCommit.Author
  423. ctx.HTML(http.StatusOK, tplWikiView)
  424. }
  425. // WikiRevision renders file revision list of wiki page
  426. func WikiRevision(ctx *context.Context) {
  427. ctx.Data["PageIsWiki"] = true
  428. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
  429. if !ctx.Repo.Repository.HasWiki() {
  430. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  431. ctx.HTML(http.StatusOK, tplWikiStart)
  432. return
  433. }
  434. wikiRepo, entry := renderRevisionPage(ctx)
  435. defer func() {
  436. if wikiRepo != nil {
  437. wikiRepo.Close()
  438. }
  439. }()
  440. if ctx.Written() {
  441. return
  442. }
  443. if entry == nil {
  444. ctx.Data["Title"] = ctx.Tr("repo.wiki")
  445. ctx.HTML(http.StatusOK, tplWikiStart)
  446. return
  447. }
  448. // Get last change information.
  449. wikiPath := entry.Name()
  450. lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
  451. if err != nil {
  452. ctx.ServerError("GetCommitByPath", err)
  453. return
  454. }
  455. ctx.Data["Author"] = lastCommit.Author
  456. ctx.HTML(http.StatusOK, tplWikiRevision)
  457. }
  458. // WikiPages render wiki pages list page
  459. func WikiPages(ctx *context.Context) {
  460. if !ctx.Repo.Repository.HasWiki() {
  461. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  462. return
  463. }
  464. ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
  465. ctx.Data["PageIsWiki"] = true
  466. ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
  467. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  468. if err != nil {
  469. if wikiRepo != nil {
  470. wikiRepo.Close()
  471. }
  472. return
  473. }
  474. defer func() {
  475. if wikiRepo != nil {
  476. wikiRepo.Close()
  477. }
  478. }()
  479. entries, err := commit.ListEntries()
  480. if err != nil {
  481. ctx.ServerError("ListEntries", err)
  482. return
  483. }
  484. pages := make([]PageMeta, 0, len(entries))
  485. for _, entry := range entries {
  486. if !entry.IsRegular() {
  487. continue
  488. }
  489. c, err := wikiRepo.GetCommitByPath(entry.Name())
  490. if err != nil {
  491. ctx.ServerError("GetCommit", err)
  492. return
  493. }
  494. wikiName, err := wiki_service.FilenameToName(entry.Name())
  495. if err != nil {
  496. if models.IsErrWikiInvalidFileName(err) {
  497. continue
  498. }
  499. ctx.ServerError("WikiFilenameToName", err)
  500. return
  501. }
  502. pages = append(pages, PageMeta{
  503. Name: wikiName,
  504. SubURL: wiki_service.NameToSubURL(wikiName),
  505. UpdatedUnix: timeutil.TimeStamp(c.Author.When.Unix()),
  506. })
  507. }
  508. ctx.Data["Pages"] = pages
  509. ctx.HTML(http.StatusOK, tplWikiPages)
  510. }
  511. // WikiRaw outputs raw blob requested by user (image for example)
  512. func WikiRaw(ctx *context.Context) {
  513. wikiRepo, commit, err := findWikiRepoCommit(ctx)
  514. defer func() {
  515. if wikiRepo != nil {
  516. wikiRepo.Close()
  517. }
  518. }()
  519. if err != nil {
  520. if git.IsErrNotExist(err) {
  521. ctx.NotFound("findEntryForFile", nil)
  522. return
  523. }
  524. ctx.ServerError("findEntryForfile", err)
  525. return
  526. }
  527. providedPath := ctx.Params("*")
  528. var entry *git.TreeEntry
  529. if commit != nil {
  530. // Try to find a file with that name
  531. entry, err = findEntryForFile(commit, providedPath)
  532. if err != nil && !git.IsErrNotExist(err) {
  533. ctx.ServerError("findFile", err)
  534. return
  535. }
  536. if entry == nil {
  537. // Try to find a wiki page with that name
  538. providedPath = strings.TrimSuffix(providedPath, ".md")
  539. wikiPath := wiki_service.NameToFilename(providedPath)
  540. entry, err = findEntryForFile(commit, wikiPath)
  541. if err != nil && !git.IsErrNotExist(err) {
  542. ctx.ServerError("findFile", err)
  543. return
  544. }
  545. }
  546. }
  547. if entry != nil {
  548. if err = common.ServeBlob(ctx, entry.Blob()); err != nil {
  549. ctx.ServerError("ServeBlob", err)
  550. }
  551. return
  552. }
  553. ctx.NotFound("findEntryForFile", nil)
  554. }
  555. // NewWiki render wiki create page
  556. func NewWiki(ctx *context.Context) {
  557. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  558. ctx.Data["PageIsWiki"] = true
  559. ctx.Data["RequireSimpleMDE"] = true
  560. if !ctx.Repo.Repository.HasWiki() {
  561. ctx.Data["title"] = "Home"
  562. }
  563. if ctx.FormString("title") != "" {
  564. ctx.Data["title"] = ctx.FormString("title")
  565. }
  566. ctx.HTML(http.StatusOK, tplWikiNew)
  567. }
  568. // NewWikiPost response for wiki create request
  569. func NewWikiPost(ctx *context.Context) {
  570. form := web.GetForm(ctx).(*forms.NewWikiForm)
  571. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  572. ctx.Data["PageIsWiki"] = true
  573. ctx.Data["RequireSimpleMDE"] = true
  574. if ctx.HasError() {
  575. ctx.HTML(http.StatusOK, tplWikiNew)
  576. return
  577. }
  578. if util.IsEmptyString(form.Title) {
  579. ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplWikiNew, form)
  580. return
  581. }
  582. wikiName := wiki_service.NormalizeWikiName(form.Title)
  583. if len(form.Message) == 0 {
  584. form.Message = ctx.Tr("repo.editor.add", form.Title)
  585. }
  586. if err := wiki_service.AddWikiPage(ctx.User, ctx.Repo.Repository, wikiName, form.Content, form.Message); err != nil {
  587. if models.IsErrWikiReservedName(err) {
  588. ctx.Data["Err_Title"] = true
  589. ctx.RenderWithErr(ctx.Tr("repo.wiki.reserved_page", wikiName), tplWikiNew, &form)
  590. } else if models.IsErrWikiAlreadyExist(err) {
  591. ctx.Data["Err_Title"] = true
  592. ctx.RenderWithErr(ctx.Tr("repo.wiki.page_already_exists"), tplWikiNew, &form)
  593. } else {
  594. ctx.ServerError("AddWikiPage", err)
  595. }
  596. return
  597. }
  598. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(wikiName))
  599. }
  600. // EditWiki render wiki modify page
  601. func EditWiki(ctx *context.Context) {
  602. ctx.Data["PageIsWiki"] = true
  603. ctx.Data["PageIsWikiEdit"] = true
  604. ctx.Data["RequireSimpleMDE"] = true
  605. if !ctx.Repo.Repository.HasWiki() {
  606. ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
  607. return
  608. }
  609. renderEditPage(ctx)
  610. if ctx.Written() {
  611. return
  612. }
  613. ctx.HTML(http.StatusOK, tplWikiNew)
  614. }
  615. // EditWikiPost response for wiki modify request
  616. func EditWikiPost(ctx *context.Context) {
  617. form := web.GetForm(ctx).(*forms.NewWikiForm)
  618. ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
  619. ctx.Data["PageIsWiki"] = true
  620. ctx.Data["RequireSimpleMDE"] = true
  621. if ctx.HasError() {
  622. ctx.HTML(http.StatusOK, tplWikiNew)
  623. return
  624. }
  625. oldWikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
  626. newWikiName := wiki_service.NormalizeWikiName(form.Title)
  627. if len(form.Message) == 0 {
  628. form.Message = ctx.Tr("repo.editor.update", form.Title)
  629. }
  630. if err := wiki_service.EditWikiPage(ctx.User, ctx.Repo.Repository, oldWikiName, newWikiName, form.Content, form.Message); err != nil {
  631. ctx.ServerError("EditWikiPage", err)
  632. return
  633. }
  634. ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(newWikiName))
  635. }
  636. // DeleteWikiPagePost delete wiki page
  637. func DeleteWikiPagePost(ctx *context.Context) {
  638. wikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
  639. if len(wikiName) == 0 {
  640. wikiName = "Home"
  641. }
  642. if err := wiki_service.DeleteWikiPage(ctx.User, ctx.Repo.Repository, wikiName); err != nil {
  643. ctx.ServerError("DeleteWikiPage", err)
  644. return
  645. }
  646. ctx.JSON(http.StatusOK, map[string]interface{}{
  647. "redirect": ctx.Repo.RepoLink + "/wiki/",
  648. })
  649. }