add rewrite-subject flag to email-edit form; hide spam-related settings if 'bypass_spam' is activated; add possibility to disable rejection of spam-mails, refs #1282
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
@@ -95,6 +95,7 @@ CREATE TABLE `mail_virtual` (
|
||||
`iscatchall` tinyint(1) unsigned NOT NULL default '0',
|
||||
`description` varchar(255) NOT NULL DEFAULT '',
|
||||
`spam_tag_level` float(4,1) NOT NULL DEFAULT 7.0,
|
||||
`rewrite_subject` tinyint(1) NOT NULL default '1',
|
||||
`spam_kill_level` float(4,1) NOT NULL DEFAULT 14.0,
|
||||
`bypass_spam` tinyint(1) NOT NULL default '0',
|
||||
`policy_greylist` tinyint(1) NOT NULL default '1',
|
||||
@@ -730,7 +731,7 @@ opcache.validate_timestamps'),
|
||||
('panel', 'settings_mode', '0'),
|
||||
('panel', 'menu_collapsed', '1'),
|
||||
('panel', 'version', '2.2.1'),
|
||||
('panel', 'db_version', '202408140');
|
||||
('panel', 'db_version', '202409280');
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `panel_tasks`;
|
||||
|
||||
@@ -169,3 +169,12 @@ if (Froxlor::isFroxlorVersion('2.2.0')) {
|
||||
Update::showUpdateStep("Updating from 2.2.0 to 2.2.1", false);
|
||||
Froxlor::updateToVersion('2.2.1');
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202408140')) {
|
||||
|
||||
Update::showUpdateStep("Adding new rewrite-subject field to email table");
|
||||
Database::query("ALTER TABLE `" . TABLE_MAIL_VIRTUAL . "` ADD `rewrite_subject` tinyint(1) NOT NULL default '1' AFTER `spam_tag_level`;");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToDbVersion('202409280');
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
* domain-name for the email-address
|
||||
* @param float $spam_tag_level
|
||||
* optional, score which is required to tag emails as spam, default: 7.0
|
||||
* @param bool $rewrite_subject
|
||||
* optional, whether to add ***SPAM*** to the email's subject if applicable, default true
|
||||
* @param float $spam_kill_level
|
||||
* optional, score which is required to discard emails, default: 14.0
|
||||
* @param boolean $bypass_spam
|
||||
@@ -85,7 +87,8 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
|
||||
// parameters
|
||||
$spam_tag_level = $this->getParam('spam_tag_level', true, '7.0');
|
||||
$spam_kill_level = $this->getParam('spam_kill_level', true, '14.0');
|
||||
$rewrite_subject = $this->getBoolParam('rewrite_subject', true, 1);
|
||||
$spam_kill_level = $this->getUlParam('spam_kill_level', 'spam_kill_level_ul', true, '14.0');
|
||||
$bypass_spam = $this->getBoolParam('bypass_spam', true, 0);
|
||||
$policy_greylist = $this->getBoolParam('policy_greylist', true, 1);
|
||||
$iscatchall = $this->getBoolParam('iscatchall', true, 0);
|
||||
@@ -155,8 +158,10 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
}
|
||||
|
||||
$spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\d{1,}(\.\d{1,2})?$/', '', [7.0], true);
|
||||
$spam_kill_level = Validate::validate($spam_kill_level, 'spam_kill_level', '/^\d{1,}(\.\d{1,2})?$/', '', [14.0], true);
|
||||
$spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\d{1,}(\.\d{1})?$/', '', [7.0], true);
|
||||
if ($spam_kill_level > -1) {
|
||||
$spam_kill_level = Validate::validate($spam_kill_level, 'spam_kill_level', '/^\d{1,}(\.\d{1})?$/', '', [14.0], true);
|
||||
}
|
||||
$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);
|
||||
|
||||
$stmt = Database::prepare("
|
||||
@@ -165,6 +170,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
`email` = :email,
|
||||
`email_full` = :email_full,
|
||||
`spam_tag_level` = :spam_tag_level,
|
||||
`rewrite_subject` = :rewrite_subject,
|
||||
`spam_kill_level` = :spam_kill_level,
|
||||
`bypass_spam` = :bypass_spam,
|
||||
`policy_greylist` = :policy_greylist,
|
||||
@@ -177,6 +183,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
"email" => $email,
|
||||
"email_full" => $email_full,
|
||||
"spam_tag_level" => $spam_tag_level,
|
||||
"rewrite_subject" => $rewrite_subject,
|
||||
"spam_kill_level" => $spam_kill_level,
|
||||
"bypass_spam" => $bypass_spam,
|
||||
"policy_greylist" => $policy_greylist,
|
||||
@@ -250,6 +257,8 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
* optional, required when called as admin (if $customerid is not specified)
|
||||
* @param float $spam_tag_level
|
||||
* optional, score which is required to tag emails as spam, default: 7.0
|
||||
* @param bool $rewrite_subject
|
||||
* optional, whether to add ***SPAM*** to the email's subject if applicable, default true
|
||||
* @param float $spam_kill_level
|
||||
* optional, score which is required to discard emails, default: 14.0
|
||||
* @param boolean $bypass_spam
|
||||
@@ -283,7 +292,8 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
|
||||
// parameters
|
||||
$spam_tag_level = $this->getParam('spam_tag_level', true, $result['spam_tag_level']);
|
||||
$spam_kill_level = $this->getParam('spam_kill_level', true, $result['spam_kill_level']);
|
||||
$rewrite_subject = $this->getBoolParam('rewrite_subject', true, $result['rewrite_subject']);
|
||||
$spam_kill_level = $this->getUlParam('spam_kill_level', 'spam_kill_level_ul', true, $result['spam_kill_level']);
|
||||
$bypass_spam = $this->getBoolParam('bypass_spam', true, $result['bypass_spam']);
|
||||
$policy_greylist = $this->getBoolParam('policy_greylist', true, $result['policy_greylist']);
|
||||
$iscatchall = $this->getBoolParam('iscatchall', true, $result['iscatchall']);
|
||||
@@ -327,13 +337,16 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
|
||||
$spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\d{1,}(\.\d{1,2})?$/', '', [7.0], true);
|
||||
$spam_kill_level = Validate::validate($spam_kill_level, 'spam_kill_level', '/^\d{1,}(\.\d{1,2})?$/', '', [14.0], true);
|
||||
if ($spam_kill_level > -1) {
|
||||
$spam_kill_level = Validate::validate($spam_kill_level, 'spam_kill_level', '/^\d{1,}(\.\d{1,2})?$/', '', [14.0], true);
|
||||
}
|
||||
$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);
|
||||
|
||||
$stmt = Database::prepare("
|
||||
UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET
|
||||
`email` = :email ,
|
||||
`spam_tag_level` = :spam_tag_level,
|
||||
`rewrite_subject` = :rewrite_subject,
|
||||
`spam_kill_level` = :spam_kill_level,
|
||||
`bypass_spam` = :bypass_spam,
|
||||
`policy_greylist` = :policy_greylist,
|
||||
@@ -344,6 +357,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
$params = [
|
||||
"email" => $email,
|
||||
"spam_tag_level" => $spam_tag_level,
|
||||
"rewrite_subject" => $rewrite_subject,
|
||||
"spam_kill_level" => $spam_kill_level,
|
||||
"bypass_spam" => $bypass_spam,
|
||||
"policy_greylist" => $policy_greylist,
|
||||
|
||||
@@ -165,7 +165,7 @@ class Rspamd
|
||||
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Generating antispam config for ' . $email['email']);
|
||||
|
||||
$email['spam_tag_level'] = floatval($email['spam_tag_level']);
|
||||
$email['spam_kill_level'] = floatval($email['spam_kill_level']);
|
||||
$email['spam_kill_level'] = $email['spam_kill_level'] == -1 ? "null" : floatval($email['spam_kill_level']);
|
||||
$email_id = md5($email['email']);
|
||||
|
||||
$this->frx_settings_file .= '# Email: ' . $email['email'] . "\n";
|
||||
@@ -185,7 +185,9 @@ class Rspamd
|
||||
$this->frx_settings_file .= ' apply {' . "\n";
|
||||
$this->frx_settings_file .= ' actions {' . "\n";
|
||||
$this->frx_settings_file .= ' "add header" = ' . $email['spam_tag_level'] . ';' . "\n";
|
||||
$this->frx_settings_file .= ' rewrite_subject = ' . ($email['spam_tag_level'] + 0.01) . ';' . "\n";
|
||||
if ((int)$email['rewrite_subject'] == 1) {
|
||||
$this->frx_settings_file .= ' rewrite_subject = ' . ($email['spam_tag_level'] + 0.01) . ';' . "\n";
|
||||
}
|
||||
$this->frx_settings_file .= ' reject = ' . $email['spam_kill_level'] . ';' . "\n";
|
||||
if ($type == 'rcpt' && (int)$email['policy_greylist'] == 0) {
|
||||
$this->frx_settings_file .= ' greylist = null;' . "\n";
|
||||
|
||||
@@ -34,7 +34,7 @@ final class Froxlor
|
||||
const VERSION = '2.2.1';
|
||||
|
||||
// Database version (YYYYMMDDC where C is a daily counter)
|
||||
const DBVERSION = '202408140';
|
||||
const DBVERSION = '202409280';
|
||||
|
||||
// Distribution branding-tag (used for Debian etc.)
|
||||
const BRANDING = '';
|
||||
|
||||
@@ -96,20 +96,6 @@ return [
|
||||
'value' => '1',
|
||||
'checked' => (int)$result['iscatchall'],
|
||||
],
|
||||
'spam_tag_level' => [
|
||||
'visible' => Settings::Get('antispam.activated') == '1',
|
||||
'label' => lng('antispam.spam_tag_level'),
|
||||
'type' => 'text',
|
||||
'string_regexp' => '/^\d{1,}(\.\d{1,2})?$/',
|
||||
'value' => $result['spam_tag_level']
|
||||
],
|
||||
'spam_kill_level' => [
|
||||
'visible' => Settings::Get('antispam.activated') == '1',
|
||||
'label' => lng('antispam.spam_kill_level'),
|
||||
'type' => 'text',
|
||||
'string_regexp' => '/^\d{1,}(\.\d{1,2})?$/',
|
||||
'value' => $result['spam_kill_level']
|
||||
],
|
||||
'bypass_spam' => [
|
||||
'visible' => Settings::Get('antispam.activated') == '1',
|
||||
'label' => lng('antispam.bypass_spam'),
|
||||
@@ -117,6 +103,29 @@ return [
|
||||
'value' => '1',
|
||||
'checked' => (int)$result['bypass_spam'],
|
||||
],
|
||||
'spam_tag_level' => [
|
||||
'visible' => Settings::Get('antispam.activated') == '1',
|
||||
'label' => lng('antispam.spam_tag_level'),
|
||||
'type' => 'number',
|
||||
'min' => 0,
|
||||
'step' => 0.1,
|
||||
'value' => $result['spam_tag_level'],
|
||||
],
|
||||
'spam_rewrite_subject' => [
|
||||
'visible' => Settings::Get('antispam.activated') == '1',
|
||||
'label' => lng('antispam.rewrite_subject'),
|
||||
'type' => 'checkbox',
|
||||
'value' => '1',
|
||||
'checked' => (int)$result['rewrite_subject'],
|
||||
],
|
||||
'spam_kill_level' => [
|
||||
'visible' => Settings::Get('antispam.activated') == '1',
|
||||
'label' => lng('antispam.spam_kill_level'),
|
||||
'desc' => lng('panel.use_checkbox_to_disable'),
|
||||
'type' => 'textul',
|
||||
'step' => 0.1,
|
||||
'value' => $result['spam_kill_level']
|
||||
],
|
||||
'policy_greylist' => [
|
||||
'visible' => Settings::Get('antispam.activated') == '1',
|
||||
'label' => lng('antispam.policy_greylist'),
|
||||
|
||||
@@ -621,6 +621,10 @@ return [
|
||||
'title' => 'Spam Level',
|
||||
'description' => 'Erforderliche Punktzahl zum Markieren einer E-Mail als Spam<br/>Standard: 7.0'
|
||||
],
|
||||
'rewrite_subject' => [
|
||||
'title' => 'Betreff ändern',
|
||||
'description' => 'Dem E-Mail Betreff <strong>***SPAM***</strong> hinzufügen, sofern zutreffend',
|
||||
],
|
||||
'spam_kill_level' => [
|
||||
'title' => 'Ablehnungs Level',
|
||||
'description' => 'Erforderliche Punktzahl für das Ablehnen einer E-Mail<br/>Standard: 14.0'
|
||||
@@ -1262,6 +1266,7 @@ Vielen Dank, Ihr Administrator',
|
||||
'upload_import' => 'Hochladen und importieren',
|
||||
'profile' => 'Mein Profil',
|
||||
'use_checkbox_for_unlimited' => 'Der Wert "0" deaktiviert die Resource. Die Checkbox rechts erlaubt "unlimitierte" Nutzung.',
|
||||
'use_checkbox_to_disable' => 'Zum Deaktivieren, klicke die Checkbox auf der rechten Seite des Eingabefeldes',
|
||||
],
|
||||
'phpfpm' => [
|
||||
'vhost_httpuser' => 'Lokaler Benutzer für PHP-FPM (Froxlor-Vhost)',
|
||||
|
||||
@@ -670,6 +670,10 @@ return [
|
||||
'title' => 'Spam tag level',
|
||||
'description' => 'Score that is required to mark an email as spam<br/>Default: 7.0'
|
||||
],
|
||||
'rewrite_subject' => [
|
||||
'title' => 'Rewrite subject',
|
||||
'description' => 'Whether to add <strong>***SPAM***</strong> to the email subject if applicable',
|
||||
],
|
||||
'spam_kill_level' => [
|
||||
'title' => 'Spam kill level',
|
||||
'description' => 'Score that is required to discard an email entirely<br/>Default: 14.0'
|
||||
@@ -1377,6 +1381,7 @@ Yours sincerely, your administrator',
|
||||
'upload_import' => 'Upload and import',
|
||||
'profile' => 'My profile',
|
||||
'use_checkbox_for_unlimited' => 'The value "0" deactivates this resource. The checkbox on the right allows "unlimited" usage.',
|
||||
'use_checkbox_to_disable' => 'To disable, activate the checkbox on the right of the input field',
|
||||
],
|
||||
'phpfpm' => [
|
||||
'vhost_httpuser' => 'Local user to use for PHP-FPM (Froxlor vHost)',
|
||||
|
||||
33
templates/Froxlor/assets/js/jquery/emails.js
vendored
Normal file
33
templates/Froxlor/assets/js/jquery/emails.js
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
|
||||
/**
|
||||
* bypass spam - hide unnecessary/unused sections
|
||||
*/
|
||||
if ($('#id') && $('#bypass_spam').is(':checked')) {
|
||||
$('#spam_tag_level').closest('.row').addClass('d-none');
|
||||
$('#spam_rewrite_subject').closest('.row').addClass('d-none');
|
||||
$('#spam_kill_level').closest('.row').addClass('d-none');
|
||||
$('#policy_greylist').closest('.row').addClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle show/hide of sections in case of bypass spam flag
|
||||
*/
|
||||
$('#bypass_spam').on('click', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
// hide unnecessary sections
|
||||
$('#spam_tag_level').closest('.row').addClass('d-none');
|
||||
$('#spam_rewrite_subject').closest('.row').addClass('d-none');
|
||||
$('#spam_kill_level').closest('.row').addClass('d-none');
|
||||
$('#policy_greylist').closest('.row').addClass('d-none');
|
||||
} else {
|
||||
// show sections
|
||||
$('#spam_tag_level').closest('.row').removeClass('d-none');
|
||||
$('#spam_rewrite_subject').closest('.row').removeClass('d-none');
|
||||
$('#spam_kill_level').closest('.row').removeClass('d-none');
|
||||
$('#policy_greylist').closest('.row').removeClass('d-none');
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -19,6 +19,8 @@
|
||||
{% endif %}
|
||||
{% if field.label.description is defined and field.label.description is not empty %}<br><small>{{ field.label.description|raw }}</small>
|
||||
{% endif %}
|
||||
{% if field.desc is defined and field.desc is not empty %}<br><small>{{ field.desc|raw }}</small>
|
||||
{% endif %}
|
||||
{% if field.requires_reconf is defined and field.requires_reconf is not empty %}
|
||||
<div class="bg-info bg-opacity-25 rounded p-2 mt-2 d-flex align-items-center" role="alert">
|
||||
<i class="fa-solid fa-circle-exclamation me-2"></i><p class="mb-0">{{ lng('serversettings.requires_reconfiguration', [field.requires_reconf|join(', ')])|raw }}</p>
|
||||
@@ -170,7 +172,7 @@
|
||||
{% if field.next_to is defined %}
|
||||
<div class="input-group">
|
||||
{% endif %}
|
||||
<input type="{{ field.type }}" {% if (field.visible is defined and field.visible == false) or (field.disabled is defined and field.disabled == true) %} disabled {% endif %} {% if field.type == 'number' and field.min is defined %} min="{{ field.min }}" {% endif %} {% if field.type == 'number' and field.max is defined %} max="{{ field.max }}" {% endif %} {% if field.type != 'number' and field.maxlength is defined %} maxlength="{{ field.maxlength }}" {% endif %} id="{{ id }}" name="{{ id }}" value="{{ field.value|raw }}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.autocomplete is defined %} autocomplete="{{ field.autocomplete }}" {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %} {% if field.type == 'file' and field.accept is defined %} accept="{{ field.accept }}" {% endif %} {% if field.pattern is defined %} pattern="{{ field.pattern }}" {% endif %}/>
|
||||
<input type="{{ field.type }}" {% if (field.visible is defined and field.visible == false) or (field.disabled is defined and field.disabled == true) %} disabled {% endif %} {% if field.type == 'number' and field.min is defined %} min="{{ field.min }}" {% endif %} {% if field.type == 'number' and field.max is defined %} max="{{ field.max }}" {% endif %} {% if field.type == 'number' and field.step is not empty %} step="{{ field.step }}" {% endif %} {% if field.type != 'number' and field.maxlength is defined %} maxlength="{{ field.maxlength }}" {% endif %} id="{{ id }}" name="{{ id }}" value="{{ field.value|raw }}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.autocomplete is defined %} autocomplete="{{ field.autocomplete }}" {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %} {% if field.type == 'file' and field.accept is defined %} accept="{{ field.accept }}" {% endif %} {% if field.pattern is defined %} pattern="{{ field.pattern }}" {% endif %}/>
|
||||
{% if field.type == 'hidden' and field.display is defined %}
|
||||
<input type="text" readonly class="form-control-plaintext" value="{{ field.display|raw }}">
|
||||
{% endif %}
|
||||
@@ -207,7 +209,7 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="input-group">
|
||||
<input type="number" min="0" {% if max is not empty %} max="{{ max }}" {% endif %} id="{{ id }}" name="{{ id }}" value="{% if field.value >= 0 %}{{ field.value }}{% endif %}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.autocomplete is defined %} autocomplete="{{ field.autocomplete }}" {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %}/>
|
||||
<input type="number" min="0" {% if max is not empty %} max="{{ max }}" {% endif %}{% if field.step is not empty %} step="{{ field.step }}" {% endif %} id="{{ id }}" name="{{ id }}" value="{% if field.value >= 0 %}{{ field.value }}{% endif %}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.autocomplete is defined %} autocomplete="{{ field.autocomplete }}" {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %}/>
|
||||
<div class="input-group-text">
|
||||
<input class="form-check-input mt-0" type="checkbox" name="{{ id }}_ul" value="1" {% if field.value == -1 %} checked="checked" {% endif %}>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user