mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-27 10:44:33 +00:00
email_server: aliases: Using Django forms instead of custom forms
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
b0e460b433
commit
f5d1cb474f
@ -1,7 +1,14 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""
|
||||||
|
Forms for the email app.
|
||||||
|
"""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from . import aliases as aliases_module
|
||||||
|
|
||||||
|
|
||||||
class EmailServerForm(forms.Form):
|
class EmailServerForm(forms.Form):
|
||||||
domain = forms.CharField(label=_('domain'), max_length=256)
|
domain = forms.CharField(label=_('domain'), max_length=256)
|
||||||
@ -10,6 +17,48 @@ class EmailServerForm(forms.Form):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AliasCreationForm(forms.Form):
|
class AliasCreateForm(forms.Form):
|
||||||
email_name = forms.CharField(label=_('New alias (without @domain)'),
|
"""Form to create a new alias."""
|
||||||
|
alias = forms.CharField(label=_('New alias (without @domain)'),
|
||||||
max_length=50)
|
max_length=50)
|
||||||
|
|
||||||
|
def clean_alias(self):
|
||||||
|
"""Return the checked value for alias."""
|
||||||
|
value = self.data['alias']
|
||||||
|
if aliases_module.exists(value):
|
||||||
|
raise ValidationError('Alias is already taken')
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class AliasListForm(forms.Form):
|
||||||
|
"""Form to list/enable/disable/delete current aliases."""
|
||||||
|
aliases = forms.MultipleChoiceField(label=_('Aliases'),
|
||||||
|
widget=forms.CheckboxSelectMultiple)
|
||||||
|
|
||||||
|
def __init__(self, aliases, *args, **kwargs):
|
||||||
|
"""Populate the choices for aliases."""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
enabled_aliases = [(alias.email_name, alias.email_name)
|
||||||
|
for alias in aliases if alias.enabled]
|
||||||
|
disabled_aliases = [(alias.email_name, alias.email_name)
|
||||||
|
for alias in aliases if not alias.enabled]
|
||||||
|
choices = []
|
||||||
|
if enabled_aliases:
|
||||||
|
choices.append((_('Enabled'), enabled_aliases))
|
||||||
|
|
||||||
|
if disabled_aliases:
|
||||||
|
choices.append((_('Disabled'), disabled_aliases))
|
||||||
|
|
||||||
|
self.fields['aliases'].choices = choices
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""Add the pressed button to cleaned data."""
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
buttons = [key[4:] for key in self.data if key.startswith('btn_')]
|
||||||
|
if len(buttons) != 1 or buttons[0] not in ('enable', 'disable',
|
||||||
|
'delete'):
|
||||||
|
raise ValidationError('Invalid button pressed')
|
||||||
|
|
||||||
|
cleaned_data['action'] = buttons[0]
|
||||||
|
return cleaned_data
|
||||||
|
|||||||
@ -15,16 +15,13 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<form action="{{ request.path }}" method="post">
|
<form action="{{ request.path }}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ alias_boxes|safe }}
|
{{ list_form|bootstrap }}
|
||||||
|
|
||||||
<input type="hidden" name="form" value="Checkboxes">
|
|
||||||
|
|
||||||
|
<input type="hidden" name="form" value="list">
|
||||||
<input class="btn btn-secondary" type="submit" name="btn_disable"
|
<input class="btn btn-secondary" type="submit" name="btn_disable"
|
||||||
value="{% trans 'Disable selected' %}">
|
value="{% trans 'Disable selected' %}">
|
||||||
|
|
||||||
<input class="btn btn-secondary" type="submit" name="btn_enable"
|
<input class="btn btn-secondary" type="submit" name="btn_enable"
|
||||||
value="{% trans 'Enable selected' %}">
|
value="{% trans 'Enable selected' %}">
|
||||||
|
|
||||||
<input class="btn btn-danger" type="submit" name="btn_delete"
|
<input class="btn btn-danger" type="submit" name="btn_delete"
|
||||||
value="{% trans 'Delete selected' %}">
|
value="{% trans 'Delete selected' %}">
|
||||||
</form>
|
</form>
|
||||||
@ -34,10 +31,9 @@
|
|||||||
|
|
||||||
<form action="{{ request.path }}" method="post">
|
<form action="{{ request.path }}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|bootstrap }}
|
{{ create_form|bootstrap }}
|
||||||
<input type="hidden" name="form" value="AliasCreationForm">
|
<input type="hidden" name="form" value="create">
|
||||||
<input class="btn btn-primary" type="submit" name="btn_add"
|
<input class="btn btn-primary" type="submit" value="{% trans 'Add' %}">
|
||||||
value="{% trans 'Add' %}">
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -1,20 +1,23 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
import io
|
"""
|
||||||
import itertools
|
Views for the email app.
|
||||||
|
"""
|
||||||
import pwd
|
import pwd
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.http import HttpResponseBadRequest
|
from django.http import HttpResponseBadRequest
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.html import escape
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic.base import TemplateView, View
|
from django.views.generic.base import TemplateView, View
|
||||||
|
from django.views.generic.edit import FormView
|
||||||
|
|
||||||
import plinth.actions
|
import plinth.actions
|
||||||
import plinth.utils
|
import plinth.utils
|
||||||
from plinth.views import AppView, render_tabs
|
from plinth.views import AppView, render_tabs
|
||||||
|
|
||||||
from . import aliases, audit, forms
|
from . import aliases as aliases_module
|
||||||
|
from . import audit, forms
|
||||||
|
|
||||||
|
|
||||||
class TabMixin(View):
|
class TabMixin(View):
|
||||||
@ -125,131 +128,88 @@ class MyMailView(TemplateView):
|
|||||||
return self.render_to_response(self.get_context_data())
|
return self.render_to_response(self.get_context_data())
|
||||||
|
|
||||||
|
|
||||||
class AliasView(TemplateView):
|
class AliasView(FormView):
|
||||||
|
"""View to create, list, enable, disable and delete aliases.
|
||||||
class Checkboxes:
|
|
||||||
|
|
||||||
def __init__(self, post=None, initial=None):
|
|
||||||
self.models = initial
|
|
||||||
self.post = post
|
|
||||||
self.cleaned_data = {}
|
|
||||||
# HTML rendering
|
|
||||||
self.sb = io.StringIO()
|
|
||||||
self.counter = 0
|
|
||||||
|
|
||||||
def render(self):
|
|
||||||
if self.models is None:
|
|
||||||
raise RuntimeError('Uninitialized form')
|
|
||||||
if self.sb.tell() > 0:
|
|
||||||
raise RuntimeError('render has been called')
|
|
||||||
|
|
||||||
enabled = [a.email_name for a in self.models if a.enabled]
|
|
||||||
disabled = [a.email_name for a in self.models if not a.enabled]
|
|
||||||
|
|
||||||
self._render_fieldset(enabled, _('Enabled aliases'))
|
|
||||||
self._render_fieldset(disabled, _('Disabled aliases'))
|
|
||||||
|
|
||||||
return self.sb.getvalue()
|
|
||||||
|
|
||||||
def _render_fieldset(self, email_names, legend):
|
|
||||||
if len(email_names) > 0:
|
|
||||||
self.sb.write('<fieldset class="form-group">')
|
|
||||||
self.sb.write('<legend>%s</legend>' % escape(legend))
|
|
||||||
self._render_boxes(email_names)
|
|
||||||
self.sb.write('</fieldset>')
|
|
||||||
|
|
||||||
def _render_boxes(self, email_names):
|
|
||||||
for email_name in email_names:
|
|
||||||
input_id = 'cb_alias_%d' % self._count()
|
|
||||||
value = escape(email_name)
|
|
||||||
self.sb.write('<div class="form-check">')
|
|
||||||
|
|
||||||
self.sb.write('<input type="checkbox" name="alias" ')
|
|
||||||
self.sb.write('class="form-check-input" ')
|
|
||||||
self.sb.write('id="%s" value="%s">' % (input_id, value))
|
|
||||||
|
|
||||||
self.sb.write('<label class="form-check-label" ')
|
|
||||||
self.sb.write('for="%s">%s</label>' % (input_id, value))
|
|
||||||
|
|
||||||
self.sb.write('</div>')
|
|
||||||
|
|
||||||
def _count(self):
|
|
||||||
self.counter += 1
|
|
||||||
return self.counter
|
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
lst = list(filter(None, self.post.getlist('alias')))
|
|
||||||
if not lst:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
self.cleaned_data['alias'] = lst
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
This view has two forms. Form to list (and manage) existing aliases, and a
|
||||||
|
form to create a new aliases. When GET operation is used, both forms
|
||||||
|
created and template is rendered. When POST operation is used, the form
|
||||||
|
posted is detected using hidden form values and the appropriate form is
|
||||||
|
initialized for the FormView base class to work with.
|
||||||
|
"""
|
||||||
template_name = 'email_alias.html'
|
template_name = 'email_alias.html'
|
||||||
form_classes = (forms.AliasCreationForm, Checkboxes)
|
form_classes = (forms.AliasCreateForm, forms.AliasListForm)
|
||||||
|
success_url = reverse_lazy('email_server:aliases')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Initialize the view."""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.posted_form = None
|
||||||
|
|
||||||
|
def get_form_class(self):
|
||||||
|
"""Return form class to build."""
|
||||||
|
if self.posted_form == 'create':
|
||||||
|
return forms.AliasCreateForm
|
||||||
|
|
||||||
|
return forms.AliasListForm
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
"""Send aliases to list form."""
|
||||||
|
kwargs = super().get_form_kwargs()
|
||||||
|
if self.posted_form != 'create':
|
||||||
|
kwargs['aliases'] = self._get_current_aliases()
|
||||||
|
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def _get_uid(self):
|
||||||
|
"""Return the UID of the user that made the request."""
|
||||||
|
return pwd.getpwnam(self.request.user.username).pw_uid
|
||||||
|
|
||||||
|
def _get_current_aliases(self):
|
||||||
|
"""Return current list of aliases."""
|
||||||
|
return aliases_module.get(self._get_uid())
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
context['form'] = forms.AliasCreationForm()
|
if isinstance(context['form'], forms.AliasCreateForm):
|
||||||
|
context['create_form'] = context['form']
|
||||||
uid = pwd.getpwnam(self.request.user.username).pw_uid
|
aliases = self._get_current_aliases()
|
||||||
models = aliases.get(uid)
|
context['list_form'] = forms.AliasListForm(aliases)
|
||||||
if len(models) > 0:
|
|
||||||
form = AliasView.Checkboxes(initial=models)
|
|
||||||
context['alias_boxes'] = form.render()
|
|
||||||
else:
|
else:
|
||||||
context['no_alias'] = True
|
context['create_form'] = forms.AliasCreateForm()
|
||||||
|
context['list_form'] = context['form']
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def _find_form(self, post):
|
def post(self, request, *args, **kwargs):
|
||||||
form_name = post.get('form')
|
"""Find which form was submitted before proceeding."""
|
||||||
for cls in self.form_classes:
|
self.posted_form = request.POST.get('form')
|
||||||
if cls.__name__ == form_name:
|
return super().post(request, *args, **kwargs)
|
||||||
return cls(post)
|
|
||||||
raise ValidationError('Form was unspecified')
|
|
||||||
|
|
||||||
def _find_button(self, post):
|
def form_valid(self, form):
|
||||||
key_filter = (k for k in post.keys() if k.startswith('btn_'))
|
"""Handle a valid submission."""
|
||||||
lst = list(itertools.islice(key_filter, 2))
|
if isinstance(form, forms.AliasListForm):
|
||||||
if len(lst) != 1:
|
self._list_form_valid(form)
|
||||||
raise ValidationError('Bad post data')
|
elif isinstance(form, forms.AliasCreateForm):
|
||||||
if not isinstance(lst[0], str):
|
self._create_form_valid(form)
|
||||||
raise ValidationError('Bad post data')
|
|
||||||
return lst[0][len('btn_'):]
|
|
||||||
|
|
||||||
def post(self, request):
|
return super().form_valid(form)
|
||||||
form = self._find_form(request.POST)
|
|
||||||
button = self._find_button(request.POST)
|
|
||||||
if not form.is_valid():
|
|
||||||
raise ValidationError('Form invalid')
|
|
||||||
|
|
||||||
if isinstance(form, AliasView.Checkboxes):
|
def _list_form_valid(self, form):
|
||||||
if button not in ('delete', 'disable', 'enable'):
|
"""Handle a valid alias list form operation."""
|
||||||
raise ValidationError('Bad button')
|
alias_list = form.cleaned_data['aliases']
|
||||||
return self.alias_operation_form_valid(form, button)
|
action = form.cleaned_data['action']
|
||||||
|
uid = self._get_uid()
|
||||||
|
if action == 'delete':
|
||||||
|
aliases_module.delete(uid, alias_list)
|
||||||
|
elif action == 'disable':
|
||||||
|
aliases_module.set_disabled(uid, alias_list)
|
||||||
|
elif action == 'enable':
|
||||||
|
aliases_module.set_enabled(uid, alias_list)
|
||||||
|
|
||||||
if isinstance(form, forms.AliasCreationForm):
|
def _create_form_valid(self, form):
|
||||||
if button != 'add':
|
"""Handle a valid create alias form operation."""
|
||||||
raise ValidationError('Bad button')
|
aliases_module.put(self._get_uid(), form.cleaned_data['alias'])
|
||||||
return self.alias_creation_form_valid(form, button)
|
|
||||||
|
|
||||||
raise RuntimeError('Unknown form')
|
|
||||||
|
|
||||||
def alias_operation_form_valid(self, form, button):
|
|
||||||
uid = pwd.getpwnam(self.request.user.username).pw_uid
|
|
||||||
alias_list = form.cleaned_data['alias']
|
|
||||||
if button == 'delete':
|
|
||||||
aliases.delete(uid, alias_list)
|
|
||||||
elif button == 'disable':
|
|
||||||
aliases.set_disabled(uid, alias_list)
|
|
||||||
elif button == 'enable':
|
|
||||||
aliases.set_enabled(uid, alias_list)
|
|
||||||
return self.render_to_response(self.get_context_data())
|
|
||||||
|
|
||||||
def alias_creation_form_valid(self, form, button):
|
|
||||||
uid = pwd.getpwnam(self.request.user.username).pw_uid
|
|
||||||
aliases.put(uid, form.cleaned_data['email_name'])
|
|
||||||
return self.render_to_response(self.get_context_data())
|
|
||||||
|
|
||||||
|
|
||||||
class TLSView(TabMixin, TemplateView):
|
class TLSView(TabMixin, TemplateView):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user