+4
-2
appview/pages/pages.go
+4
-2
appview/pages/pages.go
-3
appview/pages/templates/layouts/repobase.html
-3
appview/pages/templates/layouts/repobase.html
+3
-4
appview/pages/templates/repo/branches.html
+3
-4
appview/pages/templates/repo/branches.html
···
2
branches | {{ .RepoInfo.FullName }}
3
{{ end }}
4
5
-
{{ define "content" }}
6
-
{{ $name := .RepoInfo.Name }}
7
<h3>branches</h3>
8
<div class="refs">
9
{{ range .Branches }}
10
<div>
11
<strong>{{ .Name }}</strong>
12
-
<a href="/{{ $name }}/tree/{{ .Name }}/">browse</a>
13
-
<a href="/{{ $name }}/log/{{ .Name }}">log</a>
14
</div>
15
{{ end }}
16
</div>
···
2
branches | {{ .RepoInfo.FullName }}
3
{{ end }}
4
5
+
{{ define "repoContent" }}
6
<h3>branches</h3>
7
<div class="refs">
8
{{ range .Branches }}
9
<div>
10
<strong>{{ .Name }}</strong>
11
+
<a href="/{{ $.RepoInfo.FullName }}/tree/{{ .Name }}/">browse</a>
12
+
<a href="/{{ $.RepoInfo.FullName }}/log/{{ .Name }}">log</a>
13
</div>
14
{{ end }}
15
</div>
+21
-4
appview/pages/templates/repo/settings.html
+21
-4
appview/pages/templates/repo/settings.html
···
1
+
{{ define "repoContent" }}
2
+
<h3>settings</h3>
3
+
<em>collaborators</em>
4
+
<ol>
5
+
{{ range .Collaborators }}
6
+
<li>
7
+
{{ index . 0 }} - {{ index . 3 }}
8
+
</li>
9
+
{{ else }}
10
+
<p>no members</p>
11
+
{{ end }}
12
+
</ol>
13
+
14
+
{{ if .IsCollaboratorInviteAllowed }}
15
+
<h3>add collaborator</h3>
16
+
<form hx-put="/{{ $.RepoInfo.FullName }}/settings/collaborator">
17
+
<label for="collaborator">did or handle:</label>
18
+
<input type="text" id="collaborator" name="collaborator" required>
19
+
<button type="text">add collaborator</button>
20
+
</form>
21
+
{{ end }}
22
{{end}}
23
+19
-2
appview/state/repo.go
+19
-2
appview/state/repo.go
···
251
return
252
}
253
254
user := s.auth.GetUser(r)
255
s.pages.RepoBranches(w, pages.RepoBranchesParams{
256
LoggedInUser: user,
···
379
}
380
log.Println(repoCollaborators)
381
382
s.pages.RepoSettings(w, pages.RepoSettingsParams{
383
-
LoggedInUser: user,
384
-
Collaborators: repoCollaborators,
385
})
386
}
387
}
···
251
return
252
}
253
254
+
log.Println(result)
255
+
256
user := s.auth.GetUser(r)
257
s.pages.RepoBranches(w, pages.RepoBranchesParams{
258
LoggedInUser: user,
···
381
}
382
log.Println(repoCollaborators)
383
384
+
isCollaboratorInviteAllowed := false
385
+
if user != nil {
386
+
ok, err := s.enforcer.IsCollaboratorInviteAllowed(user.Did, f.Knot, f.OwnerSlashRepo())
387
+
if err == nil && ok {
388
+
isCollaboratorInviteAllowed = true
389
+
}
390
+
}
391
+
392
s.pages.RepoSettings(w, pages.RepoSettingsParams{
393
+
LoggedInUser: user,
394
+
RepoInfo: pages.RepoInfo{
395
+
OwnerDid: f.OwnerDid(),
396
+
OwnerHandle: f.OwnerHandle(),
397
+
Name: f.RepoName,
398
+
SettingsAllowed: settingsAllowed(s, user, f),
399
+
},
400
+
Collaborators: repoCollaborators,
401
+
IsCollaboratorInviteAllowed: isCollaboratorInviteAllowed,
402
})
403
}
404
}
+1
-1
appview/state/signer.go
+1
-1
appview/state/signer.go
+4
rbac/rbac.go
+4
rbac/rbac.go
···
163
return e.E.Enforce(user, domain, repo, "repo:settings")
164
}
165
166
+
func (e *Enforcer) IsCollaboratorInviteAllowed(user, domain, repo string) (bool, error) {
167
+
return e.E.Enforce(user, domain, repo, "repo:invite")
168
+
}
169
+
170
// keyMatch2Func is a wrapper for keyMatch2 to make it compatible with Casbin
171
func keyMatch2Func(args ...interface{}) (interface{}, error) {
172
name1 := args[0].(string)