| @@ -21,6 +21,7 @@ import ( |
| 21 | 21 | "github.com/niklasfasching/go-org/org" |
| 22 | 22 | "github.com/yuin/goldmark" |
| 23 | 23 | |
| 24 | "gitbay.org/gitbay/internal/autolink" |
| 24 | 25 | "gitbay.org/gitbay/internal/control" |
| 25 | 26 | "gitbay.org/gitbay/internal/gitutil" |
| 26 | 27 | "gitbay.org/gitbay/internal/sig" |
| @@ -403,6 +404,65 @@ func mdHTML(raw string) template.HTML { |
| 403 | 404 | return template.HTML(buf.String()) |
| 404 | 405 | } |
| 405 | 406 | |
| 407 | // webResolver answers autolink lookups for one viewer. Cross-repo |
| 408 | // references to repositories the viewer cannot read stay plain text, per |
| 409 | // the enumeration rule: a link would confirm the repo exists. |
| 410 | type webResolver struct { |
| 411 | s *Server |
| 412 | viewer store.User |
| 413 | } |
| 414 | |
| 415 | func (r webResolver) RefURL(owner, name string, kind byte, n int64) string { |
| 416 | repo, err := r.s.st.RepoByPath(owner + "/" + name) |
| 417 | if err != nil { |
| 418 | return "" |
| 419 | } |
| 420 | grant := "" |
| 421 | if r.viewer.ID != 0 { |
| 422 | grant, _ = r.s.st.AccessRole(repo.ID, r.viewer.ID) |
| 423 | } |
| 424 | if !policy.CanRead(r.viewer, repo, grant) { |
| 425 | return "" |
| 426 | } |
| 427 | if kind == '#' { |
| 428 | if _, err := r.s.st.IssueByNumber(repo.ID, n); err != nil { |
| 429 | return "" |
| 430 | } |
| 431 | return autolink.IssueURL(repo.OwnerName, repo.Name, n) |
| 432 | } |
| 433 | if _, err := r.s.st.MRByNumber(repo.ID, n); err != nil { |
| 434 | return "" |
| 435 | } |
| 436 | return autolink.MRURL(repo.OwnerName, repo.Name, n) |
| 437 | } |
| 438 | |
| 439 | func (r webResolver) UserURL(name string) string { |
| 440 | if _, err := r.s.st.UserByUsername(name); err == nil { |
| 441 | return "/" + name |
| 442 | } |
| 443 | if _, err := r.s.st.OrgByName(name); err == nil { |
| 444 | return "/" + name |
| 445 | } |
| 446 | return "" |
| 447 | } |
| 448 | |
| 449 | // ugcFor returns a renderer for user-authored markdown on one repo's pages: |
| 450 | // mdHTML plus cross-reference and mention autolinking for this viewer. |
| 451 | func (s *Server) ugcFor(r *http.Request, repo store.Repo) func(string) template.HTML { |
| 452 | viewer := store.User{} |
| 453 | if s.cfg.Web.Mode == "accounts" { |
| 454 | viewer = s.viewer(r) |
| 455 | } |
| 456 | res := webResolver{s, viewer} |
| 457 | return func(raw string) template.HTML { |
| 458 | h := mdHTML(raw) |
| 459 | if h == "" { |
| 460 | return h |
| 461 | } |
| 462 | return template.HTML(autolink.Rewrite(string(h), repo.OwnerName, repo.Name, res)) |
| 463 | } |
| 464 | } |
| 465 | |
| 406 | 466 | // renderedComment pairs a comment with its rendered body for templates. |
| 407 | 467 | type renderedComment struct { |
| 408 | 468 | Author string |
| @@ -410,10 +470,10 @@ type renderedComment struct { |
| 410 | 470 | BodyHTML template.HTML |
| 411 | 471 | } |
| 412 | 472 | |
| 413 | | func renderComments(cs []store.IssueComment) []renderedComment { |
| 473 | func renderComments(cs []store.IssueComment, md func(string) template.HTML) []renderedComment { |
| 414 | 474 | var out []renderedComment |
| 415 | 475 | for _, c := range cs { |
| 416 | | out = append(out, renderedComment{c.Author, c.CreatedAt, mdHTML(c.Body)}) |
| 476 | out = append(out, renderedComment{c.Author, c.CreatedAt, md(c.Body)}) |
| 417 | 477 | } |
| 418 | 478 | return out |
| 419 | 479 | } |
| @@ -510,7 +570,7 @@ type diffThread struct { |
| 510 | 570 | // attachThreads injects review threads under their anchored diff lines; |
| 511 | 571 | // threads whose anchor no longer appears (stale after force-push, or on a |
| 512 | 572 | // context line outside the current diff) are returned separately. |
| 513 | | func attachThreads(lines []diffLine, comments []store.DiffComment, headSHA string) ([]diffLine, []diffThread) { |
| 573 | func attachThreads(lines []diffLine, comments []store.DiffComment, headSHA string, md func(string) template.HTML) ([]diffLine, []diffThread) { |
| 514 | 574 | type anchor struct { |
| 515 | 575 | path string |
| 516 | 576 | side string |
| @@ -522,11 +582,11 @@ func attachThreads(lines []diffLine, comments []store.DiffComment, headSHA strin |
| 522 | 582 | for _, cm := range comments { |
| 523 | 583 | if cm.ReplyTo == 0 { |
| 524 | 584 | threads[cm.ID] = &diffThread{ID: cm.ID, Resolved: cm.ResolvedBy, Stale: cm.HeadSHA != headSHA, |
| 525 | | Comments: []renderedComment{{cm.Author, cm.CreatedAt, mdHTML(cm.Body)}}} |
| 585 | Comments: []renderedComment{{cm.Author, cm.CreatedAt, md(cm.Body)}}} |
| 526 | 586 | anchors[cm.ID] = anchor{cm.Path, cm.Side, cm.Line} |
| 527 | 587 | order = append(order, cm.ID) |
| 528 | 588 | } else if th, ok := threads[cm.ReplyTo]; ok { |
| 529 | | th.Comments = append(th.Comments, renderedComment{cm.Author, cm.CreatedAt, mdHTML(cm.Body)}) |
| 589 | th.Comments = append(th.Comments, renderedComment{cm.Author, cm.CreatedAt, md(cm.Body)}) |
| 530 | 590 | } |
| 531 | 591 | } |
| 532 | 592 | placed := map[int64]bool{} |
| @@ -737,13 +797,14 @@ func (s *Server) issue(w http.ResponseWriter, r *http.Request) { |
| 737 | 797 | http.Error(w, "internal error", http.StatusInternalServerError) |
| 738 | 798 | return |
| 739 | 799 | } |
| 800 | md := s.ugcFor(r, p.Repo) |
| 740 | 801 | s.render(w, "issue.html", struct { |
| 741 | 802 | repoPage |
| 742 | 803 | Issue store.Issue |
| 743 | 804 | BodyHTML template.HTML |
| 744 | 805 | Comments []renderedComment |
| 745 | 806 | LabelColors map[string]template.CSS |
| 746 | | }{p, iss, mdHTML(iss.Body), renderComments(comments), s.labelColors(p.Repo.ID)}) |
| 807 | }{p, iss, md(iss.Body), renderComments(comments, md), s.labelColors(p.Repo.ID)}) |
| 747 | 808 | } |
| 748 | 809 | |
| 749 | 810 | func (s *Server) mrs(w http.ResponseWriter, r *http.Request) { |
| @@ -806,8 +867,9 @@ func (s *Server) mr(w http.ResponseWriter, r *http.Request) { |
| 806 | 867 | lines = classifyDiff(patch) |
| 807 | 868 | } |
| 808 | 869 | } |
| 870 | md := s.ugcFor(r, p.Repo) |
| 809 | 871 | var detachedThreads []diffThread |
| 810 | | lines, detachedThreads = attachThreads(lines, diffComments, m.HeadSHA) |
| 872 | lines, detachedThreads = attachThreads(lines, diffComments, m.HeadSHA, md) |
| 811 | 873 | s.render(w, "mr.html", struct { |
| 812 | 874 | repoPage |
| 813 | 875 | MR store.MR |
| @@ -818,7 +880,7 @@ func (s *Server) mr(w http.ResponseWriter, r *http.Request) { |
| 818 | 880 | Reviews []store.MRReview |
| 819 | 881 | DiffLines []diffLine |
| 820 | 882 | DetachedThreads []diffThread |
| 821 | | }{p, m, mdHTML(m.Body), checks, store.CombinedStatus(checks), renderComments(comments), reviews, lines, detachedThreads}) |
| 883 | }{p, m, md(m.Body), checks, store.CombinedStatus(checks), renderComments(comments, md), reviews, lines, detachedThreads}) |
| 822 | 884 | } |
| 823 | 885 | |
| 824 | 886 | func (s *Server) refs(w http.ResponseWriter, r *http.Request) { |