mirror of
				https://github.com/optim-enterprises-bv/Mailu.git
				synced 2025-10-30 17:47:55 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/master' into policyd-mta-sts
This commit is contained in:
		| @@ -1,7 +1,8 @@ | ||||
| # First stage to build assets | ||||
| ARG DISTRO=alpine:3.14 | ||||
| ARG ARCH="" | ||||
| FROM ${ARCH}node:8 as assets | ||||
|  | ||||
| FROM ${ARCH}node:16 as assets | ||||
| COPY --from=balenalib/rpi-alpine:3.14 /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static | ||||
|  | ||||
| COPY package.json ./ | ||||
|   | ||||
| @@ -20,3 +20,4 @@ | ||||
| .sidebar-toggle { | ||||
|   padding: unset !important; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,17 @@ | ||||
| require('./app.css'); | ||||
|  | ||||
| import 'select2'; | ||||
| 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'; | ||||
|  | ||||
| jQuery("document").ready(function() { | ||||
|     jQuery(".mailselect").select2({ | ||||
|         tags: true, | ||||
|         tokenSeparators: [',', ' '] | ||||
|     }) | ||||
|     }); | ||||
|     jQuery(".dataTable").DataTable({ | ||||
|         "responsive": true, | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -1,19 +1,22 @@ | ||||
| // jQuery | ||||
| import jQuery from 'jquery'; | ||||
| import 'select2/dist/css/select2.css'; | ||||
| import 'admin-lte/plugins/select2/css/select2.css'; | ||||
|  | ||||
| // bootstrap | ||||
| import 'bootstrap/less/bootstrap.less'; | ||||
| import 'bootstrap'; | ||||
| // import 'bootstrap/less/bootstrap.less'; | ||||
| // import 'bootstrap'; | ||||
|  | ||||
| // FA | ||||
| import 'font-awesome/scss/font-awesome.scss'; | ||||
| // 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/build/less/AdminLTE-without-plugins.less'; | ||||
| import 'admin-lte/build/less/select2.less'; | ||||
| import 'admin-lte/build/less/skins/skin-blue.less'; | ||||
| 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'; | ||||
| import 'admin-lte/build/js/BoxRefresh.js'; | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box() %} | ||||
| {% call macros.card() %} | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|   {{ macros.form_field(form.admin, class_='mailselect') }} | ||||
|   | ||||
| @@ -5,24 +5,28 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block main_action %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.admin_create') }}"> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.admin_create') }}"> | ||||
|   {% trans %}Add administrator{% endtrans %} | ||||
| </a> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}Email{% endtrans %}</th> | ||||
| </tr> | ||||
| {% for admin in admins %} | ||||
| <tr> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.admin_delete', admin=admin.email) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|   </td> | ||||
|   <td>{{ admin }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}Email{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
|   {% for admin in admins %} | ||||
|   <tr> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.admin_delete', admin=admin.email) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|     </td> | ||||
|     <td>{{ admin }}</td> | ||||
|   </tr> | ||||
|   {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -9,10 +9,10 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box() %} | ||||
| {% call macros.card() %} | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|   {{ macros.form_field(form.localpart, append='<span class="input-group-addon">@'+domain.name+'</span>') }} | ||||
|   {{ macros.form_field(form.localpart, append='<span class="input-group-text">@'+domain.name+'</span>') }} | ||||
|   {{ macros.form_field(form.wildcard) }} | ||||
|   {{ macros.form_field(form.destination, class_='mailselect') }} | ||||
|   {{ macros.form_field(form.comment) }} | ||||
|   | ||||
| @@ -9,31 +9,35 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block main_action %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.alias_create', domain_name=domain.name) }}">{% trans %}Add alias{% endtrans %}</a> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.alias_create', domain_name=domain.name) }}">{% trans %}Add alias{% endtrans %}</a> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}Email{% endtrans %}</th> | ||||
|   <th>{% trans %}Destination{% endtrans %}</th> | ||||
|   <th>{% trans %}Comment{% endtrans %}</th> | ||||
|   <th>{% trans %}Created{% endtrans %}</th> | ||||
|   <th>{% trans %}Last edit{% endtrans %}</th> | ||||
| </tr> | ||||
| {% for alias in domain.aliases %} | ||||
| <tr> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.alias_edit', alias=alias.email) }}" title="{% trans %}Edit{% endtrans %}"><i class="fa fa-pencil"></i></a>  | ||||
|     <a href="{{ url_for('.alias_delete', alias=alias.email) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|   </td> | ||||
|   <td>{{ alias }}</td> | ||||
|   <td>{{ alias.destination|join(', ') or '-' }}</td> | ||||
|   <td>{{ alias.comment or '' }}</td> | ||||
|   <td>{{ alias.created_at }}</td> | ||||
|   <td>{{ alias.updated_at or '' }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}Email{% endtrans %}</th> | ||||
|     <th>{% trans %}Destination{% endtrans %}</th> | ||||
|     <th>{% trans %}Comment{% endtrans %}</th> | ||||
|     <th>{% trans %}Created{% endtrans %}</th> | ||||
|     <th>{% trans %}Last edit{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
|   {% for alias in domain.aliases %} | ||||
|   <tr> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.alias_edit', alias=alias.email) }}" title="{% trans %}Edit{% endtrans %}"><i class="fa fa-pencil"></i></a>  | ||||
|       <a href="{{ url_for('.alias_delete', alias=alias.email) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|     </td> | ||||
|     <td>{{ alias }}</td> | ||||
|     <td>{{ alias.destination|join(', ') or '-' }}</td> | ||||
|     <td>{{ alias.comment or '' }}</td> | ||||
|     <td>{{ alias.created_at }}</td> | ||||
|     <td>{{ alias.updated_at or '' }}</td> | ||||
|   </tr> | ||||
|   {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -9,24 +9,28 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block main_action %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.alternative_create', domain_name=domain.name) }}">{% trans %}Add alternative{% endtrans %}</a> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.alternative_create', domain_name=domain.name) }}">{% trans %}Add alternative{% endtrans %}</a> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}Name{% endtrans %}</th> | ||||
|   <th>{% trans %}Created{% endtrans %}</th> | ||||
| </tr> | ||||
| {% for alternative in domain.alternatives %} | ||||
| <tr> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.alternative_delete', alternative=alternative.name) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|   </td> | ||||
|   <td>{{ alternative }}</td> | ||||
|   <td>{{ alternative.created_at }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}Name{% endtrans %}</th> | ||||
|     <th>{% trans %}Created{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
|   {% for alternative in domain.alternatives %} | ||||
|   <tr> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.alternative_delete', alternative=alternative.name) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|     </td> | ||||
|     <td>{{ alternative }}</td> | ||||
|     <td>{{ alternative.created_at }}</td> | ||||
|   </tr> | ||||
|   {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box() %} | ||||
| {% call macros.card() %} | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|   {{ macros.form_field(form.announcement_subject) }} | ||||
|   | ||||
| @@ -8,44 +8,58 @@ | ||||
|     <link rel="stylesheet" href="{{ url_for('.static', filename='app.css') }}"> | ||||
|     <title>Mailu-Admin - {{ config["SITENAME"] }}</title> | ||||
|   </head> | ||||
|   <body class="hold-transition skin-blue sidebar-mini"> | ||||
|   <body class="hold-transition sidebar-mini layout-fixed"> | ||||
|     <div class="wrapper"> | ||||
|       <header class="main-header"> | ||||
|         <div class="logo"> | ||||
|           <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button"> | ||||
|             <span class="sr-only">Toggle navigation</span> | ||||
|           </a> | ||||
|           <a href="{{ config["WEB_ADMIN"] }}"> | ||||
|             <span class="logo-lg">{{ config["SITENAME"] }}</span> | ||||
|           </a> | ||||
|         </div> | ||||
|       </header> | ||||
|       <aside class="main-sidebar"> | ||||
|       <nav class="main-header navbar navbar-expand navbar-white navbar-light"> | ||||
|         <ul class="navbar-nav"> | ||||
|           <li class="nav-item"> | ||||
|             <a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a> | ||||
|           </li> | ||||
|         </ul> | ||||
|         <ul class="navbar-nav ml-auto"> | ||||
|           <li class="nav-item dropdown"> | ||||
|             <a class="nav-link" data-toggle="dropdown" href="#" aria-expanded="false">{{ session['language'] }}</a> | ||||
|             <div class="dropdown-menu dropdown-menu-right p-0"> | ||||
|                 {% for language in session['available_languages'] %} | ||||
|                     <a class="dropdown-item {% if language == session['language'] %}active{% endif %} " href="{{ url_for('.set_language', language=language) }}">{{ language }}</a> | ||||
|                 {% endfor %} | ||||
|             </div> | ||||
|           </li> | ||||
|         </ul> | ||||
|       </nav> | ||||
|       <aside class="main-sidebar sidebar-dark-primary"> | ||||
|         <a href="{{ config["WEB_ADMIN"] }}" class="brand-link"> | ||||
|          <span class="brand-text font-weight-light">{{ config["SITENAME"] }}</span> | ||||
|         </a> | ||||
|         {% block sidebar %} | ||||
|         {% include "sidebar.html" %} | ||||
|         {% endblock %} | ||||
|       </aside> | ||||
|       <div class="content-wrapper"> | ||||
|         <section class="content-header"> | ||||
|           <div class="pull-right"> | ||||
|             {% block main_action %} | ||||
|             {% endblock %} | ||||
|           <div class="container-fluid"> | ||||
|             <div class="row mb-2"> | ||||
|               <div class="col-sm-6"> | ||||
|                 <h1 class="m-0">{% block title %}{% endblock %}</h1> | ||||
|                 <small>{% block subtitle %}{% endblock %}</small> | ||||
|               </div> | ||||
|               <div class="col-sm-6"> | ||||
|                 {% block main_action %} | ||||
|                 {% endblock %} | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           <h1> | ||||
|             {% block title %}{% endblock %} | ||||
|             <small>{% block subtitle %}{% endblock %}</small> | ||||
|           </h1> | ||||
|         </section> | ||||
|  | ||||
|         <section class="content"> | ||||
|         <div class="content"> | ||||
|           {{ utils.flashed_messages(container=False) }} | ||||
|           {% block content %}{% endblock %} | ||||
|         </section> | ||||
|         </div> | ||||
|       </div> | ||||
|       <footer class="main-footer"> | ||||
|         Built with <i class="fa fa-heart"></i> using <a class="white-text" href="http://flask.pocoo.org/">Flask</a> and | ||||
|         <a class="white-text" href="https://almsaeedstudio.com/preview">AdminLTE</a> | ||||
|         <span class="pull-right"><i class="fa fa-code-fork"></i> on <a class="white-text" href="https://github.com/Mailu/Mailu">Github</a></a></span> | ||||
|         <a class="white-text" href="https://adminlte.io/themes/v3/index3.html">AdminLTE</a> | ||||
|         <span class="pull-right"><i class="fa fa-code-fork"></i>on <a class="white-text" href="https://github.com/Mailu/Mailu">Github</a></a></span> | ||||
|       </footer> | ||||
|     </div> | ||||
|     <script src="{{ url_for('.static', filename='vendor.js') }}"></script> | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| <!--TODO add translations for: configure your client, Incoming mail and Outgoing mail--> | ||||
|  | ||||
| {% extends "base.html" %} | ||||
|  | ||||
| {% block title %} | ||||
| @@ -9,8 +11,7 @@ configure your email client | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box(title="Incoming mail") %} | ||||
| <table class="table table-bordered"> | ||||
| {% call macros.table(title="Incoming mail", datatable=False) %} | ||||
|   <tbody> | ||||
|     <tr> | ||||
|       <th>{% trans %}Mail protocol{% endtrans %}</th> | ||||
| @@ -33,11 +34,9 @@ configure your email client | ||||
|       <td><pre>*******</pre></td> | ||||
|     </tr> | ||||
|   </tbody> | ||||
| </table> | ||||
| {% endcall %} | ||||
|  | ||||
| {% call macros.box(title="Outgoing mail") %} | ||||
| <table class="table table-bordered"> | ||||
| {% call macros.table(title="Outgoing mail", datatable=False) %} | ||||
|   <tbody> | ||||
|     <tr> | ||||
|       <th>{% trans %}Mail protocol{% endtrans %}</th> | ||||
| @@ -60,6 +59,5 @@ configure your email client | ||||
|       <td><pre>*******</pre></td> | ||||
|     </tr> | ||||
|   </tbody> | ||||
| </table> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box(theme="warning") %} | ||||
| {% call macros.card(theme="warning") %} | ||||
| <p>{% trans action %}You are about to {{ action }}. Please confirm your action.{% endtrans %}</p> | ||||
| {{ macros.form(form) }} | ||||
| {% endcall %} | ||||
|   | ||||
| @@ -5,13 +5,13 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box() %} | ||||
| {% 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-addon"><span id="quota">'+((form.max_quota_bytes.data//1000000000).__str__() if form.max_quota_bytes.data else '∞')+'</span> GiB</span>', | ||||
|       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.signup_enabled) }} | ||||
|   {{ macros.form_field(form.comment) }} | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  | ||||
| {% block main_action %} | ||||
| {% if current_user.global_admin %} | ||||
| <a class="btn btn-primary" href="{{ url_for(".domain_genkeys", domain_name=domain.name) }}"> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for(".domain_genkeys", domain_name=domain.name) }}"> | ||||
|   {% if domain.dkim_publickey %} | ||||
|   {% trans %}Regenerate keys{% endtrans %} | ||||
|   {% else %} | ||||
| @@ -21,7 +21,7 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| {% call macros.table(datatable=False) %} | ||||
| {% set hostname = config["HOSTNAMES"].split(",")[0] %} | ||||
| <tr> | ||||
|   <th>{% trans %}Domain name{% endtrans %}</th> | ||||
|   | ||||
| @@ -6,46 +6,50 @@ | ||||
|  | ||||
| {% block main_action %} | ||||
| {% if current_user.global_admin %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.domain_create') }}">{% trans %}New domain{% endtrans %}</a> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.domain_create') }}">{% trans %}New domain{% endtrans %}</a> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}Manage{% endtrans %}</th> | ||||
|   <th>{% trans %}Domain name{% endtrans %}</th> | ||||
|   <th>{% trans %}Mailbox count{% endtrans %}</th> | ||||
|   <th>{% trans %}Alias count{% endtrans %}</th> | ||||
|   <th>{% trans %}Comment{% endtrans %}</th> | ||||
|   <th>{% trans %}Created{% endtrans %}</th> | ||||
|   <th>{% trans %}Last edit{% endtrans %}</th> | ||||
| </tr> | ||||
| {% for domain in current_user.get_managed_domains() %} | ||||
| <tr> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.domain_details', domain_name=domain.name) }}" title="{% trans %}Details{% endtrans %}"><i class="fa fa-list"></i></a>  | ||||
|     {% if current_user.global_admin %} | ||||
|     <a href="{{ url_for('.domain_edit', domain_name=domain.name) }}" title="{% trans %}Edit{% endtrans %}"><i class="fa fa-pencil"></i></a>  | ||||
|     <a href="{{ url_for('.domain_delete', domain_name=domain.name) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a>  | ||||
|     {% endif %} | ||||
|   </td> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.user_list', domain_name=domain.name) }}" title="{% trans %}Users{% endtrans %}"><i class="fa fa-envelope-o"></i></a>  | ||||
|     <a href="{{ url_for('.alias_list', domain_name=domain.name) }}" title="{% trans %}Aliases{% endtrans %}"><i class="fa fa-at"></i></a>  | ||||
|     <a href="{{ url_for('.manager_list', domain_name=domain.name) }}" title="{% trans %}Managers{% endtrans %}"><i class="fa fa-user"></i></a>  | ||||
|     {% if current_user.global_admin %} | ||||
|     <a href="{{ url_for('.alternative_list', domain_name=domain.name) }}" title="{% trans %}Alternatives{% endtrans %}"><i class="fa fa-asterisk"></i></a>  | ||||
|     {% endif %} | ||||
|   </td> | ||||
|   <td>{{ domain.name }}</td> | ||||
|   <td>{{ domain.users | count }} / {{ '∞' if domain.max_users == -1 else domain.max_users }}</td> | ||||
|   <td>{{ domain.aliases | count }} / {{ '∞' if domain.max_aliases == -1 else domain.max_aliases }}</td> | ||||
|   <td>{{ domain.comment or '' }}</td> | ||||
|   <td>{{ domain.created_at }}</td> | ||||
|   <td>{{ domain.updated_at or '' }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}Manage{% endtrans %}</th> | ||||
|     <th>{% trans %}Domain name{% endtrans %}</th> | ||||
|     <th>{% trans %}Mailbox count{% endtrans %}</th> | ||||
|     <th>{% trans %}Alias count{% endtrans %}</th> | ||||
|     <th>{% trans %}Comment{% endtrans %}</th> | ||||
|     <th>{% trans %}Created{% endtrans %}</th> | ||||
|     <th>{% trans %}Last edit{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
|   {% for domain in current_user.get_managed_domains() %} | ||||
|   <tr> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.domain_details', domain_name=domain.name) }}" title="{% trans %}Details{% endtrans %}"><i class="fa fa-list"></i></a>  | ||||
|       {% if current_user.global_admin %} | ||||
|       <a href="{{ url_for('.domain_edit', domain_name=domain.name) }}" title="{% trans %}Edit{% endtrans %}"><i class="fas fa-pencil-alt"></i></a>  | ||||
|       <a href="{{ url_for('.domain_delete', domain_name=domain.name) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a>  | ||||
|       {% endif %} | ||||
|     </td> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.user_list', domain_name=domain.name) }}" title="{% trans %}Users{% endtrans %}"><i class="far fa-envelope"></i></a>  | ||||
|       <a href="{{ url_for('.alias_list', domain_name=domain.name) }}" title="{% trans %}Aliases{% endtrans %}"><i class="fa fa-at"></i></a>  | ||||
|       <a href="{{ url_for('.manager_list', domain_name=domain.name) }}" title="{% trans %}Managers{% endtrans %}"><i class="fa fa-user"></i></a>  | ||||
|       {% if current_user.global_admin %} | ||||
|       <a href="{{ url_for('.alternative_list', domain_name=domain.name) }}" title="{% trans %}Alternatives{% endtrans %}"><i class="fa fa-asterisk"></i></a>  | ||||
|       {% endif %} | ||||
|     </td> | ||||
|     <td>{{ domain.name }}</td> | ||||
|     <td>{{ domain.users | count }} / {{ '∞' if domain.max_users == -1 else domain.max_users }}</td> | ||||
|     <td>{{ domain.aliases | count }} / {{ '∞' if domain.max_aliases == -1 else domain.max_aliases }}</td> | ||||
|     <td>{{ domain.comment or '' }}</td> | ||||
|     <td>{{ domain.created_at }}</td> | ||||
|     <td>{{ domain.updated_at or '' }}</td> | ||||
|   </tr> | ||||
|   {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|  | ||||
|   {% call macros.box(title="Requirements") %} | ||||
|   {% call macros.card(title="Requirements") %} | ||||
|   <p>{% trans %}In order to register a new domain, you must first setup the | ||||
|     domain zone so that the domain <code>MX</code> points to this server{% endtrans %} | ||||
|     (<code>{{ config["HOSTNAMES"].split(",")[0] }}</code>). | ||||
| @@ -22,9 +22,9 @@ | ||||
|   </p> | ||||
|   {% endcall %} | ||||
|  | ||||
|   {% call macros.box() %} | ||||
|   {% call macros.card() %} | ||||
|   {% if form.localpart %} | ||||
|   {{ macros.form_fields((form.localpart, form.name), append='<span class="input-group-addon">@</span>') }} | ||||
|   {{ macros.form_fields((form.localpart, form.name), append='<span class="input-group-text">@</span>') }} | ||||
|   {{ macros.form_fields((form.pw, form.pw2)) }} | ||||
|   {% else %} | ||||
|   {{ macros.form_field(form.name) }} | ||||
|   | ||||
| @@ -11,18 +11,18 @@ | ||||
| {% block content %} | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|   {% call macros.box(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 %} | ||||
|  | ||||
|   {% call macros.box(title="Authentication") %} | ||||
|   {% call macros.card(title="Authentication") %} | ||||
|   {{ macros.form_field(form.username) }} | ||||
|   {{ macros.form_field(form.password) }} | ||||
|   {% endcall %} | ||||
|  | ||||
|   {% call macros.box(title="Settings") %} | ||||
|   {% call macros.card(title="Settings") %} | ||||
|   {{ macros.form_field(form.keep) }} | ||||
|   {% endcall %} | ||||
|  | ||||
|   | ||||
| @@ -9,35 +9,39 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block main_action %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.fetch_create', user_email=user.email) }}">{% trans %}Add an account{% endtrans %}</a> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.fetch_create', user_email=user.email) }}">{% trans %}Add an account{% endtrans %}</a> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}Endpoint{% endtrans %}</th> | ||||
|   <th>{% trans %}Username{% endtrans %}</th> | ||||
|   <th>{% trans %}Keep emails{% endtrans %}</th> | ||||
|   <th>{% trans %}Last check{% endtrans %}</th> | ||||
|   <th>{% trans %}Status{% endtrans %}</th> | ||||
|   <th>{% trans %}Created{% endtrans %}</th> | ||||
|   <th>{% trans %}Last edit{% endtrans %}</th> | ||||
| </tr> | ||||
| {% for fetch in user.fetches %} | ||||
| <tr> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.fetch_edit', fetch_id=fetch.id) }}" title="{% trans %}Edit{% endtrans %}"><i class="fa fa-pencil"></i></a>  | ||||
|     <a href="{{ url_for('.fetch_delete', fetch_id=fetch.id) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|   </td> | ||||
|   <td>{{ fetch.protocol }}{{ 's' if fetch.tls else '' }}://{{ fetch.host }}:{{ fetch.port }}</td> | ||||
|   <td>{{ fetch.username }}</td> | ||||
|   <td>{% if fetch.keep %}{% trans %}yes{% endtrans %}{% else %}{% trans %}no{% endtrans %}{% endif %}</td> | ||||
|   <td>{{ fetch.last_check or '-' }}</td> | ||||
|   <td>{{ fetch.error or '-' }}</td> | ||||
|   <td>{{ fetch.created_at }}</td> | ||||
|   <td>{{ fetch.updated_at or '' }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}Endpoint{% endtrans %}</th> | ||||
|     <th>{% trans %}Username{% endtrans %}</th> | ||||
|     <th>{% trans %}Keep emails{% endtrans %}</th> | ||||
|     <th>{% trans %}Last check{% endtrans %}</th> | ||||
|     <th>{% trans %}Status{% endtrans %}</th> | ||||
|     <th>{% trans %}Created{% endtrans %}</th> | ||||
|     <th>{% trans %}Last edit{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
|   {% for fetch in user.fetches %} | ||||
|   <tr> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.fetch_edit', fetch_id=fetch.id) }}" title="{% trans %}Edit{% endtrans %}"><i class="fa fa-pencil"></i></a>  | ||||
|       <a href="{{ url_for('.fetch_delete', fetch_id=fetch.id) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|     </td> | ||||
|     <td>{{ fetch.protocol }}{{ 's' if fetch.tls else '' }}://{{ fetch.host }}:{{ fetch.port }}</td> | ||||
|     <td>{{ fetch.username }}</td> | ||||
|     <td>{% if fetch.keep %}{% trans %}yes{% endtrans %}{% else %}{% trans %}no{% endtrans %}{% endif %}</td> | ||||
|     <td>{{ fetch.last_check or '-' }}</td> | ||||
|     <td>{{ fetch.error or '-' }}</td> | ||||
|     <td>{{ fetch.created_at }}</td> | ||||
|     <td>{{ fetch.updated_at or '' }}</td> | ||||
|   </tr> | ||||
|   {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| {% extends "base.html" %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box() %} | ||||
| {% call macros.card() %} | ||||
| {{ macros.form(form) }} | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -37,9 +37,10 @@ | ||||
|     {{ field.label if label else '' }} | ||||
|   {% else %} | ||||
|     {{ field.label if label else '' }}{{ form_field_errors(field) }} | ||||
|     {% if prepend or append %}<div class="input-group">{% endif %} | ||||
|       {{ prepend|safe }}{{ field(class_="form-control " + class_, **kwargs) }}{{ append|safe }} | ||||
|     {% if prepend or append %}</div>{% endif %} | ||||
|       {% 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 %} | ||||
|  | ||||
| @@ -64,18 +65,18 @@ | ||||
| </form> | ||||
| {% endmacro %} | ||||
|  | ||||
| {% macro box(title=None, theme="primary", header=True) %} | ||||
| {% macro card(title=None, theme="primary", header=True) %} | ||||
| <div class="row"> | ||||
|   <div class="col-lg-12"> | ||||
|     <div class="box box-{{ theme }}"> | ||||
|     <div class="card card-outline card-{{ theme }}"> | ||||
|       {% if header %} | ||||
|       <div class="box-header"> | ||||
|       <div class="card-header border-0"> | ||||
|         {% if title %} | ||||
|         <h3 class="box-title">{{ title }}</h3> | ||||
|         <h3 class="card-title">{{ title }}</h3> | ||||
|         {% endif %} | ||||
|       </div> | ||||
|       {% endif %} | ||||
|       <div class="box-body"> | ||||
|       <div class="card-body"> | ||||
|        {{ caller() }} | ||||
|       </div> | ||||
|     </div> | ||||
| @@ -83,15 +84,20 @@ | ||||
| </div> | ||||
| {% endmacro %} | ||||
|  | ||||
| {% macro table(theme="primary") %} | ||||
| {% macro table(title=None, theme="primary", datatable=True) %} | ||||
| <div class="row"> | ||||
|   <div class="col-lg-12"> | ||||
|     <div class="box box-{{ theme }}"> | ||||
|       <table class="table table-bordered"> | ||||
|         <tbody> | ||||
|     <div class="card card-outline card-{{ theme }}"> | ||||
|       <div class="card-header border-0"> | ||||
|         {% if title %} | ||||
|         <h3 class="card-title">{{ title }}</h3> | ||||
|         {% endif %} | ||||
|       </div> | ||||
|       <div class="card-body"> | ||||
|         <table class="table table-bordered {% if datatable %} dataTable {% endif %}"> | ||||
|           {{ caller() }} | ||||
|         </tbody> | ||||
|       </table> | ||||
|         </table> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box() %} | ||||
| {% call macros.card() %} | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|   {{ macros.form_field(form.manager, class_='mailselect') }} | ||||
|   | ||||
| @@ -9,15 +9,18 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block main_action %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.manager_create', domain_name=domain.name) }}">{% trans %}Add manager{% endtrans %}</a> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.manager_create', domain_name=domain.name) }}">{% trans %}Add manager{% endtrans %}</a> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}Email{% endtrans %}</th> | ||||
| </tr> | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}Email{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
| {% for manager in domain.managers %} | ||||
| <tr> | ||||
|   <td> | ||||
| @@ -26,5 +29,6 @@ | ||||
|   <td>{{ manager }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -6,32 +6,36 @@ | ||||
|  | ||||
| {% block main_action %} | ||||
| {% if current_user.global_admin %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.relay_create') }}">{% trans %}New relayed domain{% endtrans %}</a> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.relay_create') }}">{% trans %}New relayed domain{% endtrans %}</a> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}Domain name{% endtrans %}</th> | ||||
|   <th>{% trans %}Remote host{% endtrans %}</th> | ||||
|   <th>{% trans %}Comment{% endtrans %}</th> | ||||
|   <th>{% trans %}Created{% endtrans %}</th> | ||||
|   <th>{% trans %}Last edit{% endtrans %}</th> | ||||
| </tr> | ||||
| {% for relay in relays %} | ||||
| <tr> | ||||
|   <td> | ||||
| <a href="{{ url_for('.relay_edit', relay_name=relay.name) }}" title="{% trans %}Edit{% endtrans %}"><i class="fa fa-pencil"></i></a>  | ||||
|     <a href="{{ url_for('.relay_delete', relay_name=relay.name) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a>  | ||||
|   </td> | ||||
|   <td>{{ relay.name }}</td> | ||||
|   <td>{{ relay.smtp or '-' }}</td> | ||||
|   <td>{{ relay.comment or '' }}</td> | ||||
|   <td>{{ relay.created_at }}</td> | ||||
|   <td>{{ relay.updated_at or '' }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}Domain name{% endtrans %}</th> | ||||
|     <th>{% trans %}Remote host{% endtrans %}</th> | ||||
|     <th>{% trans %}Comment{% endtrans %}</th> | ||||
|     <th>{% trans %}Created{% endtrans %}</th> | ||||
|     <th>{% trans %}Last edit{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
|   {% for relay in relays %} | ||||
|   <tr> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.relay_edit', relay_name=relay.name) }}" title="{% trans %}Edit{% endtrans %}"><i class="fa fa-pencil"></i></a>  | ||||
|       <a href="{{ url_for('.relay_delete', relay_name=relay.name) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a>  | ||||
|     </td> | ||||
|     <td>{{ relay.name }}</td> | ||||
|     <td>{{ relay.smtp or '-' }}</td> | ||||
|     <td>{{ relay.comment or '' }}</td> | ||||
|     <td>{{ relay.created_at }}</td> | ||||
|     <td>{{ relay.updated_at or '' }}</td> | ||||
|   </tr> | ||||
|   {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,120 +1,144 @@ | ||||
| <section class="sidebar"> | ||||
| <div class="sidebar"> | ||||
|   {% if current_user.is_authenticated %} | ||||
|   <h4 class="text-center text-primary">{{ current_user }}</h4> | ||||
|   <div class="user-panel mt-3 pb-3 mb-3 d-flex"> | ||||
|     <div class="info"> | ||||
|       <span class="text-center text-primary">{{ current_user }}</span> | ||||
|     </div> | ||||
|   </div> | ||||
|   {% endif %} | ||||
|  | ||||
|   <ul class="sidebar-menu" data-widget="tree"> | ||||
|     {% if current_user.is_authenticated %} | ||||
|     <li class="header">{% trans %}My account{% endtrans %}</li> | ||||
|     <li> | ||||
|       <a href="{{ url_for('.user_settings') }}"> | ||||
|         <i class="fa fa-wrench"></i> <span>{% trans %}Settings{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="{{ url_for('.user_password') }}"> | ||||
|         <i class="fa fa-lock"></i> <span>{% trans %}Update password{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="{{ url_for('.user_reply') }}"> | ||||
|         <i class="fa fa-plane"></i> <span>{% trans %}Auto-reply{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="{{ url_for('.fetch_list') }}"> | ||||
|         <i class="fa fa-download"></i> <span>{% trans %}Fetched accounts{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="{{ url_for('.token_list') }}"> | ||||
|         <i class="fa fa-ticket"></i> <span>{% trans %}Authentication tokens{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|   <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"> | ||||
|           <i class="nav-icon fa fa-wrench"></i> | ||||
|           <p class="text">{% trans %}Settings{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       <li class="nav-item"> | ||||
|         <a href="{{ url_for('.user_password') }}" class="nav-link"> | ||||
|           <i class="nav-icon fa fa-lock"></i> | ||||
|           <p class="text">{% trans %}Update password{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       <li class="nav-item"> | ||||
|         <a href="{{ url_for('.user_reply') }}" class="nav-link"> | ||||
|           <i class="nav-icon fa fa-plane"></i> | ||||
|           <p class="text">{% trans %}Auto-reply{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       <li class="nav-item"> | ||||
|         <a href="{{ url_for('.fetch_list') }}" class="nav-link"> | ||||
|           <i class="nav-icon fas fa-download"></i> | ||||
|           <p class="text">{% trans %}Fetched accounts{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       <li class="nav-item"> | ||||
|         <a href="{{ url_for('.token_list') }}" class="nav-link"> | ||||
|           <i class="nav-icon fas fa-ticket-alt"></i> | ||||
|           <p class="text">{% trans %}Authentication tokens{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|  | ||||
|     {% if current_user.manager_of or current_user.global_admin %} | ||||
|     <li class="header">{% trans %}Administration{% endtrans %}</li> | ||||
|     {% endif %} | ||||
|     {% if current_user.global_admin %} | ||||
|     <li> | ||||
|       <a href="{{ url_for('.announcement') }}"> | ||||
|         <i class="fa fa-bullhorn"></i> <span>{% trans %}Announcement{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="{{ url_for('.admin_list') }}"> | ||||
|         <i class="fa fa-user"></i> <span>{% trans %}Administrators{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="{{ url_for('.relay_list') }}"> | ||||
|         <i class="fa fa-reply-all"></i> <span>{% trans %}Relayed domains{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="{{ config["WEB_ADMIN"] }}/antispam/" target="_blank"> | ||||
|         <i class="fa fa-trash-o"></i> <span>{% trans %}Antispam{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     {% endif %} | ||||
|     {% if current_user.manager_of or current_user.global_admin %} | ||||
|     <li> | ||||
|       <a href="{{ url_for('.domain_list') }}"> | ||||
|         <i class="fa fa-envelope"></i> <span>{% trans %}Mail domains{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     {% endif %} | ||||
|     {% endif %} | ||||
|       {% 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="header">{% trans %}Go to{% endtrans %}</li> | ||||
|     {% if config["WEBMAIL"] != "none" %} | ||||
|     <li> | ||||
|       <a href="{{ config["WEB_WEBMAIL"] }}" target="_blank"> | ||||
|         <i class="fa fa-envelope-o"></i> <span>{% trans %}Webmail{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     {% endif %} | ||||
|     <li> | ||||
|       <a href="{{ url_for('.client') }}"> | ||||
|         <i class="fa fa-laptop"></i> <span>{% trans %}Client setup{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="{{ config["WEBSITE"] }}" target="_blank"> | ||||
|         <i class="fa fa-globe"></i> <span>{% trans %}Website{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     <li> | ||||
|       <a href="https://mailu.io" target="_blank"> | ||||
|         <i class="fa fa-life-ring"></i> <span>{% trans %}Help{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     {% if config['DOMAIN_REGISTRATION'] %} | ||||
|     <li> | ||||
|       <a href="{{ url_for('.domain_signup') }}"> | ||||
|         <i class="fa fa-plus-square"></i> <span>{% trans %}Register a domain{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     {% endif %} | ||||
|     {% if current_user.is_authenticated %} | ||||
|     <li> | ||||
|       <a href="{{ url_for('.logout') }}"> | ||||
|         <i class="fa fa-sign-out"></i> <span>{% trans %}Sign out{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     {% else %} | ||||
|     <li> | ||||
|       <a href="{{ url_for('.login') }}"> | ||||
|         <i class="fa fa-sign-in"></i> <span>{% trans %}Sign in{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     {% if signup_domains %} | ||||
|     <li> | ||||
|       <a href="{{ url_for('.user_signup') }}"> | ||||
|         <i class="fa fa-user-plus"></i> <span>{% trans %}Sign up{% endtrans %}</span> | ||||
|       </a> | ||||
|     </li> | ||||
|     {% endif %} | ||||
|     {% endif %} | ||||
|   </ul> | ||||
| </section> | ||||
|       <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"> | ||||
|           <i class="nav-icon fa fa-laptop"></i> | ||||
|           <p class="text">{% trans %}Client setup{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       <li class="nav-item"> | ||||
|         <a href="{{ config["WEBSITE"] }}" target="_blank" class="nav-link"> | ||||
|         <i class="nav-icon fa fa-globe"></i> | ||||
|         <p class="text">{% trans %}Website{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       <li class="nav-item"> | ||||
|         <a href="https://mailu.io" target="_blank" class="nav-link"> | ||||
|           <i class="nav-icon fa fa-life-ring"></i> | ||||
|           <p class="text">{% trans %}Help{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       {% if config['DOMAIN_REGISTRATION'] %} | ||||
|       <li class="nav-item"> | ||||
|         <a href="{{ url_for('.domain_signup') }}" class="nav-link"> | ||||
|           <i class="nav-icon fa fa-plus-square"></i> | ||||
|           <p class="text">{% 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"> | ||||
|           <i class="nav-icon fas fa-sign-out-alt"></i> | ||||
|           <p class="text">{% trans %}Sign out{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       {% else %} | ||||
|       <li class="nav-item"> | ||||
|         <a href="{{ url_for('.login') }}" class="nav-link"> | ||||
|           <i class="nav-icon fas fa-sign-in-alt"></i> | ||||
|           <p class="text">{% trans %}Sign in{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       {% if signup_domains %} | ||||
|       <li class="nav-item"> | ||||
|         <a href="{{ url_for('.user_signup') }}" class="nav-link"> | ||||
|           <i class="nav-icon fa fa-user-plus"></i> | ||||
|           <p class="text">{% trans %}Sign up{% endtrans %}</p> | ||||
|         </a> | ||||
|       </li> | ||||
|       {% endif %} | ||||
|       {% endif %} | ||||
|     </ul> | ||||
|   </nav> | ||||
| </div> | ||||
|   | ||||
| @@ -9,26 +9,30 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block main_action %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.token_create', user_email=user.email) }}">{% trans %}New token{% endtrans %}</a> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.token_create', user_email=user.email) }}">{% trans %}New token{% endtrans %}</a> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}Comment{% endtrans %}</th> | ||||
|   <th>{% trans %}Authorized IP{% endtrans %}</th> | ||||
|   <th>{% trans %}Created{% endtrans %}</th> | ||||
| </tr> | ||||
| {% for token in user.tokens %} | ||||
| <tr> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.token_delete', token_id=token.id) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|   </td> | ||||
|   <td>{{ token.comment }}</td> | ||||
|   <td>{{ token.ip or "any" }}</td> | ||||
|   <td>{{ token.created_at }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}Comment{% endtrans %}</th> | ||||
|     <th>{% trans %}Authorized IP{% endtrans %}</th> | ||||
|     <th>{% trans %}Created{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
|   {% for token in user.tokens %} | ||||
|   <tr> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.token_delete', token_id=token.id) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|     </td> | ||||
|     <td>{{ token.comment }}</td> | ||||
|     <td>{{ token.ip or "any" }}</td> | ||||
|     <td>{{ token.created_at }}</td> | ||||
|   </tr> | ||||
|   {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -12,17 +12,17 @@ | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|  | ||||
|   {% call macros.box(_("General")) %} | ||||
|   {{ macros.form_field(form.localpart, append='<span class="input-group-addon">@'+domain.name+'</span>') }} | ||||
|   {% 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 %} | ||||
|  | ||||
|   {% call macros.box(_("Features and quotas"), theme="success") %} | ||||
|   {% 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-addon"><span id="quota">'+((form.quota_bytes.data//1000000000).__str__() if form.quota_bytes.data else '∞')+'</span> GiB</span>', | ||||
|       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);') }} | ||||
|   {{ macros.form_field(form.enable_imap) }} | ||||
|   {{ macros.form_field(form.enable_pop) }} | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box() %} | ||||
| {% call macros.card() %} | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|   {{ macros.form_field(form.forward_enabled, | ||||
|   | ||||
| @@ -9,45 +9,46 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block main_action %} | ||||
| <a class="btn btn-primary" href="{{ url_for('.user_create', domain_name=domain.name) }}">{% trans %}Add user{% endtrans %}</a> | ||||
| <a class="btn btn-primary float-right" href="{{ url_for('.user_create', domain_name=domain.name) }}">{% trans %}Add user{% endtrans %}</a> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.table() %} | ||||
| <tr> | ||||
|   <th>{% trans %}Actions{% endtrans %}</th> | ||||
|   <th>{% trans %}User settings{% endtrans %}</th> | ||||
|   <th>{% trans %}Email{% endtrans %}</th> | ||||
|   <th>{% trans %}Features{% endtrans %}</th> | ||||
|   <th>{% trans %}Storage Quota{% endtrans %}</th> | ||||
|   <th>{% trans %}Sending Quota{% endtrans %}</th> | ||||
|   <th>{% trans %}Comment{% endtrans %}</th> | ||||
|   <th>{% trans %}Created{% endtrans %}</th> | ||||
|   <th>{% trans %}Last edit{% endtrans %}</th> | ||||
| </tr> | ||||
| {% for user in domain.users %} | ||||
| <tr {% if not user.enabled %}class="warning"{% endif %}> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.user_edit', user_email=user.email) }}" title="{% trans %}Edit{% endtrans %}"><i class="fa fa-pencil"></i></a>  | ||||
|     <a href="{{ url_for('.user_delete', user_email=user.email) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|   </td> | ||||
|   <td> | ||||
|     <a href="{{ url_for('.user_settings', user_email=user.email) }}" title="{% trans %}Settings{% endtrans %}"><i class="fa fa-wrench"></i></a>  | ||||
|     <a href="{{ url_for('.user_reply', user_email=user.email) }}" title="{% trans %}Auto-reply{% endtrans %}"><i class="fa fa-plane"></i></a>  | ||||
|     <a href="{{ url_for('.fetch_list', user_email=user.email) }}" title="{% trans %}Fetched accounts{% endtrans %}"><i class="fa fa-download"></i></a>  | ||||
|   </td> | ||||
|   <td>{{ user }}</td> | ||||
|   <td> | ||||
|     {% if user.enable_imap %}<span class="label label-info">imap</span>{% endif %} | ||||
|     {% if user.enable_pop %}<span class="label label-info">pop3</span>{% endif %} | ||||
|   </td> | ||||
|   <td>{{ user.quota_bytes_used | filesizeformat }} / {{ (user.quota_bytes | filesizeformat) if user.quota_bytes else '∞' }}</td> | ||||
|   {% set limiter = user.sender_limiter %} | ||||
|   <td>{{ limiter.get_window_stats()[1] }} / {{ limiter.limit }}</td> | ||||
|   <td>{{ user.comment or '-' }}</td> | ||||
|   <td>{{ user.created_at }}</td> | ||||
|   <td>{{ user.updated_at or '' }}</td> | ||||
| </tr> | ||||
| {% endfor %} | ||||
| <thead> | ||||
|   <tr> | ||||
|     <th>{% trans %}Actions{% endtrans %}</th> | ||||
|     <th>{% trans %}User settings{% endtrans %}</th> | ||||
|     <th>{% trans %}Email{% endtrans %}</th> | ||||
|     <th>{% trans %}Features{% endtrans %}</th> | ||||
|     <th>{% trans %}Quota{% endtrans %}</th> | ||||
|     <th>{% trans %}Comment{% endtrans %}</th> | ||||
|     <th>{% trans %}Created{% endtrans %}</th> | ||||
|     <th>{% trans %}Last edit{% endtrans %}</th> | ||||
|   </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
|   {% for user in domain.users %} | ||||
|   <tr {% if not user.enabled %}class="warning"{% endif %}> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.user_edit', user_email=user.email) }}" title="{% trans %}Edit{% endtrans %}"><i class="fas fa-pencil-alt"></i></a>  | ||||
|       <a href="{{ url_for('.user_delete', user_email=user.email) }}" title="{% trans %}Delete{% endtrans %}"><i class="fa fa-trash"></i></a> | ||||
|     </td> | ||||
|     <td> | ||||
|       <a href="{{ url_for('.user_settings', user_email=user.email) }}" title="{% trans %}Settings{% endtrans %}"><i class="fa fa-wrench"></i></a>  | ||||
|       <a href="{{ url_for('.user_reply', user_email=user.email) }}" title="{% trans %}Auto-reply{% endtrans %}"><i class="fa fa-plane"></i></a>  | ||||
|       <a href="{{ url_for('.fetch_list', user_email=user.email) }}" title="{% trans %}Fetched accounts{% endtrans %}"><i class="fa fa-download"></i></a>  | ||||
|     </td> | ||||
|     <td>{{ user }}</td> | ||||
|     <td> | ||||
|       {% if user.enable_imap %}<span class="badge bg-info">imap</span>{% endif %} | ||||
|       {% if user.enable_pop %}<span class="badge bg-info">pop3</span>{% endif %} | ||||
|     </td> | ||||
|     <td>{{ user.quota_bytes_used | filesizeformat }} / {{ (user.quota_bytes | filesizeformat) if user.quota_bytes else '∞' }}</td> | ||||
|     <td>{{ user.comment or '-' }}</td> | ||||
|     <td>{{ user.created_at }}</td> | ||||
|     <td>{{ user.updated_at or '' }}</td> | ||||
|   </tr> | ||||
|   {% endfor %} | ||||
| </tbody> | ||||
| {% endcall %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| {% call macros.box() %} | ||||
| {% call macros.card() %} | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|   {{ macros.form_field(form.reply_enabled, | ||||
|   | ||||
| @@ -12,18 +12,18 @@ | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|    | ||||
|   {% call macros.box(title=_("Displayed name")) %} | ||||
|   {% call macros.card(title=_("Displayed name")) %} | ||||
|   {{ macros.form_field(form.displayed_name) }} | ||||
|   {% endcall %} | ||||
|  | ||||
|   {% call macros.box(title=_("Antispam")) %} | ||||
|   {% call macros.card(title=_("Antispam")) %} | ||||
|   {{ macros.form_field(form.spam_enabled) }} | ||||
|   {{ macros.form_field(form.spam_threshold, step=1, max=100, | ||||
|       prepend='<span class="input-group-addon"><span id="threshold">'+form.spam_threshold.data.__str__()+'</span> / 100</span>', | ||||
|       prepend='<span class="input-group-text"><span id="threshold">'+form.spam_threshold.data.__str__()+'</span> / 100</span>', | ||||
|       oninput='$("#threshold").text(this.value);') }} | ||||
|   {% endcall %} | ||||
|  | ||||
|   {% call macros.box(title=_("Auto-forward")) %} | ||||
|   {% 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', '')}") }} | ||||
|   | ||||
| @@ -11,8 +11,8 @@ | ||||
| {% block content %} | ||||
| <form class="form" method="post" role="form"> | ||||
|   {{ form.hidden_tag() }} | ||||
|   {% call macros.box() %} | ||||
|   {{ macros.form_field(form.localpart, append='<span class="input-group-addon">@'+domain.name+'</span>') }} | ||||
|   {% 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 %} | ||||
|     {{ macros.form_field(form.captcha) }} | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| __all__ = [ | ||||
|     'admins', 'aliases', 'alternatives', 'base', 'domains', 'fetches', | ||||
|     'managers', 'users', 'relays', 'tokens' | ||||
|     'managers', 'users', 'relays', 'tokens', 'languages' | ||||
| ] | ||||
|   | ||||
							
								
								
									
										9
									
								
								core/admin/mailu/ui/views/languages.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								core/admin/mailu/ui/views/languages.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| from mailu.ui import ui, forms, access | ||||
|  | ||||
| import flask | ||||
|  | ||||
|  | ||||
| @ui.route('/language/<language>', methods=['GET']) | ||||
| def set_language(language=None): | ||||
|     flask.session['language'] = language | ||||
|     return flask.redirect(flask.url_for('.user_settings')) | ||||
| @@ -70,8 +70,16 @@ babel = flask_babel.Babel() | ||||
| @babel.localeselector | ||||
| def get_locale(): | ||||
|     """ selects locale for translation """ | ||||
|     translations = [str(translation) for translation in babel.list_translations()] | ||||
|     return flask.request.accept_languages.best_match(translations) | ||||
|     translations = list(map(str, babel.list_translations())) | ||||
|     flask.session['available_languages'] = translations | ||||
|  | ||||
|     try: | ||||
|         language = flask.session['language'] | ||||
|     except KeyError: | ||||
|         language = flask.request.accept_languages.best_match(translations) | ||||
|         flask.session['language'] = language | ||||
|  | ||||
|     return language | ||||
|  | ||||
|  | ||||
| # Proxy fixer | ||||
|   | ||||
| @@ -12,20 +12,20 @@ | ||||
|   "author": "", | ||||
|   "license": "ISC", | ||||
|   "dependencies": { | ||||
|     "@babel/core": "^7.14.6", | ||||
|     "admin-lte": "^2.4.18", | ||||
|     "babel-loader": "^8.0.6", | ||||
|     "css-loader": "^2.1.1", | ||||
|     "expose-loader": "^0.7.5", | ||||
|     "@babel/core": "^7.15.0", | ||||
|     "@babel/preset-env": "^7.15.0", | ||||
|     "admin-lte": "^3.1.0", | ||||
|     "babel-loader": "^8.2.2", | ||||
|     "css-loader": "^6.2.0", | ||||
|     "expose-loader": "^3.0.0", | ||||
|     "jquery": "^3.6.0", | ||||
|     "less": "^3.13.1", | ||||
|     "less-loader": "^5.0.0", | ||||
|     "mini-css-extract-plugin": "^1.2.1", | ||||
|     "node-sass": "^4.13.1", | ||||
|     "sass-loader": "^7.3.1", | ||||
|     "less": "^4.1.1", | ||||
|     "less-loader": "^10.0.1", | ||||
|     "mini-css-extract-plugin": "^2.2.0", | ||||
|     "node-sass": "^6.0.1", | ||||
|     "sass-loader": "^12.1.0", | ||||
|     "select2": "^4.0.13", | ||||
|     "url-loader": "^2.3.0", | ||||
|     "webpack": "^4.33.0", | ||||
|     "webpack-cli": "^3.3.12" | ||||
|     "webpack": "^5.50.0", | ||||
|     "webpack-cli": "^4.7.2" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -30,19 +30,23 @@ module.exports = { | ||||
|                 test: /\.css$/, | ||||
|                 use: [css.loader, 'css-loader'] | ||||
|             }, | ||||
|             { | ||||
|                 test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/, | ||||
|                 use: ['url-loader'] | ||||
|             }, | ||||
|             { | ||||
|                 // Exposes jQuery for use outside Webpack build | ||||
|                 test: require.resolve('jquery'), | ||||
|                 use: [{ | ||||
|                     loader: 'expose-loader', | ||||
|                     options: 'jQuery' | ||||
|                 }, { | ||||
|                     loader: 'expose-loader', | ||||
|                     options: '$' | ||||
|                     options: { | ||||
|                       exposes: [ | ||||
|                         { | ||||
|                           globalName: '$', | ||||
|                           override: true, | ||||
|                         }, | ||||
|                         { | ||||
|                           globalName: 'jQuery', | ||||
|                           override: true, | ||||
|                         }, | ||||
|                       ]  | ||||
|                     },                | ||||
|                 }] | ||||
|             } | ||||
|         ] | ||||
|   | ||||
							
								
								
									
										1
									
								
								towncrier/newsfragments/1567.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								towncrier/newsfragments/1567.feature
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Implement a language selector for the admin interface. | ||||
							
								
								
									
										1
									
								
								towncrier/newsfragments/1764.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								towncrier/newsfragments/1764.feature
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Implement AdminLTE 3 for the admin interface. | ||||
		Reference in New Issue
	
	Block a user
	 Florent Daigniere
					Florent Daigniere