internal/httpd/page.go

81272b0b804d31bba1ee5e540b69cffc895ef3bf
gitbay/internal/httpd/page.go history · blame · raw

92 lines · 2631 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	Unread  int
31}
32
33// Empty reports whether the rail has nothing to show beyond the global
34// links, so the template can skip its group headings.
35func (r rail) Empty() bool { return len(r.Pinned) == 0 && len(r.Reviews) == 0 }
36
37// basePage is what the layout needs on every page, repo or not. Page
38// structs embed it so the rail and the site name are always in scope.
39type basePage struct {
40	// Site is the instance's display name; Host is the name a command or
41	// URL must use. Anything copy-pasteable takes Host.
42	Site   string
43	Host   string
44	Viewer string
45	Admin  bool // the viewer is an instance admin: the rail shows /admin
46	Rail   rail
47}
48
49// base builds the layout-wide data for a request that has not already
50// resolved a viewer.
51func (s *Server) base(r *http.Request) basePage {
52	if s.cfg.Web.Mode != "accounts" {
53		return basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}
54	}
55	return s.baseFor(s.viewer(r))
56}
57
58// baseFor is base for a handler that already holds the viewer, so the
59// session lookup is not repeated.
60func (s *Server) baseFor(viewer store.User) basePage {
61	b := basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}
62	if viewer.ID == 0 {
63		return b
64	}
65	b.Viewer = viewer.Username
66	b.Admin = viewer.IsAdmin
67	b.Rail = s.railFor(viewer)
68	return b
69}
70
71// railFor collects the viewer's pinned repositories and review queue,
72// dropping anything they may no longer read.
73func (s *Server) railFor(viewer store.User) rail {
74	var rl rail
75	pinned, _ := s.st.PinnedRepos(viewer.ID)
76	for _, rp := range pinned {
77		grant, _ := s.st.AccessRole(rp.ID, viewer.ID)
78		if policy.CanRead(viewer, rp, grant) {
79			rl.Pinned = append(rl.Pinned, railRepo{Owner: rp.OwnerName, Name: rp.Name})
80		}
81	}
82	rl.Unread = s.st.UnreadNotices(viewer.ID)
83	queue, _ := s.st.ReviewQueue(viewer.ID)
84	for _, q := range queue {
85		rl.Reviews = append(rl.Reviews, railItem{
86			RepoPath: q.RepoPath,
87			Number:   q.Number,
88			Title:    q.Title,
89		})
90	}
91	return rl
92}