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