| @@ -381,11 +381,14 @@ class DockerUtils: | |||||||
|       for container in self.docker_client.containers.list(filters={"id": container_id}): |       for container in self.docker_client.containers.list(filters={"id": container_id}): | ||||||
|         sane_name = re.sub(r'\W+', '', request_json['maildir']) |         sane_name = re.sub(r'\W+', '', request_json['maildir']) | ||||||
|         vmail_name = request_json['maildir'].replace("'", "'\\''") |         vmail_name = request_json['maildir'].replace("'", "'\\''") | ||||||
|         index_name = request_json['maildir'].split("/") |  | ||||||
|         index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''") |  | ||||||
|         cmd_vmail = "if [[ -d '/var/vmail/" + vmail_name + "' ]]; then /bin/mv '/var/vmail/" + vmail_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi" |         cmd_vmail = "if [[ -d '/var/vmail/" + vmail_name + "' ]]; then /bin/mv '/var/vmail/" + vmail_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi" | ||||||
|         cmd_vmail_index = "if [[ -d '/var/vmail_index/" + index_name + "' ]]; then /bin/mv '/var/vmail_index/" + index_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "_index'; fi" |         index_name = request_json['maildir'].split("/") | ||||||
|         cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index] |         if len(index_name) > 1: | ||||||
|  |           index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''") | ||||||
|  |           cmd_vmail_index = "if [[ -d '/var/vmail_index/" + index_name + "' ]]; then /bin/mv '/var/vmail_index/" + index_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "_index'; fi" | ||||||
|  |           cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index] | ||||||
|  |         else: | ||||||
|  |           cmd = ["/bin/bash", "-c", cmd_vmail] | ||||||
|         maildir_cleanup = container.exec_run(cmd, user='vmail') |         maildir_cleanup = container.exec_run(cmd, user='vmail') | ||||||
|         return exec_run_handler('generic', maildir_cleanup) |         return exec_run_handler('generic', maildir_cleanup) | ||||||
|   # api call: container_post - post_action: exec - cmd: rspamd - task: worker_password |   # api call: container_post - post_action: exec - cmd: rspamd - task: worker_password | ||||||
|   | |||||||
| @@ -342,6 +342,10 @@ div.dataTables_wrapper div.dt-row { | |||||||
|   position: relative; |   position: relative; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | div.dataTables_wrapper span.sorting-value { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
| div.dataTables_scrollHead table.dataTable { | div.dataTables_scrollHead table.dataTable { | ||||||
|   margin-bottom: 0 !important; |   margin-bottom: 0 !important; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -66,4 +66,6 @@ table tbody tr td input[type="checkbox"] { | |||||||
|   padding: .2em .4em .3em !important; |   padding: .2em .4em .3em !important; | ||||||
|   background-color: #ececec!important; |   background-color: #ececec!important; | ||||||
| } | } | ||||||
|  | .badge.bg-info .bi { | ||||||
|  |   font-size: inherit; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -20,6 +20,11 @@ legend { | |||||||
|     background-color: #7a7a7a !important; |     background-color: #7a7a7a !important; | ||||||
|     border-color: #5c5c5c !important; |     border-color: #5c5c5c !important; | ||||||
| } | } | ||||||
|  | .btn-dark { | ||||||
|  |   color: #000 !important;; | ||||||
|  |   background-color: #f6f6f6 !important;; | ||||||
|  |   border-color: #ddd !important;; | ||||||
|  | } | ||||||
| .btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle { | .btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle { | ||||||
|     border-color: #7a7a7a !important; |     border-color: #7a7a7a !important; | ||||||
| } | } | ||||||
| @@ -299,22 +304,22 @@ a:hover { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,  | table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover, | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover { | table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover { | ||||||
|   background-color: #7a7a7a !important; |   background-color: #7a7a7a !important; | ||||||
| } | } | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,  | table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before, | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before { | table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before { | ||||||
|   background-color: #7a7a7a !important; |   background-color: #7a7a7a !important; | ||||||
|   border: 1.5px solid #5c5c5c !important; |   border: 1.5px solid #5c5c5c !important; | ||||||
|   color: #fff !important; |   color: #fff !important; | ||||||
| } | } | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,  | table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before, | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before { | table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before { | ||||||
|   background-color: #949494; |   background-color: #949494; | ||||||
| } | } | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,  | table.dataTable.dtr-inline.collapsed>tbody>tr>td.child, | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,  | table.dataTable.dtr-inline.collapsed>tbody>tr>th.child, | ||||||
| table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty { | table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty { | ||||||
|   background-color: #444444; |   background-color: #444444; | ||||||
| } | } | ||||||
| @@ -327,7 +332,7 @@ table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty { | |||||||
| } | } | ||||||
| .btn.btn-outline-secondary { | .btn.btn-outline-secondary { | ||||||
|   color: #fff !important; |   color: #fff !important; | ||||||
|   border-color: #7a7a7a !important;   |   border-color: #7a7a7a !important; | ||||||
| } | } | ||||||
| .btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { | .btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { | ||||||
|     background-color: #9b9b9b !important; |     background-color: #9b9b9b !important; | ||||||
|   | |||||||
| @@ -1015,20 +1015,58 @@ function formatBytes($size, $precision = 2) { | |||||||
|   } |   } | ||||||
|   return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)]; |   return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)]; | ||||||
| } | } | ||||||
| function update_sogo_static_view() { | function update_sogo_static_view($mailbox = null) { | ||||||
|   if (getenv('SKIP_SOGO') == "y") { |   if (getenv('SKIP_SOGO') == "y") { | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   global $pdo; |   global $pdo; | ||||||
|   global $lang; |   global $lang; | ||||||
|   $stmt = $pdo->query("SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES |  | ||||||
|     WHERE TABLE_NAME = 'sogo_view'"); |   $mailbox_exists = false; | ||||||
|   $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); |   if ($mailbox !== null) { | ||||||
|   if ($num_results != 0) { |     // Check if the mailbox exists | ||||||
|     $stmt = $pdo->query("REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`) |     $stmt = $pdo->prepare("SELECT username FROM mailbox WHERE username = :mailbox AND active = '1'"); | ||||||
|       SELECT `c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings` from sogo_view"); |     $stmt->execute(array(':mailbox' => $mailbox)); | ||||||
|     $stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');"); |     $row = $stmt->fetch(PDO::FETCH_ASSOC);   | ||||||
|  |     if ($row){ | ||||||
|  |       $mailbox_exists = true; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   $query = "REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`) | ||||||
|  |             SELECT | ||||||
|  |               mailbox.username, | ||||||
|  |               mailbox.domain, | ||||||
|  |               mailbox.username, | ||||||
|  |               IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.force_pw_update')) = '0', | ||||||
|  |                  IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.sogo_access')) = 1, password, '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'), | ||||||
|  |                  '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'), | ||||||
|  |               mailbox.name, | ||||||
|  |               mailbox.username, | ||||||
|  |               IFNULL(GROUP_CONCAT(ga.aliases ORDER BY ga.aliases SEPARATOR ' '), ''), | ||||||
|  |               IFNULL(gda.ad_alias, ''), | ||||||
|  |               IFNULL(external_acl.send_as_acl, ''), | ||||||
|  |               mailbox.kind, | ||||||
|  |               mailbox.multiple_bookings | ||||||
|  |             FROM | ||||||
|  |               mailbox | ||||||
|  |               LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username REGEXP CONCAT('(^|,)', mailbox.username, '($|,)') | ||||||
|  |               LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username | ||||||
|  |               LEFT OUTER JOIN grouped_sender_acl_external external_acl ON external_acl.username = mailbox.username | ||||||
|  |             WHERE | ||||||
|  |               mailbox.active = '1'"; | ||||||
|  |    | ||||||
|  |   if ($mailbox_exists) { | ||||||
|  |     $query .= " AND mailbox.username = :mailbox"; | ||||||
|  |     $stmt = $pdo->prepare($query); | ||||||
|  |     $stmt->execute(array(':mailbox' => $mailbox)); | ||||||
|  |   } else { | ||||||
|  |     $query .= " GROUP BY mailbox.username"; | ||||||
|  |     $stmt = $pdo->query($query); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   $stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');"); | ||||||
|  |    | ||||||
|   flush_memcached(); |   flush_memcached(); | ||||||
| } | } | ||||||
| function edit_user_account($_data) { | function edit_user_account($_data) { | ||||||
|   | |||||||
| @@ -1264,11 +1264,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { | |||||||
|             )); |             )); | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  |           update_sogo_static_view($username); | ||||||
|           $_SESSION['return'][] = array( |           $_SESSION['return'][] = array( | ||||||
|             'type' => 'success', |             'type' => 'success', | ||||||
|             'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), |             'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||||
|             'msg' => array('mailbox_added', htmlspecialchars($username)) |             'msg' => array('mailbox_added', htmlspecialchars($username)) | ||||||
|           ); |           ); | ||||||
|  |           return true; | ||||||
|         break; |         break; | ||||||
|         case 'resource': |         case 'resource': | ||||||
|           $domain             = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46); |           $domain             = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46); | ||||||
| @@ -3130,7 +3132,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { | |||||||
|               'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), |               'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||||
|               'msg' => array('mailbox_modified', $username) |               'msg' => array('mailbox_modified', $username) | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
|  |             update_sogo_static_view($username); | ||||||
|           } |           } | ||||||
|  |           return true; | ||||||
|         break; |         break; | ||||||
|         case 'mailbox_templates': |         case 'mailbox_templates': | ||||||
|           if ($_SESSION['mailcow_cc_role'] != "admin") { |           if ($_SESSION['mailcow_cc_role'] != "admin") { | ||||||
| @@ -5053,12 +5058,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { | |||||||
|               ); |               ); | ||||||
|               continue; |               continue; | ||||||
|             } |             } | ||||||
|  |              | ||||||
|  |             update_sogo_static_view($username); | ||||||
|             $_SESSION['return'][] = array( |             $_SESSION['return'][] = array( | ||||||
|               'type' => 'success', |               'type' => 'success', | ||||||
|               'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), |               'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||||
|               'msg' => array('mailbox_removed', htmlspecialchars($username)) |               'msg' => array('mailbox_removed', htmlspecialchars($username)) | ||||||
|             ); |             ); | ||||||
|           } |           } | ||||||
|  |           return true; | ||||||
|         break; |         break; | ||||||
|         case 'mailbox_templates': |         case 'mailbox_templates': | ||||||
|           if ($_SESSION['mailcow_cc_role'] != "admin") { |           if ($_SESSION['mailcow_cc_role'] != "admin") { | ||||||
| @@ -5264,7 +5272,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { | |||||||
|       } |       } | ||||||
|     break; |     break; | ||||||
|   } |   } | ||||||
|   if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox', 'resource')) && getenv('SKIP_SOGO') != "y") { |   if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'resource')) && getenv('SKIP_SOGO') != "y") { | ||||||
|     update_sogo_static_view(); |     update_sogo_static_view(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -117,8 +117,8 @@ jQuery(function($){ | |||||||
|           data: 'tfa_active', |           data: 'tfa_active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|             render: function (data, type) { |             render: function (data, type) { | ||||||
|             if(data == 1) return '<i class="bi bi-check-lg"></i>'; |             if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>'; | ||||||
|             else return '<i class="bi bi-x-lg"></i>'; |             else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -126,8 +126,8 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             if(data == 1) return '<i class="bi bi-check-lg"></i>'; |             if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>'; | ||||||
|             else return '<i class="bi bi-x-lg"></i>'; |             else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -260,8 +260,8 @@ jQuery(function($){ | |||||||
|           data: 'tfa_active', |           data: 'tfa_active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             if(data == 1) return '<i class="bi bi-check-lg"></i>'; |             if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>'; | ||||||
|             else return '<i class="bi bi-x-lg"></i>'; |             else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -269,8 +269,8 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             if(data == 1) return '<i class="bi bi-check-lg"></i>'; |             if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>'; | ||||||
|             else return '<i class="bi bi-x-lg"></i>'; |             else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -337,7 +337,7 @@ jQuery(function($){ | |||||||
|           data: 'keep_spam', |           data: 'keep_spam', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function(data, type){ |           render: function(data, type){ | ||||||
|             return 'yes'==data?'<i class="bi bi-x-lg"></i>':'no'==data&&'<i class="bi bi-check-lg"></i>'; |             return 'yes'==data?'<i class="bi bi-x-lg"><span class="sorting-value">yes</span></i>':'no'==data&&'<i class="bi bi-check-lg"><span class="sorting-value">no</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -414,8 +414,8 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             if(data == 1) return '<i class="bi bi-check-lg"></i>'; |             if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>'; | ||||||
|             else return '<i class="bi bi-x-lg"></i>'; |             else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -492,8 +492,8 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             if(data == 1) return '<i class="bi bi-check-lg"></i>'; |             if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>'; | ||||||
|             else return '<i class="bi bi-x-lg"></i>'; |             else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -607,7 +607,7 @@ jQuery(function($){ | |||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           responsivePriority: 6, |           responsivePriority: 6, | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—'); |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—'); | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -754,7 +754,7 @@ jQuery(function($){ | |||||||
|           data: 'attributes.gal', |           data: 'attributes.gal', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -762,7 +762,7 @@ jQuery(function($){ | |||||||
|           data: 'attributes.backupmx', |           data: 'attributes.backupmx', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -770,7 +770,7 @@ jQuery(function($){ | |||||||
|           data: 'attributes.relay_all_recipients', |           data: 'attributes.relay_all_recipients', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -778,7 +778,7 @@ jQuery(function($){ | |||||||
|           data: 'attributes.relay_unknown_only', |           data: 'attributes.relay_unknown_only', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -787,7 +787,7 @@ jQuery(function($){ | |||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           responsivePriority: 4, |           responsivePriority: 4, | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1093,7 +1093,7 @@ jQuery(function($){ | |||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           responsivePriority: 4, |           responsivePriority: 4, | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—'); |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—'); | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1164,13 +1164,13 @@ jQuery(function($){ | |||||||
|  |  | ||||||
|             item.attributes.quota = humanFileSize(item.attributes.quota); |             item.attributes.quota = humanFileSize(item.attributes.quota); | ||||||
|  |  | ||||||
|             item.attributes.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>'; |             item.attributes.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_in == 1 ? '1' : '0') + '</span></i>'; | ||||||
|             item.attributes.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>'; |             item.attributes.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_out == 1 ? '1' : '0') + '</span></i>'; | ||||||
|             item.attributes.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"></i>'; |             item.attributes.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.pop3_access == 1 ? '1' : '0') + '</span></i>'; | ||||||
|             item.attributes.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"></i>'; |             item.attributes.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.imap_access == 1 ? '1' : '0') + '</span></i>'; | ||||||
|             item.attributes.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"></i>'; |             item.attributes.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.smtp_access == 1 ? '1' : '0') + '</span></i>'; | ||||||
|             item.attributes.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"></i>'; |             item.attributes.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sieve_access == 1 ? '1' : '0') + '</span></i>'; | ||||||
|             item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"></i>'; |             item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sogo_access == 1 ? '1' : '0') + '</span></i>'; | ||||||
|             if (item.attributes.quarantine_notification === 'never') { |             if (item.attributes.quarantine_notification === 'never') { | ||||||
|               item.attributes.quarantine_notification = lang.never; |               item.attributes.quarantine_notification = lang.never; | ||||||
|             } else if (item.attributes.quarantine_notification === 'hourly') { |             } else if (item.attributes.quarantine_notification === 'hourly') { | ||||||
| @@ -1188,7 +1188,6 @@ jQuery(function($){ | |||||||
|               item.attributes.quarantine_category = lang.q_all; |               item.attributes.quarantine_category = lang.q_all; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|             if (item.template.toLowerCase() == "default"){ |             if (item.template.toLowerCase() == "default"){ | ||||||
|               item.action = '<div class="btn-group">' + |               item.action = '<div class="btn-group">' + | ||||||
|                 '<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + |                 '<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + | ||||||
| @@ -1329,7 +1328,7 @@ jQuery(function($){ | |||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           responsivePriority: 4, |           responsivePriority: 4, | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—'); |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—'); | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1440,7 +1439,7 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—'); |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—'); | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1578,7 +1577,7 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—'); |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—'); | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1675,7 +1674,7 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1782,7 +1781,7 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1917,7 +1916,7 @@ jQuery(function($){ | |||||||
|           data: 'sogo_visible', |           data: 'sogo_visible', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function(data, type){ |           render: function(data, type){ | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1936,7 +1935,7 @@ jQuery(function($){ | |||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           responsivePriority: 6, |           responsivePriority: 6, | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1952,6 +1951,10 @@ jQuery(function($){ | |||||||
|     table.on('responsive-resize', function (e, datatable, columns){ |     table.on('responsive-resize', function (e, datatable, columns){ | ||||||
|       hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table'); |       hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table'); | ||||||
|     }); |     }); | ||||||
|  |      | ||||||
|  |     table.on( 'draw', function (){ | ||||||
|  |         $('#alias_table [data-bs-toggle="tooltip"]').tooltip(); | ||||||
|  |     }); | ||||||
|   } |   } | ||||||
|   function draw_aliasdomain_table() { |   function draw_aliasdomain_table() { | ||||||
|     // just recalc width if instance already exists |     // just recalc width if instance already exists | ||||||
| @@ -2031,7 +2034,7 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -2167,7 +2170,7 @@ jQuery(function($){ | |||||||
|           data: 'active', |           data: 'active', | ||||||
|           defaultContent: '', |           defaultContent: '', | ||||||
|           render: function (data, type) { |           render: function (data, type) { | ||||||
|             return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'; |             return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>'; | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -105,7 +105,8 @@ | |||||||
|         "timeout2": "Časový limit pro připojení k lokálnímu serveru", |         "timeout2": "Časový limit pro připojení k lokálnímu serveru", | ||||||
|         "username": "Uživatelské jméno", |         "username": "Uživatelské jméno", | ||||||
|         "validate": "Ověřit", |         "validate": "Ověřit", | ||||||
|         "validation_success": "Úspěšně ověřeno" |         "validation_success": "Úspěšně ověřeno", | ||||||
|  |         "tags": "Štítky" | ||||||
|     }, |     }, | ||||||
|     "admin": { |     "admin": { | ||||||
|         "access": "Přístupy", |         "access": "Přístupy", | ||||||
| @@ -333,7 +334,11 @@ | |||||||
|         "username": "Uživatelské jméno", |         "username": "Uživatelské jméno", | ||||||
|         "validate_license_now": "Ověřit GUID na licenčním serveru", |         "validate_license_now": "Ověřit GUID na licenčním serveru", | ||||||
|         "verify": "Ověřit", |         "verify": "Ověřit", | ||||||
|         "yes": "✓" |         "yes": "✓", | ||||||
|  |         "f2b_ban_time_increment": "Délka banu je prodlužována s každým dalším banem", | ||||||
|  |         "f2b_max_ban_time": "Maximální délka banu (s)", | ||||||
|  |         "ip_check": "Kontrola IP", | ||||||
|  |         "ip_check_disabled": "Kontrola IP je vypnuta. Můžete ji zapnout v <br> <strong>System > Nastavení > Options > Přizpůsobení</strong>" | ||||||
|     }, |     }, | ||||||
|     "danger": { |     "danger": { | ||||||
|         "access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři", |         "access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři", | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ elseif (isset($_GET['login'])) { | |||||||
|           ':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']) |           ':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']) | ||||||
|         )); |         )); | ||||||
|         // redirect to sogo (sogo will get the correct credentials via nginx auth_request |         // redirect to sogo (sogo will get the correct credentials via nginx auth_request | ||||||
|         header("Location: /SOGo/so/${login}"); |         header("Location: /SOGo/so/{$login}"); | ||||||
|         exit; |         exit; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -109,25 +109,25 @@ | |||||||
|         <label class="control-label col-sm-2">{{ 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="{{ acl.quarantine_notification }}"> |           <div class="btn-group" data-acl="{{ acl.quarantine_notification }}"> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'never' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'never' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailbox }}" |             data-item="{{ mailbox }}" | ||||||
|             data-id="quarantine_notification" |             data-id="quarantine_notification" | ||||||
|             data-api-url='edit/quarantine_notification' |             data-api-url='edit/quarantine_notification' | ||||||
|             data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button> |             data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'hourly' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'hourly' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailbox }}" |             data-item="{{ mailbox }}" | ||||||
|             data-id="quarantine_notification" |             data-id="quarantine_notification" | ||||||
|             data-api-url='edit/quarantine_notification' |             data-api-url='edit/quarantine_notification' | ||||||
|             data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button> |             data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'daily' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'daily' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailbox }}" |             data-item="{{ mailbox }}" | ||||||
|             data-id="quarantine_notification" |             data-id="quarantine_notification" | ||||||
|             data-api-url='edit/quarantine_notification' |             data-api-url='edit/quarantine_notification' | ||||||
|             data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button> |             data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'weekly' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'weekly' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailbox }}" |             data-item="{{ mailbox }}" | ||||||
|             data-id="quarantine_notification" |             data-id="quarantine_notification" | ||||||
| @@ -141,19 +141,19 @@ | |||||||
|         <label class="control-label col-sm-2">{{ lang.user.quarantine_category }}</label> |         <label class="control-label col-sm-2">{{ lang.user.quarantine_category }}</label> | ||||||
|         <div class="col-sm-10"> |         <div class="col-sm-10"> | ||||||
|           <div class="btn-group" data-acl="{{ acl.quarantine_category }}"> |           <div class="btn-group" data-acl="{{ acl.quarantine_category }}"> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'reject' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'reject' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailbox }}" |             data-item="{{ mailbox }}" | ||||||
|             data-id="quarantine_category" |             data-id="quarantine_category" | ||||||
|             data-api-url='edit/quarantine_category' |             data-api-url='edit/quarantine_category' | ||||||
|             data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button> |             data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'add_header' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'add_header' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailbox }}" |             data-item="{{ mailbox }}" | ||||||
|             data-id="quarantine_category" |             data-id="quarantine_category" | ||||||
|             data-api-url='edit/quarantine_category' |             data-api-url='edit/quarantine_category' | ||||||
|             data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button> |             data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'all' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'all' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailbox }}" |             data-item="{{ mailbox }}" | ||||||
|             data-id="quarantine_category" |             data-id="quarantine_category" | ||||||
| @@ -167,13 +167,13 @@ | |||||||
|         <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"> | ||||||
|           <div class="btn-group" data-acl="{{ acl.tls_policy }}"> |           <div class="btn-group" data-acl="{{ acl.tls_policy }}"> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_in == '1' %} active"{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_in == '1' %} btn-dark"{% endif %}" | ||||||
|               data-action="edit_selected" |               data-action="edit_selected" | ||||||
|               data-item="{{ mailbox }}" |               data-item="{{ mailbox }}" | ||||||
|               data-id="tls_policy" |               data-id="tls_policy" | ||||||
|               data-api-url='edit/tls_policy' |               data-api-url='edit/tls_policy' | ||||||
|               data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button> |               data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_out == '1' %} active"{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_out == '1' %} btn-dark"{% endif %}" | ||||||
|               data-action="edit_selected" |               data-action="edit_selected" | ||||||
|               data-item="{{ mailbox }}" |               data-item="{{ mailbox }}" | ||||||
|               data-id="tls_policy" |               data-id="tls_policy" | ||||||
|   | |||||||
| @@ -54,6 +54,7 @@ | |||||||
|             <li class="dropdown-header">SMTP</li> |             <li class="dropdown-header">SMTP</li> | ||||||
|             <li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"smtp_access":1}' href="#">{{ lang.mailbox.activate }}</a></li> |             <li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"smtp_access":1}' href="#">{{ lang.mailbox.activate }}</a></li> | ||||||
|             <li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"smtp_access":0}' href="#">{{ lang.mailbox.deactivate }}</a></li> |             <li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"smtp_access":0}' href="#">{{ lang.mailbox.deactivate }}</a></li> | ||||||
|  |             <li><hr class="dropdown-divider"></li> | ||||||
|             <li class="dropdown-header">Sieve</li> |             <li class="dropdown-header">Sieve</li> | ||||||
|             <li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"sieve_access":1}' href="#">{{ lang.mailbox.activate }}</a></li> |             <li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"sieve_access":1}' href="#">{{ lang.mailbox.activate }}</a></li> | ||||||
|             <li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"sieve_access":0}' href="#">{{ lang.mailbox.deactivate }}</a></li> |             <li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"sieve_access":0}' href="#">{{ lang.mailbox.deactivate }}</a></li> | ||||||
| @@ -61,7 +62,7 @@ | |||||||
|           <a class="btn btn-sm btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addMailboxModal"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_mailbox }}</a> |           <a class="btn btn-sm btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addMailboxModal"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_mailbox }}</a> | ||||||
|         </div> |         </div> | ||||||
|         <div class="btn-group d-none d-lg-flex"> |         <div class="btn-group d-none d-lg-flex"> | ||||||
|           <a class="btn btn-sm btn-secondary" id="toggle_multi_select_all" data-id="mailbox" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>           |           <a class="btn btn-sm btn-secondary" id="toggle_multi_select_all" data-id="mailbox" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a> | ||||||
|           <a class="btn btn-sm btn-xs-half btn-secondary dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.quick_actions }}</a> |           <a class="btn btn-sm btn-xs-half btn-secondary dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.quick_actions }}</a> | ||||||
|           <ul class="dropdown-menu"> |           <ul class="dropdown-menu"> | ||||||
|             <li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="mailbox_table">{{ lang.datatables.expand_all }}</a></li> |             <li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="mailbox_table">{{ lang.datatables.expand_all }}</a></li> | ||||||
|   | |||||||
| @@ -12,19 +12,19 @@ | |||||||
|         <div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.tag_handling }}:</div> |         <div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.tag_handling }}:</div> | ||||||
|         <div class="col-sm-9 col-12"> |         <div class="col-sm-9 col-12"> | ||||||
|           <div class="btn-group" data-acl="{{ acl.delimiter_action }}"> |           <div class="btn-group" data-acl="{{ acl.delimiter_action }}"> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if get_tagging_options == 'subfolder' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if get_tagging_options == 'subfolder' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="delimiter_action" |             data-id="delimiter_action" | ||||||
|             data-api-url='edit/delimiter_action' |             data-api-url='edit/delimiter_action' | ||||||
|             data-api-attr='{"tagged_mail_handler":"subfolder"}'>{{ lang.user.tag_in_subfolder }}</button> |             data-api-attr='{"tagged_mail_handler":"subfolder"}'>{{ lang.user.tag_in_subfolder }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if get_tagging_options == 'subject' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if get_tagging_options == 'subject' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="delimiter_action" |             data-id="delimiter_action" | ||||||
|             data-api-url='edit/delimiter_action' |             data-api-url='edit/delimiter_action' | ||||||
|             data-api-attr='{"tagged_mail_handler":"subject"}'>{{ lang.user.tag_in_subject }}</button> |             data-api-attr='{"tagged_mail_handler":"subject"}'>{{ lang.user.tag_in_subject }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if get_tagging_options == 'none' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if get_tagging_options == 'none' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="delimiter_action" |             data-id="delimiter_action" | ||||||
| @@ -40,13 +40,13 @@ | |||||||
|         <div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.tls_policy }}:</div> |         <div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.tls_policy }}:</div> | ||||||
|         <div class="col-sm-9 col-12"> |         <div class="col-sm-9 col-12"> | ||||||
|           <div class="btn-group" data-acl="{{ acl.tls_policy }}"> |           <div class="btn-group" data-acl="{{ acl.tls_policy }}"> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_in == '1' %} active"{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_in == '1' %} btn-dark"{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="tls_policy" |             data-id="tls_policy" | ||||||
|             data-api-url='edit/tls_policy' |             data-api-url='edit/tls_policy' | ||||||
|             data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button> |             data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_out == '1' %} active"{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_out == '1' %} btn-dark"{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="tls_policy" |             data-id="tls_policy" | ||||||
| @@ -61,25 +61,25 @@ | |||||||
|         <div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.quarantine_notification }}:</div> |         <div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.quarantine_notification }}:</div> | ||||||
|         <div class="col-sm-9 col-12"> |         <div class="col-sm-9 col-12"> | ||||||
|           <div class="btn-group" data-acl="{{ acl.quarantine_notification }}"> |           <div class="btn-group" data-acl="{{ acl.quarantine_notification }}"> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'never' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'never' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="quarantine_notification" |             data-id="quarantine_notification" | ||||||
|             data-api-url='edit/quarantine_notification' |             data-api-url='edit/quarantine_notification' | ||||||
|             data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button> |             data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'hourly' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'hourly' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="quarantine_notification" |             data-id="quarantine_notification" | ||||||
|             data-api-url='edit/quarantine_notification' |             data-api-url='edit/quarantine_notification' | ||||||
|             data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button> |             data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'daily' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'daily' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="quarantine_notification" |             data-id="quarantine_notification" | ||||||
|             data-api-url='edit/quarantine_notification' |             data-api-url='edit/quarantine_notification' | ||||||
|             data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button> |             data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'weekly' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'weekly' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="quarantine_notification" |             data-id="quarantine_notification" | ||||||
| @@ -93,19 +93,19 @@ | |||||||
|         <div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.quarantine_category }}:</div> |         <div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.quarantine_category }}:</div> | ||||||
|         <div class="col-sm-9 col-12"> |         <div class="col-sm-9 col-12"> | ||||||
|           <div class="btn-group" data-acl="{{ acl.quarantine_category }}"> |           <div class="btn-group" data-acl="{{ acl.quarantine_category }}"> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'reject' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'reject' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="quarantine_category" |             data-id="quarantine_category" | ||||||
|             data-api-url='edit/quarantine_category' |             data-api-url='edit/quarantine_category' | ||||||
|             data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button> |             data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'add_header' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'add_header' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="quarantine_category" |             data-id="quarantine_category" | ||||||
|             data-api-url='edit/quarantine_category' |             data-api-url='edit/quarantine_category' | ||||||
|             data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button> |             data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button> | ||||||
|             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'all' %} active{% endif %}" |             <button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'all' %} btn-dark{% endif %}" | ||||||
|             data-action="edit_selected" |             data-action="edit_selected" | ||||||
|             data-item="{{ mailcow_cc_username }}" |             data-item="{{ mailcow_cc_username }}" | ||||||
|             data-id="quarantine_category" |             data-id="quarantine_category" | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ services: | |||||||
|             - clamd |             - clamd | ||||||
|  |  | ||||||
|     rspamd-mailcow: |     rspamd-mailcow: | ||||||
|       image: mailcow/rspamd:1.93 |       image: mailcow/rspamd:1.92 | ||||||
|       stop_grace_period: 30s |       stop_grace_period: 30s | ||||||
|       depends_on: |       depends_on: | ||||||
|         - dovecot-mailcow |         - dovecot-mailcow | ||||||
| @@ -169,7 +169,7 @@ services: | |||||||
|             - phpfpm |             - phpfpm | ||||||
|  |  | ||||||
|     sogo-mailcow: |     sogo-mailcow: | ||||||
|       image: mailcow/sogo:1.116 |       image: mailcow/sogo:1.117 | ||||||
|       environment: |       environment: | ||||||
|         - DBNAME=${DBNAME} |         - DBNAME=${DBNAME} | ||||||
|         - DBUSER=${DBUSER} |         - DBUSER=${DBUSER} | ||||||
| @@ -510,7 +510,7 @@ services: | |||||||
|             - watchdog |             - watchdog | ||||||
|  |  | ||||||
|     dockerapi-mailcow: |     dockerapi-mailcow: | ||||||
|       image: mailcow/dockerapi:2.02 |       image: mailcow/dockerapi:2.03 | ||||||
|       security_opt: |       security_opt: | ||||||
|         - label=disable |         - label=disable | ||||||
|       restart: always |       restart: always | ||||||
|   | |||||||
| @@ -261,7 +261,7 @@ COMPOSE_PROJECT_NAME=mailcowdockerized | |||||||
| # Switch here between native (compose plugin) and standalone | # Switch here between native (compose plugin) and standalone | ||||||
| # For more informations take a look at the mailcow docs regarding the configuration options. | # For more informations take a look at the mailcow docs regarding the configuration options. | ||||||
| # Normally this should be untouched but if you decided to use either of those you can switch it manually here. | # Normally this should be untouched but if you decided to use either of those you can switch it manually here. | ||||||
| # Please be aware that at least one of those variants should be installed on your maschine or mailcow will fail. | # Please be aware that at least one of those variants should be installed on your machine or mailcow will fail. | ||||||
|  |  | ||||||
| DOCKER_COMPOSE_VERSION=${COMPOSE_VERSION} | DOCKER_COMPOSE_VERSION=${COMPOSE_VERSION} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| #!/usr/bin/env bash | #!/usr/bin/env bash | ||||||
| # renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$ | # renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$ | ||||||
| NEXTCLOUD_VERSION=26.0.0 | NEXTCLOUD_VERSION=26.0.1 | ||||||
|  |  | ||||||
| echo -ne "Checking prerequisites..." | echo -ne "Checking prerequisites..." | ||||||
| sleep 1 | sleep 1 | ||||||
| @@ -97,8 +97,8 @@ elif [[ ${NC_UPDATE} == "y" ]]; then | |||||||
|     echo -e "\033[31mError: Nextcloud occ not found. Is Nextcloud installed?\033[0m" |     echo -e "\033[31mError: Nextcloud occ not found. Is Nextcloud installed?\033[0m" | ||||||
|     exit 1 |     exit 1 | ||||||
|   fi |   fi | ||||||
|   if grep -q 'This version of Nextcloud is not compatible with PHP>=8.2.' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then |   if grep -Pq 'This version of Nextcloud is not compatible with (?:PHP)?(?>=?)(?:PHP)?(?>.+)' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then | ||||||
|     echo -e "\033[31mError: This version of Nextcloud is not compatible with PHP>=8.2, we'll fix it\033[0m" |     echo -e "\033[31mError: This version of Nextcloud is not compatible with the current PHP version of php-fpm-mailcow, we'll fix it\033[0m" | ||||||
|     wget -q https://raw.githubusercontent.com/nextcloud/server/v26.0.0/lib/versioncheck.php -O ./data/web/nextcloud/lib/versioncheck.php |     wget -q https://raw.githubusercontent.com/nextcloud/server/v26.0.0/lib/versioncheck.php -O ./data/web/nextcloud/lib/versioncheck.php | ||||||
| 	echo -e "\e[33mPlease restart the update again.\e[0m" | 	echo -e "\e[33mPlease restart the update again.\e[0m" | ||||||
|   elif ! grep -q 'installed: true' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then |   elif ! grep -q 'installed: true' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user