Implement email address deletion

main
Jan Dittberner 2 years ago
parent 99a2cde144
commit de4c6faef6

@ -856,14 +856,107 @@ func (app *application) userAddEmailSubmit(w http.ResponseWriter, r *http.Reques
http.Redirect(w, r, fmt.Sprintf("/users/%d/", userToEdit.ID), http.StatusSeeOther)
}
func (app *application) userDeleteEmailForm(_ http.ResponseWriter, _ *http.Request) {
// TODO: implement userDeleteEmailForm
panic("not implemented")
func (app *application) userDeleteEmailForm(w http.ResponseWriter, r *http.Request) {
userToEdit, emailAddress, err := app.deleteEmailParams(w, r)
if err != nil {
app.serverError(w, err)
return
}
if userToEdit == nil || emailAddress == "" {
app.notFound(w)
return
}
if userToEdit.Reminder.String == emailAddress {
// delete of reminder address should not happen
app.clientError(w, http.StatusBadRequest)
return
}
data := app.newTemplateData(r, topLevelNavUsers, subLevelNavUsers)
data.Form = &forms.DeleteEmailForm{
User: userToEdit,
EmailAddress: emailAddress,
}
app.render(w, http.StatusOK, "delete_email.html", data)
}
func (app *application) userDeleteEmailSubmit(_ http.ResponseWriter, _ *http.Request) {
// TODO: implement userDeleteEmailSubmit
panic("not implemented")
func (app *application) userDeleteEmailSubmit(w http.ResponseWriter, r *http.Request) {
userToEdit, emailAddress, err := app.deleteEmailParams(w, r)
if err != nil {
app.serverError(w, err)
return
}
if userToEdit == nil || emailAddress == "" {
app.notFound(w)
return
}
if userToEdit.Reminder.String == emailAddress {
// delete of reminder address should not happen
app.clientError(w, http.StatusBadRequest)
return
}
admin, err := app.GetUser(r)
if err != nil {
app.clientError(w, http.StatusUnauthorized)
return
}
var form forms.DeleteEmailForm
if err := app.decodePostForm(r, &form); err != nil {
app.clientError(w, http.StatusBadRequest)
return
}
form.Validate()
if !form.Valid() {
form.EmailAddress = emailAddress
form.User = userToEdit
data := app.newTemplateData(r, topLevelNavUsers, subLevelNavUsers)
data.Form = form
app.render(w, http.StatusUnprocessableEntity, "delete_email.html", data)
return
}
form.Normalize()
if err := app.users.DeleteEmail(r.Context(), userToEdit, admin, emailAddress, form.Reasoning); err != nil {
app.serverError(w, err)
return
}
app.addFlash(r, &FlashMessage{
Variant: flashWarning,
Title: "Email address deleted",
Message: fmt.Sprintf(
"Deleted email address %s of user %s",
form.EmailAddress,
userToEdit.Name,
),
})
http.Redirect(w, r, fmt.Sprintf("/users/%d/", userToEdit.ID), http.StatusSeeOther)
}
func (app *application) newUserForm(_ http.ResponseWriter, _ *http.Request) {

@ -223,6 +223,39 @@ func (app *application) userFromRequestParam(
return user
}
func (app *application) emailFromRequestParam(w http.ResponseWriter, r *http.Request, params httprouter.Params, user *models.User) (string, error) {
emailParam := params.ByName("address")
emailAddresses, err := user.EmailAddresses()
if err != nil {
return "", fmt.Errorf("could not get email addresses: %w", err)
}
for _, address := range emailAddresses {
if emailParam == address {
return emailParam, nil
}
}
return "", nil
}
func (app *application) deleteEmailParams(w http.ResponseWriter, r *http.Request) (*models.User, string, error) {
params := httprouter.ParamsFromContext(r.Context())
userToEdit := app.userFromRequestParam(w, r, params, app.users.WithEmailAddresses())
if userToEdit == nil {
return nil, "", nil
}
emailAddress, err := app.emailFromRequestParam(w, r, params, userToEdit)
if err != nil {
return nil, "", err
}
return userToEdit, emailAddress, nil
}
func (app *application) choiceFromRequestParam(w http.ResponseWriter, params httprouter.Params) *models.VoteChoice {
choiceParam := params.ByName("choice")

@ -223,3 +223,23 @@ func (f *AddEmailForm) Normalize() {
f.EmailAddress = strings.TrimSpace(f.EmailAddress)
f.Reasoning = strings.TrimSpace(f.Reasoning)
}
type DeleteEmailForm struct {
EmailAddress string `form:"-"`
User *models.User `form:"-"`
Reasoning string `form:"reasoning"`
validator.Validator `form:"-"`
}
func (f *DeleteEmailForm) Validate() {
f.CheckField(validator.NotBlank(f.Reasoning), "reasoning", "This field cannot be blank")
f.CheckField(
validator.MinChars(f.Reasoning, minimumJustificationLen),
"reasoning",
fmt.Sprintf("This field must be at least %d characters long", minimumJustificationLen),
)
}
func (f *DeleteEmailForm) Normalize() {
f.Reasoning = strings.TrimSpace(f.Reasoning)
}

@ -33,7 +33,7 @@ const (
AuditDeleteUser AuditChange = "DELETE_USER"
AuditEditUser AuditChange = "EDIT_USER"
AuditAddEmail AuditChange = "ADD_EMAIL"
AuditRemoveEmail AuditChange = "REMOVE_EMAIL"
AuditDeleteEmail AuditChange = "DELETE_EMAIL"
AuditAddRole AuditChange = "ADD_ROLE"
AuditRemoveRole AuditChange = "REMOVE_ROLE"
)

@ -789,6 +789,62 @@ func (m *UserModel) AddEmail(ctx context.Context, user *User, admin *User, email
return nil
}
func (m *UserModel) DeleteEmail(ctx context.Context, user, admin *User, emailAddress, reasoning string) error {
userBefore, err := m.ByID(ctx, user.ID, m.WithEmailAddresses())
if err != nil {
return err
}
tx, err := m.DB.BeginTxx(ctx, nil)
if err != nil {
return errCouldNotStartTransaction(err)
}
defer func(tx *sqlx.Tx) {
_ = tx.Rollback()
}(tx)
if _, err := tx.ExecContext(ctx, `DELETE FROM emails WHERE reminder=FALSE AND address=? AND voter=?`, emailAddress, user.ID); err != nil {
return errCouldNotExecuteQuery(err)
}
rows, err := tx.QueryxContext(ctx, `SELECT address FROM emails WHERE voter=? ORDER BY address`, user.ID)
if err != nil {
return errCouldNotExecuteQuery(err)
}
defer rows.Close()
emailAddresses := make([]string, 0, len(userBefore.emailAddresses)-1)
for rows.Next() {
if err := rows.Err(); err != nil {
return errCouldNotFetchRow(err)
}
var address string
if err := rows.Scan(&address); err != nil {
return errCouldNotScanResult(err)
}
emailAddresses = append(emailAddresses, address)
}
if err = AuditLog(
ctx, tx, admin, AuditDeleteEmail, reasoning,
emailChangeInfo(userBefore.emailAddresses, emailAddresses),
); err != nil {
return err
}
if err = tx.Commit(); err != nil {
return errCouldNotCommitTransaction(err)
}
return nil
}
func updateReminder(ctx context.Context, tx *sqlx.Tx, edit *User) error {
if _, err := tx.ExecContext(
ctx,

@ -0,0 +1,29 @@
{{ define "title" }}Delete email address {{ .Form.EmailAddress }} of User {{ .Form.User.Name }}{{ end }}
{{ define "main" }}
<div class="ui form segment">
<div class="ui negative message">
<div class="header">
Delete email address?
</div>
<p>Do you want to delete email address <strong>{{ .Form.EmailAddress }}</strong> of user
<strong>{{ .Form.User.Name }}</strong>?</p>
</div>
<form action="/users/{{ .Form.User.ID }}/mail/{{ .Form.EmailAddress }}/delete" method="post">
<input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
<div class="ui form{{ if .Form.FieldErrors }} error{{ end }}">
<div class="required field{{ if .Form.FieldErrors.reasoning }} error{{ end }}">
<label for="reasoning">Reasoning for the deletion</label>
<textarea id="reasoning" name="reasoning" rows="2">{{ .Form.Reasoning }}</textarea>
{{ if .Form.FieldErrors.reasoning }}
<span class="ui small error text">{{ .Form.FieldErrors.reasoning }}</span>
{{ end }}
</div>
<button class="ui negative labeled icon button" type="submit">
<i class="trash icon"></i> Delete user
</button>
<a href="/users/{{ .Form.User.ID }}/" class="ui button">Cancel</a>
</div>
</form>
</div>
{{ end }}
Loading…
Cancel
Save