diff --git a/admin.go b/admin.go index 4d8d1d6..6a1a3b5 100644 --- a/admin.go +++ b/admin.go @@ -189,6 +189,7 @@ func handleViewAdminUsers(app *App, u *User, w http.ResponseWriter, r *http.Requ *AdminPage Config config.AppCfg Message string + Flashes []string Users *[]User CurPage int @@ -201,6 +202,7 @@ func handleViewAdminUsers(app *App, u *User, w http.ResponseWriter, r *http.Requ Message: r.FormValue("m"), } + p.Flashes, _ = getSessionFlashes(app, w, r, nil) p.TotalUsers = app.db.GetAllUsersCount() ttlPages := p.TotalUsers / adminUsersPerPage p.TotalPages = []int{} @@ -312,6 +314,37 @@ func handleViewAdminUser(app *App, u *User, w http.ResponseWriter, r *http.Reque return nil } +func handleAdminDeleteUser(app *App, u *User, w http.ResponseWriter, r *http.Request) error { + if !u.IsAdmin() { + return impart.HTTPError{http.StatusForbidden, "Administrator privileges required for this action"} + } + + vars := mux.Vars(r) + username := vars["username"] + confirmUsername := r.PostFormValue("confirm-username") + + if confirmUsername != username { + return impart.HTTPError{http.StatusBadRequest, "Username was not confirmed"} + } + + user, err := app.db.GetUserForAuth(username) + if err == ErrUserNotFound { + return impart.HTTPError{http.StatusNotFound, fmt.Sprintf("User '%s' was not found", username)} + } else if err != nil { + log.Error("get user for deletion: %v", err) + return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get user with username '%s': %v", username, err)} + } + + err = app.db.DeleteAccount(user.ID) + if err != nil { + log.Error("delete user %s: %v", user.Username, err) + return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not delete user account for '%s': %v", username, err)} + } + + _ = addSessionFlash(app, w, r, fmt.Sprintf("User \"%s\" was deleted successfully.", username), nil) + return impart.HTTPError{http.StatusFound, "/admin/users"} +} + func handleAdminToggleUserStatus(app *App, u *User, w http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) username := vars["username"] diff --git a/less/core.less b/less/core.less index 9d2e1ab..6401ceb 100644 --- a/less/core.less +++ b/less/core.less @@ -1044,6 +1044,19 @@ li { background-color: #dff0d8; border-color: #d6e9c6; } + &.danger { + border-color: #856404; + background-color: white; + h3 { + margin: 0 0 0.5em 0; + font-size: 1em; + font-weight: bold; + color: black !important; + } + h3 + p, button { + font-size: 0.86em; + } + } p { margin: 0; diff --git a/less/pad.less b/less/pad.less index 6cdd383..486e2ea 100644 --- a/less/pad.less +++ b/less/pad.less @@ -340,6 +340,15 @@ body#pad { } } + .body { + line-height: 1.5; + + input[type=text].confirm { + width: 100%; + box-sizing: border-box; + } + } + .short { text-align: center; } diff --git a/routes.go b/routes.go index 8a160e6..7389fd1 100644 --- a/routes.go +++ b/routes.go @@ -166,6 +166,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router { write.HandleFunc("/admin/settings", handler.Admin(handleViewAdminSettings)).Methods("GET") write.HandleFunc("/admin/users", handler.Admin(handleViewAdminUsers)).Methods("GET") write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET") + write.HandleFunc("/admin/user/{username}/delete", handler.Admin(handleAdminDeleteUser)).Methods("POST") write.HandleFunc("/admin/user/{username}/status", handler.Admin(handleAdminToggleUserStatus)).Methods("POST") write.HandleFunc("/admin/user/{username}/passphrase", handler.Admin(handleAdminResetUserPass)).Methods("POST") write.HandleFunc("/admin/pages", handler.Admin(handleViewAdminPages)).Methods("GET") diff --git a/static/js/modals.js b/static/js/modals.js new file mode 100644 index 0000000..6705afa --- /dev/null +++ b/static/js/modals.js @@ -0,0 +1,24 @@ +/* + * Copyright © 2016-2021 A Bunch Tell LLC. + * + * This file is part of WriteFreely. + * + * WriteFreely is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, included + * in the LICENSE file in this source code package. + */ + +function showModal(id) { + document.getElementById('overlay').style.display = 'block'; + document.getElementById('modal-'+id).style.display = 'block'; +} + +var closeModals = function(e) { + e.preventDefault(); + document.getElementById('overlay').style.display = 'none'; + var modals = document.querySelectorAll('.modal'); + for (var i=0; i {{template "admin-header" .}} + + {{if .Flashes}} +

+ {{range .Flashes}}{{.}}{{end}} +

+ {{end}}
{{.TotalUsers}} {{pluralize "user" "users" .TotalUsers}} + Invite people diff --git a/templates/user/admin/view-user.tmpl b/templates/user/admin/view-user.tmpl index 95bc416..dac88bf 100644 --- a/templates/user/admin/view-user.tmpl +++ b/templates/user/admin/view-user.tmpl @@ -32,8 +32,13 @@ input.copy-text { width: 100%; box-sizing: border-box; } +.modal { + position: fixed; +}
+
+ {{template "admin-header" .}}

{{.User.Username}}

@@ -139,9 +144,60 @@ input.copy-text { {{end}} {{end}} + + {{ if not .User.IsAdmin }} +

Incinerator

+
+
+
+

Delete this user

+

Permanently erase all user data, with no way to recover it.

+
+ +
+
+ {{end}}
+ + + + +