From c69b8d66eb0019418976eeb1fc16150297f701a2 Mon Sep 17 00:00:00 2001 From: Baptiste Jonglez Date: Sat, 17 Jul 2021 13:26:33 +0200 Subject: [PATCH] Use existing form errors to flash error messages This is nice because we can reuse the translated strings of form error messages in another context. Suggested by Glandos. --- ihatemoney/utils.py | 21 +++++++++++++++++++++ ihatemoney/web.py | 16 ++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/ihatemoney/utils.py b/ihatemoney/utils.py index 97d94380..7c840f46 100644 --- a/ihatemoney/utils.py +++ b/ihatemoney/utils.py @@ -15,6 +15,7 @@ from babel.numbers import get_currency_name, get_currency_symbol from flask import current_app, escape, redirect, render_template from flask_babel import get_locale, lazy_gettext as _ import jinja2 +from markupsafe import Markup from werkzeug.routing import HTTPException, RoutingException @@ -394,3 +395,23 @@ def render_localized_template(template_name_prefix, **context): ] # render_template() supports a list of templates to try in order return render_template(templates, **context) + + +def format_form_errors(form, prefix): + """Format all form errors into a single string, with a string prefix in + front. Useful for flashing the result. + """ + if len(form.errors) == 0: + return "" + elif len(form.errors) == 1: + # I18N: Form error with only one error + return _("{prefix}: {error}").format( + prefix=prefix, error=form.errors.popitem()[1][0] + ) + else: + error_list = "
  • ".join( + str(error) for (field, errors) in form.errors.items() for error in errors + ) + errors = f"" + # I18N: Form error with a list of errors + return Markup(_("{prefix}:
    {errors}").format(prefix=prefix, errors=errors)) diff --git a/ihatemoney/web.py b/ihatemoney/web.py index ca452ed1..ccc5304c 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -55,6 +55,7 @@ from ihatemoney.models import Bill, LoggingMode, Person, Project, db from ihatemoney.utils import ( LoginThrottler, Redirect303, + format_form_errors, get_members, list_of_dicts2csv, list_of_dicts2json, @@ -524,7 +525,7 @@ def delete_project(): return redirect(url_for(".home")) else: flash( - _("Error deleting project: wrong private code or wrong CSRF token"), + format_form_errors(form, _("Error deleting project")), category="danger", ) return redirect(request.headers.get("Referer") or url_for(".home")) @@ -619,6 +620,7 @@ def invite(): @main.route("//") def list_bills(): bill_form = get_billform_for(g.project) + # Used for CSRF validation csrf_form = EmptyForm() # set the last selected payer as default choice if exists if "last_selected_payer" in session: @@ -660,7 +662,7 @@ def reactivate(member_id): # Used for CSRF validation form = EmptyForm() if not form.validate(): - flash(_("CSRF Token: The CSRF token is invalid."), category="danger") + flash(format_form_errors(form, _("Error activating member")), category="danger") return redirect(url_for(".list_bills")) person = ( @@ -680,7 +682,7 @@ def remove_member(member_id): # Used for CSRF validation form = EmptyForm() if not form.validate(): - flash(_("CSRF Token: The CSRF token is invalid."), category="danger") + flash(format_form_errors(form, _("Error removing member")), category="danger") return redirect(url_for(".list_bills")) member = g.project.remove_member(member_id) @@ -745,7 +747,7 @@ def delete_bill(bill_id): # Used for CSRF validation form = EmptyForm() if not form.validate(): - flash(_("CSRF Token: The CSRF token is invalid."), category="danger") + flash(format_form_errors(form, _("Error deleting bill")), category="danger") return redirect(url_for(".list_bills")) bill = Bill.query.get(g.project, bill_id) @@ -822,7 +824,7 @@ def erase_history(): form = DestructiveActionProjectForm(id=g.project.id) if not form.validate(): flash( - _("Error deleting project history: wrong private code or wrong CSRF token"), + format_form_errors(form, _("Error deleting project history")), category="danger", ) return redirect(url_for(".history")) @@ -841,9 +843,7 @@ def strip_ip_addresses(): form = DestructiveActionProjectForm(id=g.project.id) if not form.validate(): flash( - _( - "Error deleting recorded IP addresses: wrong private code or wrong CSRF token" - ), + format_form_errors(form, _("Error deleting recorded IP addresses")), category="danger", ) return redirect(url_for(".history"))