We could alternatively put the "add up to 100%" error on the page
using form-level errors [^1] and a custom flash message. Putting
the error on each field is slightly simpler and does make it clear
the issue is with all of the fields together.
[^1]: 22636b55ed
This replaces the slider with an integer input for each provider.
Unfortunately showing a variable number of inputs isn't easy to
achieve in WTForms [^1], but we think this is the least worst way
to do it vs e.g. not using WTForms at all.
[^1]: https://github.com/wtforms/wtforms/issues/736
This isn't used and showing priorities when we only have a single
provider or where they have no effect is unnecessarily confusing.
Removing the form makes it clearer that there's only one way to
adjust priorities for domestic SMS providers.
If we add another email or international SMS provider in future,
we would need to rewrite the form here anyway as the priorities
need to be adjusted in tandem, not individually.
We already had different functionality for email branding and will
soon be adding more for email branding pools.
Note that the "get_available_choices" class method was only used for
email branding - we can do it in the constructor for letters.
I've also tweaked some of the names to make them clearer e.g. that
the form is used to apply a change to a service.
I've constrained the scope of this change to avoid forms that may
be accessible by non-admins in the future.
This was a lot of code to be in a form and it's going to get even
more complicated with email branding pools. Moving it out means we
can also simplify the tests that target this code.
These are about to become a lot less similar to each other when we
add email branding pools. Note that the optional *args and *kwargs
weren't used anywhere.
I've often struggled to find the form associated with a particular
page due to the overlapping names e.g. "SetEmailBranding" sounds
more like the radio button form a user sees than "BrandingOptions".
Almost every form in forms.py also ends with "Form", so this also
makes the branding forms consistent with that naming convention.
On the ‘find user’ page it says ‘sms_auth’ instead of ‘Text message
code’.
This commit fixes that, and adds a handy formatter so it’s easier to do
the right thing in the future.
This was causing some tests for the "estimate_volume" endpoint to
fail due to the surprising way that form handles "''":
- The form is the exclusive user of the ForgivingIntegerField [^1].
- The field secretly/silently converts "''" to the integer 0 [^2].
If the validations fail, we don't want to surprise the user with a
"0" when they didn't enter one. The field already handles this by
massaging the values in the __call__ method that generates the HTML
for the form [^3]. However, there are two scenarios:
- User submits field with '' - converted to integer 0.
- User submits field with '0' - remains as a string.
In the case where "value" is "''", the parent class will use the
converted value from form.data instead [^4]. This seems to be an
oversight and so we get either the integer 0 (from form.data) or
the string '0' (from the value kwarg). Complicado!
Previously it was a fluke that we avoided replaying the integer 0
to the user; the previous commit removes the fluke. This fixes the
conditional to always use the data in the "value" kwarg if it has
been provided, as it's meant to override "form.data".
[^1]: 9f63449384
[^2]: a22b8cf684/app/main/forms.py (L364)
[^3]: a22b8cf684/app/main/forms.py (L393)
[^4]: a22b8cf684 (diff-a1c8d24b22d4478fe71f75fd43b71b18dd82aae97bc63de84473a6da1902909bR215)
So we do not have to go into the db when we need to change user
auth.
We do not allow this for users who use webauthn. We do not want to
enable security downgrade for those users.
Previously we duplicated the "something else" email branding form
on its own page and embedded in the choices form (if it was the
only option). See [^1] for how this looks - it's inconsistent.
This DRYs-up the "something else" form by bypassing the choices
form when "something else" is the only option. I've also tweaked
the "Back" button to be consistent with this behaviour.
Making this change also simplifies the choices form, which we'll
be adding pool options to shortly. I'd like to make the letters
form consistent, but let's see how emails pan out first.
Note that the choices form will now show a single radio button if
"something else" is the only option. I think that's OK as nothing
will link to the page, and the form still works.
[^1]: https://github.com/alphagov/notifications-admin/pull/4163#issuecomment-1050088088
Currently an integer of 0 doesn't get shown because it fails the
truthiness check in the govuk-frontend template [^1]. Note that
we can't just do str(value) as for None this would be "None".
[^1]: fd4952f1c0/src/govuk/components/input/template.njk (L51)
We want to be able to set the free allowance for a service to 0, but the
form was not allowing this - it gave an error message of `Cannot be
empty`. This can be fixed by changing the WTForms validator from
`DataRequired` (which coerces 0 to falsey) to the `InputRequired`
validator.
Having to submit the form for each choice separately slowed us down
during an incident where Redis was unavailable and came back with
stale data, which we had to clear manually.
Note: we don't want to use the "flush" feature in case there are other
keys in Redis, which may not be safe to remove.
Changes:
53.0.0
---
* `notifications_utils.columns.Columns` has moved to
`notifications_utils.insensitive_dict.InsensitiveDict`
* `notifications_utils.columns.Rows` has moved to
`notifications_utils.recipients.Rows`
* `notifications_utils.columns.Cell` has moved to
`notifications_utils.recipients.Cell`
52.0.0
---
* Deprecate the following unused `redis_client` functions:
- `redis_client.increment_hash_value`
- `redis_client.decrement_hash_value`
- `redis_client.get_all_from_hash`
- `redis_client.set_hash_and_expire`
- `redis_client.expire`
51.3.1
---
* Bump govuk-bank-holidays to cache holidays for next year.
51.3.0
---
* Log exception and stacktrace when Celery tasks fail.
For the "Something else" branding form we want the form label to be the
title. This brings in the textarea component from GOV.UK Frontend in
order to do this since that contains code to set a the textarea label as
the page heading in an accessible way.
The rest of the textarea fields have not been switched to use the new
component yet.
We were showing the form to request email branding with a button which
submits your choice immediately. Now, we only submit the form
immediately if "Something else" is the only branding option available to
you. If you select any other radio button (or select "Something else"
when it's not the only option) we take you to another page which either
contains more information or a textbox to fill in the details for the
branding you want.
There is currently some duplication between the new pages and their
tests, but these will be changed in future versions of the work so will
start to differ more.
To render text in an SVG consistently the system rendering the SVG must
have the fonts specified by the SVG installed.
If the fonts are not installed then the renderer will fall back to a
system font and the text will look different. This is especially bad
news for branding where the right font is an integral part of any brand.
To fix this, the text should instead be converted to `<path>` elements.
This process is sometimes called ‘outlining’.
A few of our logos had this problem, and I’ve fixed most of them by
hand. Adding this validation will stop the problem, coming up again.
We use the `ChangeEmailForm` if you want to change your own email
address or someone else's email address. This has various validators
which get run. We check if the email address is valid (by using a
function from utils) and if the email address is already in use
(by calling API).
If the email address is not valid, we should not call API to see if it's
already in use because this will cause an exception in API leading to a
`500` in admin. We now only call API if there were no other errors with
the email address.
(The `test_should_redirect_after_name_change` test didn't need the
`mock_email_is_not_already_in_use` fixture, so this has been removed.)
We don't currently have any radio fields or check boxes where it's
possible to get more than one validation error. However, since we
never want to show more than one error at a time for a field, this
changes the error messages for the relevant widgets to only show the
first error if there ever were multiple.
If there were multiple errors, this widget was joining the messages
together and displaying all error messages. If a text input field does
have more than one validation error, we only want to show one.
This is a quick additional check to protect the user:
- From getting a CloudFront 502 error if the file takes too
long to upload. I was surprised to find it takes about 1 minute
to upload a 70Mb file to S3.*
- From getting a CloudFront 502 error when we follow the redirect
and run through the slow processing code in utils that builds a
RecipientCSV [1].
For context, a CSV with 100K rows and a few columns is around 5Mb,
so a 10Mb limit should be enough. Analysis over the past week shows
that the vast majority of CSV uploads are actually < 2.5Mb.
I haven't added any tests for this because:
- The check isn't critical, as the worst case scenario is the user
gets a worse error than this in-app one.
- There's no easy way to mock the validation, and I didn't want to
have a test that depends on a 10Mb+ file.
*We're using "key.put" to upload the file, when we could be doing
a multipart upload [2]. However, I tried this myself with a chunk
size of 1000 bytes and found it only led to a marginal improvement.
[1]: https://github.com/alphagov/notifications-utils/pull/930
[2]: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-uploading-files.html
WTForms versions less than 3.0.0 have a security vulnerability where
arbitrary HTML can be inserted into the label of a form, allowing the
possibility of a cross-site scripting attack.
I don’t know if there’s anywhere we put user-generated content into form
labels but it’s possible we are vulnerable somewhere.
This require moving some imports because as of
https://github.com/wtforms/wtforms/pull/614/files
there is no longer a separate module for HTML 5 fields, they are now
considered core fields.
As of https://github.com/wtforms/wtforms/issues/445/files custom
implementations of `pre_validate` or `post_validate` must raise
`ValidationError` to trigger a validation message, where we were raising
`ValueError` this was no longer being caught.
As of https://github.com/wtforms/wtforms/pull/355/files `StringField`
returns `None` for empty data, not `''` but our `validate_email_address`
function only accepts strings.
We don't use this term consistently and it's not defined anywhere.
Since most of the Admin app deals with user-facing permssions, it's
OK to just use the term "permissions". Where both types of permission
are present in the same file, we can more clearly distinguish them
as "UI permissions" and "DB permissions".
These are more than a list of permissions: each item includes the
label to use when displaying it as an option on a form. Switching
to a name that reflects how the attributes are used will help to
avoid confusion when we rename some of the other attributes in the
same file in later commits.
This is so we can allow the sender name 'UC' for DWP.
Note, this is specifically only straight single quotes and not curly
quotes or double quotes. Curly quotes are not supported in the GSM
character set (https://en.wikipedia.org/wiki/GSM_03.38). There is
currently no defined user ask to support double quotes in sms sender
names.
I have tested this by sending a message through both Firetext and MMG to
make sure they both support the single quote character in SMS sender
names.
DWP also have had no particular issues using the SMS sender name with
their existing system in the past either.
At the moment we say that you either ‘add’ an alert or ‘send’ it.
This is confusing because:
- an alert isn’t received on people’s phones until it’s approved, so
this is really when it is ‘sent’ conceptually
- an alert can be rejected before anyone receives it, so the UI can say
an alert that no-one ever received was sent
This commit re-labels things so that the the first part of the process
is ‘creating’ the alert.
This makes all the permissions nice and distinct from each other. Adding
templates and adding alerts feel conceptually quite different things
(what are you adding the alert to?).