Merge pull request #137 from ssb-ngi-pointer/move-privacy-modes

Move privacy modes into proper settings view
This commit is contained in:
Alexander Cobleigh 2021-04-12 13:46:58 +02:00 committed by GitHub
commit f9652c6423
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 173 additions and 105 deletions

View File

@ -3,18 +3,12 @@
package admin
import (
// "errors"
"fmt"
"net/http"
"go.mindeco.de/http/render"
"github.com/gorilla/csrf"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
// weberrors "github.com/ssb-ngi-pointer/go-ssb-room/web/errors"
"github.com/ssb-ngi-pointer/go-ssb-room/roomstate"
"github.com/ssb-ngi-pointer/go-ssb-room/web"
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
)
type dashboardHandler struct {
@ -38,50 +32,12 @@ func (h dashboardHandler) overview(w http.ResponseWriter, req *http.Request) (in
if err != nil {
return nil, fmt.Errorf("failed to count denied keys: %w", err)
}
privacyModes := []roomdb.PrivacyMode{roomdb.ModeOpen, roomdb.ModeCommunity, roomdb.ModeRestricted}
currentMode, err := h.dbs.Config.GetPrivacyMode(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to retrieve current privacy mode: %w", err)
}
return map[string]interface{}{
"OnlineRefs": onlineRefs,
"OnlineCount": onlineCount,
"MemberCount": memberCount,
"InviteCount": inviteCount,
"DeniedCount": deniedCount,
"CurrentMode": currentMode,
"PrivacyModes": privacyModes,
csrf.TemplateTag: csrf.TemplateField(req),
"OnlineRefs": onlineRefs,
"OnlineCount": onlineCount,
"MemberCount": memberCount,
"InviteCount": inviteCount,
"DeniedCount": deniedCount,
}, nil
}
func (h dashboardHandler) setPrivacy(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
// TODO: proper error type
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("bad request"))
return
}
if err := req.ParseForm(); err != nil {
// TODO: proper error type
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("bad request: %w", err))
return
}
pmValue := req.Form.Get("privacy_mode")
pm := roomdb.ParsePrivacyMode(pmValue)
if pm == roomdb.ModeUnknown {
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("unknown privacy mode was being set: %v", pmValue))
}
err := h.dbs.Config.SetPrivacyMode(req.Context(), pm)
if err != nil {
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("something went wrong when setting the privacy mode: %w", err))
}
urlTo := web.NewURLTo(router.CompleteApp())
dashboard := urlTo(router.AdminDashboard).String()
http.Redirect(w, req, dashboard, http.StatusFound)
}

View File

@ -25,6 +25,8 @@ var HTMLTemplates = []string{
"admin/dashboard.tmpl",
"admin/menu.tmpl",
"admin/settings.tmpl",
"admin/aliases-revoke-confirm.tmpl",
"admin/denied-keys.tmpl",
@ -68,9 +70,14 @@ func Handler(
dbs: dbs,
roomState: roomState,
}
mux.HandleFunc("/dashboard", r.HTML("admin/dashboard.tmpl", dashboardHandler.overview))
mux.HandleFunc("/dashboard/set-privacy", dashboardHandler.setPrivacy)
var sh = settingsHandler{
r: r,
db: dbs.Config,
}
mux.HandleFunc("/settings", r.HTML("admin/settings.tmpl", sh.overview))
mux.HandleFunc("/settings/set-privacy", sh.setPrivacy)
mux.HandleFunc("/menu", r.HTML("admin/menu.tmpl", func(w http.ResponseWriter, req *http.Request) (interface{}, error) {
return map[string]interface{}{}, nil

View File

@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
package admin
import (
// "errors"
"fmt"
"net/http"
"go.mindeco.de/http/render"
"github.com/gorilla/csrf"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
"github.com/ssb-ngi-pointer/go-ssb-room/web"
weberrors "github.com/ssb-ngi-pointer/go-ssb-room/web/errors"
"github.com/ssb-ngi-pointer/go-ssb-room/web/members"
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
)
type settingsHandler struct {
r *render.Renderer
db roomdb.RoomConfig
}
func (h settingsHandler) overview(w http.ResponseWriter, req *http.Request) (interface{}, error) {
privacyModes := []roomdb.PrivacyMode{roomdb.ModeOpen, roomdb.ModeCommunity, roomdb.ModeRestricted}
currentMode, err := h.db.GetPrivacyMode(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to retrieve current privacy mode: %w", err)
}
return map[string]interface{}{
"CurrentMode": currentMode,
"PrivacyModes": privacyModes,
csrf.TemplateTag: csrf.TemplateField(req),
}, nil
}
func (h settingsHandler) setPrivacy(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
// TODO: proper error type
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("bad request"))
return
}
if err := req.ParseForm(); err != nil {
// TODO: proper error type
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("bad request: %w", err))
return
}
// get the member behind the POST
currentMember := members.FromContext(req.Context())
if currentMember == nil {
err := weberrors.ErrForbidden{Details: fmt.Errorf("not a registered member")}
h.r.Error(w, req, http.StatusInternalServerError, err)
return
}
// make sure the member is an admin
if currentMember.Role != roomdb.RoleAdmin {
err := weberrors.ErrForbidden{Details: fmt.Errorf("yr not an admin! naughty naughty")}
h.r.Error(w, req, http.StatusInternalServerError, err)
return
}
pmValue := req.Form.Get("privacy_mode")
pm := roomdb.ParsePrivacyMode(pmValue)
if pm == roomdb.ModeUnknown {
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("unknown privacy mode was being set: %v", pmValue))
}
err := h.db.SetPrivacyMode(req.Context(), pm)
if err != nil {
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("something went wrong when setting the privacy mode: %w", err))
}
// we successfully set the privacy mode! time to redirect to the updated settings overview
urlTo := web.NewURLTo(router.CompleteApp())
overview := urlTo(router.AdminSettings).String()
http.Redirect(w, req, overview, http.StatusFound)
}

View File

@ -123,6 +123,7 @@ ExplanationOpen = "Open invite codes, anyone may connect"
ExplanationCommunity = "Members can create invites, anyone may connect"
ExplanationRestricted = "Only admins/mods can create invites, only members may connect"
Settings = "Settings"
[MemberCount]
description = "Number of members"

View File

@ -6,9 +6,11 @@ import "github.com/gorilla/mux"
// constant names for the named routes
const (
AdminDashboard = "admin:dashboard"
AdminDashboardSetPrivacy = "admin:dashboard:set-privacy"
AdminMenu = "admin:menu"
AdminDashboard = "admin:dashboard"
AdminMenu = "admin:menu"
AdminSettings = "admin:settings:overview"
AdminSettingsSetPrivacy = "admin:settings:set-privacy"
AdminAliasesRevokeConfirm = "admin:aliases:revoke:confirm"
AdminAliasesRevoke = "admin:aliases:revoke"
@ -44,7 +46,9 @@ func Admin(m *mux.Router) *mux.Router {
}
m.Path("/dashboard").Methods("GET").Name(AdminDashboard)
m.Path("/dashboard/set-privacy").Methods("POST").Name(AdminDashboardSetPrivacy)
m.Path("/settings").Methods("GET").Name(AdminSettings)
m.Path("/settings/set-privacy").Methods("POST").Name(AdminSettingsSetPrivacy)
m.Path("/menu").Methods("GET").Name(AdminMenu)

View File

@ -85,54 +85,5 @@
</div>
{{end}}
</div>
<div class="max-w-lg">
<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>
<details class="mb-8 self-start w-96" id="change-privacy">
<summary class="px-3 py-1 w-96 rounded shadow bg-white ring-1 ring-gray-300 hover:bg-gray-100 cursor-pointer">
{{ i18n .CurrentMode.String }}
</summary>
<div class="absolute w-96 z-10 bg-white mt-2 shadow-xl ring-1 ring-gray-200 rounded divide-y flex flex-col items-stretch overflow-hidden">
{{ range .PrivacyModes }}
{{ if ne . $.CurrentMode }}
<form
action="{{urlTo "admin:dashboard:set-privacy" }}"
method="POST"
>
{{$.csrfField}}
<input type="hidden" name="privacy_mode" 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>
<div class="grid max-w-lg grid-cols-3 gap-y-2 mb-8">
<div class="text-xl text-gray-500 font-bold">Open</div>
<div class="text-md col-span-2 italic">{{ i18n "ExplanationOpen" }}</div>
<div class="text-xl text-gray-500 font-bold">Community</div>
<div class="text-md col-span-2 italic">{{ i18n "ExplanationCommunity" }}</div>
<div class="text-xl text-gray-500 font-bold">Restricted</div>
<div class="text-md col-span-2 italic">{{ i18n "ExplanationRestricted" }}</div>
</div>
</div>
</div>
{{end}}

View File

@ -0,0 +1,58 @@
{{ define "title" }}{{i18n "Settings"}}{{ end }}
{{ define "content" }}
<h1
class="text-3xl tracking-tight font-black text-black mt-2 mb-0"
>{{ i18n "Settings" }}</h1>
<div class="flex flex-col-reverse sm:flex-row justify-start items-stretch ">
<div class="max-w-lg">
<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>
<details class="mb-8 self-start w-96" id="change-privacy">
<summary class="px-3 py-1 w-96 rounded shadow bg-white ring-1 ring-gray-300 hover:bg-gray-100 cursor-pointer">
{{ i18n .CurrentMode.String }}
</summary>
<div class="absolute w-96 z-10 bg-white mt-2 shadow-xl ring-1 ring-gray-200 rounded divide-y flex flex-col items-stretch overflow-hidden">
{{ range .PrivacyModes }}
{{ if ne . $.CurrentMode }}
<form
action="{{ urlTo "admin:settings:set-privacy" }}"
method="POST"
>
{{ $.csrfField }}
<input type="hidden" name="privacy_mode" 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>
<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>
<div class="text-xl text-gray-500 font-bold">{{ i18n "ModeCommunity" }}</div>
<div class="text-md col-span-2 italic">{{ i18n "ExplanationCommunity" }}</div>
<div class="text-xl text-gray-500 font-bold">{{ i18n "ModeRestricted" }}</div>
<div class="text-md col-span-2 italic">{{ i18n "ExplanationRestricted" }}</div>
</div>
</div>
</div>
{{end}}

View File

@ -46,6 +46,15 @@
</svg>{{i18n "AdminDeniedKeysTitle"}}
</a>
<a
href="{{urlTo "admin:settings:overview"}}"
class="{{if current_page_is "admin:settings:overview"}}bg-gray-300 {{else}}hover:bg-gray-200 {{end}}pr-1 pl-2 py-3 sm:py-1 rounded-md flex flex-row items-center font-semibold text-sm text-gray-700 hover:text-gray-800 truncate"
>
<svg class="text-green-600 w-4 h-4 mr-1" viewBox="0 0 24 24">
<path fill="currentColor" d="M13 3C16.88 3 20 6.14 20 10C20 12.8 18.37 15.19 16 16.31V21H9V18H8C6.89 18 6 17.11 6 16V13H4.5C4.08 13 3.84 12.5 4.08 12.19L6 9.66C6.19 5.95 9.23 3 13 3M13 1C8.42 1 4.61 4.43 4.06 8.91L2.5 11C1.92 11.72 1.82 12.72 2.24 13.59C2.6 14.31 3.24 14.8 4 14.95V16C4 17.86 5.28 19.43 7 19.87V23H18V17.47C20.5 15.83 22 13.06 22 10C22 5.04 17.96 1 13 1M16.1 9.42V9C16.1 8.85 16.1 8.76 16.04 8.62L16.93 7.96C17 7.92 17 7.78 17 7.68L16.18 6.32C16.13 6.23 16 6.18 15.9 6.23L14.91 6.65C14.73 6.46 14.5 6.32 14.26 6.23L14.1 5.2C14.07 5.06 14 5 13.88 5H12.29C12.19 5 12.1 5.06 12.1 5.2L11.96 6.23C11.73 6.32 11.5 6.46 11.3 6.65L10.27 6.23C10.18 6.18 10.1 6.23 10.04 6.32L9.24 7.68C9.19 7.82 9.19 7.92 9.29 7.96L10.13 8.62C10.13 8.76 10.1 8.9 10.1 9C10.1 9.14 10.13 9.28 10.13 9.42L9.29 10.07C9.19 10.12 9.19 10.21 9.24 10.31L10.04 11.71C10.1 11.81 10.18 11.81 10.27 11.81L11.26 11.38C11.5 11.57 11.68 11.67 11.96 11.76L12.1 12.84C12.1 12.93 12.19 13 12.29 13H13.88C14 13 14.07 12.93 14.1 12.84L14.26 11.76C14.5 11.67 14.73 11.57 14.91 11.39L15.9 11.81C16 11.81 16.13 11.81 16.18 11.71L17 10.31C17 10.21 17 10.12 16.93 10.07L16.1 9.42M13.1 10.45C12.32 10.45 11.68 9.79 11.68 9S12.29 7.59 13.1 7.59C13.88 7.59 14.54 8.2 14.54 9S13.88 10.45 13.1 10.45Z" />
</svg>{{i18n "Settings"}}
</a>
<a
href="{{urlTo "complete:notice:list"}}"
class="{{if current_page_is "complete:notice:list"}}bg-gray-300 {{else}}hover:bg-gray-200 {{end}}pr-1 pl-2 py-3 sm:py-1 rounded-md flex flex-row items-center font-semibold text-sm text-gray-700 hover:text-gray-800 truncate"
@ -55,4 +64,4 @@
</svg>{{i18n "NavAdminNotices"}}
</a>
</div>
{{end}}
{{end}}