[Web] Allow a user to choose notification categories (junk folder, rejected mail, both/all) + user ACL

master
andryyy 2020-11-28 17:41:48 +01:00
parent 752efa2188
commit ba20db2e08
No known key found for this signature in database
GPG Key ID: 8EC34FF2794E25EF
13 changed files with 204 additions and 23 deletions

View File

@ -58,8 +58,13 @@ def query_mysql(query, headers = True, update = False):
cur.close() cur.close()
cnx.close() cnx.close()
def notify_rcpt(rcpt, msg_count, quarantine_acl): def notify_rcpt(rcpt, msg_count, quarantine_acl, category):
meta_query = query_mysql('SELECT SHA2(CONCAT(id, qid), 256) AS qhash, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = "%s" AND score < %f' % (rcpt, max_score)) if category == "add_header": category = "add header"
meta_query = query_mysql('SELECT SHA2(CONCAT(id, qid), 256) AS qhash, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = "%s" AND score < %f AND (action = "%s" OR "all" = "%s")' % (rcpt, max_score, category, category))
print("%s: %d of %d messages qualify for notification" % (rcpt, len(meta_query), msg_count))
if len(meta_query) == 0:
return
msg_count = len(meta_query)
if r.get('Q_HTML'): if r.get('Q_HTML'):
try: try:
template = Template(r.get('Q_HTML')) template = Template(r.get('Q_HTML'))
@ -117,6 +122,11 @@ records = query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl,
for record in records: for record in records:
attrs = '' attrs = ''
attrs_json = '' attrs_json = ''
time_trans = {
"hourly": 3600,
"daily": 86400,
"weekly": 604800
}
try: try:
last_notification = int(r.hget('Q_LAST_NOTIFIED', record['rcpt'])) last_notification = int(r.hget('Q_LAST_NOTIFIED', record['rcpt']))
if last_notification > time_now: if last_notification > time_now:
@ -133,18 +143,8 @@ for record in records:
else: else:
# if it's bytes then decode and load it # if it's bytes then decode and load it
attrs = json.loads(attrs.decode('utf-8')) attrs = json.loads(attrs.decode('utf-8'))
if attrs['quarantine_notification'] not in ('hourly', 'daily', 'weekly', 'never'): if attrs['quarantine_notification'] not in ('hourly', 'daily', 'weekly'):
print('Abnormal quarantine_notification value')
continue continue
if attrs['quarantine_notification'] == 'hourly': if last_notification == 0 or (last_notification + time_trans[attrs['quarantine_notification']]) < time_now:
if last_notification == 0 or (last_notification + 3600) < time_now: print("Notifying %s: Considering %d new items in quarantine (policy: %s)" % (record['rcpt'], record['counter'], attrs['quarantine_notification']))
print("Notifying %s: Considering %d new items in quarantine" % (record['rcpt'], record['counter'])) notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category'])
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'])
elif attrs['quarantine_notification'] == 'daily':
if last_notification == 0 or (last_notification + 86400) < time_now:
print("Notifying %s: Considering %d new items in quarantine" % (record['rcpt'], record['counter']))
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'])
elif attrs['quarantine_notification'] == 'weekly':
if last_notification == 0 or (last_notification + 604800) < time_now:
print("Notifying %s: Considering %d new items in quarantine" % (record['rcpt'], record['counter']))
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'])

View File

@ -572,6 +572,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
$rl = ratelimit('get', 'mailbox', $mailbox); $rl = ratelimit('get', 'mailbox', $mailbox);
$pushover_data = pushover('get', $mailbox); $pushover_data = pushover('get', $mailbox);
$quarantine_notification = mailbox('get', 'quarantine_notification', $mailbox); $quarantine_notification = mailbox('get', 'quarantine_notification', $mailbox);
$quarantine_category = mailbox('get', 'quarantine_category', $mailbox);
$get_tls_policy = mailbox('get', 'tls_policy', $mailbox); $get_tls_policy = mailbox('get', 'tls_policy', $mailbox);
if (!empty($result)) { if (!empty($result)) {
?> ?>
@ -660,7 +661,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="sender_acl"><?=$lang['user']['quarantine_notification'];?></label> <label class="control-label col-sm-2"><?=$lang['user']['quarantine_notification'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine_notification'];?>"> <div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine_notification'];?>">
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "never") ? "active" : null;?>" <button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "never") ? "active" : null;?>"
@ -688,10 +689,35 @@ if (isset($_SESSION['mailcow_cc_role'])) {
data-api-url='edit/quarantine_notification' data-api-url='edit/quarantine_notification'
data-api-attr='{"quarantine_notification":"weekly"}'><?=$lang['user']['weekly'];?></button> data-api-attr='{"quarantine_notification":"weekly"}'><?=$lang['user']['weekly'];?></button>
</div> </div>
<div style="display:none" id="user_acl_q_notify_disabled"><?=$lang['edit']['user_acl_q_notify_disabled'];?></div>
<p class="help-block"><small><?=$lang['user']['quarantine_notification_info'];?></small></p> <p class="help-block"><small><?=$lang['user']['quarantine_notification_info'];?></small></p>
</div> </div>
</div> </div>
<div class="form-group">
<label class="control-label col-sm-2"><?=$lang['user']['quarantine_category'];?></label>
<div class="col-sm-10">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine_category'];?>">
<button type="button" class="btn btn-sm btn-default <?=($quarantine_category == "reject") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($mailbox); ?>"
data-id="quarantine_category"
data-api-url='edit/quarantine_category'
data-api-attr='{"quarantine_category":"reject"}'><?=$lang['user']['q_reject'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($quarantine_category == "add_header") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($mailbox); ?>"
data-id="quarantine_category"
data-api-url='edit/quarantine_category'
data-api-attr='{"quarantine_category":"add_header"}'><?=$lang['user']['q_add_header'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($quarantine_category == "all") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($mailbox); ?>"
data-id="quarantine_category"
data-api-url='edit/quarantine_category'
data-api-attr='{"quarantine_category":"all"}'><?=$lang['user']['q_all'];?></button>
</div>
<p class="help-block"><small><?=$lang['user']['quarantine_category_info'];?></small></p>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="sender_acl"><?=$lang['user']['tls_policy'];?></label> <label class="control-label col-sm-2" for="sender_acl"><?=$lang['user']['tls_policy'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">

View File

@ -332,6 +332,9 @@ function hasDomainAccess($username, $role, $domain) {
} }
function hasMailboxObjectAccess($username, $role, $object) { function hasMailboxObjectAccess($username, $role, $object) {
global $pdo; global $pdo;
if (empty($username) || empty($role) || empty($object)) {
return false;
}
if (!filter_var(html_entity_decode(rawurldecode($username)), FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) { if (!filter_var(html_entity_decode(rawurldecode($username)), FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
return false; return false;
} }

View File

@ -949,6 +949,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$pop3_access = (isset($_data['pop3_access'])) ? intval($_data['pop3_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']); $pop3_access = (isset($_data['pop3_access'])) ? intval($_data['pop3_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
$smtp_access = (isset($_data['smtp_access'])) ? intval($_data['smtp_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']); $smtp_access = (isset($_data['smtp_access'])) ? intval($_data['smtp_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
$quarantine_notification = (isset($_data['quarantine_notification'])) ? strval($_data['quarantine_notification']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']); $quarantine_notification = (isset($_data['quarantine_notification'])) ? strval($_data['quarantine_notification']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
$quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
$quota_b = ($quota_m * 1048576); $quota_b = ($quota_m * 1048576);
$mailbox_attrs = json_encode( $mailbox_attrs = json_encode(
array( array(
@ -960,7 +961,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'pop3_access' => strval($pop3_access), 'pop3_access' => strval($pop3_access),
'smtp_access' => strval($smtp_access), 'smtp_access' => strval($smtp_access),
'mailbox_format' => strval($MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format']), 'mailbox_format' => strval($MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format']),
'quarantine_notification' => strval($quarantine_notification) 'quarantine_notification' => strval($quarantine_notification),
'quarantine_category' => strval($quarantine_category)
) )
); );
if (!is_valid_domain_name($domain)) { if (!is_valid_domain_name($domain)) {
@ -1409,6 +1411,65 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
} }
break; break;
case 'quarantine_category':
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
if (!isset($_SESSION['acl']['quarantine_category']) || $_SESSION['acl']['quarantine_category'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
foreach ($usernames as $username) {
if (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
$is_now = mailbox('get', 'quarantine_category', $username);
if (!empty($is_now)) {
$quarantine_category = (isset($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
if (!in_array($quarantine_category, array('add_header', 'reject', 'all'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("UPDATE `mailbox`
SET `attributes` = JSON_SET(`attributes`, '$.quarantine_category', :quarantine_category)
WHERE `username` = :username");
$stmt->execute(array(
':quarantine_category' => $quarantine_category,
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('mailbox_modified', $username)
);
}
break;
case 'spam_score': case 'spam_score':
if (!is_array($_data['username'])) { if (!is_array($_data['username'])) {
$usernames = array(); $usernames = array();
@ -2803,6 +2864,22 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attrs = json_decode($attrs['attributes'], true); $attrs = json_decode($attrs['attributes'], true);
return $attrs['quarantine_notification']; return $attrs['quarantine_notification'];
break; break;
case 'quarantine_category':
$attrs = array();
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
}
else {
$_data = $_SESSION['mailcow_cc_username'];
}
$stmt = $pdo->prepare("SELECT `attributes` FROM `mailbox` WHERE `username` = :username");
$stmt->execute(array(':username' => $_data));
$attrs = $stmt->fetch(PDO::FETCH_ASSOC);
$attrs = json_decode($attrs['attributes'], true);
return $attrs['quarantine_category'];
break;
case 'filters': case 'filters':
$filters = array(); $filters = array();
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) { if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try { try {
global $pdo; global $pdo;
$db_version = "16112020_1210"; $db_version = "28112020_1210";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -401,6 +401,7 @@ function init_db_schema() {
"quarantine" => "TINYINT(1) NOT NULL DEFAULT '1'", "quarantine" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine_attachments" => "TINYINT(1) NOT NULL DEFAULT '1'", "quarantine_attachments" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine_notification" => "TINYINT(1) NOT NULL DEFAULT '1'", "quarantine_notification" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine_category" => "TINYINT(1) NOT NULL DEFAULT '1'",
"app_passwds" => "TINYINT(1) NOT NULL DEFAULT '1'", "app_passwds" => "TINYINT(1) NOT NULL DEFAULT '1'",
), ),
"keys" => array( "keys" => array(
@ -1185,6 +1186,7 @@ function init_db_schema() {
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.smtp_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.smtp_access') IS NULL;"); $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.smtp_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.smtp_access') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.mailbox_format', \"maildir:\") WHERE JSON_VALUE(`attributes`, '$.mailbox_format') IS NULL;"); $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.mailbox_format', \"maildir:\") WHERE JSON_VALUE(`attributes`, '$.mailbox_format') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.quarantine_notification', \"never\") WHERE JSON_VALUE(`attributes`, '$.quarantine_notification') IS NULL;"); $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.quarantine_notification', \"never\") WHERE JSON_VALUE(`attributes`, '$.quarantine_notification') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.quarantine_category', \"reject\") WHERE JSON_VALUE(`attributes`, '$.quarantine_category') IS NULL;");
foreach($tls_options as $tls_user => $tls_options) { foreach($tls_options as $tls_user => $tls_options) {
$stmt = $pdo->prepare("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.tls_enforce_in', :tls_enforce_in), $stmt = $pdo->prepare("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.tls_enforce_in', :tls_enforce_in),
`attributes` = JSON_SET(`attributes`, '$.tls_enforce_out', :tls_enforce_out) `attributes` = JSON_SET(`attributes`, '$.tls_enforce_out', :tls_enforce_out)

View File

@ -167,6 +167,12 @@ $MAILBOX_DEFAULT_ATTRIBUTES['pop3_access'] = true;
// Mailbox has SMTP access by default // Mailbox has SMTP access by default
$MAILBOX_DEFAULT_ATTRIBUTES['smtp_access'] = true; $MAILBOX_DEFAULT_ATTRIBUTES['smtp_access'] = true;
// Mailbox receives notifications about...
// "add_header" - mail that was put into the Junk folder
// "reject" - mail that was rejected
// "all" - mail that was rejected and put into the Junk folder
$MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category'] = 'reject';
// Default mailbox format, should not be changed unless you know exactly, what you do, keep the trailing ":" // Default mailbox format, should not be changed unless you know exactly, what you do, keep the trailing ":"
// Check dovecot.conf for further changes (e.g. shared namespace) // Check dovecot.conf for further changes (e.g. shared namespace)
$MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format'] = 'maildir:'; $MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format'] = 'maildir:';

View File

@ -371,13 +371,14 @@ jQuery(function($){
'<div class="label label-last-login">SMTP @ ' + unix_time_format(Number(res[2])) + '</div>'; '<div class="label label-last-login">SMTP @ ' + unix_time_format(Number(res[2])) + '</div>';
}}, }},
{"name":"quarantine_notification","filterable": false,"title":lang.quarantine_notification,"breakpoints":"all"}, {"name":"quarantine_notification","filterable": false,"title":lang.quarantine_notification,"breakpoints":"all"},
{"name":"quarantine_category","filterable": false,"title":lang.quarantine_category,"breakpoints":"all"},
{"name":"in_use","filterable": false,"type":"html","title":lang.in_use,"sortValue": function(value){ {"name":"in_use","filterable": false,"type":"html","title":lang.in_use,"sortValue": function(value){
return Number($(value).find(".progress-bar-mailbox").attr('aria-valuenow')); return Number($(value).find(".progress-bar-mailbox").attr('aria-valuenow'));
}, },
}, },
{"name":"messages","filterable": false,"title":lang.msg_num,"breakpoints":"xs sm md"}, {"name":"messages","filterable": false,"title":lang.msg_num,"breakpoints":"xs sm md"},
{"name":"rl","title":"RL","breakpoints":"all","style":{"width":"125px"}}, {"name":"rl","title":"RL","breakpoints":"all","style":{"width":"125px"}},
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active,"formatter": function(value){return 1==value?'&#10003;':0==value&&'&#10005;';}}, {"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active,"formatter": function(value){return 1==value?'&#10003;':(0==value?'&#10005;':2==value&&'&#8212;');}},
{"name":"action","filterable": false,"sortable": false,"style":{"min-width":"290px","text-align":"right"},"type":"html","title":lang.action,"breakpoints":"xs sm md"} {"name":"action","filterable": false,"sortable": false,"style":{"min-width":"290px","text-align":"right"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
], ],
"empty": lang.empty, "empty": lang.empty,
@ -418,6 +419,13 @@ jQuery(function($){
} else if (item.attributes.quarantine_notification === 'weekly') { } else if (item.attributes.quarantine_notification === 'weekly') {
item.quarantine_notification = lang.weekly; item.quarantine_notification = lang.weekly;
} }
if (item.attributes.quarantine_category === 'reject') {
item.quarantine_category = '<span class="text-danger">' + lang.q_reject + '</span>';
} else if (item.attributes.quarantine_category === 'add_header') {
item.quarantine_category = '<span class="text-warning">' + lang.q_add_header + '</span>';
} else if (item.attributes.quarantine_category === 'all') {
item.quarantine_category = lang.q_all;
}
if (acl_data.login_as === 1) { if (acl_data.login_as === 1) {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' + '<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +

View File

@ -1576,6 +1576,9 @@ if (isset($_GET['query'])) {
case "quarantine_notification": case "quarantine_notification":
process_edit_return(mailbox('edit', 'quarantine_notification', array_merge(array('username' => $items), $attr))); process_edit_return(mailbox('edit', 'quarantine_notification', array_merge(array('username' => $items), $attr)));
break; break;
case "quarantine_category":
process_edit_return(mailbox('edit', 'quarantine_category', array_merge(array('username' => $items), $attr)));
break;
case "qitem": case "qitem":
process_edit_return(quarantine('edit', array_merge(array('id' => $items), $attr))); process_edit_return(quarantine('edit', array_merge(array('id' => $items), $attr)));
break; break;

View File

@ -14,6 +14,7 @@
"quarantine": "Quarantäne-Aktionen", "quarantine": "Quarantäne-Aktionen",
"quarantine_attachments": "Anhänge aus Quarantäne", "quarantine_attachments": "Anhänge aus Quarantäne",
"quarantine_notification": "Ändern der Quarantäne-Benachrichtigung", "quarantine_notification": "Ändern der Quarantäne-Benachrichtigung",
"quarantine_category": "Ändern der Quarantäne-Benachrichtigungskategorie",
"ratelimit": "Rate limit", "ratelimit": "Rate limit",
"recipient_maps": "Empfängerumschreibungen", "recipient_maps": "Empfängerumschreibungen",
"smtp_ip_access": "Verwalten der erlaubten Hosts für SMTP", "smtp_ip_access": "Verwalten der erlaubten Hosts für SMTP",
@ -691,7 +692,11 @@
"owner": "Besitzer", "owner": "Besitzer",
"private_comment": "Privater Kommentar", "private_comment": "Privater Kommentar",
"public_comment": "Öffentlicher Kommentar", "public_comment": "Öffentlicher Kommentar",
"q_add_header": "Junk-Ordner",
"q_all": "Alle Kategorien",
"q_reject": "Abgelehnt",
"quarantine_notification": "Quarantäne-Benachrichtigung", "quarantine_notification": "Quarantäne-Benachrichtigung",
"quarantine_category": "Quarantäne-Benachrichtigungskategorie",
"quick_actions": "Aktionen", "quick_actions": "Aktionen",
"recipient_map": "Empfängerumschreibung", "recipient_map": "Empfängerumschreibung",
"recipient_map_info": "Empfängerumschreibung ersetzen den Empfänger einer E-Mail vor dem Versand.", "recipient_map_info": "Empfängerumschreibung ersetzen den Empfänger einer E-Mail vor dem Versand.",
@ -996,8 +1001,13 @@
"pushover_title": "Notification Titel", "pushover_title": "Notification Titel",
"pushover_vars": "Wenn kein Sender-Filter definiert ist, werden alle E-Mails berücksichtigt.<br>Die direkte Absenderprüfung und reguläre Ausdrücke werden unabhängig voneinander geprüft, sie <b>hängen nicht voneinander ab</b> und werden der Reihe nach ausgeführt. <br>Verwendbare Variablen für Titel und Text (Datenschutzrichtlinien beachten)", "pushover_vars": "Wenn kein Sender-Filter definiert ist, werden alle E-Mails berücksichtigt.<br>Die direkte Absenderprüfung und reguläre Ausdrücke werden unabhängig voneinander geprüft, sie <b>hängen nicht voneinander ab</b> und werden der Reihe nach ausgeführt. <br>Verwendbare Variablen für Titel und Text (Datenschutzrichtlinien beachten)",
"pushover_verify": "Verbindung verifizieren", "pushover_verify": "Verbindung verifizieren",
"q_add_header": "Junk-Ordner",
"q_all": "Alle Kategorien",
"q_reject": "Abgelehnt",
"quarantine_notification": "Quarantäne-Benachrichtigung", "quarantine_notification": "Quarantäne-Benachrichtigung",
"quarantine_category": "Quarantäne-Benachrichtigungskategorie",
"quarantine_notification_info": "Wurde über eine E-Mail in Quarantäne informiert, wird sie als \"benachrichtigt\" markiert und keine weitere Benachrichtigung zu dieser E-Mail versendet.", "quarantine_notification_info": "Wurde über eine E-Mail in Quarantäne informiert, wird sie als \"benachrichtigt\" markiert und keine weitere Benachrichtigung zu dieser E-Mail versendet.",
"quarantine_category_info": "Die Kategorie \"Abgelehnt\" informiert über abgelehnte E-Mails, während \"Junk-Ordner\" über E-Mails berichtet, die im Junk-Ordner des jeweiligen Benutzers abgelegt wurden.",
"remove": "Entfernen", "remove": "Entfernen",
"running": "Wird ausgeführt", "running": "Wird ausgeführt",
"save": "Änderungen speichern", "save": "Änderungen speichern",

View File

@ -14,6 +14,7 @@
"quarantine": "Quarantine actions", "quarantine": "Quarantine actions",
"quarantine_attachments": "Quarantine attachments", "quarantine_attachments": "Quarantine attachments",
"quarantine_notification": "Change quarantine notifications", "quarantine_notification": "Change quarantine notifications",
"quarantine_category": "Change quarantine notification category",
"ratelimit": "Rate limit", "ratelimit": "Rate limit",
"recipient_maps": "Recipient maps", "recipient_maps": "Recipient maps",
"smtp_ip_access": "Change allowed hosts for SMTP", "smtp_ip_access": "Change allowed hosts for SMTP",
@ -691,7 +692,11 @@
"owner": "Owner", "owner": "Owner",
"private_comment": "Private comment", "private_comment": "Private comment",
"public_comment": "Public comment", "public_comment": "Public comment",
"q_add_header": "Junk folder",
"q_all": "All categories",
"q_reject": "Rejected",
"quarantine_notification": "Quarantine notifications", "quarantine_notification": "Quarantine notifications",
"quarantine_category": "Quarantine notification category",
"quick_actions": "Actions", "quick_actions": "Actions",
"recipient_map": "Recipient map", "recipient_map": "Recipient map",
"recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.", "recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.",
@ -997,8 +1002,13 @@
"pushover_title": "Notification title", "pushover_title": "Notification title",
"pushover_vars": "When no sender filter is defined, all mails will be considered.<br>Regex filters as well as exact sender checks can be defined individually and will be considered sequentially. They do not depend on each other.<br>Useable variables for text and title (please take note of data protection policies)", "pushover_vars": "When no sender filter is defined, all mails will be considered.<br>Regex filters as well as exact sender checks can be defined individually and will be considered sequentially. They do not depend on each other.<br>Useable variables for text and title (please take note of data protection policies)",
"pushover_verify": "Verify credentials", "pushover_verify": "Verify credentials",
"q_add_header": "Junk folder",
"q_all": "All categories",
"q_reject": "Rejected",
"quarantine_notification": "Quarantine notifications", "quarantine_notification": "Quarantine notifications",
"quarantine_category": "Quarantine notification category",
"quarantine_notification_info": "Once a notification has been sent, items will be marked as \"notified\" and no further notifications will be sent for this particular item.", "quarantine_notification_info": "Once a notification has been sent, items will be marked as \"notified\" and no further notifications will be sent for this particular item.",
"quarantine_category_info": "The notification category \"Rejected\" includes mail that was rejected, while \"Junk folder\" will notify a user about mails that were put into the junk folder.",
"remove": "Remove", "remove": "Remove",
"running": "Running", "running": "Running",
"save": "Save changes", "save": "Save changes",

View File

@ -116,7 +116,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<?=$lang['mailbox']['sogo_allow_admin_hint'];?> <?=$lang['mailbox']['sogo_allow_admin_hint'];?>
</div> </div>
<?php } ?> <?php } ?>
<!-- <div class="mass-actions-mailbox" data-actions-header="true"></div> --> <div class="mass-actions-mailbox" data-actions-header="true"></div>
<div class="table-responsive"> <div class="table-responsive">
<table id="mailbox_table" class="table table-striped"></table> <table id="mailbox_table" class="table table-striped"></table>
</div> </div>
@ -144,6 +144,10 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"weekly"}' href="#"><?=$lang['user']['weekly'];?></a></li> <li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"weekly"}' href="#"><?=$lang['user']['weekly'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"never"}' href="#"><?=$lang['user']['never'];?></a></li> <li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"never"}' href="#"><?=$lang['user']['never'];?></a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_category' data-api-attr='{"quarantine_category":"reject"}' href="#"><?=$lang['user']['q_reject'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_category' data-api-attr='{"quarantine_category":"add_header"}' href="#"><?=$lang['user']['q_add_header'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_category' data-api-attr='{"quarantine_category":"all"}' href="#"><?=$lang['user']['q_all'];?></a></li>
<li role="separator" class="divider"></li>
<li class="dropdown-header"><?=$lang['mailbox']['allowed_protocols'];?></li> <li class="dropdown-header"><?=$lang['mailbox']['allowed_protocols'];?></li>
<li class="dropdown-header">IMAP</li> <li class="dropdown-header">IMAP</li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"imap_access":1}' href="#"><?=$lang['mailbox']['activate'];?></a></li> <li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"imap_access":1}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
@ -165,6 +169,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['mailbox'];?> <span class="caret"></span></a> <a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['mailbox'];?> <span class="caret"></span></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li> <li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"active":"2"}' href="#"><?=$lang['mailbox']['disable_login'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li> <li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
<li><a data-action="delete_selected" data-id="mailbox" data-api-url='delete/mailbox' href="#"><?=$lang['mailbox']['remove'];?></a></li> <li><a data-action="delete_selected" data-id="mailbox" data-api-url='delete/mailbox' href="#"><?=$lang['mailbox']['remove'];?></a></li>
@ -205,6 +210,10 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"daily"}' href="#"><?=$lang['user']['daily'];?></a></li> <li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"daily"}' href="#"><?=$lang['user']['daily'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"weekly"}' href="#"><?=$lang['user']['weekly'];?></a></li> <li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"weekly"}' href="#"><?=$lang['user']['weekly'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"never"}' href="#"><?=$lang['user']['never'];?></a></li> <li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_notification' data-api-attr='{"quarantine_notification":"never"}' href="#"><?=$lang['user']['never'];?></a></li>
<li role="separator" class="divider"></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_category' data-api-attr='{"quarantine_category":"reject"}' href="#"><?=$lang['user']['q_reject'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_category' data-api-attr='{"quarantine_category":"add_header"}' href="#"><?=$lang['user']['q_add_header'];?></a></li>
<li><a data-action="edit_selected" data-id="mailbox" data-api-url='edit/quarantine_category' data-api-attr='{"quarantine_category":"all"}' href="#"><?=$lang['user']['q_all'];?></a></li>
</ul> </ul>
</div> </div>
<a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addMailboxModal"><span class="glyphicon glyphicon-plus"></span> <?=$lang['mailbox']['add_mailbox'];?></a> <a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addMailboxModal"><span class="glyphicon glyphicon-plus"></span> <?=$lang['mailbox']['add_mailbox'];?></a>

View File

@ -337,6 +337,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<?php <?php
// Show quarantine_notification options // Show quarantine_notification options
$quarantine_notification = mailbox('get', 'quarantine_notification', $username); $quarantine_notification = mailbox('get', 'quarantine_notification', $username);
$quarantine_category = mailbox('get', 'quarantine_category', $username);
?> ?>
<div class="row"> <div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['quarantine_notification'];?>:</div> <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['quarantine_notification'];?>:</div>
@ -370,6 +371,32 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<p class="help-block"><?=$lang['user']['quarantine_notification_info'];?></p> <p class="help-block"><?=$lang['user']['quarantine_notification_info'];?></p>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['quarantine_category'];?>:</div>
<div class="col-md-9 col-xs-7">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine_category'];?>">
<button type="button" class="btn btn-sm btn-default <?=($quarantine_category == "reject") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="quarantine_category"
data-api-url='edit/quarantine_category'
data-api-attr='{"quarantine_category":"reject"}'><?=$lang['user']['q_reject'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($quarantine_category == "add_header") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="quarantine_category"
data-api-url='edit/quarantine_category'
data-api-attr='{"quarantine_category":"add_header"}'><?=$lang['user']['q_add_header'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($quarantine_category == "all") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="quarantine_category"
data-api-url='edit/quarantine_category'
data-api-attr='{"quarantine_category":"all"}'><?=$lang['user']['q_all'];?></button>
</div>
<p class="help-block"><?=$lang['user']['quarantine_category_info'];?></p>
</div>
</div>
<?php if (getenv('SKIP_SOGO') != "y") { ?> <?php if (getenv('SKIP_SOGO') != "y") { ?>
<hr> <hr>
<div class="row"> <div class="row">

View File

@ -194,7 +194,7 @@ services:
- sogo - sogo
dovecot-mailcow: dovecot-mailcow:
image: mailcow/dovecot:1.136 image: mailcow/dovecot:1.137
depends_on: depends_on:
- mysql-mailcow - mysql-mailcow
dns: dns: