VPN Invite app translation

This commit is contained in:
Eduardo Silva 2025-04-16 10:18:23 -03:00
parent 036dcc75da
commit 3cb10b6ec4
7 changed files with 401 additions and 115 deletions

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-15 14:21-0300\n"
"POT-Creation-Date: 2025-04-16 10:17-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -70,7 +70,7 @@ msgstr "DNS Secundário"
#: templates/wireguard/wireguard_manage_ip.html:42
#: templates/wireguard/wireguard_manage_peer.html:170
#: templates/wireguard/wireguard_peer_list.html:166 user_manager/forms.py:49
#: user_manager/forms.py:180
#: user_manager/forms.py:180 vpn_invite/forms.py:193 vpn_invite/forms.py:327
msgid "Back"
msgstr "Voltar"
@ -82,7 +82,8 @@ msgstr "Resolução de DNS"
#: templates/wireguard/wireguard_manage_ip.html:41
#: templates/wireguard/wireguard_manage_peer.html:168
#: templates/wireguard/wireguard_manage_server.html:130
#: user_manager/forms.py:98 user_manager/forms.py:205
#: user_manager/forms.py:98 user_manager/forms.py:205 vpn_invite/forms.py:192
#: vpn_invite/forms.py:326
msgid "Save"
msgstr "Salvar"
@ -234,11 +235,13 @@ msgstr ""
"que isto é um erro."
#: templates/accounts/login.html:14 templates/user_manager/list.html:8
#: user_manager/forms.py:13
#: user_manager/forms.py:13 vpn_invite/forms.py:282
msgid "Username"
msgstr "Usuário"
#: templates/accounts/login.html:23 user_manager/forms.py:14
#: templates/accounts/login.html:23
#: templates/vpn_invite/invite_settings.html:19 user_manager/forms.py:14
#: vpn_invite/forms.py:283
msgid "Password"
msgstr "Senha"
@ -255,6 +258,8 @@ msgid "Login again"
msgstr "Acessar novamente"
#: templates/base.html:112 templates/dns/static_host_list.html:72
#: vpn_invite/forms.py:79 vpn_invite/forms.py:80 vpn_invite/forms.py:81
#: vpn_invite/forms.py:82 vpn_invite/forms.py:83
msgid "Status"
msgstr "Estado"
@ -263,6 +268,7 @@ msgid "User Manager"
msgstr "Configurar Usuários"
#: templates/base.html:176 templates/wireguard/wireguard_peer_list.html:195
#: vpn_invite/views.py:36
msgid "VPN Invite"
msgstr "Convite para VPN"
@ -406,6 +412,30 @@ msgstr "Instância do WireGuard"
msgid "Users"
msgstr "Usuários"
#: templates/vpn_invite/invite_settings.html:17 user_manager/forms.py:178
msgid "Peer"
msgstr "Peer"
#: templates/vpn_invite/invite_settings.html:18
msgid "Expiration"
msgstr "Expira em"
#: templates/vpn_invite/invite_settings.html:31
msgid "Remove VPN invitation for peer"
msgstr "Remover convite para VPN do peer"
#: templates/vpn_invite/invite_settings.html:38
msgid "No active VPN invitations"
msgstr "Nenhum convite para VPN ativo"
#: templates/vpn_invite/invite_settings.html:49 vpn_invite/views.py:76
msgid "Email Settings"
msgstr "Configurações de Email"
#: templates/vpn_invite/invite_settings.html:50
msgid "Invite Settings"
msgstr "Configurações de Convite"
#: templates/wireguard/wireguard_manage_ip.html:18
msgid "Enter Allowed IP"
msgstr "Inserir IP Permitido"
@ -743,7 +773,7 @@ msgstr "Tráfego da Instância"
msgid "Public Address"
msgstr "Endereço Público"
#: templates/wireguard/wireguard_status.html:47
#: templates/wireguard/wireguard_status.html:47 vpn_invite/forms.py:285
msgid "Port"
msgstr "Porta"
@ -779,10 +809,6 @@ msgstr "As duas senhas não coincidem."
msgid "Password must be at least 8 characters long."
msgstr "Senha deve ter pelo menos 8 caracteres."
#: user_manager/forms.py:178
msgid "Peer"
msgstr "Peer"
#: user_manager/forms.py:220
msgid "A peer group with that name already exists."
msgstr "Um grupo de peers com esse nome já existe."
@ -791,19 +817,19 @@ msgstr "Um grupo de peers com esse nome já existe."
msgid "Debugging Analyst"
msgstr "Analista de Debug"
#: user_manager/models.py:14
#: user_manager/models.py:14 vpn_invite/models.py:25
msgid "View Only"
msgstr "Somente Visualização"
#: user_manager/models.py:15
#: user_manager/models.py:15 vpn_invite/models.py:25
msgid "Peer Manager"
msgstr "Gerente de Peers"
#: user_manager/models.py:16
#: user_manager/models.py:16 vpn_invite/models.py:25
msgid "WireGuard Manager"
msgstr "Gerente do WireGuard"
#: user_manager/models.py:17
#: user_manager/models.py:17 vpn_invite/models.py:25
msgid "Administrator"
msgstr "Administrador"
@ -955,6 +981,258 @@ msgstr ""
msgid "Please type the username to proceed."
msgstr "Por favor, digite o nome de usuário para prosseguir."
#: vpn_invite/forms.py:50 vpn_invite/forms.py:295
msgid "Enabled"
msgstr "Habilitado"
#: vpn_invite/forms.py:50
msgid "Disabled"
msgstr "Desabilitado"
#: vpn_invite/forms.py:69 vpn_invite/forms.py:70 vpn_invite/forms.py:71
#: vpn_invite/forms.py:72 vpn_invite/forms.py:73
msgid "URL"
msgstr "URL"
#: vpn_invite/forms.py:74 vpn_invite/forms.py:75 vpn_invite/forms.py:76
#: vpn_invite/forms.py:77 vpn_invite/forms.py:78
msgid "Text"
msgstr "Texto"
#: vpn_invite/forms.py:84
msgid "Web Page Instructions"
msgstr "Página Web de Instruções"
#: vpn_invite/forms.py:85
msgid "Email Subject"
msgstr "Assunto do Email"
#: vpn_invite/forms.py:86
msgid "Email Message"
msgstr "Mensagem do Email"
#: vpn_invite/forms.py:87
msgid "Email Enabled"
msgstr "Email Habilitado"
#: vpn_invite/forms.py:88
msgid "WhatsApp Message"
msgstr "Mensagem do WhatsApp"
#: vpn_invite/forms.py:89
msgid "WhatsApp Enabled"
msgstr "WhatsApp Habilitado"
#: vpn_invite/forms.py:90
msgid "Text Message"
msgstr "Mensagem de Texto"
#: vpn_invite/forms.py:91
msgid "Expiration (minutes)"
msgstr "Expira em (minutos)"
#: vpn_invite/forms.py:92
msgid "Random Password"
msgstr "Senha Aleatória"
#: vpn_invite/forms.py:93
msgid "Invite URL"
msgstr "Endereço do Convite"
#: vpn_invite/forms.py:94
msgid "Required User Level"
msgstr "Nível de Acesso Requerido"
#: vpn_invite/forms.py:95
msgid "Default Password"
msgstr "Senha Padrão"
#: vpn_invite/forms.py:96
msgid "Random Password Length"
msgstr "Tamanho da Senha Aleatória"
#: vpn_invite/forms.py:97
msgid "Random Password Complexity"
msgstr "Complexidade da Senha Aleatória"
#: vpn_invite/forms.py:104
msgid "General Settings"
msgstr "Configuração Geral"
#: vpn_invite/forms.py:126
msgid "Download Buttons"
msgstr "Botões de Download"
#: vpn_invite/forms.py:159
msgid "Message templates"
msgstr "Modelos de Mensagem"
#: vpn_invite/forms.py:166
msgid "Email Message Template"
msgstr "Modelo de Mensagem de Email"
#: vpn_invite/forms.py:177
msgid "WhatsApp Message Template"
msgstr "Modelo de Mensagem do WhatsApp"
#: vpn_invite/forms.py:184
msgid "Text Message Template"
msgstr "Modelo de Mensagem de Texto"
#: vpn_invite/forms.py:206
msgid "Invite URL must start with 'https://'."
msgstr "Endereço do convite deve começar com 'https://'."
#: vpn_invite/forms.py:208
msgid "Invite URL must end with '/invite/'."
msgstr "Endereço do convite deve terminar com '/invite/'."
#: vpn_invite/forms.py:214
msgid "Expiration (minutes) must be between 1 and 1440."
msgstr "Tempo de expiração (minutos) deve estar entre 1 e 1440."
#: vpn_invite/forms.py:223
msgid "Default password must not be provided when random password is enabled."
msgstr ""
"Senha padrão não deve ser fornecida quando a senha aleatória está habilitada."
#: vpn_invite/forms.py:225
msgid "Random password length must be at least 6 characters."
msgstr "Senha aleatória deve ter pelo menos 6 caracteres."
#: vpn_invite/forms.py:230
msgid "Default password must be provided when random password is disabled."
msgstr ""
"Senha padrão deve ser fornecida quando a senha aleatória está desabilitada."
#: vpn_invite/forms.py:232
msgid "Default password must be at least 6 characters long."
msgstr "Senha padrão deve ter pelo menos 6 caracteres."
#: vpn_invite/forms.py:242
msgid "Text field must not be empty when download button is enabled."
msgstr ""
"Campo de texto não deve estar vazio quando o botão de download está "
"habilitado."
#: vpn_invite/forms.py:244
msgid "URL field must not be empty when download button is enabled."
msgstr ""
"Campo de URL não deve estar vazio quando o botão de download está habilitado."
#: vpn_invite/forms.py:253
msgid ""
"Default password must not be contained in any message template. Found at: "
msgstr ""
"Senha padrão não deve estar contida em nenhum modelo de mensagem. Encontrado "
"em: "
#: vpn_invite/forms.py:260
#, python-brace-format
msgid "The template must include the placeholder '{invite_url}'."
msgstr "O modelo deve incluir o espaço reservado '{invite_url}'."
#: vpn_invite/forms.py:284
msgid "Host"
msgstr "Endereço"
#: vpn_invite/forms.py:286
msgid "Encryption"
msgstr "Criptografia"
#: vpn_invite/forms.py:287
msgid "From Address"
msgstr "Endereço de Origem"
#: vpn_invite/forms.py:338
msgid "SMTP port must be between 1 and 65535."
msgstr "Porta SMTP deve ser entre 1 e 65535."
#: vpn_invite/models.py:8
#, python-brace-format
msgid ""
"Hello,\n"
"\n"
"You're invited to join our secure WireGuard VPN network. Please click the "
"link below to access your personalized VPN configuration:\n"
"\n"
"{invite_url}\n"
"\n"
"Note: This invitation link will expire in {expire_minutes} minutes. If you "
"need a new link after expiration, please request another invite."
msgstr ""
"Olá,\n"
"\n"
"Você está convidado a ingressar em nossa rede VPN WireGuard segura. Por "
"favor, clique no link abaixo para acessar sua configuração VPN "
"personalizada:\n"
"\n"
"{invite_url}\n"
"\n"
"Observação: Este link de convite expirará em {expire_minutes} minutos. Se "
"você precisar de um novo link após a expiração, por favor, solicite outro "
"convite."
#: vpn_invite/models.py:16
msgid ""
"<h2>Welcome to Your VPN Setup</h2>\n"
"<p>Begin by downloading the WireGuard app for your device using one of the "
"links below.</p>\n"
"<p>Once installed, you can either <strong>scan the QR code</strong> or "
"<strong>download the configuration file</strong> to quickly import your "
"settings and start using your secure VPN connection.</p>"
msgstr ""
"<h2>Bem-vindo à Configuração da Sua VPN</h2>\n"
"<p>Comece baixando o aplicativo WireGuard para o seu dispositivo usando um "
"dos links abaixo.</p>\n"
"<p>Após a instalação, você pode <strong>escanear o código QR</strong> ou "
"<strong>baixar o arquivo de configuração</strong> para importar rapidamente "
"suas configurações e começar a usar sua conexão VPN segura.</p>"
#: vpn_invite/models.py:30
msgid "Letters, Digits, Special Characters"
msgstr "Letras, Dígitos, Caracteres Especiais"
#: vpn_invite/models.py:31
msgid "Letters, Digits"
msgstr "Letras, Dígitos"
#: vpn_invite/models.py:31
msgid "Letters"
msgstr "Letras"
#: vpn_invite/models.py:31
msgid "Digits"
msgstr "Dígitos"
#: vpn_invite/models.py:55
msgid ""
"Download the WireGuard app for your device using one of the links below. "
"After installation, you can scan the QR code or download the configuration "
"file to import on your device."
msgstr ""
"Baixe o aplicativo WireGuard para o seu dispositivo usando um dos links "
"abaixo. Após a instalação, você pode escanear o código QR ou baixar o "
"arquivo de configuração para importar no seu dispositivo."
#: vpn_invite/models.py:61
msgid "WireGuard VPN Invite"
msgstr "Convite para VPN WireGuard"
#: vpn_invite/views.py:52
msgid "Invite Settings|Settings saved successfully."
msgstr "Configurações de Convite|Configurações salvas com sucesso."
#: vpn_invite/views.py:56
msgid "VPN Invite Settings"
msgstr "Configurações de Convite para VPN"
#: vpn_invite/views.py:72
#, fuzzy
#| msgid "Invite Settings|Settings saved successfully."
msgid "Email Settings|Settings saved successfully."
msgstr "Configurações de Convite|Configurações salvas com sucesso."
#: wireguard/forms.py:11
msgid "Display Name"
msgstr "Nome de Exibição"

View File

@ -1,5 +1,5 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<div class='row'>
<div class='col-xl-8'>
@ -14,9 +14,9 @@
<table class="table table-hover">
<thead>
<tr>
<th>Peer</th>
<th>Expiration</th>
<th>Password</th>
<th>{% trans 'Peer' %}</th>
<th>{% trans 'Expiration' %}</th>
<th>{% trans 'Password' %}</th>
<th></th>
</tr>
</thead>
@ -28,13 +28,15 @@
<td>{{ invite.invite_expiration }}</td>
<td>{{ invite.invite_password }}</td>
<td class="min-width">
<a href="/vpn_invite/?invite={{ invite.uuid }}&action=delete" onclick="return confirm('Remove VPN invitation for peer {{ invite.peer }}?')"><i class="fas fa-trash-alt"></i></a>
<a href="/vpn_invite/?invite={{ invite.uuid }}&action=delete" onclick="return confirm('{% trans 'Remove VPN invitation for peer' %} {{ invite.peer }}?')"><i class="fas fa-trash-alt"></i></a>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="4"></td>
<td colspan="4" class="text-center text-muted">
{% trans 'No active VPN invitations' %}
</td>
</tr>
{% endif %}
</tbody>
@ -44,8 +46,8 @@
<div class="row">
<div class="col-lg-12">
<a href="/vpn_invite/smtp_settings/" class="btn btn-primary">SMTP Settings</a>
<a href="/vpn_invite/settings/" class="btn btn-primary">Invite Settings</a>
<a href="/vpn_invite/smtp_settings/" class="btn btn-primary">{% trans 'Email Settings' %}</a>
<a href="/vpn_invite/settings/" class="btn btn-primary">{% trans 'Invite Settings' %}</a>
</div>
</div>
</div>

View File

@ -1,10 +1,10 @@
from django import forms
from django.core.exceptions import ValidationError
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
from crispy_forms.layout import Column, HTML, Layout, Row, Submit
from django import forms
from django.utils.translation import gettext_lazy as _
from wireguard_tools.models import EmailSettings
from .models import InviteSettings
class InviteSettingsForm(forms.ModelForm):
@ -46,7 +46,7 @@ class InviteSettingsForm(forms.ModelForm):
super(InviteSettingsForm, self).__init__(*args, **kwargs)
# Define boolean dropdown choices
bool_choices = [(True, 'Enabled'), (False, 'Disabled')]
bool_choices = [(True, _('Enabled')), (False, _('Disabled'))]
bool_coerce = lambda x: True if x == 'True' else False
for field_name in [
@ -65,37 +65,42 @@ class InviteSettingsForm(forms.ModelForm):
initial=self.instance.__dict__.get(field_name, True) if self.instance and self.instance.pk else True,
)
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'
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'
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'
self.fields['invite_expiration'].label = 'Expiration (minutes)'
self.fields['enforce_random_password'].label = 'Random Password'
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')
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')
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')
self.fields['invite_expiration'].label = _('Expiration (minutes)')
self.fields['enforce_random_password'].label = _('Random Password')
self.fields['invite_url'].label = _('Invite URL')
self.fields['required_user_level'].label = _('Required User Level')
self.fields['default_password'].label = _('Default Password')
self.fields['random_password_length'].label = _('Random Password Length')
self.fields['random_password_complexity'].label = _('Random Password Complexity')
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.layout = Layout(
Row(
Column(
HTML("<h3>General Settings</h3>"),
HTML("<h3>" + _("General Settings") + "</h3>"),
Row(
Column('invite_url', css_class='form-group col-md-12 mb-0'),
),
@ -117,7 +122,7 @@ class InviteSettingsForm(forms.ModelForm):
),
HTML("<hr>"),
Row(
Column(HTML("<h5>Download Buttons</h5>"), css_class='form-group col-md-12 mb-0'),
Column(HTML("<h5>" + _("Download Buttons") + "</h5>"), css_class='form-group col-md-12 mb-0'),
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'),
@ -147,24 +152,17 @@ class InviteSettingsForm(forms.ModelForm):
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> '),
css_class='col-md-12'
),
css_class='form-row'
),
css_class='col-xl-6'),
css_class='col-xl-12'),
Column(
HTML("<h3>Message templates</h3>"),
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(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'
@ -175,19 +173,26 @@ class InviteSettingsForm(forms.ModelForm):
),
HTML("<hr>"),
Row(
Column(HTML("<h5>WhatsApp Message Template</h5>"), css_class='form-group col-md-12 mb-0'),
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(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='col-xl-12'),
css_class='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):
@ -197,15 +202,15 @@ class InviteSettingsForm(forms.ModelForm):
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://'.")
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/'.")
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.")
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', '')
@ -214,16 +219,16 @@ class InviteSettingsForm(forms.ModelForm):
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.")
_("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.")
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.")
_("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.")
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):
@ -233,9 +238,9 @@ class InviteSettingsForm(forms.ModelForm):
if enabled:
if not label:
self.add_error(f'download_{i}_label',
"Text field must not be empty when download button is enabled.")
_("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.")
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']
@ -244,14 +249,14 @@ class InviteSettingsForm(forms.ModelForm):
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('_', ' ')}.")
_("Default password must not be contained in any message template. Found at: ") + f"{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}'.")
self.add_error(field, _("The template must include the placeholder '{invite_url}'."))
return cleaned_data
@ -273,12 +278,12 @@ class EmailSettingsForm(forms.ModelForm):
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_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
@ -286,6 +291,7 @@ class EmailSettingsForm(forms.ModelForm):
self.fields['smtp_encryption'].required = True
self.fields['smtp_from_address'].required = True
self.fields['smtp_username'].required = True
self.fields['enabled'].label = _('Enabled')
# Use PasswordInput widget to hide the password
self.fields['smtp_password'].widget = forms.PasswordInput(render_value=False)
@ -316,8 +322,8 @@ class EmailSettingsForm(forms.ModelForm):
),
Row(
Column(
Submit('submit', 'Save', css_class='btn btn-success'),
HTML(' <a class="btn btn-secondary" href="/vpn_invite/">Back</a> '),
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'
@ -328,7 +334,7 @@ class EmailSettingsForm(forms.ModelForm):
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.")
self.add_error('smtp_port', _("SMTP port must be between 1 and 65535."))
return cleaned_data

View File

@ -1,37 +1,34 @@
import uuid
from django.db import models
from django.utils.translation import gettext_lazy as _
from wireguard.models import Peer
DEFAULT_INVITE_MESSAGE = '''
Hello,
DEFAULT_INVITE_MESSAGE = _('''Hello,
You're invited to join our secure WireGuard VPN network. Please click the link below to access your personalized VPN configuration:
{invite_url}
Note: This invitation link will expire in {expire_minutes} minutes. If you need a new link after expiration, please request another invite.
'''
Note: This invitation link will expire in {expire_minutes} minutes. If you need a new link after expiration, please request another invite.''')
DEFAULT_HTML_MESSAGE = '''
<h2>Welcome to Your VPN Setup</h2>
DEFAULT_HTML_MESSAGE = _('''<h2>Welcome to Your VPN Setup</h2>
<p>Begin by downloading the WireGuard app for your device using one of the links below.</p>
<p>Once installed, you can either <strong>scan the QR code</strong> or <strong>download the configuration file</strong> to quickly import your settings and start using your secure VPN connection.</p>
'''
<p>Once installed, you can either <strong>scan the QR code</strong> or <strong>download the configuration file</strong> to quickly import your settings and start using your secure VPN connection.</p>''')
class InviteSettings(models.Model):
name = models.CharField(max_length=16, default='default_settings', unique=True)
default_password = models.CharField(max_length=32, default='', blank=True, null=True)
enforce_random_password = models.BooleanField(default=True)
required_user_level = models.PositiveIntegerField(default=50, choices=(
(20, 'View Only User'), (30, 'Peer Manager'), (40, 'Wireguard Manager'), (50, 'Administrator'),
(20, _('View Only')), (30, _('Peer Manager')), (40, _('WireGuard Manager')), (50, _('Administrator')),
))
random_password_length = models.IntegerField(default=6)
random_password_complexity = models.CharField(
max_length=22, default='letters_digits', choices=(
('letters_digits_special', 'Letters, Digits, Special Characters'),
('letters_digits', 'Letters, Digits'), ('letters', 'Letters'), ('digits', 'Digits')
('letters_digits_special', _('Letters, Digits, Special Characters')),
('letters_digits', _('Letters, Digits')), ('letters', _('Letters')), ('digits', _('Digits'))
)
)
invite_expiration = models.IntegerField(default=30) # minutes
@ -55,13 +52,13 @@ class InviteSettings(models.Model):
download_3_enabled = models.BooleanField(default=True)
download_4_enabled = models.BooleanField(default=True)
download_5_enabled = models.BooleanField(default=True)
download_instructions = models.TextField(default='Download the WireGuard app for your device using one of the links below. After installation, you can scan the QR code or download the configuration file to import on your device.')
download_instructions = models.TextField(default=_('Download the WireGuard app for your device using one of the links below. After installation, you can scan the QR code or download the configuration file to import on your device.'))
invite_url = models.URLField(default='')
invite_text_body = models.TextField(default=DEFAULT_INVITE_MESSAGE)
invite_email_subject = models.CharField(max_length=64, default='WireGuard VPN Invite', blank=True, null=True)
invite_email_subject = models.CharField(max_length=64, default=_('WireGuard VPN Invite'), blank=True, null=True)
invite_email_body = models.TextField(default=DEFAULT_INVITE_MESSAGE)
invite_email_enabled = models.BooleanField(default=True)

View File

@ -1,12 +1,14 @@
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from user_manager.models import UserAcl
from .models import InviteSettings, PeerInvite
from django.conf import settings
from django.utils import timezone
from .forms import InviteSettingsForm, EmailSettingsForm
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from user_manager.models import UserAcl
from wireguard_tools.models import EmailSettings
from .forms import EmailSettingsForm, InviteSettingsForm
from .models import InviteSettings, PeerInvite
@login_required
@ -22,7 +24,7 @@ def view_vpn_invite_list(request):
except:
default_invite_url = 'https://wireguard-webadmin.example.com/invite/'
invite_settings, _ = InviteSettings.objects.get_or_create(
invite_settings, invite_settings_created = InviteSettings.objects.get_or_create(
name='default_settings', defaults={'invite_url': default_invite_url,}
)
@ -32,7 +34,7 @@ def view_vpn_invite_list(request):
peer_invite_list = PeerInvite.objects.all().order_by('invite_expiration')
peer_invite_list.filter(invite_expiration__lt=timezone.now()).delete()
data = {
'page_title': 'VPN Invite',
'page_title': _('VPN Invite'),
'peer_invite_list': peer_invite_list,
}
@ -48,11 +50,11 @@ def view_vpn_invite_settings(request):
form = InviteSettingsForm(request.POST or None, instance=invite_settings)
if form.is_valid():
form.save()
messages.success(request, 'Invite Settings|Settings saved successfully.')
messages.success(request, _('Invite Settings|Settings saved successfully.'))
return redirect('/vpn_invite/')
data = {
'invite_settings': invite_settings,
'page_title': 'VPN Invite Settings',
'page_title': _('VPN Invite Settings'),
'form': form,
'form_size': 'col-lg-12'
}
@ -63,16 +65,16 @@ def view_vpn_invite_settings(request):
def view_email_settings(request):
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
email_settings, _ = EmailSettings.objects.get_or_create(name='email_settings')
email_settings, email_settings_created = EmailSettings.objects.get_or_create(name='email_settings')
form = EmailSettingsForm(request.POST or None, instance=email_settings)
if form.is_valid():
form.save()
messages.success(request, 'Email Settings|Settings saved successfully.')
messages.success(request, _('Email Settings|Settings saved successfully.'))
return redirect('/vpn_invite/')
data = {
'email_settings': email_settings,
'page_title': 'Email Settings',
'page_title': _('Email Settings'),
'form': form,
'form_size': 'col-lg-12'
}

View File

@ -14,6 +14,7 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from accounts.views import view_create_first_user, view_login, view_logout
@ -37,7 +38,7 @@ from wireguard_peer.views import view_manage_ip_address, view_wireguard_peer_lis
from wireguard_tools.views import download_config_or_qrcode, export_wireguard_configs, restart_wireguard_interfaces
urlpatterns = [
# path('admin/', admin.site.urls),
path('admin/', admin.site.urls),
path('', view_apply_db_patches, name='apply_db_patches'),
path('status/', view_wireguard_status, name='wireguard_status'),
path('dns/', view_static_host_list, name='static_host_list'),