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.
Split repository settings into tabs
e189a23e457b6aa01a35a4586586713cdc2d426d · Keenan Nemetz · 23h ago · parent 87ebcc5a
Split repository settings into tabs One page held four audiences and ten forms. Each part is now its own tab: General for the repository's own fields, Access for the instance-admin rules, Secrets for the values jobs read, and Danger for rename and delete. A tab is offered only to the accounts that may use it, and a viewer with one tab gets no row at all. The Access and Secrets tabs, and the failures on each, keep their own URLs so a redirect and an error land where the form was; the Access and Secrets fragments survive as deep links.
server/web/admin_access.go +8−6
| @@ -1,14 +1,15 @@ | |||
| 1 | 1 | package web | |
| 2 | 2 | ||
| 3 | 3 | // The instance-admin half of a repository's Settings page: the access rules, the | |
| 4 | // review policy, and which accounts' CI results count as checks. | ||
| 4 | // review policy, and which accounts' CI results count as checks. It is the Access | ||
| 5 | // tab. | ||
| 5 | 6 | // | |
| 6 | 7 | // It is a separate file from settings.go because it is a separate rule and a | |
| 7 | 8 | // separate audience. A repository admin owns the description, the default branch | |
| 8 | 9 | // and whether the repository is public; an instance admin owns who may do what in | |
| 9 | // it. Both halves render on one page because that is where an operator looks, but | ||
| 10 | // an instance admin who is not a repository admin sees only this section, and the | ||
| 11 | // page says so. | ||
| 10 | // it. Both halves are tabs of one page because that is where an operator looks; the | ||
| 11 | // Access tab is only in the row for an instance admin, and a page whose only tab is | ||
| 12 | // this one says that the repository's details stay with its admins. | ||
| 12 | 13 | ||
| 13 | 14 | import ( | |
| 14 | 15 | "net/http" | |
| @@ -155,7 +156,7 @@ func (w *Web) SaveRepositoryAccess(rw http.ResponseWriter, r *http.Request) { | |||
| 155 | 156 | } | |
| 156 | 157 | } | |
| 157 | 158 | w.audit("web.admin.acl", pr.account.Name, m.Name, "") | |
| 158 | http.Redirect(rw, r, "/r/"+m.Name+"/settings?saved=access#access", http.StatusSeeOther) | ||
| 159 | http.Redirect(rw, r, "/r/"+m.Name+"/settings?tab=access&saved=access#access", http.StatusSeeOther) | ||
| 159 | 160 | } | |
| 160 | 161 | ||
| 161 | 162 | // AddRepositoryAccess grants one account a repository role and saves | |
| @@ -209,13 +210,14 @@ func (w *Web) AddRepositoryAccess(rw http.ResponseWriter, r *http.Request) { | |||
| 209 | 210 | } | |
| 210 | 211 | } | |
| 211 | 212 | w.audit("web.admin.acl.add", pr.account.Name, m.Name, acct.Name+"="+role.String()) | |
| 212 | http.Redirect(rw, r, "/r/"+m.Name+"/settings?saved=added#access", http.StatusSeeOther) | ||
| 213 | http.Redirect(rw, r, "/r/"+m.Name+"/settings?tab=access&saved=added#access", http.StatusSeeOther) | ||
| 213 | 214 | } | |
| 214 | 215 | ||
| 215 | 216 | // accessFailure re-renders the access form with the page's current values and a | |
| 216 | 217 | // message, so a rejected role does not throw away the rest of the editor. | |
| 217 | 218 | func (w *Web) accessFailure(rw http.ResponseWriter, r *http.Request, ctx *settingsContext, msg string) { | |
| 218 | 219 | v := w.settingsView(r, ctx) | |
| 220 | v.focusTab("access") | ||
| 219 | 221 | if v.Access == nil { | |
| 220 | 222 | v.Access = &accessView{} | |
| 221 | 223 | } | |