Merge #1966
1966: AdminLTE3 optimizations & compression and caching r=mergify[bot] a=ghostwheel42 ## What type of PR? enhancement, bugfix ## What does this PR do? Optimization and cleanup of styles and javascript code for AdminLTE 3 Adds caching headers, gzip and robots.txt to nginx. ### Related issue(s) Makes #1800 even better. Thanks to `@DjVinnii` and `@Diman0` for the good work. Closes #1905 ## Prerequistes Before we can consider review and merge, please make sure the following list is done and checked. If an entry in not applicable, you can check it or remove it from the list. - [X] In case of feature or enhancement: documentation updated accordingly - [X] Unless it's docs or a minor change: add [changelog](https://mailu.io/master/contributors/workflow.html#changelog) entry file. Co-authored-by: Alexander Graf <ghostwheel42@users.noreply.github.com> Co-authored-by: Dimitri Huisman <diman@huisman.xyz>master
@ -1,23 +1,54 @@
|
||||
.select2-search--inline .select2-search__field:focus {
|
||||
border: none;
|
||||
/* mailu logo */
|
||||
.mailu-logo {
|
||||
opacity: .8;
|
||||
}
|
||||
.bg-mailu-logo {
|
||||
background-color: #2980b9!important;
|
||||
}
|
||||
|
||||
.sidebar h4 {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* user image */
|
||||
.div-circle {
|
||||
position: relative;
|
||||
width: 2.1rem;
|
||||
height: 2.1rem;
|
||||
opacity: .8;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.div-circle > i {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%)
|
||||
}
|
||||
|
||||
.sidebar-collapse .sidebar h4 {
|
||||
display: none !important;
|
||||
/* nice round preformatted configuration display */
|
||||
.pre-config {
|
||||
padding: 9px;
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: anywhere;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.logo a {
|
||||
color: #fff;
|
||||
/* fieldset */
|
||||
legend {
|
||||
font-size: inherit;
|
||||
}
|
||||
fieldset:disabled :not(legend) label {
|
||||
opacity: .5;
|
||||
}
|
||||
fieldset:disabled .form-control:disabled {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
padding: unset !important;
|
||||
/* fix animation for icons in menu text */
|
||||
.sidebar .nav-link p i {
|
||||
transition: margin-left .3s linear,opacity .3s ease,visibility .3s ease;
|
||||
}
|
||||
|
||||
/* fix select2 text color */
|
||||
.select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||||
color: black;
|
||||
}
|
||||
|
@ -1,17 +1,70 @@
|
||||
require('./app.css');
|
||||
|
||||
import 'admin-lte/plugins/select2/js/select2.js';
|
||||
import 'admin-lte/plugins/datatables/jquery.dataTables.js';
|
||||
import 'admin-lte/plugins/datatables-bs4/js/dataTables.bootstrap4.js';
|
||||
import 'admin-lte/plugins/datatables-responsive/js/dataTables.responsive.js';
|
||||
import 'admin-lte/plugins/datatables-responsive/js/responsive.bootstrap4.js';
|
||||
import logo from './mailu.png';
|
||||
import modules from "./*.json";
|
||||
|
||||
jQuery("document").ready(function() {
|
||||
jQuery(".mailselect").select2({
|
||||
// TODO: conditionally (or lazy) load select2 and dataTable
|
||||
$('document').ready(function() {
|
||||
|
||||
// intercept anchors with data-clicked attribute and open alternate location instead
|
||||
$('[data-clicked]').click(function(e) {
|
||||
e.preventDefault();
|
||||
window.location.href = $(this).data('clicked');
|
||||
});
|
||||
|
||||
// use post for language selection
|
||||
$('#mailu-languages > a').click(function(e) {
|
||||
e.preventDefault();
|
||||
$.post({
|
||||
url: $(this).attr('href'),
|
||||
success: function() {
|
||||
location.reload();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// allow en-/disabling of inputs in fieldset with checkbox in legend
|
||||
$('fieldset legend input[type=checkbox]').change(function() {
|
||||
var fieldset = $(this).parents('fieldset');
|
||||
if (this.checked) {
|
||||
fieldset.removeAttr('disabled');
|
||||
fieldset.find('input').not(this).removeAttr('disabled');
|
||||
} else {
|
||||
fieldset.attr('disabled', '');
|
||||
fieldset.find('input').not(this).attr('disabled', '');
|
||||
}
|
||||
});
|
||||
|
||||
// display of range input value
|
||||
$('input[type=range]').each(function() {
|
||||
var value_element = $('#'+this.id+'_value');
|
||||
if (value_element.length) {
|
||||
value_element = $(value_element[0]);
|
||||
var infinity = $(this).data('infinity');
|
||||
var step = $(this).attr('step');
|
||||
$(this).on('input', function() {
|
||||
value_element.text((infinity && this.value == 0) ? '∞' : this.value/step);
|
||||
}).trigger('input');
|
||||
}
|
||||
});
|
||||
|
||||
// init select2
|
||||
$('.mailselect').select2({
|
||||
tags: true,
|
||||
tokenSeparators: [',', ' ']
|
||||
tokenSeparators: [',', ' '],
|
||||
});
|
||||
jQuery(".dataTable").DataTable({
|
||||
"responsive": true,
|
||||
|
||||
// init dataTable
|
||||
var d = $(document.documentElement);
|
||||
$('.dataTable').DataTable({
|
||||
'responsive': true,
|
||||
language: {
|
||||
url: d.data('static') + d.attr('lang') + '.json',
|
||||
},
|
||||
});
|
||||
|
||||
// init clipboard.js
|
||||
new ClipboardJS('.btn-clip');
|
||||
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
@ -1,22 +1,24 @@
|
||||
// jQuery
|
||||
import jQuery from 'jquery';
|
||||
import 'admin-lte/plugins/select2/css/select2.css';
|
||||
|
||||
// bootstrap
|
||||
// import 'bootstrap/less/bootstrap.less';
|
||||
// import 'bootstrap';
|
||||
|
||||
// FontAwesome
|
||||
import 'admin-lte/plugins/fontawesome-free/css/fontawesome.css';
|
||||
import 'admin-lte/plugins/fontawesome-free/css/regular.css';
|
||||
import 'admin-lte/plugins/fontawesome-free/css/solid.css';
|
||||
|
||||
// AdminLTE
|
||||
import 'admin-lte/plugins/jquery/jquery.min.js';
|
||||
import 'admin-lte/plugins/bootstrap/js/bootstrap.bundle.min.js';
|
||||
import 'admin-lte/build/scss/adminlte.scss';
|
||||
import 'admin-lte/plugins/datatables-bs4/css/dataTables.bootstrap4.css';
|
||||
import 'admin-lte/plugins/datatables-responsive/css/responsive.bootstrap4.css';
|
||||
import 'admin-lte/plugins/bootstrap/js/bootstrap.js';
|
||||
import 'admin-lte/build/js/AdminLTE.js';
|
||||
import 'admin-lte/build/js/Layout.js';
|
||||
import 'admin-lte/build/js/ControlSidebar.js';
|
||||
import 'admin-lte/build/js/PushMenu.js';
|
||||
|
||||
// fontawesome plugin
|
||||
import 'admin-lte/plugins/fontawesome-free/css/all.min.css';
|
||||
|
||||
// select2 plugin
|
||||
import 'admin-lte/plugins/select2/css/select2.min.css';
|
||||
import 'admin-lte/plugins/select2/js/select2.min.js';
|
||||
|
||||
// dataTables plugin
|
||||
import 'admin-lte/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css';
|
||||
import 'admin-lte/plugins/datatables-responsive/css/responsive.bootstrap4.min.css';
|
||||
import 'admin-lte/plugins/datatables/jquery.dataTables.min.js';
|
||||
import 'admin-lte/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js';
|
||||
import 'admin-lte/plugins/datatables-responsive/js/dataTables.responsive.min.js';
|
||||
import 'admin-lte/plugins/datatables-responsive/js/responsive.bootstrap4.min.js';
|
||||
|
||||
// clipboard.js
|
||||
import 'clipboard/dist/clipboard.min.js';
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Add a global administrator{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.card() %}
|
||||
{%- block content %}
|
||||
{%- call macros.card() %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ macros.form_field(form.admin, class_='mailselect') }}
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% extends "alias/create.html" %}
|
||||
{%- extends "alias/create.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Edit alias{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ alias }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% extends "form.html" %}
|
||||
{%- extends "form.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Create alternative domain{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ domain }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,16 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Public announcement{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.card() %}
|
||||
{%- block content %}
|
||||
{%- call macros.card() %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ macros.form_field(form.announcement_subject) }}
|
||||
{{ macros.form_field(form.announcement_body, rows=10) }}
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -0,0 +1,15 @@
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{%- block title %}
|
||||
{% trans %}Antispam{% endtrans %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block subtitle %}
|
||||
{% trans %}RSPAMD status page{% endtrans %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block content %}
|
||||
<div class="embed-responsive embed-responsive-1by1">
|
||||
<iframe class="embed-responsive-item" src="{{ config["WEB_ADMIN"] }}/antispam/"></iframe>
|
||||
</div>
|
||||
{%- endblock %}
|
@ -1,16 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Confirm action{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ action }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.card(theme="warning") %}
|
||||
{%- block content %}
|
||||
{%- call macros.card(theme="warning") %}
|
||||
<p>{% trans action %}You are about to {{ action }}. Please confirm your action.{% endtrans %}</p>
|
||||
{{ macros.form(form) }}
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -1,14 +1,14 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Docker error{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ action }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{%- block content %}
|
||||
<p>{% trans action %}An error occurred while talking to the Docker server.{% endtrans %}</p>
|
||||
<pre>{{ error }}</pre>
|
||||
{% endblock %}
|
||||
<pre class="pre-config border bg-light">{{ error }}</pre>
|
||||
{%- endblock %}
|
||||
|
@ -1,21 +1,20 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}New domain{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.card() %}
|
||||
{%- block content %}
|
||||
{%- call macros.card() %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ macros.form_field(form.name) }}
|
||||
{{ macros.form_fields((form.max_users, form.max_aliases)) }}
|
||||
{{ macros.form_field(form.max_quota_bytes, step=1000000000, max=50000000000,
|
||||
prepend='<span class="input-group-text"><span id="quota">'+((form.max_quota_bytes.data//1000000000).__str__() if form.max_quota_bytes.data else '∞')+'</span> GiB</span>',
|
||||
oninput='$("#quota").text(this.value == 0 ? "∞" : this.value/1000000000);') }}
|
||||
{{ macros.form_field(form.max_quota_bytes, step=10**9, max=50*10**9, data_infinity="true",
|
||||
prepend='<span class="input-group-text"><span id="max_quota_bytes_value"></span> GB</span>') }}
|
||||
{{ macros.form_field(form.signup_enabled) }}
|
||||
{{ macros.form_field(form.comment) }}
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -1,66 +1,69 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Domain details{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ domain.name }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block main_action %}
|
||||
{% if current_user.global_admin %}
|
||||
{%- block main_action %}
|
||||
{%- if current_user.global_admin %}
|
||||
<a class="btn btn-primary float-right" href="{{ url_for(".domain_genkeys", domain_name=domain.name) }}">
|
||||
{% if domain.dkim_publickey %}
|
||||
{%- if domain.dkim_publickey %}
|
||||
{% trans %}Regenerate keys{% endtrans %}
|
||||
{% else %}
|
||||
{%- else %}
|
||||
{% trans %}Generate keys{% endtrans %}
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.table(datatable=False) %}
|
||||
{% set hostname = config["HOSTNAMES"].split(",")[0] %}
|
||||
{%- block content %}
|
||||
{%- call macros.table(datatable=False) %}
|
||||
<tr>
|
||||
<th>{% trans %}Domain name{% endtrans %}</th>
|
||||
<td>{{ domain.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans %}DNS MX entry{% endtrans %} <i class="fa {{ 'fa-check-circle' if domain.check_mx() else 'fa-exclamation-circle' }}"></i></th>
|
||||
<td><pre>{{ domain.name }}. 600 IN MX 10 {{ hostname }}.</pre></td>
|
||||
<th>{% trans %}DNS MX entry{% endtrans %} <i class="fa {{ 'fa-check-circle text-success' if domain.check_mx() else 'fa-exclamation-circle text-danger' }}"></i></th>
|
||||
<td>{{ macros.clip("dns_mx") }}<pre id="dns_mx" class="pre-config border bg-light">{{ domain.dns_mx }}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans %}DNS SPF entries{% endtrans %}</th>
|
||||
<td><pre>
|
||||
{{ domain.name }}. 600 IN TXT "v=spf1 mx a:{{ hostname }} -all"</pre></td>
|
||||
<td>{{ macros.clip("dns_spf") }}<pre id="dns_spf" class="pre-config border bg-light">{{ domain.dns_spf }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
{% if domain.dkim_publickey %}
|
||||
{%- if domain.dkim_publickey %}
|
||||
<tr>
|
||||
<th>{% trans %}DKIM public key{% endtrans %}</th>
|
||||
<td><pre style="white-space: pre-wrap; word-wrap: break-word;">{{ domain.dkim_publickey }}</pre></td>
|
||||
<td>{{ macros.clip("dkim_key") }}<pre id="dkim_key" class="pre-config border bg-light">{{ domain.dkim_publickey }}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans %}DNS DKIM entry{% endtrans %}</th>
|
||||
<td><pre style="white-space: pre-wrap; word-wrap: break-word;">{{ config["DKIM_SELECTOR"] }}._domainkey.{{ domain.name }}. 600 IN TXT "v=DKIM1; k=rsa; p={{ domain.dkim_publickey }}"</pre></td>
|
||||
<td>{{ macros.clip("dns_dkim") }}<pre id="dns_dkim" class="pre-config border bg-light">{{ domain.dns_dkim }}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans %}DNS DMARC entry{% endtrans %}</th>
|
||||
<td><pre>_dmarc.{{ domain.name }}. 600 IN TXT "v=DMARC1; p=reject;{% if config["DMARC_RUA"] %} rua=mailto:{{ config["DMARC_RUA"] }}@{{ config["DOMAIN"] }};{% endif %}{% if config["DMARC_RUF"] %} ruf=mailto:{{ config["DMARC_RUF"] }}@{{ config["DOMAIN"] }};{% endif %} adkim=s; aspf=s"</pre></td>
|
||||
<td>{{ macros.clip("dns_dmark") }}<pre id="dns_dmark" class="pre-config border bg-light">{{ domain.dns_dmarc }}</pre></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
{%- set tlsa_record=domain.dns_tlsa %}
|
||||
{%- if tlsa_record %}
|
||||
<tr>
|
||||
<th>{% trans %}DNS TLSA entry{% endtrans %}</br><span class="text-secondary text-xs font-weight-normal">Let's Encrypt</br>ISRG Root X1</span></th>
|
||||
<td>{{ macros.clip("dns_tlsa") }}<pre id="dns_tlsa" class="pre-config border bg-light">{{ tlsa_record }}</pre></td>
|
||||
</tr>
|
||||
{%- endif %}
|
||||
<tr>
|
||||
<th>{% trans %}DNS client auto-configuration (RFC6186) entries{% endtrans %}</th>
|
||||
<td>
|
||||
<pre style="white-space: pre-wrap; word-wrap: break-word;">_submission._tcp.{{ domain.name }}. 600 IN SRV 1 1 587 {{ config["HOSTNAMES"].split(',')[0] }}.</pre>
|
||||
<pre style="white-space: pre-wrap; word-wrap: break-word;">_imap._tcp.{{ domain.name }}. 600 IN SRV 100 1 143 {{ config["HOSTNAMES"].split(',')[0] }}.</pre>
|
||||
<pre style="white-space: pre-wrap; word-wrap: break-word;">_pop3._tcp.{{ domain.name }}. 600 IN SRV 100 1 110 {{ config["HOSTNAMES"].split(',')[0] }}.</pre>
|
||||
{% if config["TLS_FLAVOR"] != "notls" %}
|
||||
<pre style="white-space: pre-wrap; word-wrap: break-word;">_submissions._tcp.{{ domain.name }}. 600 IN SRV 10 1 465 {{ config["HOSTNAMES"].split(',')[0] }}.</pre>
|
||||
<pre style="white-space: pre-wrap; word-wrap: break-word;">_imaps._tcp.{{ domain.name }}. 600 IN SRV 10 1 993 {{ config["HOSTNAMES"].split(',')[0] }}.</pre>
|
||||
<pre style="white-space: pre-wrap; word-wrap: break-word;">_pop3s._tcp.{{ domain.name }}. 600 IN SRV 10 1 995 {{ config["HOSTNAMES"].split(',')[0] }}.</pre>
|
||||
{% endif %}</td>
|
||||
{{ macros.clip("dns_autoconfig") }}<pre id="dns_autoconfig" class="pre-config border bg-light">
|
||||
{%- for line in domain.dns_autoconfig %}
|
||||
{{ line }}
|
||||
{%- endfor -%}
|
||||
</pre></td>
|
||||
</tr>
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% extends "domain/create.html" %}
|
||||
{%- extends "domain/create.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Edit domain{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ domain }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,31 +1,31 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Add a fetched account{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ user }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{%- block content %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{% call macros.card(title="Remote server") %}
|
||||
{%- call macros.card(title="Remote server") %}
|
||||
{{ macros.form_field(form.protocol) }}
|
||||
{{ macros.form_fields((form.host, form.port)) }}
|
||||
{{ macros.form_field(form.tls) }}
|
||||
{% endcall %}
|
||||
{%- endcall %}
|
||||
|
||||
{% call macros.card(title="Authentication") %}
|
||||
{%- call macros.card(title="Authentication") %}
|
||||
{{ macros.form_field(form.username) }}
|
||||
{{ macros.form_field(form.password) }}
|
||||
{% endcall %}
|
||||
{%- endcall %}
|
||||
|
||||
{% call macros.card(title="Settings") %}
|
||||
{%- call macros.card(title="Settings") %}
|
||||
{{ macros.form_field(form.keep) }}
|
||||
{% endcall %}
|
||||
{%- endcall %}
|
||||
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% extends "fetch/create.html" %}
|
||||
{%- extends "fetch/create.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Update a fetched account{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ user }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.card() %}
|
||||
{%- block content %}
|
||||
{%- call macros.card() %}
|
||||
{{ macros.form(form) }}
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% extends "form.html" %}
|
||||
{%- extends "form.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Sign in{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{% trans %}to access the administration tools{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,104 +1,127 @@
|
||||
{% macro form_errors(form) %}
|
||||
{% if form.errors %}
|
||||
{% for fieldname, errors in form.errors.items() %}
|
||||
{% if bootstrap_is_hidden_field(form[fieldname]) %}
|
||||
{% for error in errors %}
|
||||
{%- macro form_errors(form) %}
|
||||
{%- if form.errors %}
|
||||
{%- for fieldname, errors in form.errors.items() %}
|
||||
{%- if bootstrap_is_hidden_field(form[fieldname]) %}
|
||||
{%- for error in errors %}
|
||||
<p class="error">{{error}}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro form_field_errors(field) %}
|
||||
{% if field.errors %}
|
||||
{% for error in field.errors %}
|
||||
{%- macro form_field_errors(field) %}
|
||||
{%- if field.errors %}
|
||||
{%- for error in field.errors %}
|
||||
<p class="help-block inline">{{ error }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro form_fields(fields, prepend='', append='', label=True) %}
|
||||
{% set width = (12 / fields|length)|int %}
|
||||
{%- macro form_fields(fields, prepend='', append='', label=True) %}
|
||||
{%- set width = (12 / fields|length)|int %}
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
{% for field in fields %}
|
||||
{%- for field in fields %}
|
||||
<div class="col-lg-{{ width }} col-xs-12 {{ 'has-error' if field.errors else '' }}">
|
||||
{{ form_individual_field(field, prepend=prepend, append=append, label=label, **kwargs) }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{%- endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro form_individual_field(field, prepend='', append='', label=True, class_="") %}
|
||||
{% if field.type == "BooleanField" %}
|
||||
{{ field(**kwargs) }}<span> </span>
|
||||
{{ field.label if label else '' }}
|
||||
{% else %}
|
||||
{%- macro form_individual_field(field, prepend='', append='', label=True, class_="") %}
|
||||
{%- if field.type == "BooleanField" %}
|
||||
{{ field(**kwargs) }}<span> </span>{{ field.label if label else '' }}
|
||||
{%- else %}
|
||||
{{ field.label if label else '' }}{{ form_field_errors(field) }}
|
||||
{% if prepend %}<div class="input-group-prepend">{% endif %}
|
||||
{% if append %}<div class="input-group-append">{% endif %}
|
||||
{{ prepend|safe }}{{ field(class_="form-control " + class_, **kwargs) }}{{ append|safe }}
|
||||
{% if prepend or append %}</div>{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{%- if prepend %}<div class="input-group-prepend">{%- elif append %}<div class="input-group-append">{%- endif %}
|
||||
{{ prepend|safe }}{{ field(class_=("form-control " + class_) if class_ else "form-control", **kwargs) }}{{ append|safe }}
|
||||
{%- if prepend or append %}</div>{%- endif %}
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro form_field(field) %}
|
||||
{% if field.type == 'SubmitField' %}
|
||||
{{ form_fields((field,), label=False, class="btn btn-default", **kwargs) }}
|
||||
{% else %}
|
||||
{{ form_fields((field,), **kwargs) }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{%- macro form_field(field) %}
|
||||
{%- if field.type == 'SubmitField' %}
|
||||
{{- form_fields((field,), label=False, class="btn btn-default", **kwargs) }}
|
||||
{%- else %}
|
||||
{{- form_fields((field,), **kwargs) }}
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro form(form) %}
|
||||
{%- macro form(form) %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{% for field in form %}
|
||||
{% if bootstrap_is_hidden_field(field) %}
|
||||
{%- for field in form %}
|
||||
{%- if bootstrap_is_hidden_field(field) %}
|
||||
{{ field() }}
|
||||
{% else %}
|
||||
{%- else %}
|
||||
{{ form_field(field) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</form>
|
||||
{% endmacro %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro card(title=None, theme="primary", header=True) %}
|
||||
{%- macro card(title=None, theme="primary", header=True) %}
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-outline card-{{ theme }}">
|
||||
{% if header %}
|
||||
{%- if header %}
|
||||
<div class="card-header border-0">
|
||||
{% if title %}
|
||||
{%- if title %}
|
||||
<h3 class="card-title">{{ title }}</h3>
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
<div class="card-body">
|
||||
{{ caller() }}
|
||||
{{- caller() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro table(title=None, theme="primary", datatable=True) %}
|
||||
{%- macro table(title=None, theme="primary", datatable=True) %}
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-outline card-{{ theme }}">
|
||||
{%- if title %}
|
||||
<div class="card-header border-0">
|
||||
{% if title %}
|
||||
<h3 class="card-title">{{ title }}</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endif %}
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered{% if datatable %} dataTable{% endif %}">
|
||||
{{ caller() }}
|
||||
{{- caller() }}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro fieldset(title=None, field=None, enabled=None, fields=None) %}
|
||||
{%- if field or title %}
|
||||
<fieldset{% if not enabled %} disabled{% endif %}>
|
||||
{%- if field %}
|
||||
<legend>{{ form_individual_field(field) }}</legend>
|
||||
{%- else %}
|
||||
<legend>{{ title }}</legend>
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{{- caller() }}
|
||||
{%- if fields %}
|
||||
{%- set kwargs = {"enabled" if enabled else "disabled": ""} %}
|
||||
{%- for field in fields %}
|
||||
{{ form_field(field, **kwargs) }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
</fieldset>
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro clip(target, title=_("copy to clipboard"), icon="copy", color="primary", action="copy") %}
|
||||
<button class="btn btn-{{ color }} btn-xs btn-clip float-right ml-2 mt-1" data-clipboard-action="{{ action }}" data-clipboard-target="#{{ target }}">
|
||||
<i class="fas fa-{{ icon }}" title="{{ title }}" aria-expanded="false"></i><span class="sr-only">{{ title }}</span>
|
||||
</button>
|
||||
{%- endmacro %}
|
||||
|
@ -1,19 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Add a manager{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ domain }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.card() %}
|
||||
{%- block content %}
|
||||
{%- call macros.card() %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ macros.form_field(form.manager, class_='mailselect') }}
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% extends "form.html" %}
|
||||
{%- extends "form.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}New relay domain{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% extends "form.html" %}
|
||||
{%- extends "form.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Edit relayd domain{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ relay }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,144 +1,156 @@
|
||||
<div class="sidebar">
|
||||
{% if current_user.is_authenticated %}
|
||||
<div class="sidebar text-sm">
|
||||
{%- if current_user.is_authenticated %}
|
||||
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
|
||||
<div class="image">
|
||||
<div class="div-circle elevation-2"><i class="fa fa-user text-lg text-dark"></i></div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span class="text-center text-primary">{{ current_user }}</span>
|
||||
<a href="{{ url_for('.user_settings') }}" class="d-block">{{ current_user }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{%- endif %}
|
||||
<nav class="mt-2">
|
||||
<ul class="nav nav-pills nav-sidebar flex-column" role="menu">
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="nav-header">{% trans %}My account{% endtrans %}</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.user_settings') }}" class="nav-link">
|
||||
{%- if current_user.is_authenticated %}
|
||||
<li class="nav-header text-uppercase text-primary" role="none">{% trans %}My account{% endtrans %}</li>
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.user_settings') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-wrench"></i>
|
||||
<p class="text">{% trans %}Settings{% endtrans %}</p>
|
||||
<p>{% trans %}Settings{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.user_password') }}" class="nav-link">
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.user_password') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-lock"></i>
|
||||
<p class="text">{% trans %}Update password{% endtrans %}</p>
|
||||
<p>{% trans %}Update password{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.user_reply') }}" class="nav-link">
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.user_reply') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-plane"></i>
|
||||
<p class="text">{% trans %}Auto-reply{% endtrans %}</p>
|
||||
<p>{% trans %}Auto-reply{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.fetch_list') }}" class="nav-link">
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.fetch_list') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fas fa-download"></i>
|
||||
<p class="text">{% trans %}Fetched accounts{% endtrans %}</p>
|
||||
<p>{% trans %}Fetched accounts{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.token_list') }}" class="nav-link">
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.token_list') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fas fa-ticket-alt"></i>
|
||||
<p class="text">{% trans %}Authentication tokens{% endtrans %}</p>
|
||||
<p>{% trans %}Authentication tokens{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if current_user.manager_of or current_user.global_admin %}
|
||||
<li class="nav-header">{% trans %}Administration{% endtrans %}</li>
|
||||
{% endif %}
|
||||
{% if current_user.global_admin %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.announcement') }}" class="nav-link">
|
||||
<i class="nav-icon fa fa-bullhorn"></i>
|
||||
<p class="text">{% trans %}Announcement{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.admin_list') }}" class="nav-link">
|
||||
<i class="nav-icon fa fa-user"></i>
|
||||
<p class="text">{% trans %}Administrators{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.relay_list') }}" class="nav-link">
|
||||
<i class="nav-icon fa fa-reply-all"></i>
|
||||
<p class="text">{% trans %}Relayed domains{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ config["WEB_ADMIN"] }}/antispam/" target="_blank" class="nav-link">
|
||||
<i class="nav-icon fas fa-trash-alt"></i>
|
||||
<p class="text">{% trans %}Antispam{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if current_user.manager_of or current_user.global_admin %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.domain_list') }}" class="nav-link">
|
||||
<i class="nav-icon fa fa-envelope"></i>
|
||||
<p class="text">{% trans %}Mail domains{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<li class="nav-header">{% trans %}Go to{% endtrans %}</li>
|
||||
{% if config["WEBMAIL"] != "none" %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ config["WEB_WEBMAIL"] }}" target="_blank" class="nav-link">
|
||||
<i class="nav-icon far fa-envelope"></i>
|
||||
<p class="text">{% trans %}Webmail{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.client') }}" class="nav-link">
|
||||
{%- if current_user.is_authenticated %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.client') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-laptop"></i>
|
||||
<p class="text">{% trans %}Client setup{% endtrans %}</p>
|
||||
<p>{% trans %}Client setup{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ config["WEBSITE"] }}" target="_blank" class="nav-link">
|
||||
{%- endif %}
|
||||
|
||||
{%- if current_user.manager_of or current_user.global_admin %}
|
||||
<li class="nav-header text-uppercase text-primary" role="none">{% trans %}Administration{% endtrans %}</li>
|
||||
{%- endif %}
|
||||
{%- if current_user.global_admin %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.announcement') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-bullhorn"></i>
|
||||
<p>{% trans %}Announcement{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.admin_list') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-user"></i>
|
||||
<p>{% trans %}Administrators{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.relay_list') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-reply-all"></i>
|
||||
<p>{% trans %}Relayed domains{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ config["WEB_ADMIN"] }}/antispam/" data-clicked="{{ url_for('.antispam') }}" target="_blank" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fas fa-trash-alt"></i>
|
||||
<p>{% trans %}Antispam{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- if current_user.manager_of or current_user.global_admin %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.domain_list') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-envelope"></i>
|
||||
<p>{% trans %}Mail domains{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
<li class="nav-header text-uppercase text-primary" role="none">{% trans %}Go to{% endtrans %}</li>
|
||||
{%- if config["WEBMAIL"] != "none" %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ config["WEB_WEBMAIL"] }}" target="_blank" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon far fa-envelope"></i>
|
||||
<p>{% trans %}Webmail{% endtrans %} <i class="fas fa-external-link-alt text-xs"></i></p>
|
||||
</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- if not current_user.is_authenticated %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.client') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-laptop"></i>
|
||||
<p>{% trans %}Client setup{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{%- endif %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ config["WEBSITE"] }}" target="_blank" class="nav-link" role="menuitem" rel="noreferrer">
|
||||
<i class="nav-icon fa fa-globe"></i>
|
||||
<p class="text">{% trans %}Website{% endtrans %}</p>
|
||||
<p>{% trans %}Website{% endtrans %} <i class="fas fa-external-link-alt text-xs"></i></p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="https://mailu.io" target="_blank" class="nav-link">
|
||||
<li class="nav-item" role="none">
|
||||
<a href="https://mailu.io" target="_blank" class="nav-link" role="menuitem" rel="noreferrer">
|
||||
<i class="nav-icon fa fa-life-ring"></i>
|
||||
<p class="text">{% trans %}Help{% endtrans %}</p>
|
||||
<p>{% trans %}Help{% endtrans %} <i class="fas fa-external-link-alt text-xs"></i></p>
|
||||
</a>
|
||||
</li>
|
||||
{% if config['DOMAIN_REGISTRATION'] %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.domain_signup') }}" class="nav-link">
|
||||
{%- if config['DOMAIN_REGISTRATION'] %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.domain_signup') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-plus-square"></i>
|
||||
<p class="text">{% trans %}Register a domain{% endtrans %}</p>
|
||||
<p>{% trans %}Register a domain{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.logout') }}" class="nav-link">
|
||||
{%- endif %}
|
||||
{%- if current_user.is_authenticated %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.logout') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fas fa-sign-out-alt"></i>
|
||||
<p class="text">{% trans %}Sign out{% endtrans %}</p>
|
||||
<p>{% trans %}Sign out{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.login') }}" class="nav-link">
|
||||
{%- else %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.login') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fas fa-sign-in-alt"></i>
|
||||
<p class="text">{% trans %}Sign in{% endtrans %}</p>
|
||||
<p>{% trans %}Sign in{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{% if signup_domains %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('.user_signup') }}" class="nav-link">
|
||||
{%- if signup_domains %}
|
||||
<li class="nav-item" role="none">
|
||||
<a href="{{ url_for('.user_signup') }}" class="nav-link" role="menuitem">
|
||||
<i class="nav-icon fa fa-user-plus"></i>
|
||||
<p class="text">{% trans %}Sign up{% endtrans %}</p>
|
||||
<p>{% trans %}Sign up{% endtrans %}</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% extends "form.html" %}
|
||||
{%- extends "form.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Create an authentication token{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ user }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,33 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}New user{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ domain.name }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{%- block content %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{% call macros.card(_("General")) %}
|
||||
{%- call macros.card(_("General")) %}
|
||||
{{ macros.form_field(form.localpart, append='<span class="input-group-text">@'+domain.name+'</span>') }}
|
||||
{{ macros.form_fields((form.pw, form.pw2)) }}
|
||||
{{ macros.form_field(form.displayed_name) }}
|
||||
{{ macros.form_field(form.comment) }}
|
||||
{{ macros.form_field(form.enabled) }}
|
||||
{% endcall %}
|
||||
{%- endcall %}
|
||||
|
||||
{% call macros.card(_("Features and quotas"), theme="success") %}
|
||||
{{ macros.form_field(form.quota_bytes, step=1000000000, max=(max_quota_bytes or domain.max_quota_bytes or 50000000000),
|
||||
prepend='<span class="input-group-text"><span id="quota">'+((form.quota_bytes.data//1000000000).__str__() if form.quota_bytes.data else '∞')+'</span> GiB</span>',
|
||||
oninput='$("#quota").text(this.value == 0 ? "∞" : this.value/1000000000);') }}
|
||||
{%- call macros.card(_("Features and quotas"), theme="success") %}
|
||||
{{ macros.form_field(form.quota_bytes, step=1000000000, max=(max_quota_bytes or domain.max_quota_bytes or 50*10**9), data_infinity="true",
|
||||
prepend='<span class="input-group-text"><span id="quota_bytes_value"></span> GB</span>') }}
|
||||
{{ macros.form_field(form.enable_imap) }}
|
||||
{{ macros.form_field(form.enable_pop) }}
|
||||
{% endcall %}
|
||||
{%- endcall %}
|
||||
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% extends "user/create.html" %}
|
||||
{%- extends "user/create.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Edit user{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ user }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,25 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{% trans %}Forward emails{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{{ user }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.card() %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ macros.form_field(form.forward_enabled,
|
||||
onchange="if(this.checked){$('#forward_destination,#forward_keep').removeAttr('disabled')}
|
||||
else{$('#forward_destination,#forward_keep').attr('disabled', '')}") }}
|
||||
{{ macros.form_field(form.forward_keep,
|
||||
**{("enabled" if user.forward_enabled else "disabled"): ""}) }}
|
||||
{{ macros.form_field(form.forward_destination,
|
||||
**{("enabled" if user.forward_enabled else "disabled"): ""}) }}
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endcall %}
|
||||
{% endblock %}
|
@ -1,9 +1,9 @@
|
||||
{% extends "form.html" %}
|
||||
{%- extends "form.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Password update{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ user }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,30 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Automatic reply{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ user }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.card() %}
|
||||
{%- block content %}
|
||||
{%- call macros.card() %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ macros.form_field(form.reply_enabled,
|
||||
onchange="if(this.checked){$('#reply_subject,#reply_body,#reply_enddate,#reply_startdate').removeAttr('readonly')}
|
||||
else{$('#reply_subject,#reply_body,#reply_enddate,#reply_startdate').attr('readonly', '')}") }}
|
||||
{{ macros.form_field(form.reply_subject,
|
||||
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
||||
{{ macros.form_field(form.reply_body, rows=10,
|
||||
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
||||
{{ macros.form_field(form.reply_enddate,
|
||||
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
||||
{{ macros.form_field(form.reply_startdate,
|
||||
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
||||
|
||||
{%- call macros.fieldset(
|
||||
field=form.reply_enabled,
|
||||
enabled=user.reply_enabled,
|
||||
fields=[form.reply_subject, form.reply_body, form.reply_enddate, form.reply_startdate]) %}
|
||||
{%- endcall %}
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -1,38 +1,36 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}User settings{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ user }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{%- block content %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{% call macros.card(title=_("Displayed name")) %}
|
||||
{%- call macros.card(title=_("Displayed name")) %}
|
||||
{{ macros.form_field(form.displayed_name) }}
|
||||
{% endcall %}
|
||||
{%- endcall %}
|
||||
|
||||
{% call macros.card(title=_("Antispam")) %}
|
||||
{{ macros.form_field(form.spam_enabled) }}
|
||||
{%- call macros.card(title=_("Antispam")) %}
|
||||
{%- call macros.fieldset(field=form.spam_enabled, enabled=user.spam_enabled) %}
|
||||
{{ macros.form_field(form.spam_threshold, step=1, max=100,
|
||||
prepend='<span class="input-group-text"><span id="threshold">'+form.spam_threshold.data.__str__()+'</span> / 100</span>',
|
||||
oninput='$("#threshold").text(this.value);') }}
|
||||
{% endcall %}
|
||||
prepend='<span class="input-group-text"><span id="spam_threshold_value"></span> / 100</span>') }}
|
||||
{%- endcall %}
|
||||
{%- endcall %}
|
||||
|
||||
{% call macros.card(title=_("Auto-forward")) %}
|
||||
{{ macros.form_field(form.forward_enabled,
|
||||
onchange="if(this.checked){$('#forward_destination,#forward_keep').removeAttr('disabled')}
|
||||
else{$('#forward_destination,#forward_keep').attr('disabled', '')}") }}
|
||||
{{ macros.form_field(form.forward_keep,
|
||||
**{("enabled" if user.forward_enabled else "disabled"): ""}) }}
|
||||
{{ macros.form_field(form.forward_destination,
|
||||
**{("enabled" if user.forward_enabled else "disabled"): ""}) }}
|
||||
{% endcall %}
|
||||
{%- call macros.card(title=_("Auto-forward")) %}
|
||||
{%- call macros.fieldset(
|
||||
field=form.forward_enabled,
|
||||
enabled=user.forward_enabled,
|
||||
fields=[form.forward_keep, form.forward_destination]) %}
|
||||
{%- endcall %}
|
||||
{%- endcall %}
|
||||
|
||||
{{ macros.form_field(form.submit) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,23 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Sign up{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{{ domain }}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{%- block content %}
|
||||
<form class="form" method="post" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{% call macros.card() %}
|
||||
{%- call macros.card() %}
|
||||
{{ macros.form_field(form.localpart, append='<span class="input-group-text">@'+domain.name+'</span>') }}
|
||||
{{ macros.form_fields((form.pw, form.pw2)) }}
|
||||
{% if form.captcha %}
|
||||
{%- if form.captcha %}
|
||||
{{ macros.form_field(form.captcha) }}
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
{{ macros.form_field(form.submit) }}
|
||||
{% endcall %}
|
||||
{%- endcall %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,26 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{%- block title %}
|
||||
{% trans %}Sign up{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{%- block subtitle %}
|
||||
{% trans %}pick a domain for the new account{% endtrans %}
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% call macros.table() %}
|
||||
{%- block content %}
|
||||
{%- call macros.table() %}
|
||||
<tr>
|
||||
<th>{% trans %}Domain{% endtrans %}</th>
|
||||
<th>{% trans %}Available slots{% endtrans %}</th>
|
||||
<th>{% trans %}Quota{% endtrans %}</th>
|
||||
</tr>
|
||||
{% for domain_name, domain in available_domains.items() %}
|
||||
{%- for domain_name, domain in available_domains.items() %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('.user_signup', domain_name=domain_name) }}">{{ domain_name }}</a></td>
|
||||
<td>{{ '∞' if domain.max_users == -1 else domain.max_users - (domain.users | count)}}</td>
|
||||
<td>{{ domain.max_quota_bytes or config['DEFAULT_QUOTA'] | filesizeformat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
{%- endfor %}
|
||||
{%- endcall %}
|
||||
{%- endblock %}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{%- block content %}
|
||||
<div class="alert alert-warning" role="alert">{% trans %}We are still working on this feature!{% endtrans %}</div>
|
||||
{% endblock %}
|
||||
{%- endblock %}
|
||||
|
@ -1,65 +1,76 @@
|
||||
var path = require("path");
|
||||
var webpack = require("webpack");
|
||||
var css = require("mini-css-extract-plugin");
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const css = require('mini-css-extract-plugin');
|
||||
const mini = require('css-minimizer-webpack-plugin');
|
||||
const terse = require('terser-webpack-plugin');
|
||||
const compress = require('compression-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
mode: 'production',
|
||||
entry: {
|
||||
app: "./assets/app.js",
|
||||
vendor: "./assets/vendor.js"
|
||||
app: {
|
||||
import: './assets/app.js',
|
||||
dependOn: 'vendor',
|
||||
},
|
||||
vendor: './assets/vendor.js',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, "static/"),
|
||||
filename: "[name].js"
|
||||
path: path.resolve(__dirname, 'static/'),
|
||||
filename: '[name].js',
|
||||
assetModuleFilename: '[name][ext]',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: ['babel-loader']
|
||||
use: ['babel-loader', 'import-glob'],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [css.loader, 'css-loader', 'sass-loader']
|
||||
test: /\.s?css$/i,
|
||||
use: [css.loader, 'css-loader', 'sass-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [css.loader, 'css-loader', 'less-loader']
|
||||
test: /\.less$/i,
|
||||
use: [css.loader, 'css-loader', 'less-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [css.loader, 'css-loader']
|
||||
test: /\.(json|png|svg|jpg|jpeg|gif)$/i,
|
||||
type: 'asset/resource',
|
||||
},
|
||||
{
|
||||
// Exposes jQuery for use outside Webpack build
|
||||
test: require.resolve('jquery'),
|
||||
use: [{
|
||||
loader: 'expose-loader',
|
||||
options: {
|
||||
exposes: [
|
||||
{
|
||||
globalName: '$',
|
||||
override: true,
|
||||
},
|
||||
{
|
||||
globalName: 'jQuery',
|
||||
override: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
}]
|
||||
}
|
||||
]
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new css({
|
||||
filename: "[name].css",
|
||||
chunkFilename: "[id].css"
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
}),
|
||||
new webpack.ProvidePlugin({
|
||||
$: "jquery",
|
||||
jQuery: "jquery"
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
$: 'jquery',
|
||||
jQuery: 'jquery',
|
||||
ClipboardJS: 'clipboard',
|
||||
}),
|
||||
new compress({
|
||||
filename: '[path][base].gz',
|
||||
algorithm: "gzip",
|
||||
exclude: /\.(png|gif|jpe?g)$/,
|
||||
threshold: 5120,
|
||||
minRatio: 0.8,
|
||||
deleteOriginalAssets: false,
|
||||
}),
|
||||
],
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new terse(),
|
||||
new mini({
|
||||
minimizerOptions: {
|
||||
preset: [
|
||||
'default', {
|
||||
discardComments: { removeAll: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 978 B After Width: | Height: | Size: 924 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 3.8 KiB |
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
@ -0,0 +1,33 @@
|
||||
AdminLTE3 design optimizations, asset compression and caching
|
||||
|
||||
- fixed copy of qemu-arm-static for alpine
|
||||
- added 'set -eu' safeguard
|
||||
- silenced npm update notification
|
||||
- added color to webpack call
|
||||
- changed Admin-LTE default blue
|
||||
- AdminLTE 3 style tweaks
|
||||
- localized datatables
|
||||
- moved external javascript code to vendor.js
|
||||
- added mailu logo
|
||||
- moved all inline javascript to app.js
|
||||
- added iframe display of rspamd page
|
||||
- updated language-selector to display full language names and use post
|
||||
- added fieldset to group and en/disable input fields
|
||||
- added clipboard copy buttons
|
||||
- cleaned external javascript imports
|
||||
- pre-split first hostname for further use
|
||||
- cache dns_* properties of domain object (immutable during runtime)
|
||||
- fixed and splitted dns_dkim property of domain object (space missing)
|
||||
- added autoconfig and tlsa properties to domain object
|
||||
- suppressed extra vertical spacing in jinja2 templates
|
||||
- improved accessibility for screen reader
|
||||
- deleted unused/broken /user/forward route
|
||||
- updated gunicorn to 20.1.0 to get rid of buffering error at startup
|
||||
- switched webpack to production mode
|
||||
- added css and javascript minimization
|
||||
- added pre-compression of assets (gzip)
|
||||
- removed obsolete dependencies
|
||||
- switched from node-sass to dart-sass
|
||||
- changed startup cleaning message from error to info
|
||||
- move client config to "my account" section when logged in
|
||||
|