Merge remote-tracking branch 'origin/staging' into nightly

This commit is contained in:
FreddleSpl0it
2024-08-15 12:45:52 +02:00
25 changed files with 958 additions and 127 deletions
+2 -2
View File
@@ -23,7 +23,7 @@
<li><button class="dropdown-item" data-bs-target="#tab-config-quarantine" aria-selected="false" aria-controls="tab-config-quarantine" role="tab" data-bs-toggle="tab">{{ lang.admin.quarantine }}</button></li>
<li><button class="dropdown-item" data-bs-target="#tab-config-quota" aria-selected="false" aria-controls="tab-config-quota" role="tab" data-bs-toggle="tab">{{ lang.admin.quota_notifications }}</button></li>
<li><button class="dropdown-item" data-bs-target="#tab-config-rsettings" aria-selected="false" aria-controls="tab-config-rsettings" role="tab" data-bs-toggle="tab">{{ lang.admin.rspamd_settings_map }}</button></li>
<li><button class="dropdown-item" data-bs-target="#tab-config-password-policy" aria-selected="false" aria-controls="tab-config-password-policy" role="tab" data-bs-toggle="tab">{{ lang.admin.password_policy }}</button></li>
<li><button class="dropdown-item" data-bs-target="#tab-config-password-settings" aria-selected="false" aria-controls="tab-config-password-settings" role="tab" data-bs-toggle="tab">{{ lang.admin.password_settings }}</button></li>
<li><button class="dropdown-item" data-bs-target="#tab-config-customize" aria-selected="false" aria-controls="tab-config-customize" role="tab" data-bs-toggle="tab">{{ lang.admin.customize }}</button></li>
</ul>
</li>
@@ -53,7 +53,7 @@
{% include 'admin/tab-config-quota.twig' %}
{% include 'admin/tab-config-rsettings.twig' %}
{% include 'admin/tab-config-customize.twig' %}
{% include 'admin/tab-config-password-policy.twig' %}
{% include 'admin/tab-config-password-settings.twig' %}
{% include 'admin/tab-sys-mails.twig' %}
{% include 'admin/tab-globalfilter-regex.twig' %}
</div>
@@ -1,40 +0,0 @@
<div class="tab-pane fade" id="tab-config-password-policy" role="tabpanel" aria-labelledby="tab-config-password-policy">
<div class="card mb-4">
<div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-password-policy" data-bs-toggle="collapse" aria-controls="collapse-tab-config-password-policy">
{{ lang.admin.password_policy }}
</button>
<span class="d-none d-md-block">{{ lang.admin.password_policy }}</span>
</div>
<div id="collapse-tab-config-password-policy" class="card-body collapse" data-bs-parent="#admin-content">
<form class="form-horizontal" data-id="passwordpolicy" role="form" method="post">
{% for name, value in password_complexity %}
{% if name == 'length' %}
<div class="row mb-4">
<label class="control-label col-sm-3 text-sm-end" for="length">{{ lang.admin.password_length }}:</label>
<div class="col-sm-2">
<input type="number" class="form-control" min="3" max="64" name="length" id="length" value="{{ value }}" required>
</div>
</div>
{% else %}
<input type="hidden" name="{{ name }}" value="0">
<div class="row mb-2">
<div class="offset-sm-3 col-sm-9">
<label>
<input type="checkbox" class="form-check-input" name="{{ name }}" id="{{ name }}" value="1" {% if value == 1 %}checked{% endif %}> {{ lang.admin['password_policy_'~name] }}
</label>
</div>
</div>
{% endif %}
{% endfor %}
<div class="row mt-4 mb-2">
<div class="offset-sm-3 col-sm-9">
<div class="btn-group">
<button class="btn btn-sm d-block d-sm-inline btn-success" data-item="passwordpolicy" data-action="edit_selected" data-id="passwordpolicy" data-api-url='edit/passwordpolicy' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
@@ -0,0 +1,102 @@
<div class="tab-pane fade" id="tab-config-password-settings" role="tabpanel" aria-labelledby="tab-config-password-settings">
<div class="card mb-4">
<div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-password-settings" data-bs-toggle="collapse" aria-controls="collapse-tab-config-password-settings">
{{ lang.admin.password_settings }}
</button>
<span class="d-none d-md-block">{{ lang.admin.password_settings }}</span>
</div>
<div id="collapse-tab-config-password-settings" class="card-body collapse" data-bs-parent="#admin-content">
<form class="form-horizontal" data-id="passwordpolicy" role="form" method="post">
<div class="row">
<div class="col-sm-12">
<legend>
{{ lang.admin.password_policy }}
</legend>
<hr />
</div>
</div>
{% for name, value in password_complexity %}
{% if name == 'length' %}
<div class="row mb-4">
<label class="control-label col-sm-3 text-sm-end" for="length">{{ lang.admin.password_length }}:</label>
<div class="col-sm-2">
<input type="number" class="form-control" min="3" max="64" name="length" id="length" value="{{ value }}" required>
</div>
</div>
{% else %}
<input type="hidden" name="{{ name }}" value="0">
<div class="row mb-2">
<div class="offset-sm-3 col-sm-9">
<label>
<input type="checkbox" class="form-check-input" name="{{ name }}" id="{{ name }}" value="1" {% if value == 1 %}checked{% endif %}> {{ lang.admin['password_policy_'~name] }}
</label>
</div>
</div>
{% endif %}
{% endfor %}
<div class="row mt-4 mb-2">
<div class="offset-sm-3 col-sm-9">
<div class="btn-group">
<button class="btn btn-sm d-block d-sm-inline btn-success" data-item="passwordpolicy" data-action="edit_selected" data-id="passwordpolicy" data-api-url='edit/passwordpolicy' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
</div>
</div>
</div>
</form>
<form class="form" role="form" data-id="pw_reset_notification" method="post" style="margin-top: 50px;">
<div class="row">
<div class="col-sm-12">
<legend>
{{ lang.admin.password_reset_settings }}
</legend>
<hr />
<small>{{ lang.admin.reset_password_vars|raw }}</small><br><br>
</div>
</div>
<div class="row mb-4">
<div class="col-sm-6">
<div>
<label for="pw_reset_from">{{ lang.admin.quota_notification_sender }}:</label>
<input type="email" class="form-control" id="pw_reset_from" name="from" value="{{ pw_reset_data.from }}">
</div>
</div>
<div class="col-sm-6">
<div>
<label for="pw_reset_subject">{{ lang.admin.quota_notification_subject }}:</label>
<input type="text" class="form-control" id="pw_reset_subject" name="subject" value="{{ pw_reset_data.subject }}">
</div>
</div>
</div>
<div class="row">
<div class="col-12" data-bs-target="#text_template" style="cursor:pointer" unselectable="on" data-bs-toggle="collapse">
<span class="d-block"><i style="font-size:10pt;" class="bi bi-plus-square"></i> {{ lang.admin.password_reset_tmpl_text }}</span>
<small>{{ lang.admin.restore_template }}</small>
</div>
<div id="text_template" class="col-12 collapse">
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control textarea-code mb-2" rows="20" name="text_tmpl">{{ pw_reset_data.text_tmpl|raw }}</textarea>
</div>
<div class="col-12 mt-3" data-bs-target="#html_template" style="cursor:pointer" unselectable="on" data-bs-toggle="collapse">
<span class="d-block"><i style="font-size:10pt;" class="bi bi-plus-square"></i> {{ lang.admin.password_reset_tmpl_html }}</span>
<small>{{ lang.admin.restore_template }}</small>
</div>
<div id="html_template" class="col-12 collapse">
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control textarea-code" rows="20" name="html_tmpl">{{ pw_reset_data.html_tmpl|raw }}</textarea>
</div>
</div>
<div class="row">
<div class="col-sm-10">
<div>
<br>
<a type="button" class="btn btn-sm d-block d-sm-inline btn-success" data-action="edit_selected"
data-item="pw_reset_notification"
data-id="pw_reset_notification"
data-api-url='edit/reset-password-notification'
data-api-attr='{}'><i class="bi bi-check-lg"></i> {{ lang.user.save_changes }}</a>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
@@ -112,6 +112,7 @@
<option value="quarantine_notification" {% if template.attributes.acl_quarantine_notification == '1' %} selected{% endif %}>{{ lang.acl["quarantine_notification"] }}</option>
<option value="quarantine_category" {% if template.attributes.acl_quarantine_category == '1' %} selected{% endif %}>{{ lang.acl["quarantine_category"] }}</option>
<option value="app_passwds" {% if template.attributes.acl_app_passwds == '1' %} selected{% endif %}>{{ lang.acl["app_passwds"] }}</option>
<option value="pw_reset" {% if template.attributes.acl_pw_reset == '1' %} selected{% endif %}>{{ lang.acl["pw_reset"] }}</option>
</select>
</div>
</div>
+7
View File
@@ -223,6 +223,13 @@
<input type="password" data-pwgen-field="true" class="form-control" name="password2" autocomplete="new-password">
</div>
</div>
<div class="row mb-4">
<label class="control-label col-sm-2" for="pw_recovery_email">{{ lang.edit.password_recovery_email }}</label>
<div class="col-sm-10">
<input type="email" class="form-control" name="pw_recovery_email" value="{{ result.attributes.recovery_email }}">
<small class="text-muted">{{ lang.admin.password_reset_info }}</small>
</div>
</div>
<div data-acl="{{ acl.extend_sender_acl }}" class="row mb-4">
<label class="control-label col-sm-2" for="extended_sender_acl">{{ lang.edit.extended_sender_acl }}</label>
<div class="col-sm-10">
+3
View File
@@ -69,6 +69,9 @@
{% endif %}
</div>
</form>
<div class="mt-3 mb-4">
<a href="/reset-password">{{ lang.login.forgot_password }}</a>
</div>
{% if login_delay %}
<p><div class="my-4 alert alert-info">{{ lang.login.delayed|format(login_delay) }}</b></div></p>
{% endif %}
+2
View File
@@ -152,6 +152,7 @@
<option value="quarantine_notification" selected>{{ lang.acl["quarantine_notification"] }}</option>
<option value="quarantine_category" selected>{{ lang.acl["quarantine_category"] }}</option>
<option value="app_passwds" selected>{{ lang.acl["app_passwds"] }}</option>
<option value="pw_reset" selected>{{ lang.acl["pw_reset"] }}</option>
</select>
</div>
</div>
@@ -321,6 +322,7 @@
<option value="quarantine_notification" selected>{{ lang.acl["quarantine_notification"] }}</option>
<option value="quarantine_category" selected>{{ lang.acl["quarantine_category"] }}</option>
<option value="app_passwds" selected>{{ lang.acl["app_passwds"] }}</option>
<option value="pw_reset" selected>{{ lang.acl["pw_reset"] }}</option>
</select>
</div>
</div>
+27
View File
@@ -309,6 +309,33 @@
</div>
</div>
</div><!-- pw change modal -->
<!-- pw recovery email modal -->
<div class="modal fade" id="pwRecoveryEmailModal" tabindex="-1" role="dialog" aria-labelledby="pwRecoveryEmailModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">{{ lang.user.pw_recovery_email }}</h3>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form class="form-horizontal" data-cached-form="false" data-id="pw_recovery_change" role="form" method="post" autocomplete="off">
<div class="row mb-4">
<label class="control-label col-sm-3" for="pw_recovery_email">{{ lang.user.email }}</label>
<div class="col-sm-9">
<input type="email" class="form-control" name="pw_recovery_email" value="{{ mailboxdata.attributes.recovery_email }}">
<small class="text-muted">{{ lang.user.password_reset_info }}</small>
</div>
</div>
<div class="row">
<div class="offset-sm-3 col-sm-9">
<button class="btn btn-xs-lg d-block d-sm-inline btn-success" data-action="edit_selected" data-id="pw_recovery_change" data-item="null" data-api-url='edit/self' data-api-attr='{}' href="#">{{ lang.user.save }}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div><!-- pw recovery email modal -->
<!-- temp alias modal -->
<div class="modal fade" id="tempAliasModal" tabindex="-1" role="dialog" aria-labelledby="tempAliasModalLabel">
<div class="modal-dialog modal-lg" role="document">
+57
View File
@@ -0,0 +1,57 @@
{% extends 'base.twig' %}
{% block navbar %}{% endblock %}
{% block content %}
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card-header d-flex align-items-center">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.reset_password }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
<label class="form-check-label"><i class="bi bi-moon-fill"></i></label>
<input class="form-check-input ms-2" type="checkbox" id="dark-mode-toggle">
</div>
</div>
<div class="card-body">
<div class="text-center mailcow-logo mb-4">
<img class="main-logo" src="{{ logo|default('/img/cow_mailcow.svg') }}" alt="mailcow">
<img class="main-logo-dark" src="{{ logo_dark|default('/img/cow_mailcow.svg') }}" alt="mailcow-logo-dark">
</div>
<legend>{{ ui_texts.main_name|raw }}</legend><hr />
{% if is_reset_token_valid %}
<form method="post" autofill="off">
<input type="hidden" name="token" value="{{ reset_token }}" />
<input type="password" autocorrect="off" autocapitalize="none" class="form-control mb-2" name="new_password" placeholder="{{ lang.login.new_password }}" />
<input type="password" autocorrect="off" autocapitalize="none" class="form-control mb-2" name="new_password2" placeholder="{{ lang.login.new_password_confirm }}" />
<small id="mismatch_alert" class="text-danger d-none">{{ lang.login.password_mismatch }}</small>
<div class="d-flex justify-content-end mt-4" style="position: relative">
<button type="submit" class="btn btn-xs-lg d-block d-sm-inline btn-success" name="pw_reset">{{ lang.login.reset_password }}</button>
</div>
</form>
{% elseif reset_token is null %}
<form method="post" autofill="off">
<input type="text" autocorrect="off" autocapitalize="none" class="form-control mb-2" name="username" placeholder="{{ lang.login.username }}" />
<div class="d-flex justify-content-end mt-4" style="position: relative">
<button type="submit" class="btn btn-xs-lg d-block d-sm-inline btn-success" name="pw_reset_request">{{ lang.login.request_reset_password }}</button>
</div>
</form>
{% else %}
<p class="text-center">{{ lang.login.invalid_pass_reset_token|raw }}</p>
<a href="/">{{ lang.login.back_to_mailcow }}</a>
{% endif %}
</div>
</div>
</div>
</div>
<script type='text/javascript'>
var csrf_token = '{{ csrf_token }}';
var mailcow_cc_username = '{{ mailcow_cc_username }}';
</script>
{% endblock %}
+4 -1
View File
@@ -105,8 +105,11 @@
{% if mailboxdata.authsource == "mailcow" %}
<div class="row">
<div class="col-12 col-md-3 d-flex"></div>
<div class="col-12 col-md-9 d-flex flex-wrap justify-content-center justify-content-sm-start">
<div class="col-12 col-md-9 d-flex flex-wrap">
<a class="btn btn-secondary" href="#pwChangeModal" data-bs-toggle="modal"><i class="bi bi-pencil-fill"></i> {{ lang.user.change_password }}</a>
{% if acl.pw_reset == 1 %}
<a class="btn btn-secondary ms-4" href="#pwRecoveryEmailModal" data-bs-toggle="modal"><i class="bi bi-pencil-fill"></i> {{ lang.user.pw_recovery_email }}</a></p>
{% endif %}
</div>
</div>
{% endif %}