update HTML UI to reflect role access restrictions
* disable ui if user is unelevated * disable revoke button if unelevated and not own invite * improve styling of disabled elements * remove revoke if alias not made my current user
This commit is contained in:
parent
5dba245c16
commit
2c9fdcb98e
|
@ -133,6 +133,10 @@ func newSession(t *testing.T) *testSession {
|
|||
testFuncs["urlToNotice"] = func(name string) string { return "" }
|
||||
testFuncs["language_count"] = func() int { return 1 }
|
||||
testFuncs["list_languages"] = func(*url.URL, string) string { return "" }
|
||||
testFuncs["member_is_elevated"] = func() bool { return true }
|
||||
testFuncs["member_is_admin"] = func() bool { return true }
|
||||
testFuncs["member_can_invite"] = func() bool { return true }
|
||||
testFuncs["list_languages"] = func(*url.URL, string) string { return "" }
|
||||
testFuncs["relative_time"] = func(when time.Time) string { return humanize.Time(when) }
|
||||
|
||||
renderOpts := []render.Option{
|
||||
|
|
|
@ -123,6 +123,31 @@ func New(
|
|||
}
|
||||
}),
|
||||
|
||||
render.InjectTemplateFunc("member_can_invite", func(r *http.Request) interface{} {
|
||||
return func() (bool, error) {
|
||||
member := members.FromContext(r.Context())
|
||||
if member == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
pm, err := dbs.Config.GetPrivacyMode(r.Context())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch pm {
|
||||
case roomdb.ModeOpen:
|
||||
return true, nil
|
||||
case roomdb.ModeCommunity:
|
||||
return member.Role > roomdb.RoleUnknown && member.Role <= roomdb.RoleAdmin, nil
|
||||
case roomdb.ModeRestricted:
|
||||
return member.Role == roomdb.RoleAdmin || member.Role == roomdb.RoleModerator, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
render.InjectTemplateFunc("language_count", func(r *http.Request) interface{} {
|
||||
return func() int {
|
||||
return len(locHelper.ListLanguages())
|
||||
|
|
|
@ -109,7 +109,7 @@ func TestNoticesEditButtonVisible(t *testing.T) {
|
|||
}
|
||||
|
||||
// have the database return okay for any user
|
||||
testUser := roomdb.Member{ID: 23}
|
||||
testUser := roomdb.Member{ID: 23, Role: roomdb.RoleAdmin}
|
||||
ts.AuthFallbackDB.CheckReturns(testUser.ID, nil)
|
||||
ts.MembersDB.GetByIDReturns(testUser, nil)
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ AdminMemberDetailsRemove = "Mitglied entfernen"
|
|||
AdminMemberAdded = "Mitglied erfolgreich hinzugefügt."
|
||||
AdminMemberUpdated = "Mitglied aktualisiert."
|
||||
AdminMemberRemoved = "Mitglied entfernt."
|
||||
AdminAddNewMemberTitle = "Add a new member"
|
||||
|
||||
AdminAliasesRevoke = "Widerrufen"
|
||||
AdminAliasesRevokeConfirmTitle = "Alias widerrufen"
|
||||
|
|
|
@ -124,6 +124,7 @@ AdminMemberDetailsRemove = "Remove member"
|
|||
AdminMemberAdded = "Member added successfully."
|
||||
AdminMemberUpdated = "Member updated."
|
||||
AdminMemberRemoved = "Member removed."
|
||||
AdminAddNewMemberTitle = "Add a new member"
|
||||
|
||||
AdminAliasesRevoke = "Revoke"
|
||||
AdminAliasesRevokeConfirmTitle = "Revoke Alias"
|
||||
|
|
|
@ -92,13 +92,15 @@ func ContextInjecter(mdb roomdb.MembersService, withPassword *auth.Handler, with
|
|||
}
|
||||
|
||||
// TemplateHelpers returns functions to be used with the go.mindeco.de/http/render package.
|
||||
// Each has to return a function twice because the first is evaluated with the request before it gets passed onto html/template's FuncMap.
|
||||
// Each helper has to return a function twice because the first is evaluated with the request before it gets passed onto html/template's FuncMap.
|
||||
//
|
||||
// {{ is_logged_in }} returns true or false depending if the user is logged in
|
||||
// {{ is_logged_in }} returns true or false depending on if the user is logged in
|
||||
//
|
||||
// {{ member_has_role "string" }} returns a boolean which confrms wether the member has a certain role (RoleMemeber, RoleAdmin, etc)
|
||||
//
|
||||
// {{ member_is_admin }} is a shortcut for {{ member_has_role "RoleAdmin" }}
|
||||
//
|
||||
// {{ member_is_admin }} is a shortcut for {{ or member_has_role "RoleAdmin" member_has_role "RoleModerator"}}
|
||||
func TemplateHelpers() []render.Option {
|
||||
|
||||
return []render.Option{
|
||||
|
@ -143,5 +145,19 @@ func TemplateHelpers() []render.Option {
|
|||
return member.Role == roomdb.RoleAdmin
|
||||
}
|
||||
}),
|
||||
|
||||
// shorthand for is admin || mod (used for editing notices, managing users, managing aliases)
|
||||
render.InjectTemplateFunc("member_is_elevated", func(r *http.Request) interface{} {
|
||||
no := func() bool { return false }
|
||||
|
||||
member := FromContext(r.Context())
|
||||
if member == nil {
|
||||
return no
|
||||
}
|
||||
|
||||
return func() bool {
|
||||
return member.Role == roomdb.RoleAdmin || member.Role == roomdb.RoleModerator
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,21 +22,32 @@
|
|||
{{ .csrfField }}
|
||||
<div class="flex flex-row items-center h-12">
|
||||
<input
|
||||
{{ if member_is_elevated }} {{ else }} disabled {{ end }}
|
||||
type="text"
|
||||
name="pub_key"
|
||||
placeholder="{{i18n "PubKeyRefPlaceholder"}}"
|
||||
class="font-mono truncate w-1/2 mr-2 tracking-wider h-12 text-gray-900 focus:outline-none focus:ring-1 focus:ring-green-500 focus:border-transparent placeholder-gray-300"
|
||||
class="p-1 rounded font-mono truncate w-1/2 mr-2 tracking-wider h-12 shadow text-gray-900 focus:outline-none focus:ring-1
|
||||
focus:ring-green-500 focus:border-transparent placeholder-gray-300
|
||||
{{ if member_is_elevated }} {{ else }} shadow ring-1 ring-gray-300 opacity-50 bg-gray-200 cursor-not-allowed {{ end }}
|
||||
"
|
||||
>
|
||||
<input
|
||||
{{ if member_is_elevated }} {{ else }} disabled {{ end }}
|
||||
type="text"
|
||||
name="comment"
|
||||
placeholder="{{i18n "AdminDeniedKeysComment"}}"
|
||||
class="font-mono truncate w-1/2 tracking-wider h-12 text-gray-900 focus:outline-none focus:ring-1 focus:ring-green-500 focus:border-transparent placeholder-gray-300"
|
||||
class="p-1 rounded font-mono truncate w-1/2 mr-2 tracking-wider h-12 shadow text-gray-900 focus:outline-none focus:ring-1
|
||||
focus:ring-green-500 focus:border-transparent placeholder-gray-300
|
||||
{{ if member_is_elevated }} {{ else }} shadow ring-1 ring-gray-300 opacity-50 bg-gray-200 cursor-not-allowed {{ end }}
|
||||
"
|
||||
>
|
||||
<input
|
||||
{{ if member_is_elevated }} {{ else }} disabled {{ end }}
|
||||
type="submit"
|
||||
value="{{i18n "AdminDeniedKeysAdd"}}"
|
||||
class="pl-4 w-20 py-2 text-center text-green-500 hover:text-green-600 font-bold bg-transparent cursor-pointer"
|
||||
class="pl-4 w-20 py-2 text-center font-bold bg-transparent disabled:opacity-50
|
||||
{{ if member_is_elevated }} text-green-500 hover:text-green-600 cursor-pointer {{ else }} text-gray-200 cursor-not-allowed {{ end }}
|
||||
"
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -28,8 +28,15 @@
|
|||
>
|
||||
{{ .csrfField }}
|
||||
<button
|
||||
{{ if not member_can_invite }} disabled {{ end }}
|
||||
type="submit"
|
||||
class="shadow rounded px-3 py-1.5 text-green-600 ring-1 ring-green-400 bg-white hover:bg-green-500 hover:text-gray-100 focus:outline-none focus:ring-2 focus:ring-green-400"
|
||||
class="shadow rounded px-3 py-1.5 ring-1 focus:outline-none focus:ring-2
|
||||
{{ if member_can_invite }}
|
||||
text-green-600 ring-green-400 bg-white hover:bg-green-500 hover:text-gray-100 focus:ring-green-400
|
||||
{{ else }}
|
||||
text-gray-500 ring-gray-200 bg-gray-300 cursor-not-allowed
|
||||
{{ end }}
|
||||
"
|
||||
>{{i18n "AdminInvitesCreate"}}</button>
|
||||
</form>
|
||||
</td>
|
||||
|
@ -44,6 +51,8 @@
|
|||
|
||||
<tbody id="the-table-rows" class="divide-y">
|
||||
{{range .Entries}}
|
||||
{{ $user := is_logged_in }}
|
||||
{{$hasCreatedInvite := eq $user.PubKey.Ref .CreatedBy.PubKey.Ref }}
|
||||
{{$creator := .CreatedBy.PubKey.Ref}}
|
||||
{{$creatorIsAlias := false}}
|
||||
{{range $index, $alias := .CreatedBy.Aliases}}
|
||||
|
@ -67,10 +76,12 @@
|
|||
{{end}}
|
||||
</td>
|
||||
<td class="w-3/12 pl-2 pr-3 text-right">
|
||||
{{ if or member_is_elevated $hasCreatedInvite }}
|
||||
<a
|
||||
href="{{urlTo "admin:invites:revoke:confirm" "id" .ID}}"
|
||||
class="pl-2 w-20 py-2 text-center text-gray-400 hover:text-red-600 font-bold cursor-pointer"
|
||||
>{{i18n "AdminInviteRevoke"}}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-12 table-row sm:hidden">
|
||||
|
@ -84,10 +95,12 @@
|
|||
>{{$creator}}</span>, {{human_time .CreatedAt}}
|
||||
{{end}}
|
||||
</span>
|
||||
{{ if or member_is_elevated $hasCreatedInvite }}
|
||||
<a
|
||||
href="{{urlTo "admin:invites:revoke:confirm" "id" .ID}}"
|
||||
class="pl-4 w-20 py-2 text-center text-gray-400 hover:text-red-600 font-bold cursor-pointer"
|
||||
>{{i18n "AdminInviteRevoke"}}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
|
|
@ -14,16 +14,23 @@
|
|||
method="POST"
|
||||
>
|
||||
{{ .csrfField }}
|
||||
<label class="block mt-6 mb-1 text-sm text-gray-500">Add a new member</label>
|
||||
<label class="block mt-6 mb-1 text-sm text-gray-500">{{ i18n "AdminAddNewMemberTitle" }}</label>
|
||||
<div class="flex flex-row items-center justify-start mb-6">
|
||||
<input
|
||||
{{ if member_is_elevated }} {{ else }} disabled {{ end }}
|
||||
type="text"
|
||||
name="pub_key"
|
||||
placeholder="{{i18n "PubKeyRefPlaceholder"}}"
|
||||
class="w-8/12 self-stretch shadow rounded border border-transparent h-10 p-1 pl-4 font-mono truncate text-purple-600 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:border-transparent">
|
||||
class="w-8/12 self-stretch shadow rounded border border-transparent h-10 p-1 pl-4 font-mono truncate
|
||||
text-purple-600 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:border-transparent
|
||||
{{ if member_is_elevated }} {{ else }} shadow ring-1 ring-gray-300 opacity-50 bg-gray-200 cursor-not-allowed {{ end }}
|
||||
">
|
||||
<button
|
||||
{{ if member_is_elevated }} {{ else }} disabled {{ end }}
|
||||
type="submit"
|
||||
class="ml-4 h-10 shadow rounded px-6 text-purple-600 ring-1 ring-purple-400 bg-white hover:bg-purple-600 hover:text-gray-100 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
class="ml-4 h-10 shadow rounded px-6 text-purple-600 ring-1 bg-white focus:outline-none focus:ring-2 focus:ring-purple-400
|
||||
{{ if member_is_elevated }} ring-purple-400 hover:bg-purple-600 hover:text-gray-100 {{ else }} opacity-50 ring-purple-300 bg-purple-200 cursor-not-allowed {{ end }}
|
||||
"
|
||||
>{{i18n "AdminMembersAdd"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
<p id="ssb-id" class="mb-8 font-mono font-bold tracking-wider truncate text-gray-900">{{.Member.PubKey.Ref}}</p>
|
||||
|
||||
<label class="mt-2 mb-1 font-bold text-gray-400 text-sm">{{i18n "AdminMemberDetailsRole"}}</label>
|
||||
{{ $user := is_logged_in }}
|
||||
{{ $aliasBelongsToUser := eq $user.PubKey.Ref .Member.PubKey.Ref }}
|
||||
{{ if member_is_elevated }}
|
||||
<details class="mb-8 self-start w-40" id="change-role">
|
||||
<summary class="px-3 py-1 rounded shadow bg-white ring-1 ring-gray-300 hover:bg-gray-100 cursor-pointer">
|
||||
{{range $.AllRoles}}
|
||||
|
@ -47,6 +50,13 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</details>
|
||||
{{ else }}
|
||||
{{range $.AllRoles}}
|
||||
{{if eq . $.Member.Role}}
|
||||
<p id="ssb-member-role" class="mb-8 font-bold tracking-wider truncate text-gray-900">{{i18n .String}}</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{ end }}
|
||||
|
||||
{{$aliasCount := len .Member.Aliases}} {{if gt $aliasCount 0}}
|
||||
<label class="mt-2 mb-1 font-bold text-gray-400 text-sm">{{i18n "AdminMemberDetailsAliases"}}</label>
|
||||
|
@ -59,19 +69,23 @@
|
|||
>{{.Name}}</a>
|
||||
</div>
|
||||
|
||||
{{ if or member_is_elevated $aliasBelongsToUser }}
|
||||
<a
|
||||
href="{{urlTo "admin:aliases:revoke:confirm" "id" .ID}}"
|
||||
class="w-20 py-2 text-sm text-center text-gray-400 hover:text-red-600 font-bold cursor-pointer"
|
||||
>({{i18n "AdminMemberDetailsAliasRevoke"}})</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{ if member_is_elevated }}
|
||||
<label class="mt-10 mb-1 font-bold text-gray-400 text-sm">{{i18n "AdminMemberDetailsExclusion"}}</label>
|
||||
<a
|
||||
id="remove-member"
|
||||
href="{{urlTo "admin:members:remove:confirm" "id" .Member.ID}}"
|
||||
class="mb-8 self-start shadow rounded px-3 py-1 text-red-600 ring-1 ring-red-400 bg-white hover:bg-red-600 hover:text-gray-100 focus:outline-none focus:ring-2 focus:ring-red-400 cursor-pointer"
|
||||
>{{i18n "AdminMemberDetailsRemove"}}</a>
|
||||
{{ end }}
|
||||
|
||||
{{end}}
|
|
@ -11,6 +11,7 @@
|
|||
<a class="text-pink-600 underline" href="https://ssb-ngi-pointer.github.io/rooms2/#privacy-modes">{{ i18n "RoomsSpecification" }}</a>.
|
||||
</p>
|
||||
<h3 class="text-gray-400 text-sm font-bold mb-2">{{ i18n "SetPrivacyModeTitle" }}</h3>
|
||||
{{ if member_is_admin }}
|
||||
<details class="mb-8 self-start max-w-sm" id="change-privacy">
|
||||
<summary class="px-3 py-1 max-w-sm rounded shadow bg-white ring-1 ring-gray-300 hover:bg-gray-100 cursor-pointer">
|
||||
{{ i18n .CurrentMode.String }}
|
||||
|
@ -44,6 +45,11 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</details>
|
||||
{{ else }}
|
||||
<input disabled type="text" value="{{ i18n $.CurrentMode.String }}"
|
||||
class="mb-8 self-start max-w-sm px-3 py-1 max-w-sm rounded shadow ring-1 ring-gray-300 bg-gray-200 opacity-50 cursor-not-allowed"
|
||||
>
|
||||
{{ end }}
|
||||
<div class="grid max-w-lg grid-cols-3 gap-y-2 mb-8">
|
||||
<div class="text-xl text-gray-500 font-bold">{{ i18n "ModeOpen" }}</div>
|
||||
<div class="text-md col-span-2 italic">{{ i18n "ExplanationOpen" }}</div>
|
||||
|
@ -59,6 +65,7 @@
|
|||
{{ i18n "ExplanationDefaultLanguage" }}
|
||||
</p>
|
||||
<h3 class="text-gray-400 text-sm font-bold mb-2">{{ i18n "SetDefaultLanguageTitle" }}</h3>
|
||||
{{ if member_is_admin }}
|
||||
<details class="mb-8 self-start max-w-sm">
|
||||
<summary id="language-summary" class="px-3 py-1 max-w-sm rounded shadow bg-white ring-1 ring-gray-300 hover:bg-gray-100 cursor-pointer">
|
||||
{{ $.CurrentLanguage }}
|
||||
|
@ -68,6 +75,11 @@
|
|||
{{ list_languages $adminSetLanguageUrl "pl-10 pr-3 py-2 w-full text-left bg-white text-gray-700 hover:text-gray-900 hover:bg-gray-50 cursor-pointer" }}
|
||||
</div>
|
||||
</details>
|
||||
{{ else }}
|
||||
<input disabled type="text" value="{{ $.CurrentLanguage }}"
|
||||
class="mb-8 self-start max-w-sm px-3 py-1 max-w-sm rounded shadow ring-1 ring-gray-300 bg-gray-200 opacity-50 cursor-not-allowed"
|
||||
>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</div>
|
||||
|
||||
<div class="h-8"></div>
|
||||
{{if is_logged_in}}
|
||||
{{if and is_logged_in member_is_elevated }}
|
||||
<a
|
||||
id="edit-notice"
|
||||
href="{{urlTo "admin:notice:edit" "id" .ID}}"
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
>{{.Language}}</a>
|
||||
{{end}}
|
||||
|
||||
{{if is_logged_in}}
|
||||
{{if and is_logged_in member_is_elevated }}
|
||||
<a
|
||||
href="{{urlTo "admin:notice:translation:draft" "name" .Name.String}}"
|
||||
class="inline-block rounded-full py-1 px-3 text-sm text-gray-500 font-bold border-2 border-dashed box-content border-gray-200 hover:border-transparent hover:bg-white hover:shadow"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</div>
|
||||
|
||||
<div class="h-8"></div>
|
||||
{{if is_logged_in}}
|
||||
{{if and is_logged_in member_is_elevated }}
|
||||
<a
|
||||
id="edit-notice"
|
||||
href="{{urlTo "admin:notice:edit" "id" .ID}}"
|
||||
|
|
Loading…
Reference in New Issue