335 lines
15 KiB
Python
Raw Normal View History

2025-02-27 21:53:03 -03:00
from django import forms
from django.core.exceptions import ValidationError
2025-02-27 21:53:03 -03:00
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Row, Column, Submit, HTML
from crispy_forms.templatetags.crispy_forms_field import css_class
from .models import InviteSettings
2025-02-27 23:26:44 -03:00
from wireguard_tools.models import EmailSettings
2025-02-27 21:53:03 -03:00
class InviteSettingsForm(forms.ModelForm):
class Meta:
model = InviteSettings
fields = [
'default_password',
'enforce_random_password',
'required_user_level',
'random_password_length',
'random_password_complexity',
'invite_expiration',
'download_1_label',
'download_2_label',
'download_3_label',
'download_4_label',
'download_5_label',
'download_1_url',
'download_2_url',
'download_3_url',
'download_4_url',
'download_5_url',
'download_1_enabled',
'download_2_enabled',
'download_3_enabled',
'download_4_enabled',
'download_5_enabled',
'download_instructions',
'invite_url',
'invite_text_body',
'invite_email_subject',
'invite_email_body',
'invite_email_enabled',
'invite_whatsapp_body',
'invite_whatsapp_enabled',
]
def __init__(self, *args, **kwargs):
super(InviteSettingsForm, self).__init__(*args, **kwargs)
# Define boolean dropdown choices
bool_choices = [(True, 'Enabled'), (False, 'Disabled')]
bool_coerce = lambda x: True if x == 'True' else False
for field_name in [
'download_1_enabled',
'download_2_enabled',
'download_3_enabled',
'download_4_enabled',
2025-02-27 22:15:47 -03:00
'download_5_enabled',
'enforce_random_password',
2025-02-27 21:53:03 -03:00
]:
self.fields[field_name] = forms.TypedChoiceField(
choices=bool_choices,
coerce=bool_coerce,
widget=forms.Select(),
2025-02-27 22:15:47 -03:00
required=False,
2025-02-27 21:53:03 -03:00
initial=self.instance.__dict__.get(field_name, True) if self.instance and self.instance.pk else True,
)
2025-02-27 22:15:47 -03:00
2025-02-27 21:53:03 -03:00
self.fields['download_1_url'].label = 'URL'
self.fields['download_2_url'].label = 'URL'
self.fields['download_3_url'].label = 'URL'
self.fields['download_4_url'].label = 'URL'
self.fields['download_5_url'].label = 'URL'
self.fields['download_1_label'].label = 'Text'
self.fields['download_2_label'].label = 'Text'
self.fields['download_3_label'].label = 'Text'
self.fields['download_4_label'].label = 'Text'
self.fields['download_5_label'].label = 'Text'
2025-02-27 22:15:47 -03:00
self.fields['download_1_enabled'].label = 'Status'
self.fields['download_2_enabled'].label = 'Status'
self.fields['download_3_enabled'].label = 'Status'
self.fields['download_4_enabled'].label = 'Status'
self.fields['download_5_enabled'].label = 'Status'
2025-02-27 21:53:03 -03:00
self.fields['download_instructions'].label = 'Web Page Instructions'
self.fields['invite_email_subject'].label = 'Email Subject'
self.fields['invite_email_body'].label = 'Email Message'
self.fields['invite_email_enabled'].label = 'Email Enabled'
self.fields['invite_whatsapp_body'].label = 'WhatsApp Message'
self.fields['invite_whatsapp_enabled'].label = 'WhatsApp Enabled'
self.fields['invite_text_body'].label = 'Text Message'
2025-02-27 22:15:47 -03:00
self.fields['invite_expiration'].label = 'Expiration (minutes)'
self.fields['enforce_random_password'].label = 'Random Password'
2025-02-27 21:53:03 -03:00
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.layout = Layout(
Row(
Column(
HTML("<h3>General Settings</h3>"),
Row(
2025-02-27 22:15:47 -03:00
Column('invite_url', css_class='form-group col-md-12 mb-0'),
2025-02-27 21:53:03 -03:00
),
Row(
2025-02-27 22:15:47 -03:00
Column('required_user_level', css_class='form-group col-md-6 mb-0'),
Column('invite_expiration', css_class='form-group col-md-6 mb-0'),
2025-02-27 21:53:03 -03:00
),
2025-02-27 22:15:47 -03:00
HTML('<hr>'),
2025-02-27 21:53:03 -03:00
Row(
2025-02-27 22:15:47 -03:00
Column(HTML("<h5>User Authentication</h5>"), css_class='form-group col-md-12 mb-0'),
Column('enforce_random_password', css_class='form-group col-md-6 mb-0'),
Column('default_password', css_class='form-group col-md-6 mb-0'),
2025-02-27 21:53:03 -03:00
css_class='form-row'
),
Row(
2025-02-27 22:15:47 -03:00
Column('random_password_length', css_class='form-group col-md-6 mb-0'),
Column('random_password_complexity', css_class='form-group col-md-6 mb-0'),
2025-02-27 21:53:03 -03:00
css_class='form-row'
),
2025-02-27 22:15:47 -03:00
HTML("<hr>"),
2025-02-27 21:53:03 -03:00
Row(
2025-02-27 22:15:47 -03:00
Column(HTML("<h5>Download Buttons</h5>"), css_class='form-group col-md-12 mb-0'),
2025-02-27 21:53:03 -03:00
Column('download_1_label', css_class='form-group col-md-3 mb-0'),
Column('download_1_url', css_class='form-group col-md-6 mb-0'),
Column('download_1_enabled', css_class='form-group col-md-3 mb-0'),
css_class='form-row'
),
Row(
Column('download_2_label', css_class='form-group col-md-3 mb-0'),
Column('download_2_url', css_class='form-group col-md-6 mb-0'),
Column('download_2_enabled', css_class='form-group col-md-3 mb-0'),
css_class='form-row'
),
Row(
Column('download_3_label', css_class='form-group col-md-3 mb-0'),
Column('download_3_url', css_class='form-group col-md-6 mb-0'),
Column('download_3_enabled', css_class='form-group col-md-3 mb-0'),
css_class='form-row'
),
Row(
Column('download_4_label', css_class='form-group col-md-3 mb-0'),
Column('download_4_url', css_class='form-group col-md-6 mb-0'),
Column('download_4_enabled', css_class='form-group col-md-3 mb-0'),
css_class='form-row'
),
Row(
Column('download_5_label', css_class='form-group col-md-3 mb-0'),
Column('download_5_url', css_class='form-group col-md-6 mb-0'),
Column('download_5_enabled', css_class='form-group col-md-3 mb-0'),
css_class='form-row'
),
Row(
Column(
Submit('submit', 'Save', css_class='btn btn-success'),
HTML(' <a class="btn btn-secondary" href="/vpn_invite/">Back</a> '),
2025-02-27 21:53:03 -03:00
css_class='col-md-12'
),
css_class='form-row'
),
css_class='col-xl-6'),
Column(
HTML("<h3>Message templates</h3>"),
Row(
Column('download_instructions', css_class='form-group col-md-12 mb-0'),
css_class='form-row'
),
HTML("<hr>"),
Row(
Column(HTML("<h5>Email Message Template</h5>"), css_class='form-group col-md-12 mb-0'),
Column('invite_email_subject', css_class='form-group col-md-12 mb-0'),
Column('invite_email_body', css_class='form-group col-md-12 mb-0'),
css_class='form-row'
),
Row(
Column('invite_email_enabled', css_class='form-group col-md-6 mb-0'),
css_class='form-row'
),
HTML("<hr>"),
Row(
Column(HTML("<h5>WhatsApp Message Template</h5>"), css_class='form-group col-md-12 mb-0'),
Column('invite_whatsapp_body', css_class='form-group col-md-12 mb-0'),
Column('invite_whatsapp_enabled', css_class='form-group col-md-12 mb-0'),
css_class='form-row'
),
HTML("<hr>"),
Row(
Column(HTML("<h5>Text Message Template</h5>"), css_class='form-group col-md-12 mb-0'),
Column('invite_text_body', css_class='form-group col-md-12 mb-0'),
css_class='form-row'
),
css_class='col-xl-6'),
css_class='row'),
)
def clean(self):
cleaned_data = super().clean()
# Validate invite_url: it must start with 'https://' and end with '/invite/'
invite_url = cleaned_data.get('invite_url')
if invite_url:
if not invite_url.startswith("https://"):
self.add_error('invite_url', "Invite URL must start with 'https://'.")
if not invite_url.endswith("/invite/"):
self.add_error('invite_url', "Invite URL must end with '/invite/'.")
# Validate invite_expiration: must be between 1 and 1440 minutes
invite_expiration = cleaned_data.get('invite_expiration')
if invite_expiration is not None:
if invite_expiration < 1 or invite_expiration > 1440:
self.add_error('invite_expiration', "Expiration (minutes) must be between 1 and 1440.")
# Validate default_password based on enforce_random_password flag
default_password = cleaned_data.get('default_password', '')
enforce_random_password = cleaned_data.get('enforce_random_password')
random_password_length = cleaned_data.get('random_password_length')
if enforce_random_password is True:
if default_password:
self.add_error('default_password',
"Default password must not be provided when random password is enabled.")
if random_password_length < 6:
self.add_error('random_password_length', "Random password length must be at least 6 characters.")
else:
# When random password is disabled, default password must be provided and have at least 6 characters.
if not default_password:
self.add_error('default_password',
"Default password must be provided when random password is disabled.")
elif len(default_password) < 6:
self.add_error('default_password', "Default password must be at least 6 characters long.")
# Validate download buttons: if enabled, the respective text and url fields must not be blank.
for i in range(1, 6):
enabled = cleaned_data.get(f'download_{i}_enabled')
label = (cleaned_data.get(f'download_{i}_label') or '').strip()
url = (cleaned_data.get(f'download_{i}_url') or '').strip()
if enabled:
if not label:
self.add_error(f'download_{i}_label',
"Text field must not be empty when download button is enabled.")
if not url:
self.add_error(f'download_{i}_url', "URL field must not be empty when download button is enabled.")
# Validate that default_password is not contained in any message templates or the subject
message_fields = ['invite_text_body', 'invite_email_subject', 'invite_email_body', 'invite_whatsapp_body']
if default_password:
for field in message_fields:
content = cleaned_data.get(field, '')
if default_password in content:
self.add_error('default_password',
f"Default password must not be contained in {field.replace('_', ' ')}.")
# Validate that all message templates include the placeholder '{invite_url}'
for field in message_fields:
if field != 'invite_email_subject':
content = cleaned_data.get(field, '')
if '{invite_url}' not in content:
self.add_error(field, "The template must include the placeholder '{invite_url}'.")
return cleaned_data
2025-02-27 23:26:44 -03:00
class EmailSettingsForm(forms.ModelForm):
class Meta:
model = EmailSettings
fields = [
'smtp_username',
'smtp_password',
'smtp_host',
'smtp_port',
'smtp_encryption',
'smtp_from_address',
'enabled',
]
def __init__(self, *args, **kwargs):
super(EmailSettingsForm, self).__init__(*args, **kwargs)
# Set custom labels for form fields
self.fields['smtp_username'].label = 'Username'
self.fields['smtp_password'].label = 'Password'
self.fields['smtp_host'].label = 'Host'
self.fields['smtp_port'].label = 'Port'
self.fields['smtp_encryption'].label = 'Encryption'
self.fields['smtp_from_address'].label = 'From Address'
self.fields['smtp_password'].required = True
self.fields['smtp_host'].required = True
self.fields['smtp_port'].required = True
self.fields['smtp_encryption'].required = True
self.fields['smtp_from_address'].required = True
self.fields['smtp_username'].required = True
# Use PasswordInput widget to hide the password
self.fields['smtp_password'].widget = forms.PasswordInput(render_value=False)
# Ensure that during edit the saved password is not displayed
if self.instance and self.instance.pk:
self.fields['smtp_password'].initial = ''
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.layout = Layout(
HTML("<h3>SMTP Settings</h3>"),
Row(
Column('smtp_username', css_class='form-group col-md-4 mb-0'),
Column('smtp_password', css_class='form-group col-md-4 mb-0'),
Column('smtp_from_address', css_class='form-group col-md-4 mb-0'),
css_class='form-row'
),
Row(
Column('smtp_host', css_class='form-group col-md-4 mb-0'),
Column('smtp_port', css_class='form-group col-md-4 mb-0'),
Column('smtp_encryption', css_class='form-group col-md-4 mb-0'),
css_class='form-row'
),
Row(
Column('enabled', css_class='form-group col-md-4 mb-0'),
css_class='form-row'
),
Row(
Column(
Submit('submit', 'Save', css_class='btn btn-success'),
HTML(' <a class="btn btn-secondary" href="/vpn_invite/">Back</a> '),
css_class='col-md-12'
),
css_class='form-row'
)
)
def clean(self):
cleaned_data = super().clean()
smtp_port = cleaned_data.get('smtp_port')
if smtp_port is not None and smtp_port <= 0:
self.add_error('smtp_port', "SMTP port must be a positive integer.")
return cleaned_data