mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-07 03:43:48 -05:00
440 lines
13 KiB
Python
440 lines
13 KiB
Python
import pytz
|
||
from flask_wtf import Form
|
||
from datetime import datetime, timedelta
|
||
from notifications_utils.recipients import (
|
||
validate_phone_number,
|
||
InvalidPhoneError
|
||
)
|
||
from wtforms import (
|
||
validators,
|
||
StringField,
|
||
PasswordField,
|
||
ValidationError,
|
||
TextAreaField,
|
||
FileField,
|
||
BooleanField,
|
||
HiddenField,
|
||
IntegerField,
|
||
RadioField,
|
||
FieldList
|
||
)
|
||
from wtforms.fields.html5 import EmailField, TelField
|
||
from wtforms.validators import (DataRequired, Email, Length, Regexp, Optional)
|
||
|
||
from app.main.validators import (Blacklist, CsvFileValidator, ValidEmailDomainRegex, NoCommasInPlaceHolders)
|
||
|
||
|
||
def get_time_value_and_label(future_time):
|
||
return (
|
||
future_time.replace(tzinfo=None).isoformat(),
|
||
get_human_time(future_time.astimezone(pytz.timezone('Europe/London')))
|
||
)
|
||
|
||
|
||
def get_human_time(time):
|
||
return {
|
||
'0': 'Midnight',
|
||
'12': 'Midday'
|
||
}.get(
|
||
time.strftime('%-H'),
|
||
time.strftime('%-I%p').lower()
|
||
)
|
||
|
||
|
||
def get_next_hours_from(now, hours=23):
|
||
return [
|
||
(now + timedelta(hours=i)).replace(minute=0, second=0).replace(tzinfo=pytz.utc)
|
||
for i in range(1, hours + 1)
|
||
]
|
||
|
||
|
||
def email_address(label='Email address'):
|
||
return EmailField(label, validators=[
|
||
Length(min=5, max=255),
|
||
DataRequired(message='Can’t be empty'),
|
||
Email(message='Enter a valid email address'),
|
||
ValidEmailDomainRegex()])
|
||
|
||
|
||
class UKMobileNumber(TelField):
|
||
def pre_validate(self, form):
|
||
try:
|
||
validate_phone_number(self.data)
|
||
except InvalidPhoneError as e:
|
||
raise ValidationError(e.message)
|
||
|
||
|
||
def mobile_number():
|
||
return UKMobileNumber('Mobile number',
|
||
validators=[DataRequired(message='Can’t be empty')])
|
||
|
||
|
||
def password(label='Password'):
|
||
return PasswordField(label,
|
||
validators=[DataRequired(message='Can’t be empty'),
|
||
Length(8, 255, message='Must be at least 8 characters'),
|
||
Blacklist(message='Choose a password that’s harder to guess')])
|
||
|
||
|
||
def sms_code():
|
||
verify_code = '^\d{5}$'
|
||
return StringField('Text message code',
|
||
validators=[DataRequired(message='Can’t be empty'),
|
||
Regexp(regex=verify_code,
|
||
message='Must be 5 digits')])
|
||
|
||
|
||
def email_code():
|
||
verify_code = '^\d{5}$'
|
||
return StringField("Email code",
|
||
validators=[DataRequired(message='Can’t be empty'),
|
||
Regexp(regex=verify_code, message='Must be 5 digits')])
|
||
|
||
|
||
class LoginForm(Form):
|
||
email_address = StringField('Email address', validators=[
|
||
Length(min=5, max=255),
|
||
DataRequired(message='Can’t be empty'),
|
||
Email(message='Enter a valid email address')
|
||
])
|
||
password = PasswordField('Password', validators=[
|
||
DataRequired(message='Enter your password')
|
||
])
|
||
|
||
|
||
class RegisterUserForm(Form):
|
||
name = StringField('Full name',
|
||
validators=[DataRequired(message='Can’t be empty')])
|
||
email_address = email_address()
|
||
mobile_number = mobile_number()
|
||
password = password()
|
||
|
||
|
||
class RegisterUserFromInviteForm(Form):
|
||
name = StringField('Full name',
|
||
validators=[DataRequired(message='Can’t be empty')])
|
||
mobile_number = mobile_number()
|
||
password = password()
|
||
service = HiddenField('service')
|
||
email_address = HiddenField('email_address')
|
||
|
||
|
||
class PermissionsForm(Form):
|
||
send_messages = BooleanField("Send messages from existing templates")
|
||
manage_service = BooleanField("Modify this service, its team, and its templates")
|
||
manage_api_keys = BooleanField("Create and revoke API keys")
|
||
|
||
|
||
class InviteUserForm(PermissionsForm):
|
||
email_address = email_address('Email address')
|
||
|
||
def __init__(self, invalid_email_address, *args, **kwargs):
|
||
super(InviteUserForm, self).__init__(*args, **kwargs)
|
||
self.invalid_email_address = invalid_email_address.lower()
|
||
|
||
def validate_email_address(self, field):
|
||
if field.data.lower() == self.invalid_email_address:
|
||
raise ValidationError("You can’t send an invitation to yourself")
|
||
|
||
|
||
class TwoFactorForm(Form):
|
||
def __init__(self, validate_code_func, *args, **kwargs):
|
||
'''
|
||
Keyword arguments:
|
||
validate_code_func -- Validates the code with the API.
|
||
'''
|
||
self.validate_code_func = validate_code_func
|
||
super(TwoFactorForm, self).__init__(*args, **kwargs)
|
||
|
||
sms_code = sms_code()
|
||
|
||
def validate_sms_code(self, field):
|
||
is_valid, reason = self.validate_code_func(field.data)
|
||
if not is_valid:
|
||
raise ValidationError(reason)
|
||
|
||
|
||
class EmailNotReceivedForm(Form):
|
||
email_address = email_address()
|
||
|
||
|
||
class TextNotReceivedForm(Form):
|
||
mobile_number = mobile_number()
|
||
|
||
|
||
class AddServiceForm(Form):
|
||
def __init__(self, names_func, *args, **kwargs):
|
||
"""
|
||
Keyword arguments:
|
||
names_func -- Returns a list of unique service_names already registered
|
||
on the system.
|
||
"""
|
||
self._names_func = names_func
|
||
super(AddServiceForm, self).__init__(*args, **kwargs)
|
||
|
||
name = StringField(
|
||
'Service name',
|
||
validators=[
|
||
DataRequired(message='Can’t be empty')
|
||
]
|
||
)
|
||
|
||
def validate_name(self, a):
|
||
from app.utils import email_safe
|
||
# make sure the email_from will be unique to all services
|
||
if email_safe(a.data) in self._names_func():
|
||
raise ValidationError('This service name is already in use')
|
||
|
||
|
||
class ServiceNameForm(Form):
|
||
def __init__(self, names_func, *args, **kwargs):
|
||
"""
|
||
Keyword arguments:
|
||
names_func -- Returns a list of unique service_names already registered
|
||
on the system.
|
||
"""
|
||
self._names_func = names_func
|
||
super(ServiceNameForm, self).__init__(*args, **kwargs)
|
||
|
||
name = StringField(
|
||
u'Service name',
|
||
validators=[
|
||
DataRequired(message='Can’t be empty')
|
||
])
|
||
|
||
def validate_name(self, a):
|
||
from app.utils import email_safe
|
||
# make sure the email_from will be unique to all services
|
||
if email_safe(a.data) in self._names_func():
|
||
raise ValidationError('This service name is already in use')
|
||
|
||
|
||
class ConfirmPasswordForm(Form):
|
||
def __init__(self, validate_password_func, *args, **kwargs):
|
||
self.validate_password_func = validate_password_func
|
||
super(ConfirmPasswordForm, self).__init__(*args, **kwargs)
|
||
|
||
password = PasswordField(u'Enter password')
|
||
|
||
def validate_password(self, field):
|
||
if not self.validate_password_func(field.data):
|
||
raise ValidationError('Invalid password')
|
||
|
||
|
||
class SMSTemplateForm(Form):
|
||
name = StringField(
|
||
u'Template name',
|
||
validators=[DataRequired(message="Can’t be empty")])
|
||
|
||
template_content = TextAreaField(
|
||
u'Message',
|
||
validators=[
|
||
DataRequired(message="Can’t be empty"),
|
||
NoCommasInPlaceHolders()
|
||
]
|
||
)
|
||
|
||
|
||
class EmailTemplateForm(SMSTemplateForm):
|
||
subject = TextAreaField(
|
||
u'Subject',
|
||
validators=[DataRequired(message="Can’t be empty")])
|
||
|
||
|
||
class ForgotPasswordForm(Form):
|
||
email_address = email_address()
|
||
|
||
|
||
class NewPasswordForm(Form):
|
||
new_password = password()
|
||
|
||
|
||
class ChangePasswordForm(Form):
|
||
def __init__(self, validate_password_func, *args, **kwargs):
|
||
self.validate_password_func = validate_password_func
|
||
super(ChangePasswordForm, self).__init__(*args, **kwargs)
|
||
|
||
old_password = password('Current password')
|
||
new_password = password('New password')
|
||
|
||
def validate_old_password(self, field):
|
||
if not self.validate_password_func(field.data):
|
||
raise ValidationError('Invalid password')
|
||
|
||
|
||
class CsvUploadForm(Form):
|
||
file = FileField('Add recipients', validators=[DataRequired(
|
||
message='Please pick a file'), CsvFileValidator()])
|
||
|
||
|
||
class ChangeNameForm(Form):
|
||
new_name = StringField(u'Your name')
|
||
|
||
|
||
class ChangeEmailForm(Form):
|
||
def __init__(self, validate_email_func, *args, **kwargs):
|
||
self.validate_email_func = validate_email_func
|
||
super(ChangeEmailForm, self).__init__(*args, **kwargs)
|
||
|
||
email_address = email_address()
|
||
|
||
def validate_email_address(self, field):
|
||
is_valid = self.validate_email_func(field.data)
|
||
if not is_valid:
|
||
raise ValidationError("The email address is already in use")
|
||
|
||
|
||
class ChangeMobileNumberForm(Form):
|
||
mobile_number = mobile_number()
|
||
|
||
|
||
class ConfirmMobileNumberForm(Form):
|
||
def __init__(self, validate_code_func, *args, **kwargs):
|
||
self.validate_code_func = validate_code_func
|
||
super(ConfirmMobileNumberForm, self).__init__(*args, **kwargs)
|
||
|
||
sms_code = sms_code()
|
||
|
||
def validate_sms_code(self, field):
|
||
is_valid, msg = self.validate_code_func(field.data)
|
||
if not is_valid:
|
||
raise ValidationError(msg)
|
||
|
||
|
||
class ChooseTimeForm(Form):
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
super(ChooseTimeForm, self).__init__(*args, **kwargs)
|
||
self.scheduled_for.choices = [('', 'Now')] + [
|
||
get_time_value_and_label(hour) for hour in get_next_hours_from(datetime.utcnow())
|
||
]
|
||
|
||
scheduled_for = RadioField(
|
||
'When should Notify send these messages?',
|
||
default='',
|
||
validators=[
|
||
DataRequired()
|
||
]
|
||
)
|
||
|
||
|
||
class CreateKeyForm(Form):
|
||
def __init__(self, existing_key_names=[], *args, **kwargs):
|
||
self.existing_key_names = [x.lower() for x in existing_key_names]
|
||
super(CreateKeyForm, self).__init__(*args, **kwargs)
|
||
|
||
key_type = RadioField(
|
||
'What should Notify do when you use this key?',
|
||
validators=[
|
||
DataRequired()
|
||
]
|
||
)
|
||
|
||
key_name = StringField(u'Description of key', validators=[
|
||
DataRequired(message='You need to give the key a name')
|
||
])
|
||
|
||
def validate_key_name(self, key_name):
|
||
if key_name.data.lower() in self.existing_key_names:
|
||
raise ValidationError('A key with this name already exists')
|
||
|
||
|
||
class Feedback(Form):
|
||
name = StringField('Name')
|
||
email_address = StringField('Email address')
|
||
feedback = TextAreaField(u'', validators=[DataRequired(message="Can’t be empty")])
|
||
|
||
|
||
class RequestToGoLiveForm(Form):
|
||
channel = Stringfield('Are you sending emails or text messages or both?')
|
||
start_date = Stringfield('When will you be ready to start sending messages?')
|
||
start_volume = Stringfield('How many messages do you expect to send per month to start with? Give an estimate in numbers.')
|
||
peak_volume = Stringfield('Will the number of messages a month increase and when will that start? Give an estimate.')
|
||
upload_or_api = Stringfield('Are you uploading a list of contacts that you’re sending your message to, or are you integrating your system with ours?')
|
||
|
||
|
||
class ProviderForm(Form):
|
||
priority = IntegerField('Priority', [validators.NumberRange(min=1, max=100, message="Must be between 1 and 100")])
|
||
|
||
|
||
class ServiceReplyToEmailFrom(Form):
|
||
email_address = email_address(label='Email reply to address')
|
||
|
||
|
||
class ServiceSmsSender(Form):
|
||
sms_sender = StringField(
|
||
'Text message sender',
|
||
validators=[
|
||
Length(max=11, message="Enter fewer than 11 characters")
|
||
]
|
||
)
|
||
|
||
def validate_sms_sender(form, field):
|
||
import re
|
||
if field.data and not re.match('^[a-zA-Z0-9\s]+$', field.data):
|
||
raise ValidationError('Use letters and numbers only')
|
||
|
||
|
||
class ServiceBrandingOrg(Form):
|
||
|
||
def __init__(self, organisations=[], *args, **kwargs):
|
||
self.organisation.choices = organisations
|
||
super(ServiceBrandingOrg, self).__init__(*args, **kwargs)
|
||
|
||
branding_type = RadioField(
|
||
'Branding',
|
||
choices=[
|
||
('govuk', 'GOV.UK only'),
|
||
('both', 'GOV.UK and organisation'),
|
||
('org', 'Organisation only')
|
||
],
|
||
validators=[
|
||
DataRequired()
|
||
]
|
||
)
|
||
|
||
organisation = RadioField(
|
||
'Organisation',
|
||
validators=[
|
||
DataRequired()
|
||
]
|
||
)
|
||
|
||
|
||
class Whitelist(Form):
|
||
|
||
def populate(self, email_addresses, phone_numbers):
|
||
for form_field, existing_whitelist in (
|
||
(self.email_addresses, email_addresses),
|
||
(self.phone_numbers, phone_numbers)
|
||
):
|
||
for index, value in enumerate(existing_whitelist):
|
||
form_field[index].data = value
|
||
|
||
email_addresses = FieldList(
|
||
EmailField(
|
||
'',
|
||
validators=[
|
||
Optional(),
|
||
Email(message='Enter valid email addresses')
|
||
],
|
||
default=''
|
||
),
|
||
min_entries=5,
|
||
max_entries=5,
|
||
label="Email addresses"
|
||
)
|
||
|
||
phone_numbers = FieldList(
|
||
UKMobileNumber(
|
||
'',
|
||
validators=[
|
||
Optional()
|
||
],
|
||
default=''
|
||
),
|
||
min_entries=5,
|
||
max_entries=5,
|
||
label="Mobile numbers"
|
||
)
|