internal/httpd/page.go
90 lines · 2575 bytes
1package httpd
2
3import (
4 "net/http"
5
6 "gitbay.org/gitbay/internal/policy"
7 "gitbay.org/gitbay/internal/store"
8)
9
10// railRepo is one pinned repository in the rail.
11type railRepo struct {
12 Owner string
13 Name string
14}
15
16// railItem is one merge request in the rail's review queue.
17type railItem struct {
18 RepoPath string
19 Number int64
20 Title string
21}
22
23// rail is the cross-repo state in the left rail: the repositories and the
24// review work a viewer moves between. A repo's own navigation lives in its
25// header tabs, not here — the rail holds only what changes as you switch
26// context, which is what earns it the width.
27type rail struct {
28 Pinned []railRepo
29 Reviews []railItem
30}
31
32// Empty reports whether the rail has nothing to show beyond the global
33// links, so the template can skip its group headings.
34func (r rail) Empty() bool { return len(r.Pinned) == 0 && len(r.Reviews) == 0 }
35
36// basePage is what the layout needs on every page, repo or not. Page
37// structs embed it so the rail and the site name are always in scope.
38type basePage struct {
39 // Site is the instance's display name; Host is the name a command or
40 // URL must use. Anything copy-pasteable takes Host.
41 Site string
42 Host string
43 Viewer string
44 Admin bool // the viewer is an instance admin: the rail shows /admin
45 Rail rail
46}
47
48// base builds the layout-wide data for a request that has not already
49// resolved a viewer.
50func (s *Server) base(r *http.Request) basePage {
51 if s.cfg.Web.Mode != "accounts" {
52 return basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}
53 }
54 return s.baseFor(s.viewer(r))
55}
56
57// baseFor is base for a handler that already holds the viewer, so the
58// session lookup is not repeated.
59func (s *Server) baseFor(viewer store.User) basePage {
60 b := basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}
61 if viewer.ID == 0 {
62 return b
63 }
64 b.Viewer = viewer.Username
65 b.Admin = viewer.IsAdmin
66 b.Rail = s.railFor(viewer)
67 return b
68}
69
70// railFor collects the viewer's pinned repositories and review queue,
71// dropping anything they may no longer read.
72func (s *Server) railFor(viewer store.User) rail {
73 var rl rail
74 pinned, _ := s.st.PinnedRepos(viewer.ID)
75 for _, rp := range pinned {
76 grant, _ := s.st.AccessRole(rp.ID, viewer.ID)
77 if policy.CanRead(viewer, rp, grant) {
78 rl.Pinned = append(rl.Pinned, railRepo{Owner: rp.OwnerName, Name: rp.Name})
79 }
80 }
81 queue, _ := s.st.ReviewQueue(viewer.ID)
82 for _, q := range queue {
83 rl.Reviews = append(rl.Reviews, railItem{
84 RepoPath: q.RepoPath,
85 Number: q.Number,
86 Title: q.Title,
87 })
88 }
89 return rl
90}