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/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

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

Give every account a profile page and a directory of accounts

a6b42433c087ab0c936e34a1d05cf488db7a92d6 · Keenan Nemetz · 8d ago · parent 727df386

Give every account a profile page and a directory of accounts

Clicking a name now opens /u/<name>: the issues and changes somebody
opened, the reviews they gave, the comments they left, and the
repositories that work is in. The numbers and the feed are grouped reads
of the same event rows the activity feed pages through, so a profile
cannot disagree with the feed, and every read is limited to the
repositories the reader may open. A private repository's work is not a
number an outsider gets to learn, so two readers can see different
totals and the page says what its numbers cover.

/u lists the roster. Account names are public on this instance; their
activity is not. Both pages ask crawlers to leave them out of an index.

@name in an issue, a comment, a change request, or a release note links
to the account it addresses, using the mention scanner CI already
evaluates: what reads as an address on the page is what a workflow sees.
A name that is not an account stays text, and code samples are skipped.

Git author names are not linked and commit counts are not shown. A
commit author is a name somebody typed, not the credential that proves
an account, and the index only keeps the top eight authors of each
default branch anyway.

server/web/page.go +35

@@ -7,6 +7,7 @@ package web
7 7
8 8 import (
9 9 "net/http"
10 "net/url"
10 11 "strings"
11 12
12 13 "github.com/rodent-software/vole/forge/access"
@@ -43,6 +44,10 @@ type Page struct {
43 44 // InstanceAdmin reports whether the viewer may use the instance-wide admin
44 45 // surface. The global bar uses it to show Admin.
45 46 InstanceAdmin bool
47 // NoIndex asks a crawler to leave the page out of an index. The account
48 // directory and a profile are public on purpose, but a scraped roster is not
49 // what "public" has to mean.
50 NoIndex bool
46 51 // Query is the repository search on the index page. Other pages leave it
47 52 // empty.
48 53 Query string
@@ -68,6 +73,8 @@ func pageSection(path string) string {
68 73 return "activity"
69 74 case path == "/users" || strings.HasPrefix(path, "/users/"):
70 75 return "users"
76 case path == "/u" || strings.HasPrefix(path, "/u/"):
77 return "people"
71 78 case strings.HasPrefix(path, "/account"):
72 79 return "account"
73 80 }
@@ -87,6 +94,34 @@ func (w *Web) who(name string) string {
87 94 return a.Name
88 95 }
89 96
97 // userRef is an account name as a template should render it. Known is false for
98 // a name that does not belong to an account, and the template then prints the
99 // name without a link: a commit author who never registered is a string, not a
100 // person, and a typo in a mention should not become a 404 somebody can click.
101 //
102 // Nothing is authorized by what this returns; the profile page applies the same
103 // read rules as every other page.
104 type userRef struct {
105 Name string
106 Href string
107 Known bool
108 }
109
110 func (w *Web) userRef(name string) userRef {
111 ref := userRef{Name: name}
112 if w.Ident == nil || name == "" {
113 return ref
114 }
115 a, err := w.Ident.Find(name)
116 if err != nil {
117 return ref
118 }
119 ref.Name = a.Name
120 ref.Href = "/u/" + url.PathEscape(a.Name)
121 ref.Known = true
122 return ref
123 }
124
90 125 // repoNav is the repository header's data: which repository, which tab is
91 126 // current, whether anonymous readers can see it, the clone transports and site
92 127 // to offer, and how many open issues and change requests its tabs should