diff --git a/static/default.css b/static/default.css index 56bb632..be53619 100644 --- a/static/default.css +++ b/static/default.css @@ -107,10 +107,6 @@ input, kbd { font: inherit; color: inherit; } textarea {font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;} ::-webkit-file-upload-button, -.btn { line-height: normal; display: inline-block; border: 1px #999 solid; border-radius: .25rem; text-decoration: none; padding: .25rem; font-size: 1rem; margin: 0; } -.btn_weak { border: 1px #999 dashed; } -.btn_accent { font-weight: bold; } -.btn:hover, .btn:active { cursor: pointer; } .edit { min-height: 80vh; } .edit__title { margin-top: 0; } @@ -300,10 +296,6 @@ article .codeblock, textarea, table { border: 0; background-color: #444444; color: #ddd; } .transclusion_stand-out { background-color: rgba(68, 68, 68, 0.5); } -.btn:visited { color: #ddd;} - - .btn { border: #444 solid 1px; border-radius: .25rem; } - .btn_weak { background-color: transparent; } .transclusion code, .transclusion .codeblock { background-color: #454545; } @@ -446,23 +438,32 @@ kbd { } @media (min-width: 600px) { - .form-field { + .form--double .form-field { display: grid; grid-template-columns: 150px max-content; grid-column-gap: 16px; } - .form-field label { + .form--double .form-field label { grid-column: 1; } - .form-field input, - .form-field button, - .form-field select, - .form-field textarea, - .form-field .form-field__input { + .form--double .form-field input, + .form--double .form-field button, + .form--double .form-field select, + .form--double .form-field textarea, + .form--double .form-field__input { grid-column: 2; } } +/* + * Form wrap + */ + +.form-wrap h2 { + margin: 1.5em 0 0.25em; + font-size: 1.2em; +} + /* * Notices */ @@ -484,3 +485,55 @@ kbd { background-color: #5b3535; } } + +/* + * Buttons + */ + +.btn { + line-height: normal; + display: inline-block; + border: 1px #999 solid; + border-radius: .15rem; + text-decoration: none; + padding: .25rem .5rem; + font-size: 1rem; + margin: 0; +} + +.btn:hover { + cursor: pointer; +} + +.btn_accent { + font-weight: bold; +} + +.btn_weak { + border: 1px dashed #999; +} + +.btn_destructive { + border-color: #aa1818; + background-color: #ee4343; + color: white; +} + +@media (prefers-color-scheme: dark) { + .btn { + border-color: #444 solid 1px; + } + + .btn:visited { + color: #ddd; + } + + .btn_weak { + background-color: transparent; + } + + .btn_destructive { + border-color: #e34343; + background-color: #b92828; + } +} diff --git a/user/users.go b/user/users.go index 2abd829..6daa301 100644 --- a/user/users.go +++ b/user/users.go @@ -60,6 +60,18 @@ func UserByName(username string) *User { return EmptyUser() } +func DeleteUser(name string) error { + user, loaded := users.LoadAndDelete(name) + if loaded { + u := user.(*User) + u.Name = "anon" + u.Group = "anon" + u.Password = "" + return SaveUserDatabase() + } + return nil +} + func commenceSession(username, token string) { tokens.Store(token, username) dumpTokens() diff --git a/views/admin.qtpl b/views/admin.qtpl index 9eeba97..e7f1dc3 100644 --- a/views/admin.qtpl +++ b/views/admin.qtpl @@ -80,37 +80,37 @@ {% endfunc %} -{% func AdminUserNewHTML(formData util.FormData) %} +{% func AdminUserNewHTML(f util.FormData) %}
-
+

New user

- {% if formData.HasError() %} + {% if f.HasError() %}
Error: - {%s formData.Error() %} + {%s f.Error() %}
{% endif %} -
+
- +
- +
@@ -125,25 +125,65 @@
{% endfunc %} -{% func AdminUsersUserHTML(u *user.User) %} +{% func AdminUserEditHTML(u *user.User, f util.FormData) %}
-
-

{%s u.Name %}

+
+

+ + {%s u.Name %} +

+ +

Change group

+ + {% if f.HasError() %} +
+ Error: + {%s f.Error() %} +
+ {% endif %}
- - + anon + editor + trusted + moderator + admin
- - Cancel +
+ +
+ + +

Delete user

+

Remove the user from the database. Changes made by the user will + be preserved.

+ Delete +
+
+{% endfunc %} + +{% func AdminUserDeleteHTML(u *user.User, f util.FormData) %} +
+
+

Delete user

+ + {% if f.HasError() %} +
+ Error: + {%s f.Error() %} +
+ {% endif %} + +

Are you sure you want to delete {%s u.Name %} + from the database? This action is irreversible.

+ +
+ + Cancel
diff --git a/views/admin.qtpl.go b/views/admin.qtpl.go index cfb7011..3a34d2f 100644 --- a/views/admin.qtpl.go +++ b/views/admin.qtpl.go @@ -211,23 +211,23 @@ func AdminUsersPanelHTML(userList []*user.User) string { } //line views/admin.qtpl:83 -func StreamAdminUserNewHTML(qw422016 *qt422016.Writer, formData util.FormData) { +func StreamAdminUserNewHTML(qw422016 *qt422016.Writer, f util.FormData) { //line views/admin.qtpl:83 qw422016.N().S(`
-
+

New user

`) //line views/admin.qtpl:88 - if formData.HasError() { + if f.HasError() { //line views/admin.qtpl:88 qw422016.N().S(`
Error: `) //line views/admin.qtpl:91 - qw422016.E().S(formData.Error()) + qw422016.E().S(f.Error()) //line views/admin.qtpl:91 qw422016.N().S(`
@@ -237,12 +237,12 @@ func StreamAdminUserNewHTML(qw422016 *qt422016.Writer, formData util.FormData) { //line views/admin.qtpl:93 qw422016.N().S(` -
+
@@ -251,7 +251,7 @@ func StreamAdminUserNewHTML(qw422016 *qt422016.Writer, formData util.FormData) {
@@ -261,7 +261,7 @@ func StreamAdminUserNewHTML(qw422016 *qt422016.Writer, formData util.FormData) { + - - Cancel +
+ +
+ + +

Delete user

+

Remove the user from the database. Changes made by the user will + be preserved.

+ Delete + + +`) +//line views/admin.qtpl:167 +} + +//line views/admin.qtpl:167 +func WriteAdminUserEditHTML(qq422016 qtio422016.Writer, u *user.User, f util.FormData) { +//line views/admin.qtpl:167 + qw422016 := qt422016.AcquireWriter(qq422016) +//line views/admin.qtpl:167 + StreamAdminUserEditHTML(qw422016, u, f) +//line views/admin.qtpl:167 + qt422016.ReleaseWriter(qw422016) +//line views/admin.qtpl:167 +} + +//line views/admin.qtpl:167 +func AdminUserEditHTML(u *user.User, f util.FormData) string { +//line views/admin.qtpl:167 + qb422016 := qt422016.AcquireByteBuffer() +//line views/admin.qtpl:167 + WriteAdminUserEditHTML(qb422016, u, f) +//line views/admin.qtpl:167 + qs422016 := string(qb422016.B) +//line views/admin.qtpl:167 + qt422016.ReleaseByteBuffer(qb422016) +//line views/admin.qtpl:167 + return qs422016 +//line views/admin.qtpl:167 +} + +//line views/admin.qtpl:169 +func StreamAdminUserDeleteHTML(qw422016 *qt422016.Writer, u *user.User, f util.FormData) { +//line views/admin.qtpl:169 + qw422016.N().S(` +
+
+

Delete user

+ + `) +//line views/admin.qtpl:174 + if f.HasError() { +//line views/admin.qtpl:174 + qw422016.N().S(` +
+ Error: + `) +//line views/admin.qtpl:177 + qw422016.E().S(f.Error()) +//line views/admin.qtpl:177 + qw422016.N().S(` +
+ `) +//line views/admin.qtpl:179 + } +//line views/admin.qtpl:179 + qw422016.N().S(` + +

Are you sure you want to delete `) +//line views/admin.qtpl:181 + qw422016.E().S(u.Name) +//line views/admin.qtpl:181 + qw422016.N().S(` + from the database? This action is irreversible.

+ +
+ + Cancel
`) -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 } -//line views/admin.qtpl:150 -func WriteAdminUsersUserHTML(qq422016 qtio422016.Writer, u *user.User) { -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 +func WriteAdminUserDeleteHTML(qq422016 qtio422016.Writer, u *user.User, f util.FormData) { +//line views/admin.qtpl:190 qw422016 := qt422016.AcquireWriter(qq422016) -//line views/admin.qtpl:150 - StreamAdminUsersUserHTML(qw422016, u) -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 + StreamAdminUserDeleteHTML(qw422016, u, f) +//line views/admin.qtpl:190 qt422016.ReleaseWriter(qw422016) -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 } -//line views/admin.qtpl:150 -func AdminUsersUserHTML(u *user.User) string { -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 +func AdminUserDeleteHTML(u *user.User, f util.FormData) string { +//line views/admin.qtpl:190 qb422016 := qt422016.AcquireByteBuffer() -//line views/admin.qtpl:150 - WriteAdminUsersUserHTML(qb422016, u) -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 + WriteAdminUserDeleteHTML(qb422016, u, f) +//line views/admin.qtpl:190 qs422016 := string(qb422016.B) -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 qt422016.ReleaseByteBuffer(qb422016) -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 return qs422016 -//line views/admin.qtpl:150 +//line views/admin.qtpl:190 } diff --git a/web/admin.go b/web/admin.go index fa25936..1be4c75 100644 --- a/web/admin.go +++ b/web/admin.go @@ -90,44 +90,76 @@ func handlerAdminUsers(w http.ResponseWriter, r *http.Request) { return } - // User edit page - if len(parts) == 2 && parts[1] == "edit" { - u := user.UserByName(parts[0]) + if len(parts) != 2 { + util.HTTP404Page(w, "404 page not found") + return + } - if u != nil && u.Name != "anon" { - if r.Method == http.MethodGet { - html := views.AdminUsersUserHTML(u) - html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r)) + u := user.UserByName(parts[0]) + if u == nil { + util.HTTP404Page(w, "404 page not found") + return + } - w.Header().Set("Content-Type", mime.TypeByExtension(".html")) - if _, err := io.WriteString(w, html); err != nil { + switch parts[1] { + case "edit": + f := util.FormDataFromRequest(r, []string{"group"}) + + if r.Method == http.MethodPost { + oldGroup := u.Group + newGroup := f.Get("group") + + if user.ValidGroup(newGroup) { + u.Group = newGroup + if err := user.SaveUserDatabase(); err != nil { + u.Group = oldGroup log.Println(err) - } - return - } else if r.Method == http.MethodPost { - oldGroup := u.Group - newGroup := r.PostFormValue("group") - if user.ValidGroup(newGroup) { - u.Group = newGroup - if err := user.SaveUserDatabase(); err != nil { - u.Group = oldGroup - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - io.WriteString(w, err.Error()) - } else { - http.Redirect(w, r, "/admin/users/", http.StatusSeeOther) - } + f = f.WithError(err) } else { - w.WriteHeader(http.StatusBadRequest) - io.WriteString(w, "invalid group") + http.Redirect(w, r, "/admin/users/", http.StatusSeeOther) + return } - return + } else { + f = f.WithError(fmt.Errorf("invalid group \"%s\"", newGroup)) } } - } - } - util.HTTP404Page(w, "404 page not found") + f.Put("group", u.Group) + + html := views.AdminUserEditHTML(u, f) + html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r)) + + if f.HasError() { + w.WriteHeader(http.StatusBadRequest) + } + w.Header().Set("Content-Type", mime.TypeByExtension(".html")) + io.WriteString(w, html) + return + case "delete": + f := util.NewFormData() + + if r.Method == http.MethodPost { + f = f.WithError(user.DeleteUser(u.Name)) + if !f.HasError() { + http.Redirect(w, r, "/admin/users/", http.StatusSeeOther) + } else { + log.Println(f.Error()) + } + } + + html := views.AdminUserDeleteHTML(u, util.NewFormData()) + html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r)) + + if f.HasError() { + w.WriteHeader(http.StatusBadRequest) + } + w.Header().Set("Content-Type", mime.TypeByExtension(".html")) + io.WriteString(w, html) + return + } + + util.HTTP404Page(w, "404 page not found") + } } func handlerAdminUserNew(w http.ResponseWriter, r *http.Request) {