Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · server/web/tokens.go

Line attribution on main · 103 line(s)

c717d4eb
Keenan Nemetz
13d ago
1 package web
2
306f36fc
Keenan Nemetz
12d ago
3 // The two token inputs: labels and assignees. Both are a set of names the chip UI
4 // (static/app.js) writes into hidden form fields, so the parsing and normalising
5 // of that set lives here rather than beside either issue page.
c717d4eb
Keenan Nemetz
13d ago
6
7 import (
8 "sort"
9
d37d301f
Keenan Nemetz
17h ago
10 "rodent.software/vole/forge/access"
11 "rodent.software/vole/forge/repo"
12 "rodent.software/vole/metadata"
c717d4eb
Keenan Nemetz
13d ago
13 )
14
15 // tokenPickerData is the small view model behind the token input. It is used for
306f36fc
Keenan Nemetz
12d ago
16 // labels and assignees: Selected seeds the current set, All is what to suggest,
17 // and Item names the hidden form field the chip UI writes.
c717d4eb
Keenan Nemetz
13d ago
18 type tokenPickerData struct {
19 Selected []string
20 All []string
21 Item string
22 Placeholder string
23 }
24
306f36fc
Keenan Nemetz
12d ago
25 func tokenPicker(selected, all []string, item, placeholder string) tokenPickerData {
26 return tokenPickerData{Selected: selected, All: all, Item: item, Placeholder: placeholder}
c717d4eb
Keenan Nemetz
13d ago
27 }
28
29 // assignableAccounts lists active accounts that can see this repository. A
30 // read-only account is still a valid assignee: the point is that they can read
31 // the issue they were given, even if they cannot comment on it.
32 func (w *Web) assignableAccounts(m *repo.Meta) []string {
33 if w.Ident == nil {
34 return nil
35 }
36 acl, err := m.ACL()
37 if err != nil {
38 return nil
39 }
40 snap := w.Ident.Snapshot()
41 if snap == nil {
42 return nil
43 }
44 var names []string
45 for _, acct := range snap.ByName {
46 if acct.Active() && acl.Can(acct.Name, access.Read) {
47 names = append(names, acct.Name)
48 }
49 }
50 sort.Strings(names)
51 return names
52 }
53
54 // unknownNames reports names that are not in the allowed set. It keeps the
55 // browser from storing an assignee nobody can resolve.
56 func unknownNames(want, allowed []string) []string {
57 set := map[string]bool{}
58 for _, n := range allowed {
59 set[n] = true
60 }
61 var bad []string
62 for _, n := range want {
63 if !set[n] {
64 bad = append(bad, n)
65 }
66 }
67 return bad
68 }
69
70 // mergeNames returns the union of two name lists, sorted, without duplicates.
71 func mergeNames(a, b []string) []string {
72 set := map[string]bool{}
73 for _, n := range a {
74 set[n] = true
75 }
76 for _, n := range b {
77 set[n] = true
78 }
79 return metadata.SortedNames(set)
80 }
81
82 // normalizeLabelSet is what the browser writes: NormalizeLabels plus dedupe, so
83 // checking two spellings of the same label cannot store it twice.
84 func normalizeLabelSet(in []string) []string {
85 set := map[string]bool{}
86 for _, v := range metadata.NormalizeLabels(in) {
87 set[v] = true
88 }
89 return metadata.SortedNames(set)
90 }
91
92 // sameStrings compares the normalised label lists a writer would store.
93 func sameStrings(a, b []string) bool {
94 if len(a) != len(b) {
95 return false
96 }
97 for i := range a {
98 if a[i] != b[i] {
99 return false
100 }
101 }
102 return true
103 }