diff --git a/app/assets/javascripts/notificationBanner.js b/app/assets/javascripts/notificationBanner.js new file mode 100644 index 0000000000..519bac66d1 --- /dev/null +++ b/app/assets/javascripts/notificationBanner.js @@ -0,0 +1,12 @@ +(function(window) { + "use strict"; + + // Based on GOVUK.ErrorBanner + window.GOVUK.NotificationBanner = { + hideBanner: () => $('.govuk-notification-banner').addClass('govuk-!-display-none'), + showBanner: () => + $('.govuk-notification-banner') + .removeClass('govuk-!-display-none') + .trigger('focus') + }; +})(window); diff --git a/app/assets/stylesheets/govuk-frontend/_all.scss b/app/assets/stylesheets/govuk-frontend/_all.scss index b93f401d2e..1d0808304a 100644 --- a/app/assets/stylesheets/govuk-frontend/_all.scss +++ b/app/assets/stylesheets/govuk-frontend/_all.scss @@ -17,6 +17,7 @@ $govuk-assets-path: "/static/"; @import "govuk/components/button/index"; @import "govuk/components/details/index"; @import "govuk/components/error-summary/index"; +@import "govuk/components/notification-banner/index"; @import "govuk/components/radios/index"; @import "govuk/components/checkboxes/index"; @import "govuk/components/input/index"; diff --git a/app/main/forms.py b/app/main/forms.py index 8038787fd8..74b0171018 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -1298,7 +1298,7 @@ def populate(self, domains_list): class CreateServiceForm(StripWhitespaceForm): name = GovukTextInputField( - "Service name", + "", validators=[ DataRequired(message="Enter a service name"), MustContainAlphanumericCharacters(), @@ -1308,6 +1308,24 @@ class CreateServiceForm(StripWhitespaceForm): organisation_type = OrganisationTypeField("Who runs this service?") +class ChooseOrganisationForm(StripWhitespaceForm): + choices = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.organisation.choices = [ + ("default", kwargs["default_organisation"]), + ("other", "Other public sector body"), + ] + + organisation = GovukRadiosField( + "", + validators=[DataRequired()], + choices=choices, + thing="an organisation", + ) + + class CreateNhsServiceForm(CreateServiceForm): organisation_type = OrganisationTypeField( "Who runs this service?", @@ -1744,6 +1762,29 @@ def validate(self, *args, **kwargs): return super().validate(*args, **kwargs) +class ExpectToSendForm(StripWhitespaceForm): + message_types = GovukCheckboxesField( + "", + validators=[DataRequired(message="Select at least 1 option")], + choices=[ + ("emails", "Emails"), + ("texts", "Text messages"), + ("letters", "Letters"), + ], + ) + + +class EmailUsageForm(StripWhitespaceForm): + high_volume_emails = GovukRadiosField( + "", + choices=[ + ("yes", "Yes"), + ("no", "No"), + ("maybe", "Maybe"), + ], + ) + + class AdminProviderRatioForm(OrderableFieldsForm): def __init__(self, providers): self._providers = providers @@ -1935,19 +1976,23 @@ class ServiceEmailSenderForm(StripWhitespaceForm): "alert", } - use_custom_email_sender_name = OnOffField( - "Choose a sender name", - choices_for_error_message="same or custom", + CHOICE_CUSTOM = "custom" + CHOICE_ORGANISATION = "organisation" + CHOICE_SERVICE = "service" + + use_custom_email_sender_name = GovukRadiosField( + "", choices=[ - (False, "Use the name of your service"), - (True, "Enter a custom sender name"), + (CHOICE_CUSTOM, "Enter a ‘from’ name"), + (CHOICE_ORGANISATION, "Use the name of your organisation"), + (CHOICE_SERVICE, "Use the name of your service"), ], ) custom_email_sender_name = GovukTextInputField("Sender name", validators=[]) def validate(self, *args, **kwargs): - if self.use_custom_email_sender_name.data is True: + if self.use_custom_email_sender_name.data == self.CHOICE_CUSTOM: self.custom_email_sender_name.validators = [ NotifyDataRequired(thing="a sender name"), MustContainAlphanumericCharacters(thing="sender name"), @@ -1961,7 +2006,7 @@ def validate_custom_email_sender_name(self, field): Validate that the email from name ("Sender Name" trial mode' + 'test mode' ) }, } diff --git a/app/main/views/backlink_manager.py b/app/main/views/backlink_manager.py new file mode 100644 index 0000000000..b6c7da3a0b --- /dev/null +++ b/app/main/views/backlink_manager.py @@ -0,0 +1,75 @@ +from urllib.parse import urlparse + +from flask import request, session, url_for + +from app import Service + + +def get_backlink_email_sender(service: Service, template_id: str): + has_from_name = service.email_sender_name is not None + has_reply_to = bool(service.email_reply_to_addresses) + has_multiple_reply_to = len(service.email_reply_to_addresses) > 1 + + args = {"service_id": service.id, "template_id": template_id} + + # Needs to choose from name + if not has_from_name: + base = ["view_template", "service_email_sender_change"] + + # 1a. clicks do this later -> add recipients + # 1b. clicks add reply to email -> add recipients (endpoint will be added on the go) + if not has_reply_to: + expected_flow = base + ["service_email_reply_to"] + # 2. choose reply to email -> add recipients + elif has_multiple_reply_to: + expected_flow = base + ["set_sender"] + # 3. add recipients + else: + expected_flow = base + # Needs to choose reply to email + elif not has_reply_to: + expected_flow = ["view_template", "service_email_reply_to"] + + # Has multiple reply to email + elif has_multiple_reply_to: + expected_flow = ["view_template", "set_sender"] + # Contains all necessary fields and single reply to email (DONE) + else: + expected_flow = ["view_template"] + + return create_backlinks(expected_flow, **args) + + +def create_backlinks(routes, **kwargs): + return [url_for(f"main.{route}", **kwargs) for route in routes] + + +def get_previous_backlink(service_id, template_id): + if service_id is None or template_id is None: + return None + + backlinks = session.get("email_sender_backlinks", []) + + current_path = request.path # Just the path, no query string + + parsed_paths = [urlparse(url).path for url in backlinks] + + # Special journey check: user completed reply-to add and verify, now at step-0 + # In this case we are redirecting it back to view_template as too many values + # have been set and we want to keep things consistent + has_add_reply_to = any("email-reply-to/add" in path for path in parsed_paths) + has_verify_reply_to = any("email-reply-to/" in path and "/verify" in path for path in parsed_paths) + is_on_step_0 = "/one-off/step-0" in current_path + + if is_on_step_0 and has_add_reply_to and has_verify_reply_to: + return url_for("main.view_template", service_id=service_id, template_id=template_id) + + try: + paths = [urlparse(url).path for url in backlinks] + current_index = paths.index(current_path) + if current_index > 0: + return backlinks[current_index - 1] + except ValueError: + pass + + return url_for("main.view_template", service_id=service_id, template_id=template_id) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 386974f450..bbf9046879 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -47,6 +47,8 @@ def old_service_dashboard(service_id): @main.route("/services/") @user_has_permissions() def service_dashboard(service_id): + session["from_sender_flow_check"] = False + if session.get("invited_user_id"): session.pop("invited_user_id", None) session["service_id"] = service_id diff --git a/app/main/views/send.py b/app/main/views/send.py index 68537db296..fc433b6ecd 100644 --- a/app/main/views/send.py +++ b/app/main/views/send.py @@ -31,12 +31,14 @@ ) from app.main import main, no_cookie from app.main.forms import ( + AddRecipientForm, ChooseTimeForm, CsvUploadForm, LetterAddressForm, SetSenderForm, get_placeholder_form_instance, ) +from app.main.views.backlink_manager import get_backlink_email_sender, get_previous_backlink from app.models.contact_list import ContactList, ContactListsAlphabetical from app.models.user import Users from app.s3_client.s3_csv_client import ( @@ -162,6 +164,7 @@ def send_messages(service_id, template_id): form=form, allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS, error_summary_enabled=True, + from_sender_flow=session.get("from_sender_flow_check", False), ) @@ -196,14 +199,38 @@ def _should_show_set_sender_page(service_id, template) -> bool: @main.route("/services//send//set-sender", methods=["GET", "POST"]) @user_has_permissions("send_messages") def set_sender(service_id, template_id): + template = current_service.get_template_with_user_permission_or_403(template_id, current_user) + + session["from_sender_flow_check"] = True + + if template.template_type == "email": + if current_service.email_sender_name is None: + session["email_sender_backlinks"] = get_backlink_email_sender(current_service, template_id) + + return redirect( + url_for( + "main.service_email_sender_change", + service_id=service_id, + from_sender_flow="yes", + template_id=template_id, + ) + ) + + if not current_service.email_reply_to_addresses: + session["email_sender_backlinks"] = get_backlink_email_sender(current_service, template_id) + return redirect(url_for("main.service_email_reply_to", service_id=service_id, template_id=template_id)) + + backlinks = session.get("email_sender_backlinks", []) + + if len(backlinks) == 0: + session["email_sender_backlinks"] = get_backlink_email_sender(current_service, template_id) + from_back_link = request.args.get("from_back_link") == "yes" # If we're returning to the page, we want to use the sender_id already in the session instead of resetting it session["sender_id"] = session.get("sender_id") if from_back_link else None redirect_to_one_off = redirect(url_for(".send_one_off", service_id=service_id, template_id=template_id)) - template = current_service.get_template_with_user_permission_or_403(template_id, current_user) - if template.template_type == "letter": return redirect_to_one_off @@ -243,20 +270,25 @@ def set_sender(service_id, template_id): session["sender_id"] = form.sender.data return redirect(url_for(".send_one_off", service_id=service_id, template_id=template_id)) + backlink = _get_set_sender_back_link(service_id, template) + + if template.template_type == "email" and len(backlinks) > 0: + backlink = get_previous_backlink(service_id, template_id) + return render_template( "views/templates/set-sender.html", form=form, template_id=template_id, sender_context={"title": sender_context["title"], "description": sender_context["description"]}, option_hints=option_hints, - back_link=_get_set_sender_back_link(service_id, template), + back_link=backlink, ) def get_sender_context(sender_details, template_type): context = { "email": { - "title": "Where should replies come back to?", + "title": "Choose a reply-to email address", "description": "Where should replies come back to?", "field_name": "email_address", }, @@ -274,6 +306,11 @@ def get_sender_context(sender_details, template_type): sender_format = context["field_name"] + if not sender_details: + context["default_id"] = None + context["value_and_label"] = [] + return context + context["default_id"] = next(sender["id"] for sender in sender_details if sender["is_default"]) if template_type == "sms": inbound = [sender["id"] for sender in sender_details if sender["inbound_number_id"]] @@ -513,6 +550,9 @@ def send_one_off_step(service_id, template_id, step_index): # noqa: C901 back_link = get_back_link(service_id, template, step_index, placeholders) template.values[current_placeholder] = None + if step_index == 0 and template.template_type == "email": + return render_email_add_recipients(service_id, template_id, template, placeholders) + return render_template( "views/send-test.html", page_title=get_send_test_page_title( @@ -526,6 +566,53 @@ def send_one_off_step(service_id, template_id, step_index): # noqa: C901 back_link=back_link, link_to_upload=(request.endpoint == "main.send_one_off_step" and step_index == 0), error_summary_enabled=True, + from_sender_flow=session.get("from_sender_flow_check", False), + ) + + +def render_email_add_recipients(service_id, template_id, template, placeholders): + session["placeholders"] = {} + + session["email_sender_backlinks"].append( + url_for("main.send_one_off_step", service_id=service_id, template_id=template_id, step_index=0) + ) + + backlink = get_previous_backlink(service_id, template_id) + + form = AddRecipientForm() + + if form.validate_on_submit(): + choice = form.add_recipient_method.data + + if choice == AddRecipientForm.CHOICE_UPLOAD_CSV: + return redirect(url_for("main.send_messages", service_id=service_id, template_id=template_id)) + + email_placeholder = placeholders[0] + + if choice == AddRecipientForm.CHOICE_USE_OWN_EMAIL: + session["recipient"] = current_user.email_address + + elif choice == AddRecipientForm.CHOICE_ENTER_SINGLE_EMAIL: + session["recipient"] = form.enter_single_address.data + + session["placeholders"][email_placeholder] = session["recipient"] + template.values[email_placeholder] = session["recipient"] + + return redirect( + url_for( + ".send_one_off_step", + service_id=service_id, + template_id=template_id, + step_index=1, + ) + ) + + return render_template( + "views/add-recipients.html", + form=form, + template_id=template_id, + service_id=service_id, + alternative_backlink=backlink, ) @@ -945,6 +1032,7 @@ def check_notification(service_id, template_id): return render_template( "views/notifications/check.html", **_check_notification(service_id, template_id), + from_sender_flow=session.get("from_sender_flow_check", False), ) @@ -1042,6 +1130,7 @@ def send_notification(service_id, template_id): return render_template( "views/notifications/check.html", **_check_notification(service_id, template_id, exception), + from_sender_flow=session.get("from_sender_flow_check", False), ) session.pop("placeholders") @@ -1065,6 +1154,8 @@ def get_email_reply_to_address_from_session(): if session.get("sender_id"): return current_service.get_email_reply_to_address(session["sender_id"])["email_address"] + return current_service.default_email_reply_to_address + def get_sms_sender_from_session(): if session.get("sender_id"): diff --git a/app/main/views/service_settings/index.py b/app/main/views/service_settings/index.py index 9121c0f9b3..0af37999a7 100644 --- a/app/main/views/service_settings/index.py +++ b/app/main/views/service_settings/index.py @@ -8,6 +8,7 @@ redirect, render_template, request, + session, url_for, ) from flask_login import current_user @@ -25,6 +26,7 @@ ) from app.constants import SIGN_IN_METHOD_TEXT_OR_EMAIL from app.event_handlers import Events +from app.extensions import redis_client from app.main import json_updates, main from app.main.forms import ( AdminBillingDetailsForm, @@ -41,7 +43,8 @@ AdminSetEmailBrandingForm, AdminSetLetterBrandingForm, AdminSetOrganisationForm, - EstimateUsageForm, + EmailUsageForm, + ExpectToSendForm, OnOffSettingForm, RenameServiceForm, SearchByNameForm, @@ -58,6 +61,7 @@ SMSPrefixForm, YesNoSettingForm, ) +from app.main.views.backlink_manager import get_previous_backlink from app.models.branding import ( AllEmailBranding, AllLetterBranding, @@ -67,6 +71,7 @@ from app.models.letter_rates import LetterRates from app.models.organisation import Organisation from app.models.sms_rate import SMSRate +from app.notify_client import cache from app.utils import DELIVERED_STATUSES, FAILURE_STATUSES, SENDING_STATUSES from app.utils.services import service_has_or_is_expected_to_send_x_or_more_notifications from app.utils.user import user_has_permissions, user_is_platform_admin @@ -117,18 +122,38 @@ def service_name_change(service_id): ) -@main.route("/services//service-settings/email-sender", methods=["GET", "POST"]) +@main.route("/services//service-settings/from-name", methods=["GET", "POST"]) @user_has_permissions("manage_service") def service_email_sender_change(service_id): + from_sender_flow = request.args.get("from_sender_flow") == "yes" + template_id = request.args.get("template_id") + alternative_backlink = get_previous_backlink(current_service.id, template_id) + form = ServiceEmailSenderForm( - use_custom_email_sender_name=current_service.custom_email_sender_name is not None, + use_custom_email_sender_name="custom", custom_email_sender_name=current_service.custom_email_sender_name, ) if form.validate_on_submit(): - new_sender = form.custom_email_sender_name.data if form.use_custom_email_sender_name.data else None + choice = form.use_custom_email_sender_name.data + + new_sender = None + + if choice == ServiceEmailSenderForm.CHOICE_CUSTOM: + new_sender = form.custom_email_sender_name.data + if choice == ServiceEmailSenderForm.CHOICE_ORGANISATION: + new_sender = current_service.organisation_name + if choice == ServiceEmailSenderForm.CHOICE_SERVICE: + new_sender = current_service.name current_service.update(custom_email_sender_name=new_sender) + redis_client.set(f"{service_id}_has_confirmed_email_sender", b"true", ex=cache.DEFAULT_TTL) + + if from_sender_flow and not current_service.email_reply_to_addresses: + return redirect(url_for(".service_email_reply_to", service_id=service_id, template_id=template_id)) + + elif from_sender_flow and current_service.email_reply_to_addresses: + return redirect(url_for(".set_sender", service_id=service_id, template_id=template_id)) return redirect(url_for(".service_settings", service_id=service_id)) @@ -137,9 +162,20 @@ def service_email_sender_change(service_id): form=form, organisation_type=current_service.organisation_type, error_summary_enabled=True, + alternative_backlink=alternative_backlink, ) +def determine_sender_choice(service): + sender_name = service.custom_email_sender_name + if sender_name: + if sender_name == service.organisation_name: + return ServiceEmailSenderForm.CHOICE_ORGANISATION + if sender_name == service.name: + return ServiceEmailSenderForm.CHOICE_SERVICE + return ServiceEmailSenderForm.CHOICE_SERVICE + + @main.post("/services//service-settings/email-sender/preview-address") @user_has_permissions("manage_service") def service_email_sender_preview(service_id): @@ -153,6 +189,22 @@ def service_email_sender_preview(service_id): ) +@main.post("/services//service-settings/from-name-preview") +@user_has_permissions("manage_service") +def service_from_name_preview(service_id): + choice = request.form.get("use_custom_email_sender_name") + name = "" + + if choice == ServiceEmailSenderForm.CHOICE_CUSTOM: + name = request.form.get("custom_email_sender_name", "") + elif choice == ServiceEmailSenderForm.CHOICE_ORGANISATION: + name = current_service.organisation_name + elif choice == ServiceEmailSenderForm.CHOICE_SERVICE: + name = current_service.name + + return jsonify({"html": render_template("partials/preview-full-email-sender.html", email_sender_name=name)}) + + @main.route("/services//service-settings/set-data-retention", methods=["GET", "POST"]) @user_has_permissions("manage_service") def service_data_retention(service_id): @@ -181,18 +233,30 @@ def service_data_retention(service_id): @main.route("/services//service-settings/request-to-go-live/estimate-usage", methods=["GET", "POST"]) @user_has_permissions("manage_service") def estimate_usage(service_id): - form = EstimateUsageForm( - volume_email=current_service.volume_email, - volume_sms=current_service.volume_sms, - volume_letter=current_service.volume_letter, - ) + form = ExpectToSendForm() + + if request.method == "GET": + form.message_types.data = [] + + if current_service.will_send_emails: + form.message_types.data.append("emails") + if current_service.will_send_texts: + form.message_types.data.append("texts") + if current_service.will_send_letters: + form.message_types.data.append("letters") if form.validate_on_submit(): - current_service.update( - volume_email=form.volume_email.data, - volume_sms=form.volume_sms.data, - volume_letter=form.volume_letter.data, - ) + selected_message_types = form.message_types.data + + for message_type in ("emails", "texts", "letters"): + if message_type in selected_message_types: + redis_client.set(f"{service_id}_will_send_{message_type}", b"true", ex=cache.DEFAULT_TTL) + else: + redis_client.delete(f"{service_id}_will_send_{message_type}") + + if "emails" in selected_message_types: + return redirect(url_for("main.email_usage", service_id=service_id)) + return redirect( url_for( "main.request_to_go_live", @@ -206,6 +270,25 @@ def estimate_usage(service_id): ) +@main.route("/services//service-settings/request-to-go-live/email-usage", methods=["GET", "POST"]) +@user_has_permissions("manage_service") +def email_usage(service_id): + form = EmailUsageForm() + + if form.validate_on_submit(): + return redirect( + url_for( + "main.request_to_go_live", + service_id=service_id, + ) + ) + + return render_template( + "views/service-settings/email-usage.html", + form=form, + ) + + @main.route("/services//service-settings/switch-live", methods=["GET", "POST"]) @user_is_platform_admin def service_switch_live(service_id): @@ -341,12 +424,29 @@ def service_set_reply_to_email(service_id): @main.route("/services//service-settings/email-reply-to", methods=["GET"]) @user_has_permissions("manage_service", "manage_api_keys") def service_email_reply_to(service_id): - return render_template("views/service-settings/email_reply_to.html") + template_id = request.args.get("template_id") + + backlink = get_previous_backlink(service_id, template_id) + + return render_template( + "views/service-settings/email_reply_to.html", + alternative_backlink=backlink, + template_id=template_id, + service_id=current_service.id, + ) @main.route("/services//service-settings/email-reply-to/add", methods=["GET", "POST"]) @user_has_permissions("manage_service") def service_add_email_reply_to(service_id): + template_id = request.args.get("template_id") + + session["email_sender_backlinks"] = session["email_sender_backlinks"] + [ + url_for("main.service_add_email_reply_to", service_id=service_id, template_id=template_id) + ] + + backlink = get_previous_backlink(service_id, template_id) + form = ServiceReplyToEmailForm() first_email_address = current_service.count_email_reply_to_addresses == 0 is_default = first_email_address if first_email_address else form.is_default.data @@ -379,6 +479,7 @@ def service_add_email_reply_to(service_id): service_id=service_id, notification_id=notification_id, is_default=is_default, + template_id=template_id, ) ) @@ -387,6 +488,7 @@ def service_add_email_reply_to(service_id): form=form, first_email_address=first_email_address, error_summary_enabled=True, + alternative_backlink=backlink, ) @@ -397,6 +499,21 @@ def service_add_email_reply_to(service_id): def service_verify_reply_to_address(service_id, notification_id): replace = request.args.get("replace", False) is_default = request.args.get("is_default", False) + template_id = request.args.get("template_id") + + session["email_sender_backlinks"].append( + url_for( + "main.service_verify_reply_to_address", + service_id=service_id, + notification_id=notification_id, + is_default=is_default, + template_id=template_id, + replace=replace, + ) + ) + + backlink = get_previous_backlink(service_id, template_id) + return render_template( "views/service-settings/email-reply-to/verify.html", service_id=service_id, @@ -404,6 +521,8 @@ def service_verify_reply_to_address(service_id, notification_id): partials=get_service_verify_reply_to_address_partials(service_id, notification_id), replace=replace, is_default=is_default, + template_id=template_id, + alternative_backlink=backlink, ) @@ -420,12 +539,24 @@ def get_service_verify_reply_to_address_partials(service_id, notification_id): replace = request.args.get("replace", False) replace = False if replace == "False" else replace existing_is_default = False + redirect_after = True if session.get("email_sender_backlinks") else False + + alternative_redirect_link = None + if redirect_after is True: + template_id = request.args.get("template_id") + alternative_redirect_link = url_for( + "main.send_one_off_step", + service_id=service_id, + template_id=template_id, + step_index=0, + ) + if replace: existing = current_service.get_email_reply_to_address(replace) existing_is_default = existing["is_default"] verification_status = "pending" is_default = True if (request.args.get("is_default", False) == "True") else False - if notification["status"] in DELIVERED_STATUSES: + if notification["status"] in (DELIVERED_STATUSES + SENDING_STATUSES): verification_status = "success" if notification["to"] not in [i["email_address"] for i in current_service.email_reply_to_addresses]: if replace: @@ -459,6 +590,7 @@ def get_service_verify_reply_to_address_partials(service_id, notification_id): form=form, first_email_address=first_email_address, replace=replace, + alternative_redirect_link=alternative_redirect_link, ), "stop": 0 if verification_status == "pending" else 1, } @@ -1249,7 +1381,7 @@ def link_service_to_organisation(service_id): if form.organisations.data != current_service.organisation_id: organisations_client.update_service_organisation(service_id, form.organisations.data) - # if it's a GP in trial mode, we need to set their daily sms_message_limit to 0 + # if it's a GP in test mode, we need to set their daily sms_message_limit to 0 organisation = Organisation.from_id(form.organisations.data) if current_service.trial_mode and organisation.organisation_type == Organisation.TYPE_NHS_GP: current_service.update(sms_message_limit=0) diff --git a/app/main/views/sub_navigation_dictionaries.py b/app/main/views/sub_navigation_dictionaries.py index 4352890516..fd1621e2f1 100644 --- a/app/main/views/sub_navigation_dictionaries.py +++ b/app/main/views/sub_navigation_dictionaries.py @@ -171,7 +171,7 @@ def using_notify_nav(): "link": "main.guidance_text_message_sender", }, { - "name": "Trial mode", + "name": "Test mode", "link": "main.guidance_trial_mode", }, { diff --git a/app/main/views/templates.py b/app/main/views/templates.py index fcb2fc74ae..6d34f07526 100644 --- a/app/main/views/templates.py +++ b/app/main/views/templates.py @@ -14,6 +14,7 @@ redirect, render_template, request, + session, url_for, ) from flask_login import current_user @@ -102,6 +103,9 @@ def as_error_dict(self) -> dict[str, int]: @main.route("/services//templates/") @user_has_permissions(allow_org_user=True) def view_template(service_id, template_id): + session["email_sender_backlinks"] = [] + session["from_sender_flow_check"] = False + template = current_service.get_template( template_id, letter_preview_url=url_for( @@ -112,6 +116,7 @@ def view_template(service_id, template_id): ), show_recipient=True, include_letter_edit_ui_overlay=True, + email_reply_to=current_service.default_email_reply_to_address, ) if template._template["archived"]: diff --git a/app/main/views/tour.py b/app/main/views/tour.py index 9b91c1ce1c..2ea951de6e 100644 --- a/app/main/views/tour.py +++ b/app/main/views/tour.py @@ -92,6 +92,7 @@ def tour_step(service_id, template_id, step_index): back_link=back_link, help="2", error_summary_enabled=True, + from_sender_flow=session.get("from_sender_flow_check", False), ) @@ -130,6 +131,7 @@ def check_tour_notification(service_id, template_id): template=template, back_link=back_link, help="2", + from_sender_flow=session.get("from_sender_flow_check", False), ) diff --git a/app/main/views/verify.py b/app/main/views/verify.py index e6bc1b73b1..53d123117b 100644 --- a/app/main/views/verify.py +++ b/app/main/views/verify.py @@ -87,9 +87,9 @@ def activate_user(user_id): return redirect(url_for("main.organisation_dashboard", org_id=organisation_id)) if user.default_organisation.can_ask_to_join_a_service: - return redirect(url_for("main.your_services")) + return redirect(url_for("main.your_services", new_user_account="yes")) - return redirect(url_for("main.add_service")) + return redirect(url_for("main.your_services", new_user_account="yes")) def _add_invited_user_to_service(invitation): diff --git a/app/main/views/your_services.py b/app/main/views/your_services.py index 798cbe403a..bee372f825 100644 --- a/app/main/views/your_services.py +++ b/app/main/views/your_services.py @@ -1,4 +1,4 @@ -from flask import redirect, render_template, session, url_for +from flask import redirect, render_template, request, session, url_for from flask_login import current_user from app import status_api_client @@ -21,6 +21,8 @@ def services_or_dashboard(): @main.route("/your-services") @user_is_logged_in def your_services(): + new_user_account = request.args.get("new_user_account") + org_count, live_service_count = None, None if current_user.platform_admin: org_count, live_service_count = ( @@ -32,6 +34,7 @@ def your_services(): can_add_service=current_user.is_gov_user, org_count=org_count, live_service_count=live_service_count, + new_user_account=new_user_account, ) diff --git a/app/models/event.py b/app/models/event.py index 5b3962814e..b2781e9cf7 100644 --- a/app/models/event.py +++ b/app/models/event.py @@ -55,7 +55,7 @@ def format_restricted(self): if self.value_to is False: return "Made this service live" if self.value_to is True: - return "Put this service back into trial mode" + return "Put this service back into test mode" def format_active(self): if self.value_to is False: diff --git a/app/models/service.py b/app/models/service.py index d4e15170ea..f46668f26f 100644 --- a/app/models/service.py +++ b/app/models/service.py @@ -13,6 +13,7 @@ SIGN_IN_METHOD_TEXT, SIGN_IN_METHOD_TEXT_OR_EMAIL, ) +from app.extensions import redis_client from app.models import JSONModel from app.models.api_key import APIKeys from app.models.branding import EmailBranding, LetterBranding @@ -293,6 +294,16 @@ def intending_to_send_sms(self): return self.has_sms_templates return self.volume_sms > 0 + @property + def has_confirmed_email_sender(self): + if redis_client.get(f"{self.id}_has_confirmed_email_sender"): + return True + return False + + @property + def needs_to_confirm_email_sender(self): + return self.intending_to_send_email and not self.has_confirmed_email_sender + @cached_property def email_reply_to_addresses(self): return service_api_client.get_reply_to_email_addresses(self.id) @@ -405,13 +416,30 @@ def get_letter_contact_block(self, id): def volumes_by_channel(self): return {channel: getattr(self, f"volume_{channel}") for channel in ("email", "sms", "letter")} + @property + def will_send_emails(self): + return redis_client.get(f"{self.id}_will_send_emails") + + @property + def will_send_texts(self): + return redis_client.get(f"{self.id}_will_send_texts") + + @property + def will_send_letters(self): + return redis_client.get(f"{self.id}_will_send_letters") + + @property + def go_live_sending_options_complete(self): + return any([self.will_send_emails, self.will_send_texts, self.will_send_letters]) + @property def go_live_checklist_completed(self): return all( ( - any(self.volumes_by_channel.values()), + self.go_live_sending_options_complete, self.has_team_members_with_manage_service_permission, self.has_templates, + not self.needs_to_confirm_email_sender, not self.needs_to_add_email_reply_to_address, not self.needs_to_change_sms_sender, self.confirmed_unique, @@ -624,8 +652,8 @@ def sign_in_method(self) -> str: return SIGN_IN_METHOD_TEXT @property - def email_sender_name(self) -> str: - return self.custom_email_sender_name or self.name + def email_sender_name(self) -> str | None: + return self.custom_email_sender_name @property def unsubscribe_request_reports_summary(self): diff --git a/app/navigation.py b/app/navigation.py index e6087e9ced..0f00a1d2c3 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -351,6 +351,7 @@ class MainNavigation(Navigation): "add_organisation_from_gp_service", "add_organisation_from_nhs_local_service", "confirm_service_is_unique", + "email_usage", "estimate_usage", "request_to_go_live", "service_agreement", diff --git a/app/notify_session.py b/app/notify_session.py index f3d7b0e192..9e9c88a728 100644 --- a/app/notify_session.py +++ b/app/notify_session.py @@ -1,4 +1,4 @@ -from datetime import UTC, datetime, timedelta +from datetime import UTC, datetime from flask import Flask, Request, Response, request from flask.sessions import SecureCookieSession, SecureCookieSessionInterface @@ -17,10 +17,7 @@ def _get_inactive_session_expiry(self, app, session_start: datetime): """ absolute_expiration = session_start + app.permanent_session_lifetime - if current_user and current_user.platform_admin: - refresh_duration = timedelta(seconds=app.config["PLATFORM_ADMIN_INACTIVE_SESSION_TIMEOUT"]) - else: - refresh_duration = app.permanent_session_lifetime + refresh_duration = app.permanent_session_lifetime return min(datetime.now(UTC) + refresh_duration, absolute_expiration) diff --git a/app/templates/org_nav.html b/app/templates/org_nav.html index e015d6f93c..035c4a5526 100644 --- a/app/templates/org_nav.html +++ b/app/templates/org_nav.html @@ -4,7 +4,7 @@ {% if current_user.platform_admin %} - + {% endif %} diff --git a/app/templates/partials/check/not-allowed-to-send-to.html b/app/templates/partials/check/not-allowed-to-send-to.html index e127e67977..7b2c9a2dc2 100644 --- a/app/templates/partials/check/not-allowed-to-send-to.html +++ b/app/templates/partials/check/not-allowed-to-send-to.html @@ -7,6 +7,6 @@

{%- endif %}

- In trial mode you can only + In test mode you can only send to yourself and members of your team

diff --git a/app/templates/partials/check/too-many-messages.html b/app/templates/partials/check/too-many-messages.html index f19a55519a..c1b1183572 100644 --- a/app/templates/partials/check/too-many-messages.html +++ b/app/templates/partials/check/too-many-messages.html @@ -8,7 +8,7 @@

You can only send {{ current_service.get_message_limit(template.template_type) | message_count(template.template_type) }} per day {%- if current_service.trial_mode %} - in trial mode + in test mode {%- endif -%} .

diff --git a/app/templates/partials/check/trying-to-send-letters-in-trial-mode.html b/app/templates/partials/check/trying-to-send-letters-in-trial-mode.html index 212ac1e8f1..a2e92f2392 100644 --- a/app/templates/partials/check/trying-to-send-letters-in-trial-mode.html +++ b/app/templates/partials/check/trying-to-send-letters-in-trial-mode.html @@ -3,6 +3,6 @@

{{ 'this letter' if count_of_recipients == 1 else 'these letters' }}

- In trial mode you + In test mode you can only preview how your letters will look

diff --git a/app/templates/partials/preview-full-email-sender.html b/app/templates/partials/preview-full-email-sender.html new file mode 100644 index 0000000000..2b6f33f7cf --- /dev/null +++ b/app/templates/partials/preview-full-email-sender.html @@ -0,0 +1,8 @@ +
From
+
+ {% set name = email_sender_name or 'Example' %} + {{ name }}

+ + {{ name|make_string_safe_for_email_local_part }}@notifications.service.gov.uk + +
diff --git a/app/templates/partials/templates/email_preview_template.jinja2 b/app/templates/partials/templates/email_preview_template.jinja2 index 942c66127d..7a8df78c01 100644 --- a/app/templates/partials/templates/email_preview_template.jinja2 +++ b/app/templates/partials/templates/email_preview_template.jinja2 @@ -3,28 +3,24 @@ {% set email_meta = [] %} {% if show_recipient %} - {% if from_name %} {% do email_meta.append({ "key": { "html": "From" }, "value": { - "html": from_name + "html": from_name if from_name else "Not set" } }) %} - {% endif %} - {% if reply_to %} {% do email_meta.append({ "key": { "html": "Reply to" | safe }, "value": { "classes": "email-message-meta__reply-to", - "html": reply_to + "html": reply_to if reply_to else "Not set" } }) %} - {% endif %} {% endif %} {% do email_meta.append({ diff --git a/app/templates/service_navigation.html b/app/templates/service_navigation.html index 869e8432fb..827b3e1a23 100644 --- a/app/templates/service_navigation.html +++ b/app/templates/service_navigation.html @@ -1,10 +1,22 @@ -{% macro navigation_service_name(service) %} - +{% from "govuk_frontend_jinja/components/tag/macro.html" import govukTag %} + +{% macro navigation_service_name(service, show_test_mode_tag=False) %} + {% endmacro %} {% macro service_navigation(user, service) %} @@ -16,7 +28,7 @@ {{ service.organisation_name }} {% endif %} {% endif %} - {{ navigation_service_name(service) }} + {{ navigation_service_name(service, show_test_mode_tag=True) }} {% endmacro %} diff --git a/app/templates/views/add-recipients.html b/app/templates/views/add-recipients.html new file mode 100644 index 0000000000..b34de87431 --- /dev/null +++ b/app/templates/views/add-recipients.html @@ -0,0 +1,74 @@ +{% extends "withnav_template.html" %} +{% from "components/page-header.html" import page_header %} +{% from "components/form.html" import form_wrapper %} +{% from "components/page-footer.html" import page_footer %} +{% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %} + +{% set heading = 'Add recipients' %} + +{% block service_page_title %} + {{ heading }} +{% endblock %} + +{% block backLink %} + {{ govukBackLink({ "href": alternative_backlink or url_for('main.view_template', service_id=service_id, template_id=template_id) }) }} +{% endblock %} + +{% block maincolumn_content %} +
+
+ {{ page_header(heading) }} + +

Choose how you want to add recipients:

+ + {% set enter_single_field_html %} + {{ form.enter_single_address( + param_extensions={ + "classes": "", + "label": {"classes": "govuk-visually-hidden"}, "hint": None, + "formGroup": {"classes": "govuk-!-margin-bottom-3"}, + } + ) }} + {% endset %} + + {% call form_wrapper() %} + {{ form.add_recipient_method(param_extensions={ + "fieldset": { + "legend": { + "classes": "", + } + }, + "items": [ + {"value": "upload_csv"}, + { + "value": "enter_single", + "conditional": { + "html": enter_single_field_html + } + }, + {"value": "use_my_email"} + ] + }) }} + + {% if current_service.trial_mode %} +
+

Your service is in test mode. You can only:

+ +
    +
  • send 50 emails per day
  • +
  • send test emails to yourself and your team members
  • +
+

+ To lift these restrictions, + + make your service live + +

+
+ {% endif %} + + {{ page_footer('Continue') }} + {% endcall %} +
+
+{% endblock %} diff --git a/app/templates/views/add-service-choose-organisation.html b/app/templates/views/add-service-choose-organisation.html new file mode 100644 index 0000000000..28bf37d229 --- /dev/null +++ b/app/templates/views/add-service-choose-organisation.html @@ -0,0 +1,60 @@ +{% extends "withoutnav_template.html" %} +{% from "components/govuk-page-header.html" import govuk_page_header %} +{% from "components/page-footer.html" import page_footer %} +{% from "components/form.html" import form_wrapper %} +{% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %} +{% from "govuk_frontend_jinja/components/details/macro.html" import govukDetails %} + +{% set caption = "Add a new service" %} +{% set heading = 'Choose an organisation' %} + +{% block backLink %} + {% if request.args.get("back") == "your_services" %} + {{ govukBackLink({ "href": url_for('main.your_services') }) }} + {% endif %} +{% endblock %} + + +{% block maincolumn_content %} +
+
+
+ +
+ {{ caption }} + {{ govuk_page_header(heading) }} +
+ + {% call form_wrapper(class="govuk-!-margin-top-3") %} + {{ form.organisation(param_extensions={ + 'fieldset': { + 'legend': { + 'isPageHeading': True, + 'classes': 'govuk-fieldset__legend--l' + } + } + }) }} + + {{ page_footer('Continue') }} + {% endcall %} + + {% set service_name_visibility_details %} +

An organisation is the public sector body responsible for a service.

+ +

If your organisation is new to GOV.UK Notify, we’ll ask you to accept our data processing and financial agreement.

+ +

This is so we know who to invoice if you:

+
    +
  • exceed the free text message allowance
  • +
  • send any letters
  • +
+ {% endset %} + + {{ govukDetails({ + "summaryText": "What is an organisation?", + "html": service_name_visibility_details + }) }} + +
+
+{% endblock %} diff --git a/app/templates/views/add-service-test-mode.html b/app/templates/views/add-service-test-mode.html new file mode 100644 index 0000000000..957893f049 --- /dev/null +++ b/app/templates/views/add-service-test-mode.html @@ -0,0 +1,55 @@ +{% extends "withoutnav_template.html" %} +{% from "components/govuk-page-header.html" import govuk_page_header %} +{% from "components/page-footer.html" import page_footer %} +{% from "components/form.html" import form_wrapper %} +{% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %} +{% from "govuk_frontend_jinja/components/details/macro.html" import govukDetails %} + +{% set caption = "Add a new service" %} +{% set heading = 'Your service will start in test mode' %} + +{% block backLink %} + {% if request.args.get("back") == "your_services" %} + {{ govukBackLink({ "href": url_for('main.add_service_service_name') }) }} + {% endif %} +{% endblock %} + + +{% block maincolumn_content %} +
+
+
+ +
+ {{ caption }} + {{ govuk_page_header(heading) }} +
+ +

You can only send messages to yourself and your team members.

+

There’s a daily limit of 50 emails and 50 text messages.

+

You cannot send any letters in test mode.

+ +
+ + {% call form_wrapper() %} + {{ page_footer('Continue') }} + {% endcall %} + +
+ + {% set test_mode_visibility_details %} +

Complete the tasks on the ‘Make your service live’ page.

+ +

It can take up to one working day to make your service live.

+ +

This is so we can check that you’ve met our terms of use.

+ {% endset %} + + {{ govukDetails({ + "summaryText": "How can I switch off test mode?", + "html": test_mode_visibility_details + }) }} + +
+
+{% endblock %} diff --git a/app/templates/views/add-service.html b/app/templates/views/add-service.html index 8e39971283..972b9ba201 100644 --- a/app/templates/views/add-service.html +++ b/app/templates/views/add-service.html @@ -3,57 +3,62 @@ {% from "components/page-footer.html" import page_footer %} {% from "components/form.html" import form_wrapper %} {% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %} +{% from "govuk_frontend_jinja/components/details/macro.html" import govukDetails %} + +{% set caption = "Add a new service" %} {% set heading = 'Enter a service name' %} {% block backLink %} - {% if request.args.get("back") == "your_services" %} - {{ govukBackLink({ "href": url_for('main.your_services') }) }} - {% endif %} + {% if request.args.get("back") == "your_services" %} + {{ govukBackLink({ "href": url_for('main.add_service_choose_organisation') }) }} + {% endif %} {% endblock %} {% block per_page_title %} - {{ heading }} + {{ heading }} {% endblock %} {% block maincolumn_content %}
-
- {{ govuk_page_header(heading) }} -

This is the name your emails will come from.

-

You can also display it at the start of every text message you send.

- - {% if default_organisation_type == 'central' or default_organisation_type == 'local' %} -

Your service name should tell the recipient what your message is about, as well as who it’s from. For example:

- -
    - {% if default_organisation_type == 'central' %} -
  • Register to vote
  • -
  • Renew your Passport
  • -
  • Check your state pension
  • - {% elif default_organisation_type == 'local' %} -
  • School admissions - {{ current_user.default_organisation.name }}
  • -
  • Electoral services - {{ current_user.default_organisation.name }}
  • -
  • Blue Badge - {{ current_user.default_organisation.name }}
  • - {% endif %} -
- {% else %} -

Your service name should tell the recipient what your message is about, as well as who it’s from.

- {% endif %} +
+ +
+ {{ caption }} + {{ govuk_page_header(heading) }} +
+ +

Choose a name that describes the purpose of your service.

+

GOV.UK Notify will use it to check that your organisation keeps to our terms of use.

+ +

Do not include:

+ +
    +
  • acronyms, initialisms or abbreviations
  • +
  • your own name or the name of someone on your team
  • +
-

Do not use an acronym, initialism or abbreviation unless your recipients are already familiar with it.

+ {% call form_wrapper() %} + {{ form.name(param_extensions={"hint": {"text": "You can change your service name later if you need to"} }) }} - {% call form_wrapper() %} - {{ form.name(param_extensions={"hint": {"text": "You can change this later"} }) }} + {{ page_footer('Save and continue') }} + {% endcall %} - {% if not default_organisation_type %} - {{ form.organisation_type }} - {% endif %} + {% set service_name_visibility_details %} +

The only people that can see your service name are:

+
    +
  • you and your team members
  • +
  • GOV.UK Notify
  • +
  • other members of your organisation
  • +
+ {% endset %} - {{ page_footer('Add service') }} - {% endcall %} + {{ govukDetails({ + "summaryText": "Who can see my service name?", + "html": service_name_visibility_details + }) }}
-
+
{% endblock %} diff --git a/app/templates/views/confirm-your-service-is-unique.html b/app/templates/views/confirm-your-service-is-unique.html index 99a67f8fe2..9de398bbed 100644 --- a/app/templates/views/confirm-your-service-is-unique.html +++ b/app/templates/views/confirm-your-service-is-unique.html @@ -40,7 +40,6 @@
  • acronyms, initialisms or abbreviations unless your recipients are familiar with them
  • your own name or the name of an individual
  • -
  • the name of a web application or back office system
{% call form_wrapper() %} diff --git a/app/templates/views/edit-email-template.html b/app/templates/views/edit-email-template.html index e1d0c1b577..90d09b8d3f 100644 --- a/app/templates/views/edit-email-template.html +++ b/app/templates/views/edit-email-template.html @@ -48,7 +48,7 @@ } }) }} {{ sticky_page_footer( - 'Save' + 'Save and preview' ) }}
diff --git a/app/templates/views/find-users/user-information.html b/app/templates/views/find-users/user-information.html index dfe14b2fb3..340aba650d 100644 --- a/app/templates/views/find-users/user-information.html +++ b/app/templates/views/find-users/user-information.html @@ -52,7 +52,7 @@

Live services

{% endif %} -

Trial mode services

+

Test mode services

diff --git a/app/templates/views/guidance/features/who-can-use-notify.html b/app/templates/views/guidance/features/who-can-use-notify.html index d62208f33b..3dc28a7cac 100644 --- a/app/templates/views/guidance/features/who-can-use-notify.html +++ b/app/templates/views/guidance/features/who-can-use-notify.html @@ -59,7 +59,7 @@

GP surgeries and pharmacies

Members of the public

diff --git a/app/templates/views/guidance/pricing/index.html b/app/templates/views/guidance/pricing/index.html index 14b2bb1c8a..5e03675746 100644 --- a/app/templates/views/guidance/pricing/index.html +++ b/app/templates/views/guidance/pricing/index.html @@ -22,7 +22,7 @@

Pricing

Try Notify for free

-

When you add a new service it will start in trial mode.

+

When you add a new service it will start in test mode.

{% if not current_user.is_authenticated %} diff --git a/app/templates/views/guidance/using-notify/index.html b/app/templates/views/guidance/using-notify/index.html index 93cb3c7af7..53061b9c8c 100644 --- a/app/templates/views/guidance/using-notify/index.html +++ b/app/templates/views/guidance/using-notify/index.html @@ -56,7 +56,7 @@

Uploads and attachments

Settings and permissions

    -
  • Trial mode
  • +
  • Test mode
  • Sign-in method
  • Team members and permissions
  • Data retention period
  • diff --git a/app/templates/views/guidance/using-notify/trial-mode.html b/app/templates/views/guidance/using-notify/trial-mode.html index 45f194440f..bc9a1111b0 100644 --- a/app/templates/views/guidance/using-notify/trial-mode.html +++ b/app/templates/views/guidance/using-notify/trial-mode.html @@ -4,26 +4,26 @@ {% set navigation_label_prefix = 'Using Notify' %} {% block per_page_title %} - Trial mode + Test mode {% endblock %} {% block content_column_content %} -

    Trial mode

    +

    Test mode

    -

    When you add a new service it will start in trial mode.

    -

    While your service is in trial mode, you can only send messages to yourself and your team members.

    +

    When you add a new service it will start in test mode.

    +

    While your service is in test mode, you can only send messages to yourself and your team members.

    There’s a daily limit of {{ email_and_sms_daily_limit | message_count('email') }} and {{ email_and_sms_daily_limit | message_count('sms') }}.

    -

    You cannot send any letters in trial mode, but you can still create letter templates and upload letters, to see how it works.

    +

    You cannot send any letters in test mode, but you can still create letter templates and upload letters, to see how it works.

    -
    -

    GP surgeries and pharmacies cannot send any text messages in trial mode.

    +
    +

    GP surgeries and pharmacies cannot send any text messages in test mode.

    -

    How to switch off trial mode

    +

    How to switch off test mode

    {% if current_service and current_service.trial_mode %} -

    Complete the tasks on the Make your service live page.

    +

    Complete the tasks on the Make your service live page.

    {% else %}

    Complete the tasks on the Make your service live page.

    {% endif %} diff --git a/app/templates/views/notifications/check.html b/app/templates/views/notifications/check.html index 14f1ae5bad..8428037929 100644 --- a/app/templates/views/notifications/check.html +++ b/app/templates/views/notifications/check.html @@ -3,6 +3,7 @@ {% from "components/page-header.html" import page_header %} {% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %} {% from "govuk_frontend_jinja/components/button/macro.html" import govukButton %} +{% from "govuk_frontend_jinja/components/inset-text/macro.html" import govukInsetText %} {% block service_page_title %} {{ "Error" if error else "Preview of ‘{}’".format(template.name) }} @@ -69,6 +70,20 @@ {{ template|string }} + {% if current_service.trial_mode and from_sender_flow and template.template_type == "email" %} + {% set insetHtml %} +

    Your service is in test mode. You can only:

    +
      +
    • send 50 emails per day
    • +
    • send test emails to yourself and your team members
    • +
    +

    To lift these restrictions, make your service live

    + + {% endset %} + + {{ govukInsetText({"html": insetHtml}) }} + {% endif %} +
  • acronyms, initialisms or abbreviations, unless your recipients will recognise them
  • your own name or the name of someone on your team
  • -
  • the name of a web application or back office system

A service name should tell the recipient what the message is about, as well as who it’s from. For example:

- +
    {% if organisation.organisation_type == 'local' %}
  • School admissions - {{ organisation.name }}
  • diff --git a/app/templates/views/organisations/organisation/trial-mode-services.html b/app/templates/views/organisations/organisation/trial-mode-services.html index 6ceac01530..637c0b0ca2 100644 --- a/app/templates/views/organisations/organisation/trial-mode-services.html +++ b/app/templates/views/organisations/organisation/trial-mode-services.html @@ -8,7 +8,7 @@ {% block maincolumn_content %}

    - Trial mode services + Test mode services

    {{ live_search(target_selector='.browse-list-item', show=True, form=_search_form, label='Search by name') }}
      diff --git a/app/templates/views/request-to-go-live.html b/app/templates/views/request-to-go-live.html index 11084138ae..c668b43b06 100644 --- a/app/templates/views/request-to-go-live.html +++ b/app/templates/views/request-to-go-live.html @@ -3,11 +3,19 @@ {% from "components/page-header.html" import page_header %} {% from "components/page-footer.html" import page_footer %} {% from "govuk_frontend_jinja/components/task-list/macro.html" import govukTaskList %} +{% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %} + {% block service_page_title %} Make your service live {% endblock %} +{% block backLink %} + {% if alternative_backlink %} + {{ govukBackLink({ "href": alternative_backlink }) }} + {% endif %} +{% endblock %} + {% block maincolumn_content %} {# as options objects are different for completed vs not completed #} {# we set them here to reuse them later #} @@ -21,7 +29,7 @@

      {{ requester }} sent a request to go live for this service.

      It can take up to one working day to make a service live. This is because we need to check that it meets our terms of use.

      {% else %} -

      To remove the trial mode restrictions and make your service live:

      +

      To remove the test mode restrictions and make your service live:

      1. Complete the tasks on this page.
      2. Select ‘Send a request to go live’
      3. @@ -43,7 +51,7 @@ "classes": "govuk-link--no-visited-state", }, "href": url_for('main.estimate_usage', service_id=current_service.id), - "status": completed_status if current_service.has_estimated_usage else not_completed_status, + "status": completed_status if current_service.go_live_sending_options_complete else not_completed_status, }, { "title": { @@ -65,7 +73,15 @@ {# all the conditional task #} {% if current_service.intending_to_send_email %} - {% do task_list_items.append( + {% do task_list_items.extend( + [{ + "title": { + "text": "Choose a ‘from’ name", + "classes": "govuk-link--no-visited-state", + }, + "href": url_for('main.service_email_sender_change', service_id=current_service.id), + "status": completed_status if current_service.has_confirmed_email_sender else not_completed_status, + }, { "title": { "text": "Add a reply-to email address", @@ -73,7 +89,7 @@ }, "href": url_for('main.service_email_reply_to', service_id=current_service.id), "status": completed_status if current_service.has_email_reply_to_address else not_completed_status, - } + }] )%} {% endif %} {% if (current_service.intending_to_send_sms and current_service.shouldnt_use_govuk_as_sms_sender) %} diff --git a/app/templates/views/send-test.html b/app/templates/views/send-test.html index 0c0def85a8..d8e7d02d2e 100644 --- a/app/templates/views/send-test.html +++ b/app/templates/views/send-test.html @@ -3,6 +3,7 @@ {% from "components/page-footer.html" import page_footer %} {% from "components/form.html" import form_wrapper %} {% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %} +{% from "govuk_frontend_jinja/components/inset-text/macro.html" import govukInsetText %} {% block service_page_title %} {{ page_title }} @@ -46,6 +47,21 @@

        {% endif %} + + {% if current_service.trial_mode and from_sender_flow and template.template_type == "email" %} + {% set insetHtml %} +

        Your service is in test mode. You can only:

        +
          +
        • send 50 emails per day
        • +
        • send test emails to yourself and your team members
        • +
        +

        To lift these restrictions, make your service live

        + + {% endset %} + + {{ govukInsetText({"html": insetHtml}) }} + {% endif %} + {{ page_footer('Continue') }} {% endcall %} diff --git a/app/templates/views/send.html b/app/templates/views/send.html index 754e879889..d83ef1e9eb 100644 --- a/app/templates/views/send.html +++ b/app/templates/views/send.html @@ -3,6 +3,7 @@ {% from "components/file-upload.html" import file_upload %} {% from "components/table.html" import list_table, text_field, row_heading %} {% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %} +{% from "govuk_frontend_jinja/components/inset-text/macro.html" import govukInsetText %} {% block service_page_title %} Upload a list of {{ 999|recipient_count_label(template.template_type) }} @@ -10,13 +11,27 @@ {% block backLink %} - {{ govukBackLink({ "href": url_for('main.send_one_off', service_id=current_service.id, template_id=template.id) }) }} + {{ govukBackLink({ "href": url_for('main.send_one_off', service_id=current_service.id, template_id=template.id) }) }} {% endblock %} {% block maincolumn_content %} {{ page_header('Upload a list of {}'.format(999|recipient_count_label(template.template_type))) }} + {% if current_service.trial_mode and from_sender_flow and template.template_type == "email" %} + {% set insetHtml %} +

        Your service is in test mode. You can only:

        +
          +
        • send 50 emails per day
        • +
        • send test emails to yourself and your team members
        • +
        +

        To lift these restrictions, make your service live

        + + {% endset %} + + {{ govukInsetText({"html": insetHtml}) }} + {% endif %} +