Merge pull request #179 from ssb-ngi-pointer/disable-ui-if-unelevated
disable ui if user is unelevated
This commit is contained in:
commit
5e9dc63153
|
@ -8,6 +8,7 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
||||
|
@ -32,6 +33,85 @@ func TestDeniedKeysEmpty(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestDeniedKeysDisabledInterface(t *testing.T) {
|
||||
ts := newSession(t)
|
||||
a := assert.New(t)
|
||||
|
||||
listURL := ts.URLTo(router.AdminDeniedKeysOverview)
|
||||
|
||||
ts.User = roomdb.Member{
|
||||
ID: 1234,
|
||||
Role: roomdb.RoleAdmin,
|
||||
}
|
||||
|
||||
html, resp := ts.Client.GetHTML(listURL)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
|
||||
|
||||
formSelection := html.Find("form#add-entry")
|
||||
a.EqualValues(1, formSelection.Length())
|
||||
|
||||
method, ok := formSelection.Attr("method")
|
||||
a.True(ok, "form has method set")
|
||||
a.Equal("POST", method)
|
||||
|
||||
action, ok := formSelection.Attr("action")
|
||||
a.True(ok, "form has action set")
|
||||
|
||||
addURL := ts.URLTo(router.AdminDeniedKeysAdd)
|
||||
a.Equal(addURL.String(), action)
|
||||
|
||||
webassert.ElementsInForm(t, formSelection, []webassert.FormElement{
|
||||
{Name: "pub_key", Type: "text"},
|
||||
{Name: "comment", Type: "text"},
|
||||
})
|
||||
|
||||
newKey := "@x7iOLUcq3o+sjGeAnipvWeGzfuYgrXl8L4LYlxIhwDc=.ed25519"
|
||||
addVals := url.Values{
|
||||
"comment": []string{"some comment"},
|
||||
// just any key that looks valid
|
||||
"pub_key": []string{newKey},
|
||||
}
|
||||
rec := ts.Client.PostForm(addURL, addVals)
|
||||
a.Equal(http.StatusTemporaryRedirect, rec.Code)
|
||||
|
||||
a.Equal(1, ts.DeniedKeysDB.AddCallCount())
|
||||
_, addedKey, addedComment := ts.DeniedKeysDB.AddArgsForCall(0)
|
||||
a.Equal(newKey, addedKey.Ref())
|
||||
a.Equal("some comment", addedComment)
|
||||
|
||||
/* Verify that the inputs are visible/hidden depending on user roles */
|
||||
checkInputsAreDisabled := func(shouldBeDisabled bool) {
|
||||
html, resp = ts.Client.GetHTML(listURL)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
|
||||
inputContainer := html.Find("#denied-keys-input-container")
|
||||
a.Equal(1, inputContainer.Length())
|
||||
inputs := inputContainer.Find("input")
|
||||
// pubkey, comment, submit button
|
||||
a.Equal(3, inputs.Length())
|
||||
inputs.Each(func(i int, el *goquery.Selection) {
|
||||
_, disabled := el.Attr("disabled")
|
||||
a.Equal(shouldBeDisabled, disabled)
|
||||
})
|
||||
}
|
||||
|
||||
// verify that inputs are enabled for RoleAdmin
|
||||
checkInputsAreDisabled(false)
|
||||
|
||||
// verify that inputs are enabled for RoleModerator
|
||||
ts.User = roomdb.Member{
|
||||
ID: 9001,
|
||||
Role: roomdb.RoleModerator,
|
||||
}
|
||||
checkInputsAreDisabled(false)
|
||||
|
||||
// verify that inputs are disabled for RoleMember
|
||||
ts.User = roomdb.Member{
|
||||
ID: 7331,
|
||||
Role: roomdb.RoleMember,
|
||||
}
|
||||
checkInputsAreDisabled(true)
|
||||
}
|
||||
|
||||
func TestDeniedKeysAdd(t *testing.T) {
|
||||
ts := newSession(t)
|
||||
a := assert.New(t)
|
||||
|
|
|
@ -64,6 +64,50 @@ func TestInvitesOverview(t *testing.T) {
|
|||
a.True(yes, "a-tag has href attribute")
|
||||
wantURL := ts.URLTo(router.AdminInvitesRevokeConfirm, "id", 666)
|
||||
a.Equal(wantURL.String(), link)
|
||||
|
||||
testInviteButtonDisabled := func(shouldBeDisabled bool) {
|
||||
html, resp = ts.Client.GetHTML(invitesOverviewURL)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
|
||||
inviteButton := html.Find("#create-invite button")
|
||||
_, disabled := inviteButton.Attr("disabled")
|
||||
a.EqualValues(shouldBeDisabled, disabled, "invite button should be disabled")
|
||||
}
|
||||
|
||||
// member, mod, admin should all be able to invite in ModeCommunity
|
||||
ts.ConfigDB.GetPrivacyModeReturns(roomdb.ModeCommunity, nil)
|
||||
ts.User = roomdb.Member{
|
||||
ID: 1234,
|
||||
Role: roomdb.RoleAdmin,
|
||||
}
|
||||
testInviteButtonDisabled(false)
|
||||
ts.User = roomdb.Member{
|
||||
ID: 7331,
|
||||
Role: roomdb.RoleModerator,
|
||||
}
|
||||
testInviteButtonDisabled(false)
|
||||
ts.User = roomdb.Member{
|
||||
ID: 9001,
|
||||
Role: roomdb.RoleMember,
|
||||
}
|
||||
testInviteButtonDisabled(false)
|
||||
|
||||
// mod and admin should be able to invite, member should not
|
||||
ts.ConfigDB.GetPrivacyModeReturns(roomdb.ModeRestricted, nil)
|
||||
ts.User = roomdb.Member{
|
||||
ID: 1234,
|
||||
Role: roomdb.RoleAdmin,
|
||||
}
|
||||
testInviteButtonDisabled(false)
|
||||
ts.User = roomdb.Member{
|
||||
ID: 7331,
|
||||
Role: roomdb.RoleModerator,
|
||||
}
|
||||
testInviteButtonDisabled(false)
|
||||
ts.User = roomdb.Member{
|
||||
ID: 9001,
|
||||
Role: roomdb.RoleMember,
|
||||
}
|
||||
testInviteButtonDisabled(true)
|
||||
}
|
||||
|
||||
func TestInvitesCreateForm(t *testing.T) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
||||
|
@ -35,6 +36,11 @@ func TestMembersAdd(t *testing.T) {
|
|||
ts := newSession(t)
|
||||
a := assert.New(t)
|
||||
|
||||
ts.User = roomdb.Member{
|
||||
ID: 1234,
|
||||
Role: roomdb.RoleAdmin,
|
||||
}
|
||||
|
||||
listURL := ts.URLTo(router.AdminMembersOverview)
|
||||
|
||||
html, resp := ts.Client.GetHTML(listURL)
|
||||
|
@ -70,6 +76,43 @@ func TestMembersAdd(t *testing.T) {
|
|||
a.Equal(newKey, addedPubKey.Ref())
|
||||
a.Equal(roomdb.RoleMember, addedRole)
|
||||
|
||||
/* Verify that the inputs are visible/hidden depending on user roles */
|
||||
checkInputsAreDisabled := func(shouldBeDisabled bool) {
|
||||
html, resp = ts.Client.GetHTML(listURL)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
|
||||
inputContainer := html.Find("#add-member-input-container")
|
||||
a.Equal(1, inputContainer.Length())
|
||||
inputs := inputContainer.Find("input")
|
||||
// pubkey
|
||||
a.Equal(1, inputs.Length())
|
||||
inputs.Each(func(i int, el *goquery.Selection) {
|
||||
_, disabled := el.Attr("disabled")
|
||||
a.Equal(shouldBeDisabled, disabled)
|
||||
})
|
||||
button := inputContainer.Find("button")
|
||||
a.Equal(1, button.Length())
|
||||
button.Each(func(i int, el *goquery.Selection) {
|
||||
_, disabled := el.Attr("disabled")
|
||||
a.Equal(shouldBeDisabled, disabled)
|
||||
})
|
||||
}
|
||||
|
||||
// verify that inputs are enabled for RoleAdmin
|
||||
checkInputsAreDisabled(false)
|
||||
|
||||
// verify that inputs are enabled for RoleModerator
|
||||
ts.User = roomdb.Member{
|
||||
ID: 9001,
|
||||
Role: roomdb.RoleModerator,
|
||||
}
|
||||
checkInputsAreDisabled(false)
|
||||
|
||||
// verify that inputs are disabled for RoleMember
|
||||
ts.User = roomdb.Member{
|
||||
ID: 7331,
|
||||
Role: roomdb.RoleMember,
|
||||
}
|
||||
checkInputsAreDisabled(true)
|
||||
}
|
||||
|
||||
func TestMembersDontAddInvalid(t *testing.T) {
|
||||
|
@ -173,6 +216,11 @@ func TestMemberDetails(t *testing.T) {
|
|||
|
||||
memberURL := ts.URLTo(router.AdminMemberDetails, "id", "1")
|
||||
|
||||
ts.User = roomdb.Member{
|
||||
ID: 1234,
|
||||
Role: roomdb.RoleAdmin,
|
||||
}
|
||||
|
||||
html, resp := ts.Client.GetHTML(memberURL)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
|
||||
|
||||
|
@ -180,14 +228,6 @@ func TestMemberDetails(t *testing.T) {
|
|||
{"title", "AdminMemberDetailsTitle"},
|
||||
})
|
||||
|
||||
// check for SSB ID
|
||||
ssbID := html.Find("#ssb-id")
|
||||
a.Equal(feedRef.Ref(), ssbID.Text())
|
||||
|
||||
// check for change-role dropdown
|
||||
roleDropdown := html.Find("#change-role")
|
||||
a.EqualValues(roleDropdown.Length(), 1)
|
||||
|
||||
aliasList := html.Find("#alias-list").Find("a")
|
||||
|
||||
// check for link to resolve 1st Alias
|
||||
|
@ -220,6 +260,37 @@ func TestMemberDetails(t *testing.T) {
|
|||
a.True(yes, "a-tag has href attribute")
|
||||
wantLink = ts.URLTo(router.AdminMembersRemoveConfirm, "id", 1)
|
||||
a.Equal(wantLink.String(), removeLink)
|
||||
|
||||
testDisabledBehaviour := func(isElevated bool) {
|
||||
html, resp := ts.Client.GetHTML(memberURL)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
|
||||
// check for SSB ID
|
||||
ssbID := html.Find("#ssb-id")
|
||||
a.Equal(feedRef.Ref(), ssbID.Text())
|
||||
|
||||
// check for change-role dropdown
|
||||
roleDropdown := html.Find("#change-role")
|
||||
if isElevated {
|
||||
a.Equal(1, roleDropdown.Length())
|
||||
} else {
|
||||
a.Equal(0, roleDropdown.Length())
|
||||
}
|
||||
}
|
||||
testDisabledBehaviour(true)
|
||||
|
||||
/* Now: verify that moderators cannot make room settings changes */
|
||||
ts.User = roomdb.Member{
|
||||
ID: 7331,
|
||||
Role: roomdb.RoleModerator,
|
||||
}
|
||||
testDisabledBehaviour(true)
|
||||
|
||||
/* Finally: verify that members cannot make room settings changes */
|
||||
ts.User = roomdb.Member{
|
||||
ID: 9001,
|
||||
Role: roomdb.RoleMember,
|
||||
}
|
||||
testDisabledBehaviour(false)
|
||||
}
|
||||
|
||||
func TestMembersRemoveConfirmation(t *testing.T) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -33,6 +34,10 @@ func TestLanguageSetDefaultLanguage(t *testing.T) {
|
|||
a := assert.New(t)
|
||||
|
||||
ts.ConfigDB.GetDefaultLanguageReturns("de", nil)
|
||||
ts.User = roomdb.Member{
|
||||
ID: 1234,
|
||||
Role: roomdb.RoleAdmin,
|
||||
}
|
||||
|
||||
u := ts.URLTo(router.AdminSettings)
|
||||
html, resp := ts.Client.GetHTML(u)
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
|
||||
)
|
||||
|
||||
func TestSettingsOverview(t *testing.T) {
|
||||
ts := newSession(t)
|
||||
a := assert.New(t)
|
||||
|
||||
/* First: make sure everything renders correctly for admins */
|
||||
ts.User = roomdb.Member{
|
||||
ID: 1234,
|
||||
Role: roomdb.RoleAdmin,
|
||||
}
|
||||
|
||||
ts.ConfigDB.GetPrivacyModeReturns(roomdb.ModeCommunity, nil)
|
||||
ts.ConfigDB.GetDefaultLanguageReturns("en", nil)
|
||||
|
||||
settingsURL := ts.URLTo(router.AdminSettings)
|
||||
|
||||
html, resp := ts.Client.GetHTML(settingsURL)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
|
||||
|
||||
// the privacy mode form & its summary/details container should exist
|
||||
privacyFormContainer := html.Find("#change-privacy")
|
||||
a.Equal(1, privacyFormContainer.Length())
|
||||
a.Equal(1, privacyFormContainer.Find("summary").Length())
|
||||
// chosen privacy mode is ModeCommunity (english translation will only be the name of the label, due to testing suite is set up atm)
|
||||
a.Equal("ModeCommunity", strings.TrimSpace(privacyFormContainer.Find("summary").Text()))
|
||||
// details-dropdown should have two forms, one for each of the other two privacy modes
|
||||
// that can be selected (ModeOpen, ModeRestricted)
|
||||
a.Equal(2, privacyFormContainer.Find("form").Length())
|
||||
// and one span, showing the selected mode
|
||||
a.Equal(1, privacyFormContainer.Find("#selected-mode").Length())
|
||||
inputs := privacyFormContainer.Find("input")
|
||||
// verify none of the privacy mode container's inputs are disabled
|
||||
inputs.Each(func(i int, el *goquery.Selection) {
|
||||
_, exists := el.Attr("disabled")
|
||||
a.False(exists)
|
||||
})
|
||||
|
||||
// verify that the change language form exists & is enabled
|
||||
languageFormContainer := html.Find("#change-language-container")
|
||||
a.Equal(1, languageFormContainer.Length())
|
||||
a.Equal(1, languageFormContainer.Find("summary").Length())
|
||||
// (english translation will only be the name of the label, due to testing suite is set up atm)
|
||||
a.Equal("LanguageName", strings.TrimSpace(languageFormContainer.Find("summary").Text()))
|
||||
|
||||
testDisabledBehaviour := func() {
|
||||
settingsURL := ts.URLTo(router.AdminSettings)
|
||||
html, resp := ts.Client.GetHTML(settingsURL)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
|
||||
|
||||
// we do not have the summary/details hack if the forms are hidden
|
||||
privacyFormContainer := html.Find("#change-privacy")
|
||||
a.Equal(0, privacyFormContainer.Length())
|
||||
// the should still be the parent container, however
|
||||
privacyContainer := html.Find("#privacy-mode-container")
|
||||
a.Equal(1, privacyContainer.Length())
|
||||
// there should only be one input in the privacy mode container now
|
||||
inputs := privacyContainer.Find("input")
|
||||
a.Equal(1, inputs.Length())
|
||||
// the input should be disabled
|
||||
_, disabled := inputs.Attr("disabled")
|
||||
a.True(disabled)
|
||||
|
||||
// next, verify that the change language setting is disabled
|
||||
languageContainer := html.Find("#change-language-container")
|
||||
a.Equal(1, languageContainer.Length())
|
||||
// there should only be one input in the language mode container now
|
||||
inputs = languageContainer.Find("input")
|
||||
a.Equal(1, inputs.Length())
|
||||
// the input should be disabled
|
||||
_, disabled = inputs.Attr("disabled")
|
||||
a.True(disabled)
|
||||
}
|
||||
|
||||
/* Now: verify that moderators cannot make room settings changes */
|
||||
ts.User = roomdb.Member{
|
||||
ID: 7331,
|
||||
Role: roomdb.RoleModerator,
|
||||
}
|
||||
testDisabledBehaviour()
|
||||
|
||||
/* Finally: verify that members cannot make room settings changes */
|
||||
ts.User = roomdb.Member{
|
||||
ID: 9001,
|
||||
Role: roomdb.RoleMember,
|
||||
}
|
||||
testDisabledBehaviour()
|
||||
}
|
|
@ -133,6 +133,15 @@ 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 ts.User.Role == roomdb.RoleAdmin || ts.User.Role == roomdb.RoleModerator }
|
||||
testFuncs["member_is_admin"] = func() bool { return ts.User.Role == roomdb.RoleAdmin }
|
||||
testFuncs["member_can_invite"] = func() bool {
|
||||
pm, _ := ts.ConfigDB.GetPrivacyMode(ctx)
|
||||
memberElevated := ts.User.Role == roomdb.RoleAdmin || ts.User.Role == roomdb.RoleModerator
|
||||
memberCanInvite := ts.User.Role == roomdb.RoleMember && (pm == roomdb.ModeCommunity || pm == roomdb.ModeOpen)
|
||||
return memberElevated || memberCanInvite
|
||||
}
|
||||
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_elevated }} 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
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,23 +20,34 @@
|
|||
method="POST"
|
||||
>
|
||||
{{ .csrfField }}
|
||||
<div class="flex flex-row items-center h-12">
|
||||
<div id="denied-keys-input-container" 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>
|
||||
|
@ -98,4 +109,4 @@
|
|||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -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>
|
||||
<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>
|
||||
{{ 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}}
|
||||
|
@ -134,4 +147,4 @@
|
|||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{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>
|
||||
<div class="flex flex-row items-center justify-start mb-6">
|
||||
<label class="block mt-6 mb-1 text-sm text-gray-500">{{ i18n "AdminAddNewMemberTitle" }}</label>
|
||||
<div id="add-member-input-container" 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>
|
||||
|
@ -106,4 +113,4 @@
|
|||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -8,45 +8,55 @@
|
|||
<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>
|
||||
<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">
|
||||
{{ $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}}
|
||||
{{if eq . $.Member.Role}}
|
||||
{{i18n .String}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
</summary>
|
||||
|
||||
<div class="absolute z-10 bg-white w-40 mt-2 shadow-xl ring-1 ring-gray-200 rounded divide-y flex flex-col items-stretch overflow-hidden">
|
||||
{{range $.AllRoles}}
|
||||
{{if ne . $.Member.Role}}
|
||||
<form
|
||||
action="{{urlTo "admin:members:change-role" "id" $.Member.ID}}"
|
||||
method="POST"
|
||||
>
|
||||
{{$.csrfField}}
|
||||
<input type="hidden" name="role" value="{{.}}">
|
||||
<input
|
||||
type="submit"
|
||||
value="{{i18n .String}}"
|
||||
class="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"
|
||||
/>
|
||||
</form>
|
||||
{{else}}
|
||||
<div
|
||||
class="pr-3 py-2 text-gray-600 flex flex-row items-center cursor-default"
|
||||
>
|
||||
<div class="w-10 flex flex-row items-center justify-center">
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span>{{i18n .String}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</details>
|
||||
{{ else }}
|
||||
{{range $.AllRoles}}
|
||||
{{if eq . $.Member.Role}}
|
||||
{{i18n .String}}
|
||||
{{end}}
|
||||
<p id="ssb-member-role" class="mb-8 font-bold tracking-wider truncate text-gray-900">{{i18n .String}}</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</summary>
|
||||
|
||||
<div class="absolute z-10 bg-white w-40 mt-2 shadow-xl ring-1 ring-gray-200 rounded divide-y flex flex-col items-stretch overflow-hidden">
|
||||
{{range $.AllRoles}}
|
||||
{{if ne . $.Member.Role}}
|
||||
<form
|
||||
action="{{urlTo "admin:members:change-role" "id" $.Member.ID}}"
|
||||
method="POST"
|
||||
>
|
||||
{{$.csrfField}}
|
||||
<input type="hidden" name="role" value="{{.}}">
|
||||
<input
|
||||
type="submit"
|
||||
value="{{i18n .String}}"
|
||||
class="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"
|
||||
/>
|
||||
</form>
|
||||
{{else}}
|
||||
<div
|
||||
class="pr-3 py-2 text-gray-600 flex flex-row items-center cursor-default"
|
||||
>
|
||||
<div class="w-10 flex flex-row items-center justify-center">
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span>{{i18n .String}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</details>
|
||||
{{ 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>
|
||||
|
||||
<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"
|
||||
{{ 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}}
|
||||
{{end}}
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
class="text-3xl tracking-tight font-black text-black mt-2 mb-0"
|
||||
>{{ i18n "Settings" }}</h1>
|
||||
|
||||
<div class="max-w-2xl">
|
||||
<div class="max-w-2xl" id="privacy-mode-container">
|
||||
<h2 class="text-xl tracking-tight font-bold text-black mt-2 mb-2">{{ i18n "PrivacyModesTitle" }}</h2>
|
||||
<p class="mb-4">
|
||||
{{ i18n "ExplanationPrivacyModes" }}
|
||||
<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 }}
|
||||
|
@ -38,12 +39,17 @@
|
|||
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span>{{ i18n .String }}</span>
|
||||
<span id="selected-mode">{{ i18n .String }}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{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>
|
||||
|
@ -53,12 +59,13 @@
|
|||
<div class="text-md col-span-2 italic">{{ i18n "ExplanationRestricted" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-2xl">
|
||||
<div class="max-w-2xl" id="change-language-container">
|
||||
<h2 class="text-xl tracking-tight font-bold text-black mt-2 mb-2">{{ i18n "DefaultLanguageTitle" }}</h2>
|
||||
<p class="mb-4">
|
||||
{{ 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,11 +9,11 @@
|
|||
</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}}"
|
||||
class="self-start shadow rounded px-4 h-8 flex flex-row justify-center items-center text-gray-100 bg-pink-600 hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-pink-600 focus:ring-opacity-50"
|
||||
>{{i18n "NoticeEditTitle"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -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"
|
||||
|
@ -29,4 +29,4 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
</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}}"
|
||||
class="self-start shadow rounded px-4 h-8 flex flex-row justify-center items-center text-gray-100 bg-pink-600 hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-pink-600 focus:ring-opacity-50"
|
||||
>{{i18n "NoticeEditTitle"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
Loading…
Reference in New Issue