mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-23 15:57:23 -04:00
Merge branch 'main' into 976-convert-start-pages-to-generate-markdown
This commit is contained in:
@@ -9,7 +9,6 @@ from app.main.views import ( # noqa isort:skip
|
||||
code_not_received,
|
||||
conversation,
|
||||
dashboard,
|
||||
email_branding,
|
||||
feedback,
|
||||
find_services,
|
||||
find_users,
|
||||
|
||||
@@ -61,9 +61,8 @@ from app.main.validators import (
|
||||
ValidEmail,
|
||||
ValidGovEmail,
|
||||
)
|
||||
from app.models.feedback import PROBLEM_TICKET_TYPE, QUESTION_TICKET_TYPE
|
||||
from app.models.organization import Organization
|
||||
from app.utils import branding, merge_jsonlike
|
||||
from app.utils import merge_jsonlike
|
||||
from app.utils.csv import get_user_preferred_timezone
|
||||
from app.utils.user_permissions import all_ui_permissions, permission_options
|
||||
|
||||
@@ -815,49 +814,6 @@ class GovukCheckboxField(BooleanField):
|
||||
)
|
||||
|
||||
|
||||
class GovukTextareaField(TextAreaField):
|
||||
def __init__(self, label="", validators=None, param_extensions=None, **kwargs):
|
||||
super(TextAreaField, self).__init__(label, validators, **kwargs)
|
||||
self.param_extensions = param_extensions
|
||||
|
||||
# self.__call__ renders the HTML for the field by:
|
||||
# 1. delegating to self.meta.render_field which
|
||||
# 2. calls field.widget
|
||||
# this bypasses that by making self.widget a method with the same interface as widget.__call__
|
||||
def widget(self, field, param_extensions=None, **kwargs):
|
||||
# error messages
|
||||
error_message = None
|
||||
if field.errors:
|
||||
error_message = {"text": field.errors[0]}
|
||||
|
||||
params = {
|
||||
"name": field.name,
|
||||
"id": field.id,
|
||||
"rows": 8,
|
||||
"label": {
|
||||
"text": field.label.text,
|
||||
"classes": None,
|
||||
"isPageHeading": False,
|
||||
},
|
||||
"hint": {"text": None},
|
||||
"errorMessage": error_message,
|
||||
}
|
||||
|
||||
# extend default params with any sent in during instantiation
|
||||
if self.param_extensions:
|
||||
merge_jsonlike(params, self.param_extensions)
|
||||
|
||||
# add any sent in though use in templates
|
||||
if param_extensions:
|
||||
merge_jsonlike(params, param_extensions)
|
||||
|
||||
return Markup(
|
||||
render_template(
|
||||
"components/components/textarea/template.njk", params=params
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# based on work done by @richardjpope: https://github.com/richardjpope/recourse/blob/master/recourse/forms.py#L6
|
||||
class GovukCheckboxesField(SelectMultipleField):
|
||||
render_as_list = False
|
||||
@@ -1362,49 +1318,6 @@ class CreateKeyForm(StripWhitespaceForm):
|
||||
raise ValidationError("A key with this name already exists")
|
||||
|
||||
|
||||
class SupportType(StripWhitespaceForm):
|
||||
support_type = GovukRadiosField(
|
||||
"How can we help you?",
|
||||
choices=[
|
||||
(PROBLEM_TICKET_TYPE, "Report a problem"),
|
||||
(QUESTION_TICKET_TYPE, "Ask a question or give feedback"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class SupportRedirect(StripWhitespaceForm):
|
||||
who = GovukRadiosField(
|
||||
"What do you need help with?",
|
||||
choices=[
|
||||
(
|
||||
"public-sector",
|
||||
"I work in the public sector and need to send emails or text messages",
|
||||
),
|
||||
("public", "I’m a member of the public with a question for the government"),
|
||||
],
|
||||
param_extensions={"fieldset": {"legend": {"classes": "usa-sr-only"}}},
|
||||
)
|
||||
|
||||
|
||||
class FeedbackOrProblem(StripWhitespaceForm):
|
||||
name = GovukTextInputField("Name (optional)")
|
||||
email_address = email_address(label="Email address", gov_user=False, required=True)
|
||||
feedback = TextAreaField(
|
||||
"Your message", validators=[DataRequired(message="Cannot be empty")]
|
||||
)
|
||||
|
||||
|
||||
class Triage(StripWhitespaceForm):
|
||||
severe = GovukRadiosField(
|
||||
"Is it an emergency?",
|
||||
choices=[
|
||||
("yes", "Yes"),
|
||||
("no", "No"),
|
||||
],
|
||||
thing="yes or no",
|
||||
)
|
||||
|
||||
|
||||
class EstimateUsageForm(StripWhitespaceForm):
|
||||
volume_email = ForgivingIntegerField(
|
||||
"How many emails do you expect to send in the next year?",
|
||||
@@ -1543,65 +1456,6 @@ class ServiceSwitchChannelForm(ServiceOnOffSettingForm):
|
||||
super().__init__(name, *args, **kwargs)
|
||||
|
||||
|
||||
class AdminSetEmailBrandingForm(StripWhitespaceForm):
|
||||
branding_style = GovukRadiosFieldWithNoneOption(
|
||||
"Branding style",
|
||||
param_extensions={"fieldset": {"legend": {"classes": "usa-sr-only"}}},
|
||||
thing="a branding style",
|
||||
)
|
||||
|
||||
DEFAULT = (FieldWithNoneOption.NONE_OPTION_VALUE, "GOV.UK")
|
||||
|
||||
def __init__(self, all_branding_options, current_branding):
|
||||
super().__init__(branding_style=current_branding)
|
||||
|
||||
self.branding_style.choices = sorted(
|
||||
all_branding_options + [self.DEFAULT],
|
||||
key=lambda branding: (
|
||||
branding[0] != current_branding,
|
||||
branding[0] is not self.DEFAULT[0],
|
||||
branding[1].lower(),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class AdminPreviewBrandingForm(StripWhitespaceForm):
|
||||
branding_style = HiddenFieldWithNoneOption("branding_style")
|
||||
|
||||
|
||||
class AdminEditEmailBrandingForm(StripWhitespaceForm):
|
||||
name = GovukTextInputField("Name of brand")
|
||||
text = GovukTextInputField("Text")
|
||||
colour = GovukTextInputField(
|
||||
"Colour",
|
||||
validators=[
|
||||
Regexp(
|
||||
regex="^$|^#(?:[0-9a-fA-F]{3}){1,2}$",
|
||||
message="Must be a valid color hex code (starting with #)",
|
||||
)
|
||||
],
|
||||
param_extensions={
|
||||
"attributes": {"data-module": "colour-preview"},
|
||||
},
|
||||
)
|
||||
file = FileField_wtf(
|
||||
"Upload a PNG logo", validators=[FileAllowed(["png"], "PNG Images only!")]
|
||||
)
|
||||
brand_type = GovukRadiosField(
|
||||
"Brand type",
|
||||
choices=[
|
||||
("both", "GOV.UK and branding"),
|
||||
("org", "Branding only"),
|
||||
("org_banner", "Branding banner"),
|
||||
],
|
||||
)
|
||||
|
||||
def validate_name(self, name):
|
||||
op = request.form.get("operation")
|
||||
if op == "email-branding-details" and not self.name.data:
|
||||
raise ValidationError("This field is required")
|
||||
|
||||
|
||||
class SVGFileUpload(StripWhitespaceForm):
|
||||
file = FileField_wtf(
|
||||
"Upload an SVG logo",
|
||||
@@ -1816,42 +1670,6 @@ class AdminSetOrganizationForm(StripWhitespaceForm):
|
||||
)
|
||||
|
||||
|
||||
class ChooseBrandingForm(StripWhitespaceForm):
|
||||
FALLBACK_OPTION_VALUE = "something_else"
|
||||
FALLBACK_OPTION = (FALLBACK_OPTION_VALUE, "Something else")
|
||||
|
||||
@property
|
||||
def something_else_is_only_option(self):
|
||||
return self.options.choices == (self.FALLBACK_OPTION,)
|
||||
|
||||
|
||||
class ChooseEmailBrandingForm(ChooseBrandingForm):
|
||||
options = RadioField("Choose your new email branding")
|
||||
|
||||
def __init__(self, service):
|
||||
super().__init__()
|
||||
|
||||
self.options.choices = tuple(
|
||||
list(branding.get_email_choices(service)) + [self.FALLBACK_OPTION]
|
||||
)
|
||||
|
||||
|
||||
class SomethingElseBrandingForm(StripWhitespaceForm):
|
||||
something_else = GovukTextareaField(
|
||||
"Describe the branding you want",
|
||||
validators=[DataRequired("Cannot be empty")],
|
||||
param_extensions={
|
||||
"label": {
|
||||
"isPageHeading": True,
|
||||
"classes": "font-body-xl",
|
||||
},
|
||||
"hint": {
|
||||
"text": "Include links to your brand guidelines or examples of how to use your branding."
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class AdminServiceAddDataRetentionForm(StripWhitespaceForm):
|
||||
notification_type = GovukRadiosField(
|
||||
"What notification type?",
|
||||
@@ -2043,13 +1861,6 @@ class AdminClearCacheForm(StripWhitespaceForm):
|
||||
raise ValidationError("Select at least one option")
|
||||
|
||||
|
||||
class AdminOrganizationGoLiveNotesForm(StripWhitespaceForm):
|
||||
request_to_go_live_notes = TextAreaField(
|
||||
"Go live notes",
|
||||
filters=[lambda x: x or None],
|
||||
)
|
||||
|
||||
|
||||
class ChangeSecurityKeyNameForm(StripWhitespaceForm):
|
||||
security_key_name = GovukTextInputField(
|
||||
"Name of key",
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
from flask import current_app, redirect, render_template, session, url_for
|
||||
|
||||
from app import email_branding_client
|
||||
from app.main import main
|
||||
from app.main.forms import AdminEditEmailBrandingForm, SearchByNameForm
|
||||
from app.s3_client.s3_logo_client import (
|
||||
TEMP_TAG,
|
||||
delete_email_temp_file,
|
||||
delete_email_temp_files_created_by,
|
||||
permanent_email_logo_name,
|
||||
persist_logo,
|
||||
upload_email_logo,
|
||||
)
|
||||
from app.utils.user import user_is_platform_admin
|
||||
|
||||
|
||||
@main.route("/email-branding", methods=["GET", "POST"])
|
||||
@user_is_platform_admin
|
||||
def email_branding():
|
||||
brandings = email_branding_client.get_all_email_branding(sort_key="name")
|
||||
|
||||
return render_template(
|
||||
"views/email-branding/select-branding.html",
|
||||
email_brandings=brandings,
|
||||
search_form=SearchByNameForm(),
|
||||
)
|
||||
|
||||
|
||||
@main.route("/email-branding/<uuid:branding_id>/edit", methods=["GET", "POST"])
|
||||
@main.route("/email-branding/<uuid:branding_id>/edit/<logo>", methods=["GET", "POST"])
|
||||
@user_is_platform_admin
|
||||
def update_email_branding(branding_id, logo=None):
|
||||
email_branding = email_branding_client.get_email_branding(branding_id)[
|
||||
"email_branding"
|
||||
]
|
||||
|
||||
form = AdminEditEmailBrandingForm(
|
||||
name=email_branding["name"],
|
||||
text=email_branding["text"],
|
||||
colour=email_branding["colour"],
|
||||
brand_type=email_branding["brand_type"],
|
||||
)
|
||||
|
||||
logo = logo if logo else email_branding.get("logo") if email_branding else None
|
||||
|
||||
if form.validate_on_submit():
|
||||
if form.file.data:
|
||||
upload_filename = upload_email_logo(
|
||||
form.file.data.filename, form.file.data, user_id=session["user_id"]
|
||||
)
|
||||
|
||||
if logo and logo.startswith(TEMP_TAG.format(user_id=session["user_id"])):
|
||||
delete_email_temp_file(logo)
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
".update_email_branding",
|
||||
branding_id=branding_id,
|
||||
logo=upload_filename,
|
||||
)
|
||||
)
|
||||
|
||||
updated_logo_name = (
|
||||
permanent_email_logo_name(logo, session["user_id"]) if logo else None
|
||||
)
|
||||
|
||||
email_branding_client.update_email_branding(
|
||||
branding_id=branding_id,
|
||||
logo=updated_logo_name,
|
||||
name=form.name.data,
|
||||
text=form.text.data,
|
||||
colour=form.colour.data,
|
||||
brand_type=form.brand_type.data,
|
||||
)
|
||||
|
||||
if logo:
|
||||
persist_logo(logo, updated_logo_name)
|
||||
|
||||
delete_email_temp_files_created_by(session["user_id"])
|
||||
|
||||
return redirect(url_for(".email_branding", branding_id=branding_id))
|
||||
|
||||
return render_template(
|
||||
"views/email-branding/manage-branding.html",
|
||||
form=form,
|
||||
email_branding=email_branding,
|
||||
cdn_url=current_app.config["LOGO_CDN_DOMAIN"],
|
||||
logo=logo,
|
||||
)
|
||||
|
||||
|
||||
@main.route("/email-branding/create", methods=["GET", "POST"])
|
||||
@main.route("/email-branding/create/<logo>", methods=["GET", "POST"])
|
||||
@user_is_platform_admin
|
||||
def create_email_branding(logo=None):
|
||||
form = AdminEditEmailBrandingForm(brand_type="org")
|
||||
|
||||
if form.validate_on_submit():
|
||||
if form.file.data:
|
||||
upload_filename = upload_email_logo(
|
||||
form.file.data.filename, form.file.data, user_id=session["user_id"]
|
||||
)
|
||||
|
||||
if logo and logo.startswith(TEMP_TAG.format(user_id=session["user_id"])):
|
||||
delete_email_temp_file(logo)
|
||||
|
||||
return redirect(url_for(".create_email_branding", logo=upload_filename))
|
||||
|
||||
updated_logo_name = (
|
||||
permanent_email_logo_name(logo, session["user_id"]) if logo else None
|
||||
)
|
||||
|
||||
email_branding_client.create_email_branding(
|
||||
logo=updated_logo_name,
|
||||
name=form.name.data,
|
||||
text=form.text.data,
|
||||
colour=form.colour.data,
|
||||
brand_type=form.brand_type.data,
|
||||
)
|
||||
|
||||
if logo:
|
||||
persist_logo(logo, updated_logo_name)
|
||||
|
||||
delete_email_temp_files_created_by(session["user_id"])
|
||||
|
||||
return redirect(url_for(".email_branding"))
|
||||
|
||||
return render_template(
|
||||
"views/email-branding/manage-branding.html",
|
||||
form=form,
|
||||
cdn_url=current_app.config["LOGO_CDN_DOMAIN"],
|
||||
logo=logo,
|
||||
)
|
||||
@@ -1,239 +1,10 @@
|
||||
from datetime import datetime
|
||||
from flask import render_template
|
||||
|
||||
import pytz
|
||||
from flask import redirect, render_template, request, session, url_for
|
||||
from flask_login import current_user
|
||||
from govuk_bank_holidays.bank_holidays import BankHolidays
|
||||
from notifications_utils.clients.zendesk.zendesk_client import NotifySupportTicket
|
||||
|
||||
from app import convert_to_boolean, current_service
|
||||
from app.extensions import zendesk_client
|
||||
from app.main import main
|
||||
from app.main.forms import FeedbackOrProblem, SupportRedirect, SupportType, Triage
|
||||
from app.models.feedback import (
|
||||
GENERAL_TICKET_TYPE,
|
||||
PROBLEM_TICKET_TYPE,
|
||||
QUESTION_TICKET_TYPE,
|
||||
)
|
||||
from app.utils import hide_from_search_engines
|
||||
|
||||
bank_holidays = BankHolidays(use_cached_holidays=True)
|
||||
from app.utils.user import user_is_logged_in
|
||||
|
||||
|
||||
@main.route("/support", methods=["GET", "POST"])
|
||||
@hide_from_search_engines
|
||||
@main.route("/support", methods=["GET"])
|
||||
@user_is_logged_in
|
||||
def support():
|
||||
if current_user.is_authenticated:
|
||||
form = SupportType()
|
||||
if form.validate_on_submit():
|
||||
return redirect(
|
||||
url_for(
|
||||
".feedback",
|
||||
ticket_type=form.support_type.data,
|
||||
)
|
||||
)
|
||||
else:
|
||||
form = SupportRedirect()
|
||||
if form.validate_on_submit():
|
||||
if form.who.data == "public":
|
||||
return redirect(url_for(".support_public"))
|
||||
else:
|
||||
return redirect(
|
||||
url_for(
|
||||
".feedback",
|
||||
ticket_type=GENERAL_TICKET_TYPE,
|
||||
)
|
||||
)
|
||||
|
||||
return render_template("views/support/index.html", form=form)
|
||||
|
||||
|
||||
@main.route("/support/public")
|
||||
@hide_from_search_engines
|
||||
def support_public():
|
||||
return render_template("views/support/public.html")
|
||||
|
||||
|
||||
@main.route("/support/triage", methods=["GET", "POST"])
|
||||
@main.route("/support/triage/<ticket_type:ticket_type>", methods=["GET", "POST"])
|
||||
@hide_from_search_engines
|
||||
def triage(ticket_type=PROBLEM_TICKET_TYPE):
|
||||
form = Triage()
|
||||
if form.validate_on_submit():
|
||||
return redirect(
|
||||
url_for(".feedback", ticket_type=ticket_type, severe=form.severe.data)
|
||||
)
|
||||
return render_template(
|
||||
"views/support/triage.html",
|
||||
form=form,
|
||||
page_title={
|
||||
PROBLEM_TICKET_TYPE: "Report a problem",
|
||||
GENERAL_TICKET_TYPE: "Contact Notify.gov support",
|
||||
}.get(ticket_type),
|
||||
)
|
||||
|
||||
|
||||
@main.route("/support/<ticket_type:ticket_type>", methods=["GET", "POST"])
|
||||
@hide_from_search_engines
|
||||
def feedback(ticket_type):
|
||||
form = FeedbackOrProblem()
|
||||
|
||||
if not form.feedback.data:
|
||||
form.feedback.data = session.pop("feedback_message", "")
|
||||
|
||||
if request.args.get("severe") in ["yes", "no"]:
|
||||
severe = convert_to_boolean(request.args.get("severe"))
|
||||
else:
|
||||
severe = None
|
||||
|
||||
out_of_hours_emergency = all(
|
||||
(
|
||||
ticket_type != QUESTION_TICKET_TYPE,
|
||||
not in_business_hours(),
|
||||
severe,
|
||||
)
|
||||
)
|
||||
|
||||
if needs_triage(ticket_type, severe):
|
||||
session["feedback_message"] = form.feedback.data
|
||||
return redirect(url_for(".triage", ticket_type=ticket_type))
|
||||
|
||||
if needs_escalation(ticket_type, severe):
|
||||
return redirect(url_for(".bat_phone"))
|
||||
|
||||
if current_user.is_authenticated:
|
||||
form.email_address.data = current_user.email_address
|
||||
form.name.data = current_user.name
|
||||
|
||||
if form.validate_on_submit():
|
||||
user_email = form.email_address.data
|
||||
user_name = form.name.data or None
|
||||
|
||||
feedback_msg = render_template(
|
||||
"support-tickets/support-ticket.txt",
|
||||
content=form.feedback.data,
|
||||
)
|
||||
|
||||
ticket = NotifySupportTicket(
|
||||
subject="Notify feedback",
|
||||
message=feedback_msg,
|
||||
ticket_type=get_zendesk_ticket_type(ticket_type),
|
||||
p1=out_of_hours_emergency,
|
||||
user_name=user_name,
|
||||
user_email=user_email,
|
||||
org_id=current_service.organization_id if current_service else None,
|
||||
org_type=current_service.organization_type if current_service else None,
|
||||
service_id=current_service.id if current_service else None,
|
||||
)
|
||||
zendesk_client.send_ticket_to_zendesk(ticket)
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
".thanks",
|
||||
out_of_hours_emergency=out_of_hours_emergency,
|
||||
email_address_provided=(
|
||||
current_user.is_authenticated or bool(form.email_address.data)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"views/support/form.html",
|
||||
form=form,
|
||||
back_link=(
|
||||
url_for(".support")
|
||||
if severe is None
|
||||
else url_for(".triage", ticket_type=ticket_type)
|
||||
),
|
||||
show_status_page_banner=(ticket_type == PROBLEM_TICKET_TYPE),
|
||||
page_title={
|
||||
GENERAL_TICKET_TYPE: "Contact Notify.gov support",
|
||||
PROBLEM_TICKET_TYPE: "Report a problem",
|
||||
QUESTION_TICKET_TYPE: "Ask a question or give feedback",
|
||||
}.get(ticket_type),
|
||||
)
|
||||
|
||||
|
||||
@main.route("/support/escalate", methods=["GET", "POST"])
|
||||
@hide_from_search_engines
|
||||
def bat_phone():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for("main.feedback", ticket_type=PROBLEM_TICKET_TYPE))
|
||||
|
||||
return render_template("views/support/bat-phone.html")
|
||||
|
||||
|
||||
@main.route("/support/thanks", methods=["GET", "POST"])
|
||||
@hide_from_search_engines
|
||||
def thanks():
|
||||
return render_template(
|
||||
"views/support/thanks.html",
|
||||
out_of_hours_emergency=convert_to_boolean(
|
||||
request.args.get("out_of_hours_emergency")
|
||||
),
|
||||
email_address_provided=convert_to_boolean(
|
||||
request.args.get("email_address_provided")
|
||||
),
|
||||
out_of_hours=not in_business_hours(),
|
||||
)
|
||||
|
||||
|
||||
def in_business_hours():
|
||||
now = datetime.utcnow().replace(tzinfo=pytz.utc)
|
||||
|
||||
if is_weekend(now) or is_bank_holiday(now):
|
||||
return False
|
||||
|
||||
return london_time_today_as_utc(9, 30) <= now < london_time_today_as_utc(17, 30)
|
||||
|
||||
|
||||
def london_time_today_as_utc(hour, minute):
|
||||
return (
|
||||
pytz.timezone("Europe/London")
|
||||
.localize(datetime.now().replace(hour=hour, minute=minute))
|
||||
.astimezone(pytz.utc)
|
||||
)
|
||||
|
||||
|
||||
def is_weekend(time):
|
||||
return time.strftime("%A") in {
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
}
|
||||
|
||||
|
||||
def is_bank_holiday(time):
|
||||
return bank_holidays.is_holiday(time.date())
|
||||
|
||||
|
||||
def needs_triage(ticket_type, severe):
|
||||
return all(
|
||||
(
|
||||
ticket_type != QUESTION_TICKET_TYPE,
|
||||
severe is None,
|
||||
(not current_user.is_authenticated or current_user.live_services),
|
||||
not in_business_hours(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def needs_escalation(ticket_type, severe):
|
||||
return all(
|
||||
(
|
||||
ticket_type != QUESTION_TICKET_TYPE,
|
||||
severe,
|
||||
not current_user.is_authenticated,
|
||||
not in_business_hours(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_zendesk_ticket_type(ticket_type):
|
||||
# Zendesk has 4 ticket types - "problem", "incident", "task" and "question".
|
||||
# We don't want to use a Zendesk "problem" ticket type when someone reports a
|
||||
# Notify problem because they are designed to group multiple incident tickets together,
|
||||
# allowing them to be solved as a group.
|
||||
if ticket_type == PROBLEM_TICKET_TYPE:
|
||||
return NotifySupportTicket.TYPE_INCIDENT
|
||||
|
||||
return NotifySupportTicket.TYPE_QUESTION
|
||||
return render_template("views/support/index.html")
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
from flask import (
|
||||
abort,
|
||||
current_app,
|
||||
make_response,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
url_for,
|
||||
)
|
||||
from flask import abort, redirect, render_template, request, url_for
|
||||
from flask_login import current_user
|
||||
from notifications_utils.template import HTMLEmailTemplate
|
||||
|
||||
from app import email_branding_client, status_api_client
|
||||
from app import status_api_client
|
||||
from app.formatters import convert_markdown_template
|
||||
from app.main import main
|
||||
from app.main.forms import FieldWithNoneOption
|
||||
from app.main.views.pricing import CURRENT_SMS_RATE
|
||||
from app.main.views.sub_navigation_dictionaries import features_nav, using_notify_nav
|
||||
from app.utils.user import user_is_logged_in
|
||||
@@ -64,105 +54,6 @@ def design_content():
|
||||
)
|
||||
|
||||
|
||||
@main.route("/_email")
|
||||
@user_is_logged_in
|
||||
def email_template():
|
||||
branding_type = "govuk"
|
||||
branding_style = request.args.get("branding_style", None)
|
||||
|
||||
if branding_style == FieldWithNoneOption.NONE_OPTION_VALUE:
|
||||
branding_style = None
|
||||
|
||||
if branding_style is not None:
|
||||
email_branding = email_branding_client.get_email_branding(branding_style)[
|
||||
"email_branding"
|
||||
]
|
||||
branding_type = email_branding["brand_type"]
|
||||
|
||||
if branding_type == "govuk":
|
||||
brand_text = None
|
||||
brand_colour = None
|
||||
brand_logo = None
|
||||
govuk_banner = True
|
||||
brand_banner = False
|
||||
brand_name = None
|
||||
else:
|
||||
colour = email_branding["colour"]
|
||||
brand_text = email_branding["text"]
|
||||
brand_colour = colour
|
||||
brand_logo = (
|
||||
f"https://{current_app.config['LOGO_CDN_DOMAIN']}/{email_branding['logo']}"
|
||||
if email_branding["logo"]
|
||||
else None
|
||||
)
|
||||
govuk_banner = branding_type in ["govuk", "both"]
|
||||
brand_banner = branding_type == "org_banner"
|
||||
brand_name = email_branding["name"]
|
||||
|
||||
template = {
|
||||
"template_type": "email",
|
||||
"subject": "Email branding preview",
|
||||
"content": (
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting "
|
||||
"industry.\n\nLorem Ipsum has been the industry’s standard dummy "
|
||||
"text ever since the 1500s, when an unknown printer took a galley "
|
||||
"of type and scrambled it to make a type specimen book. "
|
||||
"\n\n"
|
||||
"# History"
|
||||
"\n\n"
|
||||
"It has "
|
||||
"survived not only"
|
||||
"\n\n"
|
||||
"* five centuries"
|
||||
"\n"
|
||||
"* but also the leap into electronic typesetting"
|
||||
"\n\n"
|
||||
"It was "
|
||||
"popularised in the 1960s with the release of Letraset sheets "
|
||||
"containing Lorem Ipsum passages, and more recently with desktop "
|
||||
"publishing software like Aldus PageMaker including versions of "
|
||||
"Lorem Ipsum."
|
||||
"\n\n"
|
||||
"^ It is a long established fact that a reader will be distracted "
|
||||
"by the readable content of a page when looking at its layout."
|
||||
"\n\n"
|
||||
"The point of using Lorem Ipsum is that it has a more-or-less "
|
||||
"normal distribution of letters, as opposed to using ‘Content "
|
||||
"here, content here’, making it look like readable English."
|
||||
"\n\n\n"
|
||||
"1. One"
|
||||
"\n"
|
||||
"2. Two"
|
||||
"\n"
|
||||
"10. Three"
|
||||
"\n\n"
|
||||
"This is an example of an email sent using Notify.gov."
|
||||
"\n\n"
|
||||
"https://www.notifications.service.gov.uk"
|
||||
),
|
||||
}
|
||||
|
||||
if not bool(request.args):
|
||||
resp = make_response(str(HTMLEmailTemplate(template)))
|
||||
else:
|
||||
resp = make_response(
|
||||
str(
|
||||
HTMLEmailTemplate(
|
||||
template,
|
||||
govuk_banner=govuk_banner,
|
||||
brand_text=brand_text,
|
||||
brand_colour=brand_colour,
|
||||
brand_logo=brand_logo,
|
||||
brand_banner=brand_banner,
|
||||
brand_name=brand_name,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
resp.headers["X-Frame-Options"] = "SAMEORIGIN"
|
||||
return resp
|
||||
|
||||
|
||||
@main.route("/documentation")
|
||||
@user_is_logged_in
|
||||
def documentation():
|
||||
@@ -197,14 +88,6 @@ def roadmap():
|
||||
return render_template("views/roadmap.html", navigation_links=features_nav())
|
||||
|
||||
|
||||
@main.route("/features/email")
|
||||
@user_is_logged_in
|
||||
def features_email():
|
||||
return render_template(
|
||||
"views/features/emails.html", navigation_links=features_nav()
|
||||
)
|
||||
|
||||
|
||||
@main.route("/features/sms")
|
||||
@user_is_logged_in
|
||||
def features_sms():
|
||||
@@ -290,15 +173,6 @@ def guidance_index():
|
||||
)
|
||||
|
||||
|
||||
@main.route("/using-notify/guidance/branding-and-customisation")
|
||||
@user_is_logged_in
|
||||
def branding_and_customisation():
|
||||
return render_template(
|
||||
"views/guidance/branding-and-customisation.html",
|
||||
navigation_links=using_notify_nav(),
|
||||
)
|
||||
|
||||
|
||||
@main.route("/using-notify/guidance/create-and-send-messages")
|
||||
@user_is_logged_in
|
||||
def create_and_send_messages():
|
||||
|
||||
@@ -9,6 +9,7 @@ from app.event_handlers import (
|
||||
create_invite_user_to_service_event,
|
||||
create_mobile_number_change_event,
|
||||
create_remove_user_from_service_event,
|
||||
create_resend_user_invite_to_service_event,
|
||||
)
|
||||
from app.formatters import redact_mobile_number
|
||||
from app.main import main
|
||||
@@ -331,3 +332,22 @@ def cancel_invited_user(service_id, invited_user_id):
|
||||
|
||||
flash(f"Invitation cancelled for {invited_user.email_address}", "default_with_tick")
|
||||
return redirect(url_for("main.manage_users", service_id=service_id))
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/resend-invite/<uuid:invited_user_id>",
|
||||
methods=["GET"],
|
||||
)
|
||||
@user_has_permissions("manage_service")
|
||||
def resend_invite(service_id, invited_user_id):
|
||||
current_service.resend_invite(invited_user_id)
|
||||
|
||||
invited_user = InvitedUser.by_id_and_service_id(service_id, invited_user_id)
|
||||
create_resend_user_invite_to_service_event(
|
||||
email_address=invited_user.email_address,
|
||||
resent_by_id=current_user.id,
|
||||
service_id=service_id,
|
||||
)
|
||||
|
||||
flash(f"Invitation resent for {invited_user.email_address}", "default_with_tick")
|
||||
return redirect(url_for("main.manage_users", service_id=service_id))
|
||||
|
||||
@@ -3,6 +3,7 @@ from datetime import datetime
|
||||
|
||||
from flask import (
|
||||
Response,
|
||||
flash,
|
||||
jsonify,
|
||||
render_template,
|
||||
request,
|
||||
@@ -32,14 +33,16 @@ from app.utils.user import user_has_permissions
|
||||
|
||||
@main.route("/services/<uuid:service_id>/notification/<uuid:notification_id>")
|
||||
@user_has_permissions("view_activity", "send_messages")
|
||||
def view_notification(service_id, notification_id):
|
||||
def view_notification(service_id, notification_id, error_message=None):
|
||||
if error_message:
|
||||
flash(error_message)
|
||||
|
||||
notification = notification_api_client.get_notification(
|
||||
service_id, str(notification_id)
|
||||
)
|
||||
notification["template"].update({"reply_to_text": notification["reply_to_text"]})
|
||||
|
||||
personalisation = get_all_personalisation_from_notification(notification)
|
||||
error_message = None
|
||||
|
||||
template = get_template(
|
||||
notification["template"],
|
||||
|
||||
@@ -6,21 +6,13 @@ from flask import flash, redirect, render_template, request, url_for
|
||||
from flask_login import current_user
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
from app import (
|
||||
current_organization,
|
||||
email_branding_client,
|
||||
org_invite_api_client,
|
||||
organizations_client,
|
||||
)
|
||||
from app import current_organization, org_invite_api_client, organizations_client
|
||||
from app.main import main
|
||||
from app.main.forms import (
|
||||
AdminBillingDetailsForm,
|
||||
AdminNewOrganizationForm,
|
||||
AdminNotesForm,
|
||||
AdminOrganizationDomainsForm,
|
||||
AdminOrganizationGoLiveNotesForm,
|
||||
AdminPreviewBrandingForm,
|
||||
AdminSetEmailBrandingForm,
|
||||
InviteOrgUserForm,
|
||||
OrganizationOrganizationTypeForm,
|
||||
RenameOrganizationForm,
|
||||
@@ -31,7 +23,6 @@ from app.main.views.dashboard import (
|
||||
get_tuples_of_financial_years,
|
||||
requested_and_current_financial_year,
|
||||
)
|
||||
from app.main.views.service_settings import get_branding_as_value_and_label
|
||||
from app.models.organization import AllOrganizations, Organization
|
||||
from app.models.user import InvitedOrgUser, User
|
||||
from app.utils.csv import Spreadsheet
|
||||
@@ -283,58 +274,6 @@ def edit_organization_type(org_id):
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/organizations/<uuid:org_id>/settings/set-email-branding", methods=["GET", "POST"]
|
||||
)
|
||||
@user_is_platform_admin
|
||||
def edit_organization_email_branding(org_id):
|
||||
email_branding = email_branding_client.get_all_email_branding()
|
||||
|
||||
form = AdminSetEmailBrandingForm(
|
||||
all_branding_options=get_branding_as_value_and_label(email_branding),
|
||||
current_branding=current_organization.email_branding_id,
|
||||
)
|
||||
|
||||
if form.validate_on_submit():
|
||||
return redirect(
|
||||
url_for(
|
||||
".organization_preview_email_branding",
|
||||
org_id=org_id,
|
||||
branding_style=form.branding_style.data,
|
||||
)
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"views/organizations/organization/settings/set-email-branding.html",
|
||||
form=form,
|
||||
search_form=SearchByNameForm(),
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/organizations/<uuid:org_id>/settings/preview-email-branding",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@user_is_platform_admin
|
||||
def organization_preview_email_branding(org_id):
|
||||
branding_style = request.args.get("branding_style", None)
|
||||
|
||||
form = AdminPreviewBrandingForm(branding_style=branding_style)
|
||||
|
||||
if form.validate_on_submit():
|
||||
current_organization.update(
|
||||
email_branding_id=form.branding_style.data,
|
||||
delete_services_cache=True,
|
||||
)
|
||||
return redirect(url_for(".organization_settings", org_id=org_id))
|
||||
|
||||
return render_template(
|
||||
"views/organizations/organization/settings/preview-email-branding.html",
|
||||
form=form,
|
||||
action=url_for("main.organization_preview_email_branding", org_id=org_id),
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/organizations/<uuid:org_id>/settings/edit-organization-domains",
|
||||
methods=["GET", "POST"],
|
||||
@@ -373,28 +312,6 @@ def edit_organization_domains(org_id):
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/organizations/<uuid:org_id>/settings/edit-go-live-notes", methods=["GET", "POST"]
|
||||
)
|
||||
@user_is_platform_admin
|
||||
def edit_organization_go_live_notes(org_id):
|
||||
form = AdminOrganizationGoLiveNotesForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
organizations_client.update_organization(
|
||||
org_id, request_to_go_live_notes=form.request_to_go_live_notes.data
|
||||
)
|
||||
return redirect(url_for(".organization_settings", org_id=org_id))
|
||||
|
||||
org = organizations_client.get_organization(org_id)
|
||||
form.request_to_go_live_notes.data = org["request_to_go_live_notes"]
|
||||
|
||||
return render_template(
|
||||
"views/organizations/organization/settings/edit-go-live-notes.html",
|
||||
form=form,
|
||||
)
|
||||
|
||||
|
||||
@main.route("/organizations/<uuid:org_id>/settings/notes", methods=["GET", "POST"])
|
||||
@user_is_platform_admin
|
||||
def edit_organization_notes(org_id):
|
||||
|
||||
@@ -596,13 +596,6 @@ def clear_cache():
|
||||
"service-????????-????-????-????-????????????-template-????????-????-????-????-????????????-versions", # noqa
|
||||
],
|
||||
),
|
||||
(
|
||||
"email_branding",
|
||||
[
|
||||
"email_branding",
|
||||
"email_branding-????????-????-????-????-????????????",
|
||||
],
|
||||
),
|
||||
(
|
||||
"organization",
|
||||
[
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import itertools
|
||||
import time
|
||||
import uuid
|
||||
from string import ascii_uppercase
|
||||
from zipfile import BadZipFile
|
||||
|
||||
from flask import (
|
||||
abort,
|
||||
current_app,
|
||||
flash,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
session,
|
||||
url_for,
|
||||
)
|
||||
from flask import abort, flash, redirect, render_template, request, session, url_for
|
||||
from flask_login import current_user
|
||||
from notifications_python_client.errors import HTTPError
|
||||
from notifications_utils import SMS_CHAR_COUNT_LIMIT
|
||||
@@ -495,7 +488,6 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
remaining_messages = current_service.message_limit - notification_count
|
||||
|
||||
contents = s3download(service_id, upload_id)
|
||||
|
||||
db_template = current_service.get_template_with_user_permission_or_403(
|
||||
template_id, current_user
|
||||
)
|
||||
@@ -836,7 +828,6 @@ def get_template_error_dict(exception):
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def send_notification(service_id, template_id):
|
||||
recipient = get_recipient()
|
||||
|
||||
if not recipient:
|
||||
return redirect(
|
||||
url_for(
|
||||
@@ -846,38 +837,69 @@ def send_notification(service_id, template_id):
|
||||
)
|
||||
)
|
||||
|
||||
db_template = current_service.get_template_with_user_permission_or_403(
|
||||
template_id, current_user
|
||||
keys = []
|
||||
values = []
|
||||
for k, v in session["placeholders"].items():
|
||||
keys.append(k)
|
||||
values.append(v)
|
||||
|
||||
data = ",".join(keys)
|
||||
vals = ",".join(values)
|
||||
data = f"{data}\r\n{vals}"
|
||||
|
||||
filename = f"one-off-{current_user.name}-{uuid.uuid4()}.csv"
|
||||
my_data = {"filename": filename, "template_id": template_id, "data": data}
|
||||
upload_id = s3upload(service_id, my_data)
|
||||
form = CsvUploadForm()
|
||||
form.file.data = my_data
|
||||
form.file.name = filename
|
||||
|
||||
check_message_output = check_messages(service_id, template_id, upload_id, 2)
|
||||
if "You cannot send to" in check_message_output:
|
||||
return check_messages(service_id, template_id, upload_id, 2)
|
||||
|
||||
job_api_client.create_job(
|
||||
upload_id,
|
||||
service_id,
|
||||
scheduled_for="",
|
||||
template_id=template_id,
|
||||
original_file_name=filename,
|
||||
notification_count=1,
|
||||
valid="True",
|
||||
)
|
||||
|
||||
try:
|
||||
noti = notification_api_client.send_notification(
|
||||
service_id,
|
||||
template_id=db_template["id"],
|
||||
recipient=recipient,
|
||||
personalisation=session["placeholders"],
|
||||
sender_id=session.get("sender_id", None),
|
||||
session.pop("recipient")
|
||||
session.pop("placeholders")
|
||||
|
||||
# We have to wait for the job to run and create the notification in the database
|
||||
time.sleep(0.1)
|
||||
notifications = notification_api_client.get_notifications_for_service(
|
||||
service_id, job_id=upload_id, include_one_off=True
|
||||
)
|
||||
attempts = 0
|
||||
while notifications["total"] == 0 and attempts < 5:
|
||||
notifications = notification_api_client.get_notifications_for_service(
|
||||
service_id, job_id=upload_id, include_one_off=True
|
||||
)
|
||||
except HTTPError as exception:
|
||||
current_app.logger.error(
|
||||
'Service {} could not send notification: "{}"'.format(
|
||||
current_service.id, exception.message
|
||||
time.sleep(0.1)
|
||||
attempts = attempts + 1
|
||||
|
||||
if notifications["total"] == 0 and attempts == 5:
|
||||
# This shows the job we auto-generated for the user
|
||||
return redirect(
|
||||
url_for(
|
||||
"main.view_job",
|
||||
service_id=service_id,
|
||||
job_id=upload_id,
|
||||
)
|
||||
)
|
||||
return render_template(
|
||||
"views/notifications/check.html",
|
||||
**_check_notification(service_id, template_id, exception),
|
||||
)
|
||||
|
||||
session.pop("placeholders")
|
||||
session.pop("recipient")
|
||||
session.pop("sender_id", None)
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
".view_notification",
|
||||
service_id=service_id,
|
||||
notification_id=noti["id"],
|
||||
from_job=upload_id,
|
||||
notification_id=notifications["notifications"][0]["id"],
|
||||
# used to show the final step of the tour (help=3) or not show
|
||||
# a back link on a just sent one off notification (help=0)
|
||||
help=request.args.get("help"),
|
||||
|
||||
@@ -13,12 +13,10 @@ from flask import (
|
||||
)
|
||||
from flask_login import current_user
|
||||
from notifications_python_client.errors import HTTPError
|
||||
from notifications_utils.clients.zendesk.zendesk_client import NotifySupportTicket
|
||||
|
||||
from app import (
|
||||
billing_api_client,
|
||||
current_service,
|
||||
email_branding_client,
|
||||
inbound_number_client,
|
||||
notification_api_client,
|
||||
organizations_client,
|
||||
@@ -29,23 +27,18 @@ from app.event_handlers import (
|
||||
create_resume_service_event,
|
||||
create_suspend_service_event,
|
||||
)
|
||||
from app.extensions import zendesk_client
|
||||
from app.formatters import email_safe
|
||||
from app.main import main
|
||||
from app.main.forms import (
|
||||
AdminBillingDetailsForm,
|
||||
AdminNotesForm,
|
||||
AdminPreviewBrandingForm,
|
||||
AdminServiceAddDataRetentionForm,
|
||||
AdminServiceEditDataRetentionForm,
|
||||
AdminServiceInboundNumberForm,
|
||||
AdminServiceMessageLimitForm,
|
||||
AdminServiceRateLimitForm,
|
||||
AdminServiceSMSAllowanceForm,
|
||||
AdminSetEmailBrandingForm,
|
||||
AdminSetOrganizationForm,
|
||||
ChooseEmailBrandingForm,
|
||||
EstimateUsageForm,
|
||||
RenameServiceForm,
|
||||
SearchByNameForm,
|
||||
ServiceContactDetailsForm,
|
||||
@@ -55,16 +48,10 @@ from app.main.forms import (
|
||||
ServiceSmsSenderForm,
|
||||
ServiceSwitchChannelForm,
|
||||
SMSPrefixForm,
|
||||
SomethingElseBrandingForm,
|
||||
)
|
||||
from app.utils import DELIVERED_STATUSES, FAILURE_STATUSES, SENDING_STATUSES
|
||||
from app.utils.branding import get_email_choices as get_email_branding_choices
|
||||
from app.utils.time import parse_naive_dt
|
||||
from app.utils.user import (
|
||||
user_has_permissions,
|
||||
user_is_gov_user,
|
||||
user_is_platform_admin,
|
||||
)
|
||||
from app.utils.user import user_has_permissions, user_is_platform_admin
|
||||
|
||||
PLATFORM_ADMIN_SERVICE_PERMISSIONS = OrderedDict(
|
||||
[
|
||||
@@ -87,7 +74,6 @@ def service_settings(service_id):
|
||||
return render_template(
|
||||
"views/service-settings.html",
|
||||
service_permissions=PLATFORM_ADMIN_SERVICE_PERMISSIONS,
|
||||
email_branding_options=ChooseEmailBrandingForm(current_service),
|
||||
)
|
||||
|
||||
|
||||
@@ -127,81 +113,6 @@ def service_name_change(service_id):
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/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,
|
||||
consent_to_research={
|
||||
True: "yes",
|
||||
False: "no",
|
||||
}.get(current_service.consent_to_research),
|
||||
)
|
||||
|
||||
if form.validate_on_submit():
|
||||
current_service.update(
|
||||
volume_email=form.volume_email.data,
|
||||
volume_sms=form.volume_sms.data,
|
||||
consent_to_research=(form.consent_to_research.data == "yes"),
|
||||
)
|
||||
return redirect(
|
||||
url_for(
|
||||
"main.request_to_go_live",
|
||||
service_id=service_id,
|
||||
)
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"views/service-settings/estimate-usage.html",
|
||||
form=form,
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/request-to-go-live", methods=["GET"]
|
||||
)
|
||||
@user_has_permissions("manage_service")
|
||||
def request_to_go_live(service_id):
|
||||
if current_service.live:
|
||||
return render_template("views/service-settings/service-already-live.html")
|
||||
|
||||
return render_template("views/service-settings/request-to-go-live.html")
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/request-to-go-live", methods=["POST"]
|
||||
)
|
||||
@user_has_permissions("manage_service")
|
||||
@user_is_gov_user
|
||||
def submit_request_to_go_live(service_id):
|
||||
ticket_message = render_template("support-tickets/go-live-request.txt") + "\n"
|
||||
|
||||
ticket = NotifySupportTicket(
|
||||
subject=f"Request to go live - {current_service.name}",
|
||||
message=ticket_message,
|
||||
ticket_type=NotifySupportTicket.TYPE_QUESTION,
|
||||
user_name=current_user.name,
|
||||
user_email=current_user.email_address,
|
||||
requester_sees_message_content=False,
|
||||
org_id=current_service.organization_id,
|
||||
org_type=current_service.organization_type,
|
||||
service_id=current_service.id,
|
||||
)
|
||||
zendesk_client.send_ticket_to_zendesk(ticket)
|
||||
|
||||
current_service.update(go_live_user=current_user.id)
|
||||
|
||||
flash(
|
||||
"Thanks for your request to go live. We’ll get back to you within one working day.",
|
||||
"default",
|
||||
)
|
||||
return redirect(url_for(".service_settings", service_id=service_id))
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/switch-live", methods=["GET", "POST"]
|
||||
)
|
||||
@@ -876,57 +787,6 @@ def set_rate_limit(service_id):
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/set-email-branding",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@user_is_platform_admin
|
||||
def service_set_email_branding(service_id):
|
||||
email_branding = email_branding_client.get_all_email_branding()
|
||||
|
||||
form = AdminSetEmailBrandingForm(
|
||||
all_branding_options=get_branding_as_value_and_label(email_branding),
|
||||
current_branding=current_service.email_branding_id,
|
||||
)
|
||||
|
||||
if form.validate_on_submit():
|
||||
return redirect(
|
||||
url_for(
|
||||
".service_preview_email_branding",
|
||||
service_id=service_id,
|
||||
branding_style=form.branding_style.data,
|
||||
)
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"views/service-settings/set-email-branding.html",
|
||||
form=form,
|
||||
search_form=SearchByNameForm(),
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/preview-email-branding",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@user_is_platform_admin
|
||||
def service_preview_email_branding(service_id):
|
||||
branding_style = request.args.get("branding_style", None)
|
||||
|
||||
form = AdminPreviewBrandingForm(branding_style=branding_style)
|
||||
|
||||
if form.validate_on_submit():
|
||||
current_service.update(email_branding=form.branding_style.data)
|
||||
return redirect(url_for(".service_settings", service_id=service_id))
|
||||
|
||||
return render_template(
|
||||
"views/service-settings/preview-email-branding.html",
|
||||
form=form,
|
||||
service_id=service_id,
|
||||
action=url_for("main.service_preview_email_branding", service_id=service_id),
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/link-service-to-organization",
|
||||
methods=["GET", "POST"],
|
||||
@@ -957,145 +817,6 @@ def link_service_to_organization(service_id):
|
||||
)
|
||||
|
||||
|
||||
def create_email_branding_zendesk_ticket(form_option_selected, detail=None):
|
||||
form = ChooseEmailBrandingForm(current_service)
|
||||
|
||||
ticket_message = render_template(
|
||||
"support-tickets/branding-request.txt",
|
||||
current_branding=current_service.email_branding_name,
|
||||
branding_requested=dict(form.options.choices)[form_option_selected],
|
||||
detail=detail,
|
||||
)
|
||||
ticket = NotifySupportTicket(
|
||||
subject=f"Email branding request - {current_service.name}",
|
||||
message=ticket_message,
|
||||
ticket_type=NotifySupportTicket.TYPE_QUESTION,
|
||||
user_name=current_user.name,
|
||||
user_email=current_user.email_address,
|
||||
org_id=current_service.organization_id,
|
||||
org_type=current_service.organization_type,
|
||||
service_id=current_service.id,
|
||||
)
|
||||
zendesk_client.send_ticket_to_zendesk(ticket)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/email-branding",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@user_has_permissions("manage_service")
|
||||
def email_branding_request(service_id):
|
||||
form = ChooseEmailBrandingForm(current_service)
|
||||
branding_name = current_service.email_branding_name
|
||||
if form.validate_on_submit():
|
||||
return redirect(
|
||||
url_for(
|
||||
f".email_branding_{form.options.data}",
|
||||
service_id=current_service.id,
|
||||
)
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"views/service-settings/branding/email-branding-options.html",
|
||||
form=form,
|
||||
branding_name=branding_name,
|
||||
)
|
||||
|
||||
|
||||
def check_email_branding_allowed_for_service(branding):
|
||||
allowed_branding_for_service = dict(get_email_branding_choices(current_service))
|
||||
|
||||
if branding not in allowed_branding_for_service:
|
||||
abort(404)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/email-branding/govuk",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@user_has_permissions("manage_service")
|
||||
def email_branding_govuk(service_id):
|
||||
check_email_branding_allowed_for_service("govuk")
|
||||
|
||||
if request.method == "POST":
|
||||
current_service.update(email_branding=None)
|
||||
|
||||
flash("You’ve updated your email branding", "default")
|
||||
return redirect(url_for(".service_settings", service_id=current_service.id))
|
||||
|
||||
return render_template("views/service-settings/branding/email-branding-govuk.html")
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/email-branding/govuk-and-org",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@user_has_permissions("manage_service")
|
||||
def email_branding_govuk_and_org(service_id):
|
||||
check_email_branding_allowed_for_service("govuk_and_org")
|
||||
|
||||
if request.method == "POST":
|
||||
create_email_branding_zendesk_ticket("govuk_and_org")
|
||||
|
||||
flash(
|
||||
"Thanks for your branding request. We’ll get back to you within one working day.",
|
||||
"default",
|
||||
)
|
||||
return redirect(url_for(".service_settings", service_id=current_service.id))
|
||||
|
||||
return render_template(
|
||||
"views/service-settings/branding/email-branding-govuk-org.html"
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/email-branding/organization",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@user_has_permissions("manage_service")
|
||||
def email_branding_organization(service_id):
|
||||
check_email_branding_allowed_for_service("organization")
|
||||
|
||||
if request.method == "POST":
|
||||
create_email_branding_zendesk_ticket("organization")
|
||||
|
||||
flash(
|
||||
"Thanks for your branding request. We’ll get back to you within one working day.",
|
||||
"default",
|
||||
)
|
||||
return redirect(url_for(".service_settings", service_id=current_service.id))
|
||||
|
||||
return render_template(
|
||||
"views/service-settings/branding/email-branding-organization.html"
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/service-settings/email-branding/something-else",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@user_has_permissions("manage_service")
|
||||
def email_branding_something_else(service_id):
|
||||
form = SomethingElseBrandingForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
create_email_branding_zendesk_ticket(
|
||||
"something_else", detail=form.something_else.data
|
||||
)
|
||||
|
||||
flash(
|
||||
"Thanks for your branding request. We’ll get back to you within one working day.",
|
||||
"default",
|
||||
)
|
||||
return redirect(url_for(".service_settings", service_id=current_service.id))
|
||||
|
||||
return render_template(
|
||||
"views/service-settings/branding/email-branding-something-else.html",
|
||||
form=form,
|
||||
branding_options=ChooseEmailBrandingForm(current_service),
|
||||
)
|
||||
|
||||
|
||||
@main.route("/services/<uuid:service_id>/data-retention", methods=["GET"])
|
||||
@user_is_platform_admin
|
||||
def data_retention(service_id):
|
||||
@@ -1184,10 +905,6 @@ def edit_service_billing_details(service_id):
|
||||
)
|
||||
|
||||
|
||||
def get_branding_as_value_and_label(email_branding):
|
||||
return [(branding["id"], branding["name"]) for branding in email_branding]
|
||||
|
||||
|
||||
def convert_dictionary_to_wtforms_choices_format(dictionary, value, label):
|
||||
return [(item[value], item[label]) for item in dictionary]
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@ def features_nav():
|
||||
"name": "Features",
|
||||
"link": "main.features",
|
||||
"sub_navigation_items": [
|
||||
# {
|
||||
# "name": "Emails",
|
||||
# "link": "main.features_email",
|
||||
# },
|
||||
# {
|
||||
# "name": "Text messages",
|
||||
# "link": "main.features_sms",
|
||||
@@ -56,10 +52,6 @@ def using_notify_nav():
|
||||
# "link": "main.edit_and_format_messages",
|
||||
# },
|
||||
# {
|
||||
# "name": "Branding",
|
||||
# "link": "main.branding_and_customisation",
|
||||
# },
|
||||
# {
|
||||
# "name": "Send files by email",
|
||||
# "link": "main.send_files_by_email",
|
||||
# },
|
||||
|
||||
Reference in New Issue
Block a user