Add education toggle to user admin settings

This commit is contained in:
Ben Visness 2022-09-10 16:52:02 -05:00
parent 168b210c5b
commit 045a2c2379
5 changed files with 50 additions and 25 deletions

View File

@ -112,7 +112,7 @@ func TestUserSettings(t *testing.T) {
func TestAdmin(t *testing.T) { func TestAdmin(t *testing.T) {
AssertRegexMatch(t, BuildAdminAtomFeed(), RegexAdminAtomFeed, nil) AssertRegexMatch(t, BuildAdminAtomFeed(), RegexAdminAtomFeed, nil)
AssertRegexMatch(t, BuildAdminApprovalQueue(), RegexAdminApprovalQueue, nil) AssertRegexMatch(t, BuildAdminApprovalQueue(), RegexAdminApprovalQueue, nil)
AssertRegexMatch(t, BuildAdminSetUserStatus(), RegexAdminSetUserStatus, nil) AssertRegexMatch(t, BuildAdminSetUserOptions(), RegexAdminSetUserOptions, nil)
AssertRegexMatch(t, BuildAdminNukeUser(), RegexAdminNukeUser, nil) AssertRegexMatch(t, BuildAdminNukeUser(), RegexAdminNukeUser, nil)
} }

View File

@ -245,11 +245,11 @@ func BuildAdminApprovalQueue() string {
return Url("/admin/approvals", nil) return Url("/admin/approvals", nil)
} }
var RegexAdminSetUserStatus = regexp.MustCompile(`^/admin/setuserstatus$`) var RegexAdminSetUserOptions = regexp.MustCompile(`^/admin/setuseroptions$`)
func BuildAdminSetUserStatus() string { func BuildAdminSetUserOptions() string {
defer CatchPanic() defer CatchPanic()
return Url("/admin/setuserstatus", nil) return Url("/admin/setuseroptions", nil)
} }
var RegexAdminNukeUser = regexp.MustCompile(`^/admin/nukeuser$`) var RegexAdminNukeUser = regexp.MustCompile(`^/admin/nukeuser$`)

View File

@ -73,22 +73,33 @@
</div> </div>
<div class="relative w-100"> <div class="relative w-100">
<div class="bg--card cover absolute w-100 h-100 br2"></div> <div class="bg--card cover absolute w-100 h-100 br2"></div>
<div class="mt3"> <form id="admin_set_options_form" method="POST" action="{{ .AdminSetOptionsUrl }}">
<div>User status:</div> {{ csrftoken .Session }}
<form id="admin_set_status_form" method="POST" action="{{ .AdminSetStatusUrl }}"> <input type="hidden" name="user_id" value="{{ .ProfileUser.ID }}" />
{{ csrftoken .Session }} <input type="hidden" name="username" value="{{ .ProfileUser.Username }}" />
<input type="hidden" name="user_id" value="{{ .ProfileUser.ID }}" /> <div class="mt3">
<input type="hidden" name="username" value="{{ .ProfileUser.Username }}" /> <div>User status:</div>
<select name="status"> <select name="status">
<option value="inactive" {{ if eq .ProfileUser.Status 1 }}selected{{ end }}>Brand new</option> <option value="inactive" {{ if eq .ProfileUser.Status 1 }}selected{{ end }}>Brand new</option>
<option value="confirmed" {{ if eq .ProfileUser.Status 2 }}selected{{ end }}>Email confirmed</option> <option value="confirmed" {{ if eq .ProfileUser.Status 2 }}selected{{ end }}>Email confirmed</option>
<option value="approved" {{ if eq .ProfileUser.Status 3 }}selected{{ end }}>Admin approved</option> <option value="approved" {{ if eq .ProfileUser.Status 3 }}selected{{ end }}>Admin approved</option>
<option value="banned" {{ if eq .ProfileUser.Status 4 }}selected{{ end }}>Banned</option> <option value="banned" {{ if eq .ProfileUser.Status 4 }}selected{{ end }}>Banned</option>
</select> </select>
<input type="submit" value="Set" />
<div class="c--dim f7">Only sets status. Doesn't delete anything.</div> <div class="c--dim f7">Only sets status. Doesn't delete anything.</div>
</form> </div>
</div> <div class="mt3">
<div>Education role:</div>
<select name="edu_role">
<option value="none" {{ if not .ProfileUser.IsEduTester }}selected{{ end }}>None</option>
<option value="beta" {{ if and .ProfileUser.IsEduTester (not .ProfileUser.IsEduAuthor) }}selected{{ end }}>Beta Tester</option>
<option value="author" {{ if .ProfileUser.IsEduAuthor }}selected{{ end }}>Author</option>
</select>
</div>
<div class="mt3">
<input type="submit" value="Save" />
</div>
</form>
<div class="mt3"> <div class="mt3">
<div>Danger zone:</div> <div>Danger zone:</div>
<form id="admin_nuke_form" method="POST" action="{{ .AdminNukeUrl }}"> <form id="admin_nuke_form" method="POST" action="{{ .AdminNukeUrl }}">
@ -110,7 +121,7 @@
panelEl.style.display = "none"; panelEl.style.display = "none";
}); });
document.querySelector("#admin_set_status_form").addEventListener("submit", function(ev) { document.querySelector("#admin_set_options_form").addEventListener("submit", function(ev) {
if (!adminUnlocked) { if (!adminUnlocked) {
ev.preventDefault(); ev.preventDefault();
} }

View File

@ -81,7 +81,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexAdminAtomFeed, AdminAtomFeed) hmnOnly.GET(hmnurl.RegexAdminAtomFeed, AdminAtomFeed)
hmnOnly.GET(hmnurl.RegexAdminApprovalQueue, adminsOnly(AdminApprovalQueue)) hmnOnly.GET(hmnurl.RegexAdminApprovalQueue, adminsOnly(AdminApprovalQueue))
hmnOnly.POST(hmnurl.RegexAdminApprovalQueue, adminsOnly(csrfMiddleware(AdminApprovalQueueSubmit))) hmnOnly.POST(hmnurl.RegexAdminApprovalQueue, adminsOnly(csrfMiddleware(AdminApprovalQueueSubmit)))
hmnOnly.POST(hmnurl.RegexAdminSetUserStatus, adminsOnly(csrfMiddleware(UserProfileAdminSetStatus))) hmnOnly.POST(hmnurl.RegexAdminSetUserOptions, adminsOnly(csrfMiddleware(UserProfileAdminSetOptions)))
hmnOnly.POST(hmnurl.RegexAdminNukeUser, adminsOnly(csrfMiddleware(UserProfileAdminNuke))) hmnOnly.POST(hmnurl.RegexAdminNukeUser, adminsOnly(csrfMiddleware(UserProfileAdminNuke)))
hmnOnly.GET(hmnurl.RegexFeed, Feed) hmnOnly.GET(hmnurl.RegexFeed, Feed)

View File

@ -36,8 +36,8 @@ type UserProfileTemplateData struct {
CanAddProject bool CanAddProject bool
NewProjectUrl string NewProjectUrl string
AdminSetStatusUrl string AdminSetOptionsUrl string
AdminNukeUrl string AdminNukeUrl string
SnippetEdit templates.SnippetEdit SnippetEdit templates.SnippetEdit
} }
@ -194,8 +194,8 @@ func UserProfile(c *RequestContext) ResponseData {
CanAddProject: numPersonalProjects < maxPersonalProjects, CanAddProject: numPersonalProjects < maxPersonalProjects,
NewProjectUrl: hmnurl.BuildProjectNew(), NewProjectUrl: hmnurl.BuildProjectNew(),
AdminSetStatusUrl: hmnurl.BuildAdminSetUserStatus(), AdminSetOptionsUrl: hmnurl.BuildAdminSetUserOptions(),
AdminNukeUrl: hmnurl.BuildAdminNukeUser(), AdminNukeUrl: hmnurl.BuildAdminNukeUser(),
SnippetEdit: snippetEdit, SnippetEdit: snippetEdit,
}, c.Perf) }, c.Perf)
@ -479,7 +479,7 @@ func UserSettingsSave(c *RequestContext) ResponseData {
return res return res
} }
func UserProfileAdminSetStatus(c *RequestContext) ResponseData { func UserProfileAdminSetOptions(c *RequestContext) ResponseData {
c.Req.ParseForm() c.Req.ParseForm()
userIdStr := c.Req.Form.Get("user_id") userIdStr := c.Req.Form.Get("user_id")
@ -503,17 +503,31 @@ func UserProfileAdminSetStatus(c *RequestContext) ResponseData {
return c.RejectRequest("No legal user status provided") return c.RejectRequest("No legal user status provided")
} }
eduRole := c.Req.Form.Get("edu_role")
var desiredEduRole models.EduRole
switch eduRole {
case "none":
desiredEduRole = models.EduRoleNone
case "beta":
desiredEduRole = models.EduRoleBeta
case "author":
desiredEduRole = models.EduRoleAuthor
default:
return c.RejectRequest("the education role is bad and you should feel bad")
}
_, err = c.Conn.Exec(c, _, err = c.Conn.Exec(c,
` `
UPDATE hmn_user UPDATE hmn_user
SET status = $1 SET status = $2, education_role = $3
WHERE id = $2 WHERE id = $1
`, `,
desiredStatus,
userId, userId,
desiredStatus,
desiredEduRole,
) )
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user status")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user admin settings"))
} }
if desiredStatus == models.UserStatusBanned { if desiredStatus == models.UserStatusBanned {
err = auth.DeleteSessionForUser(c, c.Conn, c.Req.Form.Get("username")) err = auth.DeleteSessionForUser(c, c.Conn, c.Req.Form.Get("username"))
@ -522,7 +536,7 @@ func UserProfileAdminSetStatus(c *RequestContext) ResponseData {
} }
} }
res := c.Redirect(hmnurl.BuildUserProfile(c.Req.Form.Get("username")), http.StatusSeeOther) res := c.Redirect(hmnurl.BuildUserProfile(c.Req.Form.Get("username")), http.StatusSeeOther)
res.AddFutureNotice("success", "Successfully set status") res.AddFutureNotice("success", "Successfully set admin options")
return res return res
} }