email: Add templates for TLS and domains

This commit is contained in:
fliu 2021-07-21 01:47:07 +00:00 committed by Sunil Mohan Adapa
parent 62c501e9c7
commit 502cfa4953
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
6 changed files with 240 additions and 60 deletions

View File

@ -13,3 +13,15 @@ class EmailServerForm(forms.Form):
class AliasCreationForm(forms.Form):
email_name = forms.CharField(label=_('New alias (without @domain)'),
max_length=50)
class MailnameForm(forms.Form):
mailname = forms.CharField(label=_('New value'), max_length=256)
class MydomainForm(forms.Form):
mydomain = forms.CharField(label=_('New value'), max_length=256)
class MydestinationForm(forms.Form):
mydestination = forms.CharField(label=_('New value'), max_length=4000)

View File

@ -0,0 +1,46 @@
{# SPDX-License-Identifier: AGPL-3.0-or-later #}
{% extends "form_base.html" %}
{% load bootstrap %}
{% load i18n %}
{% block content %}
{{ block.super }}
<div class="form-group">
<strong>/etc/mailname</strong>
<span> = {{ mailname }}</span>
<form action="{{ request.path }}" method="post">
{% csrf_token %}
{{ mailname_form|bootstrap }}
<input type="hidden" name="form" value="MailnameForm">
<input class="btn btn-primary" type="submit" name="btn_mailname"
value="{% trans 'Change' %}">
</form>
</div>
<div class="form-group">
<strong>$mydomain</strong>
<span> = {{ mydomain }}</span>
<form action="{{ request.path }}" method="post">
{% csrf_token %}
{{ mydomain_form|bootstrap }}
<input type="hidden" name="form" value="MydomainForm">
<input class="btn btn-primary" type="submit" name="btn_mydomain"
value="{% trans 'Change' %}">
</form>
</div>
<div class="form-group">
<strong>$mydestination</strong>
<span> = {{ mydestination }}</span>
<form action="{{ request.path }}" method="post">
{% csrf_token %}
{{ mydestination_form|bootstrap }}
<input type="hidden" name="form" value="MydestinationForm">
<input class="btn btn-primary" type="submit" name="btn_mydestination"
value="{% trans 'Change' %}">
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
{# SPDX-License-Identifier: AGPL-3.0-or-later #}
{% extends "form_base.html" %}
{% load i18n %}
{% block content %}
{{ block.super }}
<form action="{{ request.path }}" method="post">
{% csrf_token %}
<fieldset>
<legend>{% trans "Postfix TLS" %}</legend>
{% include "tls_form.html" with prefix="postfix" proto="smtp" %}
</fieldset>
<fieldset>
<legend>{% trans "Dovecot TLS" %}</legend>
{% include "tls_form.html" with prefix="dovecot" proto="imap" %}
</fieldset>
<input class="btn btn-primary" type="submit"
name="btn_update" value="{% trans 'Update' %}">
</form>
{% endblock %}

View File

@ -0,0 +1,69 @@
{# SPDX-License-Identifier: AGPL-3.0-or-later #}
{% load i18n %}
<div class="form-check form-group">
<input class="form-check-input" type="radio" id="{{ prefix }}_unchanged"
name="{{ prefix }}" value="unchanged" checked="1">
<label class="form-check-label" for="{{ prefix }}_unchanged">
{% trans "Keep current settings" %}
</label>
</div>
<div class="form-check form-group">
<input class="form-check-input" type="radio" id="{{ prefix }}_le"
name="{{ prefix }}" value="le">
<label class="form-check-label" for="{{ prefix }}_le">
{% trans "Use Let's Encrypt" %}
</label>
<div class="form-group row pl-4">
<label for="{{ prefix }}_le_text" class="col-sm-3 col-form-label">
{% trans "Common name" %}
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="{{ prefix }}_le_text"
name="{{ prefix }}_le" placeholder="{{ proto }}.example.com">
</div>
</div>
</div>
<div class="form-check form-group">
<input class="form-check-input" type="radio" id="{{ prefix }}_custom"
name="{{ prefix }}" value="custom">
<label class="form-check-label" for="{{ prefix }}_custom">
{% trans "Use custom values" %}
</label>
<div class="form-group row pl-4">
<label for="{{ prefix }}_cert" class="col-sm-3 col-form-label">
{% trans "Certificate path" %}
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="{{ prefix }}_cert"
name="{{ prefix }}_cert" placeholder="/path/to/ssl.pem">
</div>
</div>
<div class="form-group row pl-4">
<label for="{{ prefix }}_private" class="col-sm-3 col-form-label">
{% trans "Private key path" %}
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="{{ prefix }}_private"
name="{{ prefix }}_private" placeholder="/path/to/ssl.key">
</div>
</div>
</div>
<div class="form-check form-group">
<input class="form-check-input" type="radio" id="{{ prefix }}_sys"
name="{{ prefix }}" value="sys">
<label class="form-check-label" for="{{ prefix }}_sys">
{% trans "Use system default" %}
</label>
</div>

View File

@ -7,6 +7,7 @@ from . import views
urlpatterns = [
path('apps/email_server/', views.EmailServerView.as_view(), name='index'),
path('apps/email_server/security', views.TLSView.as_view()),
path('apps/email_server/domains', views.DomainView.as_view()),
path('apps/email_server/my_mail',
non_admin_view(views.MyMailView.as_view()), name='my_mail'),

View File

@ -4,12 +4,12 @@ import itertools
import pwd
import plinth.utils
import plinth.views
from django.core.exceptions import ValidationError
from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView
from django.views.generic.base import TemplateView, View
from plinth.views import AppView
from . import aliases
from . import audit
@ -18,7 +18,9 @@ from . import forms
admin_tabs = [
('', _('Home')),
('my_mail', _('My Mail')),
('my_aliases', _('My Aliases'))
('my_aliases', _('My Aliases')),
('security', _('Security')),
('domains', _('Domains'))
]
user_tabs = [
@ -27,23 +29,74 @@ user_tabs = [
]
class EmailServerView(plinth.views.AppView):
class TabMixin(View):
def get_context_data(self, *args, **kwargs):
# Retrieve context data from the next method in the MRO
context = super().get_context_data(*args, **kwargs)
# Populate context with customized data
context['tabs'] = self.render_tabs()
return context
def render_tabs(self):
if plinth.utils.is_user_admin(self.request):
return self.__render_tabs(self.request.path, admin_tabs)
else:
return self.__render_tabs(self.request.path, user_tabs)
@staticmethod
def __render_tabs(path, tab_data):
sb = io.StringIO()
sb.write('<ul class="nav nav-tabs">')
for page_name, link_text in tab_data:
cls = 'active' if path.endswith('/' + page_name) else ''
href = '#' if cls == 'active' else ('./' + page_name)
# -- Begin list
sb.write('<li class="nav-item">')
# -- Begin link
sb.write('<a class="nav-link {}" '.format(cls))
sb.write('href="{}">'.format(escape(href)))
sb.write('{}</a>'.format(escape(link_text)))
# -- End link
sb.write('</li>')
# -- End list
sb.write('</ul>')
return sb.getvalue()
def render_validation_error(self, validation_error, status=400):
context = self.get_context_data()
context['error'] = validation_error
return self.render_to_response(context, status=status)
def find_button(self, post):
key_filter = (k for k in post.keys() if k.startswith('btn_'))
lst = list(itertools.islice(key_filter, 2))
if len(lst) != 1:
raise ValidationError('Bad post data')
if not isinstance(lst[0], str):
raise ValidationError('Bad post data')
return lst[0][len('btn_'):]
def find_form(self, post):
form_name = post.get('form')
for cls in self.form_classes:
if cls.__name__ == form_name:
return cls(post)
raise ValidationError('Form was unspecified')
class EmailServerView(TabMixin, AppView):
"""Server configuration page"""
app_id = 'email_server'
template_name = 'email_server.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['tabs'] = render_tabs(self.request)
return context
class MyMailView(TemplateView):
class MyMailView(TabMixin, TemplateView):
template_name = 'my_mail.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['tabs'] = render_tabs(self.request)
nam = self.request.user.username
context['has_homedir'] = audit.home.exists_nam(nam)
@ -54,22 +107,20 @@ class MyMailView(TemplateView):
try:
return self._post(request)
except ValidationError as validation_error:
context = self.get_context_data()
context['error'] = validation_error
return self.render_to_response(context, status=400)
return self.render_validation_error(validation_error)
except RuntimeError as runtime_error:
context = self.get_context_data()
context['error'] = [str(runtime_error)]
return self.render_to_response(context, status=500)
def _post(self, request):
if not 'btn_mkhome' in request.POST:
if 'btn_mkhome' not in request.POST:
raise ValidationError('Bad post data')
audit.home.put_nam(request.user.username)
return self.render_to_response(self.get_context_data())
class AliasView(TemplateView):
class AliasView(TabMixin, TemplateView):
class Checkboxes:
def __init__(self, post=None, initial=None):
self.models = initial
@ -133,7 +184,6 @@ class AliasView(TemplateView):
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['form'] = forms.AliasCreationForm()
context['tabs'] = render_tabs(self.request)
uid = pwd.getpwnam(self.request.user.username).pw_uid
models = aliases.get(uid)
@ -144,29 +194,11 @@ class AliasView(TemplateView):
context['no_alias'] = True
return context
def find_form(self, post):
form_name = post.get('form')
for cls in self.form_classes:
if cls.__name__ == form_name:
return cls(post)
raise ValidationError('Form was unspecified')
def find_button(self, post):
key_filter = (k for k in post.keys() if k.startswith('btn_'))
lst = list(itertools.islice(key_filter, 2))
if len(lst) != 1:
raise ValidationError('Bad post data')
if not isinstance(lst[0], str):
raise ValidationError('Bad post data')
return lst[0][len('btn_'):]
def post(self, request):
try:
return self._post(request)
except ValidationError as e:
context = self.get_context_data()
context['error'] = e
return self.render_to_response(context, status=400)
return self.render_validation_error(e)
def _post(self, request):
form = self.find_form(request.POST)
@ -203,30 +235,23 @@ class AliasView(TemplateView):
return self.render_to_response(self.get_context_data())
def render_tabs(request):
if plinth.utils.is_user_admin(request):
return _render_tabs(request, admin_tabs)
else:
return _render_tabs(request, user_tabs)
class TLSView(TabMixin, TemplateView):
template_name = 'security.html'
def _render_tabs(request, tab_data):
sb = io.StringIO()
sb.write('<ul class="nav nav-tabs">')
for page_name, link_text in tab_data:
if request.path.endswith('/' + page_name):
cls = 'active'
else:
cls = ''
if cls == 'active':
href = '#'
else:
href = escape('./' + page_name)
class DomainView(TabMixin, TemplateView):
template_name = 'domains.html'
form_classes = (forms.MailnameForm, forms.MydomainForm,
forms.MydestinationForm)
sb.write('<li class="nav-item">')
sb.write('<a class="nav-link {cls}" href="{href}">{text}</a>'.format(
cls=cls, href=href, text=escape(link_text)
))
sb.write('</li>')
sb.write('</ul>')
return sb.getvalue()
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['mailname'] = 'placeholder'
context['mydomain'] = 'placeholder.exmaple.com'
context['mydestination'] = '$mydomain, placeholder.example'
context['mailname_form'] = forms.MailnameForm()
context['mydomain_form'] = forms.MydomainForm()
context['mydestination_form'] = forms.MydestinationForm()
return context