Broadcast services only have broadcast templates. But we show the
template type under the name of the template. This is redundant. It
would be better to preview the content of the template instead.
This then makes the templates page consistent with the dashboard.
Depends on:
- [ ] https://github.com/alphagov/notifications-api/pull/2996
Includes the following pages:
- letter contact (in service settings)
- sms-senders page (in service settings)
- email reply-to (in service settings)
- API key page
Note: the call on the letter contact page uses
the first line of the contact address as the
unique identifier for each button.
This commit removes the code the puts areas into the session and instead
creates and then updates a draft broadcast in the database.
This is so we can avoid session-related bugs, and potentially having a
large session when we start adding personalisation etc.
Once a broadcast is ready to go it is set to `broadcasting` straight
away with no approval. We’ll revisit this as we learn more about how
users might want to manage who can create and approve broadcasts.
The tests are a bit light in terms of checking what’s on the page, but
clicking through the pages is probably good enough for now.
So you can check you’ve chosen the right areas, and to give you a clear
idea of where the boundaries of an area are.
The Javascript and CSS for the map is only loaded on this page because
it adds quite a few kb, and we don’t want to be sending assets to the
majority of our users who will never see them.
This solves two problems
- it makes our response times more accurate as it means we start
measuring the response time earlier (otherwise we aren't recording the
time spent by `csrf` and `login_manager`s `before_request` functions
- is a temporary fix for a bug in the gds python metrics library as
explained below.
Currently, when a request comes in it goes through various
`before_request` functions. Currently it goes through the function
introduced by the csrf client and then the one introduced by the metrics
client. If an exception is thrown by the csrf.before_request function
then we do not run the `metrics.before_request` function. This would
happen in the case that a CSRF token is invalid and then the main body
of the request would not process but then all `teardown_request`
functions will run. When the `metrics.teardown_request` function runs it
looks for `g._gds_metrics_start_time`, however this attribute is not
availble on the flask global object as it was not created as the
`metrics.before_request` function that creates it did not run. This then
throws an `AttributeError` and results in a 500 for the user. The short
term solution for this (initing metrics before csrf) means that
`_gds_metrics_start_time` will be set before csrf is at risk of throwing
an exception.
A separate PR will be put into the gds metrics python library to remove
the risk of an `AttributeError` and instead to log a warning instead of
throwing an uncaught exception.
Some teams have started uploading quite a lot of letters (in the
hundreds per week). They’re also uploading CSVs of emails. This means
the uploads page ends up quite jumbled.
This is because:
- there’s just a lot of items to scan through
- conceptually it’s a bit odd to have batches of things displayed
alongside individual things on the same page
So instead we’re going to start grouping together uploaded letters. This
will be by the date on which we ‘start’ printing them, or in other
words the time at which they can no longer be cancelled.
This feels like a natural grouping, and it matches what we know about
people’s mental models of ‘batches’ and ‘runs’ when talking about
printing.
This grouping will be done in the API, so all this commit need to do is:
- be ready to display this new type of pseudo-job
- link to the page that displays all the uploaded letters for a given
print day
Because we won’t be showing uploaded letters individually on the uploads
page any more we need a way of listing them. This should be by printing
day, to match how we’re grouping them on the uploads page.
This code reuses the notifications.html template, but flips the
precedence of the filename and recipient because I reckon when you’re
looking at uploads you’re thinking filename-first.
It was saying ‘16 hours ago’ instead of today. This is because, in
strftime:
- `%M` means minute, not month
- `%D` means short MM/DD/YY date, not day of the month
The test wasn’t catching this because the freeze time and mocked value
from the API were set to the same minute.
We increasingly have teams wanting to do business-continuity type
messaging. They might be without access to their normal systems, which
is where they would otherwise go to get the list of email addresses or
phone numbers.
So we want to give them a place in Notify where they can store their
spreadsheets and use them at a later date.
For the initial pass we’re going to scope this to only allowing
spreadsheets with one column, ie just phone numbers/email addresses.
This is because:
- it minimises the amount of personal info we’re storing
- it reduces the chance of getting a placeholder error when you go to
send the message, which is probably a high-stress situation where you
might not be able to re-generate the file
The code for this is mostly copied from the existing upload CSV journey.
It’s quite duplicative, but that’s what I needed to do to get this out
quickly. There are opportunities for refactoring later.
Similarly, I would have liked to split this up into better commit
messages, but it really was a case of just bashing code out until it
worked 😳
This commit does not:
- implement the ‘view a contact list page’ (it just has a placeholder
because the API isn’t ready at the moment)
- link to this page (because it’s not ready to use yet)
flask_login moves from `user_id` to `_user_id`. unfortunately, this
isn't backwards compatible as if an old cookie only has the old
`user_id`, then flask_login won't find `_user_id` so will mark the user
as unauthenticated.
this code will manually migrate the three flask login cookie variables,
before the flask_login code runs, so that it doesn't freak out
it just shows a h1, so isn't helpful for people. We can re-use the 500
error page, which includes instructings "Try again later" and
instructions on what to do next (check the status page, email notify
support).
this required refactoring to ensure we can show the 500 error page while
still returning the required status code
if we expect a 400 (for example, the api returns 400 if the service name
is already in use) then we should handle that from the view function so
we can correctly display a relevation error message to the user. But if
it returns a 400 that we didn't expect (for example, because we sent
variables of the wrong type through to an endpoint with a schema), then
that is a bug that we should fix as any other raised exception. By
treating it as a 500 we encourage users to report it to us, and also
will get an alert email
Currently you have no way of getting to the returned letter page. This
commit adds a link to it from the dashboard, following the pattern of
the new received text messages banner.
This is a friendlier and better way of showing dates anywhere except in
a report which might be archived and referred back to later.
Bit trickier to implement here because a dat requires ‘on’ beforehand,
but we don’t say ‘on today’ in English.
This helps people understand how delivery dates fit into the week,
because if sending a letter spans the weekend it will take a bit longer.
We can remove `|string` now because `utc_string_to_aware_gmt_datetime`
now accepts datetimes.
service contact blocks contain new lines - and jinja2 normally ignores
newlines (as in it keeps them as new lines) - but we need to turn them
into `<br>` tags so that we can show the formatting that the user has
added. We were previously just doing `{{ block | nl2br | safe }}`. nl2br
turns the new lines into `<br>` tags, and then `safe` tells jinja that
it doesn't need to escape the html.
this causes issues if the user adds `<script>alert(1)</script>` to their
contact block (or some other evil xss hack), where that will get let
through due to the safe flag
To solve this, use `Markup(html='escape')` to sanitise any html, and
then convert new lines to <br>.
bump utils
another xss
Then it’s one less cookie we have to get users to opt in to. We don’t
derive any value from Youtube setting cookies.
`youtube-nocookie.com` is a domain provided by Google for this purpose.
Includes:
- in gulpfile.js:
- add details macro to list of those copied from GOVUK Frontend
- remove existing details polyfill
- convert all, but one, <details> tags to use GOVUK Frontend details component
- add jinja boolean filter to help setting 'open' attribute of <details> tags
Notes on the `<details>` not included in this:
The `<details>` used for notifications items on
the API integration page is not possible to
reproduce with the GOV.UK Frontend macro so I'm
splitting the resulting work out into it's own
commit.
Includes:
- in gulpfile.js:
- add details macro to list of those copied from GOVUK Frontend
- remove existing details polyfill
- convert all, but one, <details> tags to use GOVUK Frontend details component
- add jinja boolean filter to help setting 'open' attribute of <details> tags
Notes on the `<details>` not included in this:
The `<details>` used for notifications items on
the API integration page is not possible to
reproduce with the GOV.UK Frontend macro so I'm
splitting the resulting work out into it's own
commit.
we have a hunch that some session related issues that we've seen over
the last few weeks might be related to weird race conditions where
cookies set by subresources (image previews of letters on the send flow)
arrive just as the img request is cancelled because the user has clicked
on a button to navigate to a new page, but still manage to set the
cookie? We're not entirely sure what's going on, but we've got a hunch
that not setting cookies on image fetches sounds sensible. Images are
always loaded as a subresource (ie: through a `src` tag in an html
element), so they should never need to change the cookies, so this seems
sensible. We've done this by creating a new blueprint that doesn't set
session.permanent, and doesn't call `save_serivce_or_org_after_request`
either.
cookies are sent back to the browser if:
`sesion.modified or (session.permanent and 'REFRESH_EVERY_REQUEST')`
(where the latter is a config setting).
Turning off REFRESH_EVERY_REQUEST (which is True by default) means that
we will only update the sesion if it's been modified. In practice,
literally every request is modified in the after_request handler
`save_service_or_org_after_request`. This is accidentally convenient,
as it guarantees that we'll still send back the cookie normally even
though refresh_every_request is disabled. Sending back the cookie
updates the expiry time (20 hours), so we need to keep doing this to
preserve existing session timeout behaviour.
`node_modules` isn't available in a live
environment so the `.njk` templates we're now
using from GOVUK Frontend need to go in the repo'
code.
Adds them in a new `app/templates/vendor` folder
to match how we do that in
`app/assets/stylesheets`.
`node_modules` isn't available in a live
environment so the `.njk` templates we're now
using from GOVUK Frontend need to go in the repo'
code.
Adds them in a new `app/templates/vendor` folder
to match how we do that in
`app/assets/stylesheets`.
Sometimes we manually check that a URL parameter is in a required set.
Sometimes we don’t bother.
This commit adds a URL converter to do this so that:
- we don’t have to re-write the same code every time
- it’s easier to apply this check to other endpoints
This means endpoints that previously allowed a `template_type` or
`message_type` of `None` now 404. So I’ve had to add new routes for
with URLs that don’t include such parameters.
So this…:
```
/services/128b91b6-2996-4107-bb65-51b7c24a728d/notifications/sms.csv
/services/128b91b6-2996-4107-bb65-51b7c24a728d/notifications/None.csv
```
…becomes:
```
/services/128b91b6-2996-4107-bb65-51b7c24a728d/notifications/sms.csv
/services/128b91b6-2996-4107-bb65-51b7c24a728d/notifications.csv
```
This matches what we do for the HTML-responding equivalent (see
265931d217/app/main/views/jobs.py (L215-L216))
Because it means you often have to cast to string in your application
code just to get your tests passing.
The method being monkey patched is originally defined here: b81aa0f18c/src/werkzeug/routing.py (L1272)
Scanning the page is difficult at the moment because it’s hard to tell
how far apart in time events are, and thereby determine which events
might be related.
Grouping the events by day quickly lets users narrow their focus to
a meaningful subset of the events.
We store our audit history in two ways:
1. A list of versions of a service
2. A list of events to do with API keys
In the future there could be auditing data which we want to display that
is stored in other formats (for example the event table).
This commit adds some objects which wrap around the different types of
auditing data, and expose a consistent interface to them. This
architecture will let us:
- write clean code in the presentation layer to display these events on
a page
- add more types of events in the future by subclassing the `Event` data
type, without having to rewrite anything in the presentation layer