diff --git a/data/web/css/site/quarantine.css b/data/web/css/site/quarantine.css
index 98a74d66..0455b7c1 100644
--- a/data/web/css/site/quarantine.css
+++ b/data/web/css/site/quarantine.css
@@ -1,102 +1,104 @@
-.pagination a {
-  text-decoration: none !important;
-}
-
-.panel.panel-default {
-  overflow: visible !important;
-}
-
-.table-responsive {
-  overflow: visible !important;
-}
-
-.table-responsive {
-  overflow-x: scroll !important;
-}
-
-.footer-add-item {
-  display: block;
-  text-align: center;
-  font-style: italic;
-  padding: 10px;
-  background: #F5F5F5;
-}
-
-@media (min-width: 992px) {
-  .container {
-    width: 100%;
-  }
-}
-@media (min-width: 1920px) {
-  .container {
-      width: 80%;
-  }
-}
-
-.mass-actions-quarantine {
-  user-select: none;
-}
-
-.inputMissingAttr {
-  border-color: #FF4136;
-}
-
-.modal#qidDetailModal p {
-  word-break: break-all;
-}
-
-span#qid_detail_score {
-  font-weight: 700;
-  margin-left: 5px;
-}
-
-span.rspamd-symbol {
-  display: inline-block;
-  margin: 2px 6px 2px 0;
-  border-radius: 4px;
-  padding: 0 7px;
-}
-
-span.rspamd-symbol.positive {
-  background: #4CAF50;
-  border: 1px solid #4CAF50;
-  color: white;
-}
-
-span.rspamd-symbol.negative {
-  background: #ff4136;
-  border: 1px solid #ff4136;
-  color: white;
-}
-
-span.rspamd-symbol.neutral {
-  background: #f5f5f5;
-  color: #333;
-  border: 1px solid #ccc;
-}
-
-span.rspamd-symbol span.score {
-  font-weight: 700;
-}
-
-span.mail-address-item {
-  background-color: #f5f5f5;
-  border-radius: 4px;
-  border: 1px solid #ccc;
-  padding: 2px 7px;
-  display: inline-block;
-  margin: 2px 6px 2px 0;
-}
-
-table tbody tr {
-  cursor: pointer;
-}
-
-table tbody tr td input[type="checkbox"] {
-  cursor: pointer;
-}
-.label-rspamd-action {
-  font-size:110%;
-  margin:20px;
-}
-
+.pagination a {
+  text-decoration: none !important;
+}
+
+.panel.panel-default {
+  overflow: visible !important;
+}
+
+.table-responsive {
+  overflow: visible !important;
+}
+
+.table-responsive {
+  overflow-x: scroll !important;
+}
+
+.footer-add-item {
+  display: block;
+  text-align: center;
+  font-style: italic;
+  padding: 10px;
+  background: #F5F5F5;
+}
+
+@media (min-width: 992px) {
+  .container {
+    width: 100%;
+  }
+}
+@media (min-width: 1920px) {
+  .container {
+      width: 80%;
+  }
+}
+
+.mass-actions-quarantine {
+  user-select: none;
+}
+
+.inputMissingAttr {
+  border-color: #FF4136;
+}
+
+.modal#qidDetailModal p {
+  word-break: break-all;
+}
+
+span#qid_detail_score {
+  font-weight: 700;
+  margin-left: 5px;
+}
+
+span.rspamd-symbol {
+  display: inline-block;
+  margin: 2px 6px 2px 0;
+  border-radius: 4px;
+  padding: 0 7px;
+}
+
+span.rspamd-symbol.positive {
+  background: #4CAF50;
+  border: 1px solid #4CAF50;
+  color: white;
+}
+
+span.rspamd-symbol.negative {
+  background: #ff4136;
+  border: 1px solid #ff4136;
+  color: white;
+}
+
+span.rspamd-symbol.neutral {
+  background: #f5f5f5;
+  color: #333;
+  border: 1px solid #ccc;
+}
+
+span.rspamd-symbol span.score {
+  font-weight: 700;
+}
+
+span.mail-address-item {
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  border: 1px solid #ccc;
+  padding: 2px 7px;
+  display: inline-block;
+  margin: 2px 6px 2px 0;
+}
+
+table tbody tr {
+  cursor: pointer;
+}
+
+table tbody tr td input[type="checkbox"] {
+  cursor: pointer;
+}
+.label-rspamd-action {
+  font-size:110%;
+  margin:20px;
+}
+.senders-mw220 {
+  max-width: 220px;
+}
diff --git a/data/web/js/site/admin.js b/data/web/js/site/admin.js
index 0dba1aa8..0e5a9ae6 100644
--- a/data/web/js/site/admin.js
+++ b/data/web/js/site/admin.js
@@ -1,731 +1,737 @@
-// Base64 functions
-var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
-jQuery(function($){
-  // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
-  var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
-  function jq(myid) {return "#" + myid.replace( /(:|\.|\[|\]|,|=|@)/g, "\\$1" );}
-  function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
-  function validateRegex(e){var t=e.split("/"),n=e,r="";t.length>1&&(n=t[1],r=t[2]);try{return new RegExp(n,r),!0}catch(e){return!1}}
-  function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
-  function hashCode(t){for(var n=0,r=0;r<t.length;r++)n=t.charCodeAt(r)+((n<<5)-n);return n}
-  function intToRGB(t){var n=(16777215&t).toString(16).toUpperCase();return"00000".substring(0,6-n.length)+n}
-  $("#dkim_missing_keys").on('click', function(e) {
-    e.preventDefault();
-     var domains = [];
-     $('.dkim_missing').each(function() {
-       domains.push($(this).val());
-     });
-     $('#dkim_add_domains').val(domains);
-  });
-  $(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
-  $("#mass_exclude").change(function(){ $("#mass_include").selectpicker('deselectAll'); });
-  $("#mass_include").change(function(){ $("#mass_exclude").selectpicker('deselectAll'); });
-  $("#mass_disarm").click(function() { $("#mass_send").attr("disabled", !this.checked); });
-  $(".admin-ays-dialog").click(function() { return confirm(lang.ays); });
-  $(".validate_rspamd_regex").click(function( event ) {
-    event.preventDefault();
-    var regex_map_id = $(this).data('regex-map');
-    var regex_data = $(jq(regex_map_id)).val().split(/\r?\n/);
-    var regex_valid = true;
-    for(var i = 0;i < regex_data.length;i++){
-      if(regex_data[i].startsWith('#') || !regex_data[i]){
-        continue;
-      }
-      if(!validateRegex(regex_data[i])) {
-        mailcow_alert_box('Cannot build regex from line ' + (i+1), 'danger');
-        var regex_valid = false;
-        break;
-      }
-      if(!regex_data[i].startsWith('/') || !/\/[ims]?$/.test(regex_data[i])){
-        mailcow_alert_box('Line ' + (i+1) + ' is invalid', 'danger');
-        var regex_valid = false;
-        break;
-      }
-    }
-    if (regex_valid) {
-      mailcow_alert_box('Regex OK', 'success');
-      $('button[data-id="' + regex_map_id + '"]').attr({"disabled": false});
-    }
-  });
-	$('.textarea-code').on('keyup', function() {
-    $('.submit_rspamd_regex').attr({"disabled": true});
-	});
-  $("#show_rspamd_global_filters").click(function() {
-    $.get("inc/ajax/show_rspamd_global_filters.php");
-    $("#confirm_show_rspamd_global_filters").hide();
-    $("#rspamd_global_filters").removeClass("d-none");
-  });
-  $("#super_delete").click(function() { return confirm(lang.queue_ays); });
-  
-  $(".refresh_table").on('click', function(e) {
-    e.preventDefault();
-    var table_name = $(this).data('table');
-    $('#' + table_name).DataTable().ajax.reload();
-  });
-  function draw_domain_admins() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#domainadminstable') ) {
-      $('#domainadminstable').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#domainadminstable').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/domain-admin/all",
-        dataSrc: function(data){
-          return process_table_data(data, 'domainadminstable');
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: lang.username,
-            data: 'username',
-            defaultContent: ''
-          },
-          {
-            title: lang.admin_domains,
-            data: 'selected_domains',
-            defaultContent: '',
-          },
-          {
-            title: "TFA",
-            data: 'tfa_active',
-            defaultContent: '',
-            render: function (data, type) {
-              if(data == 1) return '<i class="bi bi-check-lg"></i>';
-              else return '<i class="bi bi-x-lg"></i>'
-            }
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            render: function (data, type) {
-              if(data == 1) return '<i class="bi bi-check-lg"></i>';
-              else return '<i class="bi bi-x-lg"></i>'
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'text-md-end dt-sm-head-hidden dt-body-right',
-            defaultContent: ''
-          },
-      ],
-      initComplete: function(settings, json){
-      }
-    });
-  }
-  function draw_oauth2_clients() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#oauth2clientstable') ) {
-      $('#oauth2clientstable').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#oauth2clientstable').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/oauth2-client/all",
-        dataSrc: function(data){
-          return process_table_data(data, 'oauth2clientstable');
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            defaultContent: ''
-          },
-          {
-            title: lang.oauth2_client_id,
-            data: 'client_id',
-            defaultContent: ''
-          },
-          {
-            title: lang.oauth2_client_secret,
-            data: 'client_secret',
-            defaultContent: ''
-          },
-          {
-            title: lang.oauth2_redirect_uri,
-            data: 'redirect_uri',
-            defaultContent: ''
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'text-md-end dt-sm-head-hidden dt-body-right',
-            defaultContent: ''
-          },
-      ]
-    });
-  }
-  function draw_admins() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#adminstable') ) {
-      $('#adminstable').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#adminstable').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/admin/all",
-        dataSrc: function(data){
-          return process_table_data(data, 'adminstable');
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: lang.username,
-            data: 'username',
-            defaultContent: ''
-          },
-          {
-            title: "TFA",
-            data: 'tfa_active',
-            defaultContent: '',
-            render: function (data, type) {
-              if(data == 1) return '<i class="bi bi-check-lg"></i>';
-              else return '<i class="bi bi-x-lg"></i>'
-            }
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            render: function (data, type) {
-              if(data == 1) return '<i class="bi bi-check-lg"></i>';
-              else return '<i class="bi bi-x-lg"></i>'
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            defaultContent: '',
-            className: 'text-md-end dt-sm-head-hidden dt-body-right'
-          },
-      ]
-    });
-  }
-  function draw_fwd_hosts() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#forwardinghoststable') ) {
-      $('#forwardinghoststable').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#forwardinghoststable').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/fwdhost/all",
-        dataSrc: function(data){
-          return process_table_data(data, 'forwardinghoststable');
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: lang.host,
-            data: 'host',
-            defaultContent: ''
-          },
-          {
-            title: lang.source,
-            data: 'source',
-            defaultContent: ''
-          },
-          {
-            title: lang.spamfilter,
-            data: 'keep_spam',
-            defaultContent: '',
-            render: function(data, type){
-              return 'yes'==data?'<i class="bi bi-x-lg"></i>':'no'==data&&'<i class="bi bi-check-lg"></i>';
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'text-md-end dt-sm-head-hidden dt-body-right',
-            defaultContent: ''
-          },
-      ]
-    });
-  }
-  function draw_relayhosts() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#relayhoststable') ) {
-      $('#relayhoststable').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#relayhoststable').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/relayhost/all",
-        dataSrc: function(data){
-          return process_table_data(data, 'relayhoststable');
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            defaultContent: ''
-          },
-          {
-            title: lang.host,
-            data: 'hostname',
-            defaultContent: ''
-          },
-          {
-            title: lang.username,
-            data: 'username',
-            defaultContent: ''
-          },
-          {
-            title: lang.in_use_by,
-            data: 'in_use_by',
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            render: function (data, type) {
-              if(data == 1) return '<i class="bi bi-check-lg"></i>';
-              else return '<i class="bi bi-x-lg"></i>'
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'text-md-end dt-sm-head-hidden dt-body-right',
-            defaultContent: ''
-          },
-      ]
-    });
-  }
-  function draw_transport_maps() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#transportstable') ) {
-      $('#transportstable').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#transportstable').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/transport/all",
-        dataSrc: function(data){
-          return process_table_data(data, 'transportstable');
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            defaultContent: ''
-          },
-          {
-            title: lang.destination,
-            data: 'destination',
-            defaultContent: ''
-          },
-          {
-            title: lang.nexthop,
-            data: 'nexthop',
-            defaultContent: ''
-          },
-          {
-            title: lang.username,
-            data: 'username',
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            render: function (data, type) {
-              if(data == 1) return '<i class="bi bi-check-lg"></i>';
-              else return '<i class="bi bi-x-lg"></i>'
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'text-md-end dt-sm-head-hidden dt-body-right',
-            defaultContent: ''
-          },
-      ]
-    });
-  }
-
-  function process_table_data(data, table) {
-    if (table == 'relayhoststable') {
-      $.each(data, function (i, item) {
-        item.action = '<div class="btn-group">' +
-          '<a href="#" data-bs-toggle="modal" data-bs-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="sender-dependent" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-caret-right-fill"></i> Test</a>' +
-          '<a href="/edit/relayhost/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-          '<a href="#" data-action="delete_selected" data-id="single-rlyhost" data-api-url="delete/relayhost" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-          '</div>';
-        if (item.used_by_mailboxes == '') { item.in_use_by = item.used_by_domains; }
-        else if (item.used_by_domains == '') { item.in_use_by = item.used_by_mailboxes; }
-        else { item.in_use_by = item.used_by_mailboxes + '<hr style="margin:5px 0px 5px 0px;">' + item.used_by_domains; }
-        item.chkbox = '<input type="checkbox" data-id="rlyhosts" name="multi_select" value="' + item.id + '" />';
-      });
-    } else if (table == 'transportstable') {
-      $.each(data, function (i, item) {
-        if (item.is_mx_based) {
-          item.destination = '<i class="bi bi-info-circle-fill text-info mx-info" data-bs-toggle="tooltip" title="' + lang.is_mx_based + '"></i> <code>' + item.destination + '</code>';
-        }
-        if (item.username) {
-          item.username = '<i style="color:#' + intToRGB(hashCode(item.nexthop)) + ';" class="bi bi-square-fill"></i> ' + item.username;
-        }
-        item.action = '<div class="btn-group">' +
-          '<a href="#" data-bs-toggle="modal" data-bs-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="transport-map" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-caret-right-fill"></i> Test</a>' +
-          '<a href="/edit/transport/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-          '<a href="#" data-action="delete_selected" data-id="single-transport" data-api-url="delete/transport" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-          '</div>';
-        item.chkbox = '<input type="checkbox" data-id="transports" name="multi_select" value="' + item.id + '" />';
-      });
-    } else if (table == 'queuetable') {
-      $.each(data, function (i, item) {
-        item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
-        rcpts = $.map(item.recipients, function(i) {
-          return escapeHtml(i);
-        });
-        item.recipients = rcpts.join('<hr style="margin:1px!important">');
-        item.action = '<div class="btn-group">' +
-          '<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.queue_show_message + '</a>' +
-          '</div>';
-      });
-    } else if (table == 'forwardinghoststable') {
-      $.each(data, function (i, item) {
-        item.action = '<div class="btn-group">' +
-          '<a href="#" data-action="delete_selected" data-id="single-fwdhost" data-api-url="delete/fwdhost" data-item="' + encodeURI(item.host) + '" class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-          '</div>';
-        item.chkbox = '<input type="checkbox" data-id="fwdhosts" name="multi_select" value="' + item.host + '" />';
-      });
-    } else if (table == 'oauth2clientstable') {
-      $.each(data, function (i, item) {
-        item.action = '<div class="btn-group">' +
-          '<a href="/edit.php?oauth2client=' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-          '<a href="#" data-action="delete_selected" data-id="single-oauth2-client" data-api-url="delete/oauth2-client" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-          '</div>';
-        item.scope = "profile";
-        item.grant_types = 'refresh_token password authorization_code';
-        item.chkbox = '<input type="checkbox" data-id="oauth2_clients" name="multi_select" value="' + item.id + '" />';
-      });
-    } else if (table == 'domainadminstable') {
-      $.each(data, function (i, item) {
-        item.selected_domains = escapeHtml(item.selected_domains);
-        item.selected_domains = item.selected_domains.toString().replace(/,/g, "<br>");
-        item.chkbox = '<input type="checkbox" data-id="domain_admins" name="multi_select" value="' + item.username + '" />';
-        item.action = '<div class="btn-group">' +
-          '<a href="/edit/domainadmin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-          '<a href="#" data-action="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-          '<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-third btn-success"><i class="bi bi-person-fill"></i> Login</a>' +
-          '</div>';
-      });
-    } else if (table == 'adminstable') {
-      $.each(data, function (i, item) {
-        if (admin_username.toLowerCase() == item.username.toLowerCase()) {
-          item.usr = '<i class="bi bi-person-check"></i> ' + item.username;
-        } else {
-          item.usr = item.username;
-        }
-        item.chkbox = '<input type="checkbox" data-id="admins" name="multi_select" value="' + item.username + '" />';
-        item.action = '<div class="btn-group">' +
-          '<a href="/edit/admin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-          '<a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-          '</div>';
-      });
-    }
-    return data
-  };
-
-  // detect element visibility changes
-  function onVisible(element, callback) {
-    $(document).ready(function() {
-      element_object = document.querySelector(element);
-      if (element_object === null) return;
-
-      new IntersectionObserver((entries, observer) => {
-        entries.forEach(entry => {
-          if(entry.intersectionRatio > 0) {
-            callback(element_object);
-          }
-        });
-      }).observe(element_object);
-    });
-  }
-  // Draw Table if tab is active
-  onVisible("[id^=adminstable]", () => draw_admins());
-  onVisible("[id^=domainadminstable]", () => draw_domain_admins());
-  onVisible("[id^=oauth2clientstable]", () => draw_oauth2_clients());
-  onVisible("[id^=forwardinghoststable]", () => draw_fwd_hosts());
-  onVisible("[id^=relayhoststable]", () => draw_relayhosts());
-  onVisible("[id^=transportstable]", () => draw_transport_maps());
-
-
-  $('body').on('click', 'span.footable-toggle', function () {
-    event.stopPropagation();
-  })
-
-  // API IP check toggle
-  $("#skip_ip_check_ro").click(function( event ) {
-   $("#skip_ip_check_ro").not(this).prop('checked', false);
-    if ($("#skip_ip_check_ro:checked").length > 0) {
-      $('#allow_from_ro').prop('disabled', true);
-    }
-    else {
-      $("#allow_from_ro").removeAttr('disabled');
-    }
-  });
-  $("#skip_ip_check_rw").click(function( event ) {
-   $("#skip_ip_check_rw").not(this).prop('checked', false);
-    if ($("#skip_ip_check_rw:checked").length > 0) {
-      $('#allow_from_rw').prop('disabled', true);
-    }
-    else {
-      $("#allow_from_rw").removeAttr('disabled');
-    }
-  });
-  // Relayhost
-  $('#testRelayhostModal').on('show.bs.modal', function (e) {
-    $('#test_relayhost_result').text("-");
-    button = $(e.relatedTarget)
-    if (button != null) {
-      $('#relayhost_id').val(button.data('relayhost-id'));
-    }
-  })
-  $('#test_relayhost').on('click', function (e) {
-    e.preventDefault();
-    prev = $('#test_relayhost').text();
-    $(this).prop("disabled",true);
-    $(this).html('<i class="bi bi-arrow-repeat icon-spin"></i> ');
-    $.ajax({
-        type: 'GET',
-        url: 'inc/ajax/relay_check.php',
-        dataType: 'text',
-        data: $('#test_relayhost_form').serialize(),
-        complete: function (data) {
-          $('#test_relayhost_result').html(data.responseText);
-          $('#test_relayhost').prop("disabled",false);
-          $('#test_relayhost').text(prev);
-        }
-    });
-  })
-  // Transport
-  $('#testTransportModal').on('show.bs.modal', function (e) {
-    $('#test_transport_result').text("-");
-    button = $(e.relatedTarget)
-    if (button != null) {
-      $('#transport_id').val(button.data('transport-id'));
-      $('#transport_type').val(button.data('transport-type'));
-    }
-  })
-  $('#test_transport').on('click', function (e) {
-    e.preventDefault();
-    prev = $('#test_transport').text();
-    $(this).prop("disabled",true);
-    $(this).html('<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div> ');
-    $.ajax({
-        type: 'GET',
-        url: 'inc/ajax/transport_check.php',
-        dataType: 'text',
-        data: $('#test_transport_form').serialize(),
-        complete: function (data) {
-          $('#test_transport_result').html(data.responseText);
-          $('#test_transport').prop("disabled",false);
-          $('#test_transport').text(prev);
-        }
-    });
-  })
-  // DKIM private key modal
-  $('#showDKIMprivKey').on('show.bs.modal', function (e) {
-    $('#priv_key_pre').text("-");
-    p_related = $(e.relatedTarget)
-    if (p_related != null) {
-      var decoded_key = Base64.decode((p_related.data('priv-key')));
-      $('#priv_key_pre').text(decoded_key);
-    }
-  })
-  // FIDO2 friendly name modal
-  $('#fido2ChangeFn').on('show.bs.modal', function (e) {
-    rename_link = $(e.relatedTarget)
-    if (rename_link != null) {
-      $('#fido2_cid').val(rename_link.data('cid'));
-      $('#fido2_subject_desc').text(Base64.decode(rename_link.data('subject')));
-    }
-  })
-  // App links
-  function add_table_row(table_id, type) {
-    var row = $('<tr />');
-    if (type == "app_link") {
-      cols = '<td><input class="input-sm input-xs-lg form-control" data-id="app_links" type="text" name="app" required></td>';
-      cols += '<td><input class="input-sm input-xs-lg form-control" data-id="app_links" type="text" name="href" required></td>';
-      cols += '<td><a href="#" role="button" class="btn btn-sm btn-xs-lg btn-secondary h-100 w-100" type="button">' + lang.remove_row + '</a></td>';
-    } else if (type == "f2b_regex") {
-      cols = '<td><input style="text-align:center" class="input-sm input-xs-lg form-control" data-id="f2b_regex" type="text" value="+" disabled></td>';
-      cols += '<td><input class="input-sm input-xs-lg form-control regex-input" data-id="f2b_regex" type="text" name="regex" required></td>';
-      cols += '<td><a href="#" role="button" class="btn btn-sm btn-xs-lg btn-secondary h-100 w-100" type="button">' + lang.remove_row + '</a></td>';
-    }
-    row.append(cols);
-    table_id.append(row);
-  }
-  $('#app_link_table').on('click', 'tr a', function (e) {
-    e.preventDefault();
-    $(this).parents('tr').remove();
-  });
-  $('#f2b_regex_table').on('click', 'tr a', function (e) {
-    e.preventDefault();
-    $(this).parents('tr').remove();
-  });
-  $('#add_app_link_row').click(function() {
-      add_table_row($('#app_link_table'), "app_link");
-  });
-  $('#add_f2b_regex_row').click(function() {
-      add_table_row($('#f2b_regex_table'), "f2b_regex");
-  });
-});
+// Base64 functions
+var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
+jQuery(function($){
+  // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
+  var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
+  function jq(myid) {return "#" + myid.replace( /(:|\.|\[|\]|,|=|@)/g, "\\$1" );}
+  function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
+  function validateRegex(e){var t=e.split("/"),n=e,r="";t.length>1&&(n=t[1],r=t[2]);try{return new RegExp(n,r),!0}catch(e){return!1}}
+  function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
+  function hashCode(t){for(var n=0,r=0;r<t.length;r++)n=t.charCodeAt(r)+((n<<5)-n);return n}
+  function intToRGB(t){var n=(16777215&t).toString(16).toUpperCase();return"00000".substring(0,6-n.length)+n}
+  $("#dkim_missing_keys").on('click', function(e) {
+    e.preventDefault();
+     var domains = [];
+     $('.dkim_missing').each(function() {
+       domains.push($(this).val());
+     });
+     $('#dkim_add_domains').val(domains);
+  });
+  $(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
+  $("#mass_exclude").change(function(){ $("#mass_include").selectpicker('deselectAll'); });
+  $("#mass_include").change(function(){ $("#mass_exclude").selectpicker('deselectAll'); });
+  $("#mass_disarm").click(function() { $("#mass_send").attr("disabled", !this.checked); });
+  $(".admin-ays-dialog").click(function() { return confirm(lang.ays); });
+  $(".validate_rspamd_regex").click(function( event ) {
+    event.preventDefault();
+    var regex_map_id = $(this).data('regex-map');
+    var regex_data = $(jq(regex_map_id)).val().split(/\r?\n/);
+    var regex_valid = true;
+    for(var i = 0;i < regex_data.length;i++){
+      if(regex_data[i].startsWith('#') || !regex_data[i]){
+        continue;
+      }
+      if(!validateRegex(regex_data[i])) {
+        mailcow_alert_box('Cannot build regex from line ' + (i+1), 'danger');
+        var regex_valid = false;
+        break;
+      }
+      if(!regex_data[i].startsWith('/') || !/\/[ims]?$/.test(regex_data[i])){
+        mailcow_alert_box('Line ' + (i+1) + ' is invalid', 'danger');
+        var regex_valid = false;
+        break;
+      }
+    }
+    if (regex_valid) {
+      mailcow_alert_box('Regex OK', 'success');
+      $('button[data-id="' + regex_map_id + '"]').attr({"disabled": false});
+    }
+  });
+  $('.textarea-code').on('keyup', function() {
+    $('.submit_rspamd_regex').attr({"disabled": true});
+  });
+  $("#show_rspamd_global_filters").click(function() {
+    $.get("inc/ajax/show_rspamd_global_filters.php");
+    $("#confirm_show_rspamd_global_filters").hide();
+    $("#rspamd_global_filters").removeClass("d-none");
+  });
+  $("#super_delete").click(function() { return confirm(lang.queue_ays); });
+
+  $(".refresh_table").on('click', function(e) {
+    e.preventDefault();
+    var table_name = $(this).data('table');
+    $('#' + table_name).DataTable().ajax.reload();
+  });
+  function draw_domain_admins() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#domainadminstable') ) {
+      $('#domainadminstable').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#domainadminstable').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/domain-admin/all",
+        dataSrc: function(data){
+          return process_table_data(data, 'domainadminstable');
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: lang.username,
+          data: 'username',
+          defaultContent: ''
+        },
+        {
+          title: lang.admin_domains,
+          data: 'selected_domains',
+          defaultContent: '',
+        },
+        {
+          title: "TFA",
+          data: 'tfa_active',
+          defaultContent: '',
+            render: function (data, type) {
+            if(data == 1) return '<i class="bi bi-check-lg"></i>';
+            else return '<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            if(data == 1) return '<i class="bi bi-check-lg"></i>';
+            else return '<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        },
+      ],
+      initComplete: function(settings, json){
+      }
+    });
+  }
+  function draw_oauth2_clients() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#oauth2clientstable') ) {
+      $('#oauth2clientstable').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#oauth2clientstable').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/oauth2-client/all",
+        dataSrc: function(data){
+          return process_table_data(data, 'oauth2clientstable');
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          defaultContent: ''
+        },
+        {
+          title: lang.oauth2_client_id,
+          data: 'client_id',
+          defaultContent: ''
+        },
+        {
+          title: lang.oauth2_client_secret,
+          data: 'client_secret',
+          defaultContent: ''
+        },
+        {
+          title: lang.oauth2_redirect_uri,
+          data: 'redirect_uri',
+          defaultContent: ''
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        },
+      ]
+    });
+  }
+  function draw_admins() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#adminstable') ) {
+      $('#adminstable').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#adminstable').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/admin/all",
+        dataSrc: function(data){
+          return process_table_data(data, 'adminstable');
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: lang.username,
+          data: 'username',
+          defaultContent: ''
+        },
+        {
+          title: "TFA",
+          data: 'tfa_active',
+          defaultContent: '',
+          render: function (data, type) {
+            if(data == 1) return '<i class="bi bi-check-lg"></i>';
+            else return '<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            if(data == 1) return '<i class="bi bi-check-lg"></i>';
+            else return '<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          defaultContent: '',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right'
+        },
+      ]
+    });
+  }
+  function draw_fwd_hosts() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#forwardinghoststable') ) {
+      $('#forwardinghoststable').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#forwardinghoststable').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/fwdhost/all",
+        dataSrc: function(data){
+          return process_table_data(data, 'forwardinghoststable');
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: lang.host,
+          data: 'host',
+          defaultContent: ''
+        },
+        {
+          title: lang.source,
+          data: 'source',
+          defaultContent: ''
+        },
+        {
+          title: lang.spamfilter,
+          data: 'keep_spam',
+          defaultContent: '',
+          render: function(data, type){
+            return 'yes'==data?'<i class="bi bi-x-lg"></i>':'no'==data&&'<i class="bi bi-check-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        },
+      ]
+    });
+  }
+  function draw_relayhosts() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#relayhoststable') ) {
+      $('#relayhoststable').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#relayhoststable').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/relayhost/all",
+        dataSrc: function(data){
+          return process_table_data(data, 'relayhoststable');
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          defaultContent: ''
+        },
+        {
+          title: lang.host,
+          data: 'hostname',
+          defaultContent: ''
+        },
+        {
+          title: lang.username,
+          data: 'username',
+          defaultContent: ''
+        },
+        {
+          title: lang.in_use_by,
+          data: 'in_use_by',
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            if(data == 1) return '<i class="bi bi-check-lg"></i>';
+            else return '<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        },
+      ]
+    });
+  }
+  function draw_transport_maps() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#transportstable') ) {
+      $('#transportstable').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#transportstable').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/transport/all",
+        dataSrc: function(data){
+          return process_table_data(data, 'transportstable');
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          defaultContent: ''
+        },
+        {
+          title: lang.destination,
+          data: 'destination',
+          defaultContent: ''
+        },
+        {
+          title: lang.nexthop,
+          data: 'nexthop',
+          defaultContent: ''
+        },
+        {
+          title: lang.username,
+          data: 'username',
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            if(data == 1) return '<i class="bi bi-check-lg"></i>';
+            else return '<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        },
+      ]
+    });
+  }
+
+  function process_table_data(data, table) {
+    if (table == 'relayhoststable') {
+      $.each(data, function (i, item) {
+        item.action = '<div class="btn-group">' +
+          '<a href="#" data-bs-toggle="modal" data-bs-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="sender-dependent" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-caret-right-fill"></i> Test</a>' +
+          '<a href="/edit/relayhost/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+          '<a href="#" data-action="delete_selected" data-id="single-rlyhost" data-api-url="delete/relayhost" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+          '</div>';
+        if (item.used_by_mailboxes == '') { item.in_use_by = item.used_by_domains; }
+        else if (item.used_by_domains == '') { item.in_use_by = item.used_by_mailboxes; }
+        else { item.in_use_by = item.used_by_mailboxes + '<hr style="margin:5px 0px 5px 0px;">' + item.used_by_domains; }
+        item.chkbox = '<input type="checkbox" data-id="rlyhosts" name="multi_select" value="' + item.id + '" />';
+      });
+    } else if (table == 'transportstable') {
+      $.each(data, function (i, item) {
+        if (item.is_mx_based) {
+          item.destination = '<i class="bi bi-info-circle-fill text-info mx-info" data-bs-toggle="tooltip" title="' + lang.is_mx_based + '"></i> <code>' + item.destination + '</code>';
+        }
+        if (item.username) {
+          item.username = '<i style="color:#' + intToRGB(hashCode(item.nexthop)) + ';" class="bi bi-square-fill"></i> ' + item.username;
+        }
+        item.action = '<div class="btn-group">' +
+          '<a href="#" data-bs-toggle="modal" data-bs-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="transport-map" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-caret-right-fill"></i> Test</a>' +
+          '<a href="/edit/transport/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+          '<a href="#" data-action="delete_selected" data-id="single-transport" data-api-url="delete/transport" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+          '</div>';
+        item.chkbox = '<input type="checkbox" data-id="transports" name="multi_select" value="' + item.id + '" />';
+      });
+    } else if (table == 'queuetable') {
+      $.each(data, function (i, item) {
+        item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
+        rcpts = $.map(item.recipients, function(i) {
+          return escapeHtml(i);
+        });
+        item.recipients = rcpts.join('<hr style="margin:1px!important">');
+        item.action = '<div class="btn-group">' +
+          '<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.queue_show_message + '</a>' +
+          '</div>';
+      });
+    } else if (table == 'forwardinghoststable') {
+      $.each(data, function (i, item) {
+        item.action = '<div class="btn-group">' +
+          '<a href="#" data-action="delete_selected" data-id="single-fwdhost" data-api-url="delete/fwdhost" data-item="' + encodeURI(item.host) + '" class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+          '</div>';
+        item.chkbox = '<input type="checkbox" data-id="fwdhosts" name="multi_select" value="' + item.host + '" />';
+      });
+    } else if (table == 'oauth2clientstable') {
+      $.each(data, function (i, item) {
+        item.action = '<div class="btn-group">' +
+          '<a href="/edit.php?oauth2client=' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+          '<a href="#" data-action="delete_selected" data-id="single-oauth2-client" data-api-url="delete/oauth2-client" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+          '</div>';
+        item.scope = "profile";
+        item.grant_types = 'refresh_token password authorization_code';
+        item.chkbox = '<input type="checkbox" data-id="oauth2_clients" name="multi_select" value="' + item.id + '" />';
+      });
+    } else if (table == 'domainadminstable') {
+      $.each(data, function (i, item) {
+        item.selected_domains = escapeHtml(item.selected_domains);
+        item.selected_domains = item.selected_domains.toString().replace(/,/g, "<br>");
+        item.chkbox = '<input type="checkbox" data-id="domain_admins" name="multi_select" value="' + item.username + '" />';
+        item.action = '<div class="btn-group">' +
+          '<a href="/edit/domainadmin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+          '<a href="#" data-action="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+          '<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-third btn-success"><i class="bi bi-person-fill"></i> Login</a>' +
+          '</div>';
+      });
+    } else if (table == 'adminstable') {
+      $.each(data, function (i, item) {
+        if (admin_username.toLowerCase() == item.username.toLowerCase()) {
+          item.usr = '<i class="bi bi-person-check"></i> ' + item.username;
+        } else {
+          item.usr = item.username;
+        }
+        item.chkbox = '<input type="checkbox" data-id="admins" name="multi_select" value="' + item.username + '" />';
+        item.action = '<div class="btn-group">' +
+          '<a href="/edit/admin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+          '<a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+          '</div>';
+      });
+    }
+    return data
+  };
+
+  // detect element visibility changes
+  function onVisible(element, callback) {
+    $(document).ready(function() {
+      element_object = document.querySelector(element);
+      if (element_object === null) return;
+
+      new IntersectionObserver((entries, observer) => {
+        entries.forEach(entry => {
+          if(entry.intersectionRatio > 0) {
+            callback(element_object);
+          }
+        });
+      }).observe(element_object);
+    });
+  }
+  // Draw Table if tab is active
+  onVisible("[id^=adminstable]", () => draw_admins());
+  onVisible("[id^=domainadminstable]", () => draw_domain_admins());
+  onVisible("[id^=oauth2clientstable]", () => draw_oauth2_clients());
+  onVisible("[id^=forwardinghoststable]", () => draw_fwd_hosts());
+  onVisible("[id^=relayhoststable]", () => draw_relayhosts());
+  onVisible("[id^=transportstable]", () => draw_transport_maps());
+
+
+  $('body').on('click', 'span.footable-toggle', function () {
+    event.stopPropagation();
+  })
+
+  // API IP check toggle
+  $("#skip_ip_check_ro").click(function( event ) {
+   $("#skip_ip_check_ro").not(this).prop('checked', false);
+    if ($("#skip_ip_check_ro:checked").length > 0) {
+      $('#allow_from_ro').prop('disabled', true);
+    }
+    else {
+      $("#allow_from_ro").removeAttr('disabled');
+    }
+  });
+  $("#skip_ip_check_rw").click(function( event ) {
+   $("#skip_ip_check_rw").not(this).prop('checked', false);
+    if ($("#skip_ip_check_rw:checked").length > 0) {
+      $('#allow_from_rw').prop('disabled', true);
+    }
+    else {
+      $("#allow_from_rw").removeAttr('disabled');
+    }
+  });
+  // Relayhost
+  $('#testRelayhostModal').on('show.bs.modal', function (e) {
+    $('#test_relayhost_result').text("-");
+    button = $(e.relatedTarget)
+    if (button != null) {
+      $('#relayhost_id').val(button.data('relayhost-id'));
+    }
+  })
+  $('#test_relayhost').on('click', function (e) {
+    e.preventDefault();
+    prev = $('#test_relayhost').text();
+    $(this).prop("disabled",true);
+    $(this).html('<i class="bi bi-arrow-repeat icon-spin"></i> ');
+    $.ajax({
+      type: 'GET',
+      url: 'inc/ajax/relay_check.php',
+      dataType: 'text',
+      data: $('#test_relayhost_form').serialize(),
+      complete: function (data) {
+        $('#test_relayhost_result').html(data.responseText);
+        $('#test_relayhost').prop("disabled",false);
+        $('#test_relayhost').text(prev);
+      }
+    });
+  })
+  // Transport
+  $('#testTransportModal').on('show.bs.modal', function (e) {
+    $('#test_transport_result').text("-");
+    button = $(e.relatedTarget)
+    if (button != null) {
+      $('#transport_id').val(button.data('transport-id'));
+      $('#transport_type').val(button.data('transport-type'));
+    }
+  })
+  $('#test_transport').on('click', function (e) {
+    e.preventDefault();
+    prev = $('#test_transport').text();
+    $(this).prop("disabled",true);
+    $(this).html('<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div> ');
+    $.ajax({
+      type: 'GET',
+      url: 'inc/ajax/transport_check.php',
+      dataType: 'text',
+      data: $('#test_transport_form').serialize(),
+      complete: function (data) {
+        $('#test_transport_result').html(data.responseText);
+        $('#test_transport').prop("disabled",false);
+        $('#test_transport').text(prev);
+      }
+    });
+  })
+  // DKIM private key modal
+  $('#showDKIMprivKey').on('show.bs.modal', function (e) {
+    $('#priv_key_pre').text("-");
+    p_related = $(e.relatedTarget)
+    if (p_related != null) {
+      var decoded_key = Base64.decode((p_related.data('priv-key')));
+      $('#priv_key_pre').text(decoded_key);
+    }
+  })
+  // FIDO2 friendly name modal
+  $('#fido2ChangeFn').on('show.bs.modal', function (e) {
+    rename_link = $(e.relatedTarget)
+    if (rename_link != null) {
+      $('#fido2_cid').val(rename_link.data('cid'));
+      $('#fido2_subject_desc').text(Base64.decode(rename_link.data('subject')));
+    }
+  })
+  // App links
+  function add_table_row(table_id, type) {
+    var row = $('<tr />');
+    if (type == "app_link") {
+      cols = '<td><input class="input-sm input-xs-lg form-control" data-id="app_links" type="text" name="app" required></td>';
+      cols += '<td><input class="input-sm input-xs-lg form-control" data-id="app_links" type="text" name="href" required></td>';
+      cols += '<td><a href="#" role="button" class="btn btn-sm btn-xs-lg btn-secondary h-100 w-100" type="button">' + lang.remove_row + '</a></td>';
+    } else if (type == "f2b_regex") {
+      cols = '<td><input style="text-align:center" class="input-sm input-xs-lg form-control" data-id="f2b_regex" type="text" value="+" disabled></td>';
+      cols += '<td><input class="input-sm input-xs-lg form-control regex-input" data-id="f2b_regex" type="text" name="regex" required></td>';
+      cols += '<td><a href="#" role="button" class="btn btn-sm btn-xs-lg btn-secondary h-100 w-100" type="button">' + lang.remove_row + '</a></td>';
+    }
+    row.append(cols);
+    table_id.append(row);
+  }
+  $('#app_link_table').on('click', 'tr a', function (e) {
+    e.preventDefault();
+    $(this).parents('tr').remove();
+  });
+  $('#f2b_regex_table').on('click', 'tr a', function (e) {
+    e.preventDefault();
+    $(this).parents('tr').remove();
+  });
+  $('#add_app_link_row').click(function() {
+    add_table_row($('#app_link_table'), "app_link");
+  });
+  $('#add_f2b_regex_row').click(function() {
+    add_table_row($('#f2b_regex_table'), "f2b_regex");
+  });
+});
diff --git a/data/web/js/site/debug.js b/data/web/js/site/debug.js
index a7b06e5a..e0b9a5ab 100644
--- a/data/web/js/site/debug.js
+++ b/data/web/js/site/debug.js
@@ -34,7 +34,7 @@ $(document).ready(function() {
   });
 
   // set update loop container list
-  containersToUpdate = {}
+  containersToUpdate = {};
   // set default ChartJs Font Color
   Chart.defaults.color = '#999';
   // create host cpu and mem charts
@@ -44,14 +44,13 @@ $(document).ready(function() {
     check_update(mailcow_info.version_tag, mailcow_info.project_url);
   }
   $("#maiclow_version").click(function(){
-    if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" ||
-       mailcow_info.branch !== "master")
+    if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
       return;
 
     showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag);
   })
   // get public ips
-  $("#host_show_ip").click(function(){  
+  $("#host_show_ip").click(function(){
     $("#host_show_ip").find(".text").addClass("d-none");
     $("#host_show_ip").find(".spinner-border").removeClass("d-none");
 
@@ -76,7 +75,7 @@ $(document).ready(function() {
       $("#host_ipv6").addClass("d-block");
     }).catch(function(error){
       console.log(error);
-      
+
       $("#host_ipv6").removeClass("d-none");
       $("#host_ipv6").addClass("d-block");
       $("#host_ipv6").addClass("text-danger");
@@ -119,10 +118,11 @@ jQuery(function($){
     }
 
     var table = $('#autodiscover_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -188,10 +188,11 @@ jQuery(function($){
     }
 
     var table = $('#postfix_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -242,10 +243,11 @@ jQuery(function($){
     }
 
     var table = $('#watchdog_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -300,10 +302,11 @@ jQuery(function($){
     }
 
     var table =  $('#api_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -352,7 +355,7 @@ jQuery(function($){
         }
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
     });
@@ -365,10 +368,11 @@ jQuery(function($){
     }
 
     var table = $('#rl_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -455,7 +459,7 @@ jQuery(function($){
         }
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
     });
@@ -468,10 +472,11 @@ jQuery(function($){
     }
 
     var table = $('#ui_logs').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -538,7 +543,7 @@ jQuery(function($){
         }
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log');
     });
@@ -551,10 +556,11 @@ jQuery(function($){
     }
 
     var table = $('#sasl_logs').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -598,7 +604,7 @@ jQuery(function($){
         }
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
     });
@@ -611,10 +617,11 @@ jQuery(function($){
     }
 
     var table = $('#acme_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -647,7 +654,7 @@ jQuery(function($){
         }
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
     });
@@ -660,10 +667,11 @@ jQuery(function($){
     }
 
     var table = $('#netfilter_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -701,7 +709,7 @@ jQuery(function($){
         }
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
     });
@@ -714,10 +722,11 @@ jQuery(function($){
     }
 
     var table = $('#sogo_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -755,7 +764,7 @@ jQuery(function($){
         }
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
     });
@@ -768,10 +777,11 @@ jQuery(function($){
     }
 
     var table = $('#dovecot_log').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -883,10 +893,11 @@ jQuery(function($){
     }
 
     var table = $('#rspamd_history').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: log_pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -983,7 +994,7 @@ jQuery(function($){
         }
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history');
     });
@@ -998,31 +1009,31 @@ jQuery(function($){
         item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
       }
       item.symbols = Object.keys(item.symbols).sort(function (a, b) {
-        if (item.symbols[a].score === 0) return 1
-        if (item.symbols[b].score === 0) return -1
+        if (item.symbols[a].score === 0) return 1;
+        if (item.symbols[b].score === 0) return -1;
         if (item.symbols[b].score < 0 && item.symbols[a].score < 0) {
-          return item.symbols[a].score - item.symbols[b].score
+          return item.symbols[a].score - item.symbols[b].score;
         }
         if (item.symbols[b].score > 0 && item.symbols[a].score > 0) {
-          return item.symbols[b].score - item.symbols[a].score
+          return item.symbols[b].score - item.symbols[a].score;
         }
-        return item.symbols[b].score - item.symbols[a].score
+        return item.symbols[b].score - item.symbols[a].score;
       }).map(function(key) {
         var sym = item.symbols[key];
         if (sym.score < 0) {
-          sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
+          sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)';
         }
         else if (sym.score === 0) {
-          sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)'
+          sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)';
         }
         else {
-          sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
+          sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)';
         }
         var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
         if (sym.options) {
           str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
         }
-        return str
+        return str;
       }).join('<br>\n');
       item.subject = escapeHtml(item.subject);
       var scan_time = item.time_real.toFixed(3);
@@ -1155,14 +1166,14 @@ jQuery(function($){
         }
       });
     }
-    return data
+    return data;
   };
   $('.add_log_lines').on('click', function (e) {
     e.preventDefault();
-    var log_table= $(this).data("table")
-    var new_nrows = $(this).data("nrows")
-    var post_process = $(this).data("post-process")
-    var log_url = $(this).data("log-url")
+    var log_table= $(this).data("table");
+    var new_nrows = $(this).data("nrows");
+    var post_process = $(this).data("post-process");
+    var log_url = $(this).data("log-url");
     if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
       console.log("no data-table or data-nrows or log_url or data-post-process attr found");
       return;
@@ -1184,9 +1195,9 @@ jQuery(function($){
   })
   function hideTableExpandCollapseBtn(tab, table){
     if ($(table).hasClass('collapsed'))
-      $(tab).find(".table_collapse_option").show(); 
+      $(tab).find(".table_collapse_option").show();
     else
-      $(tab).find(".table_collapse_option").hide(); 
+      $(tab).find(".table_collapse_option").hide();
   }
 
   // detect element visibility changes
@@ -1220,7 +1231,6 @@ jQuery(function($){
   onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
 
 
-
   // start polling host stats if tab is active
   onVisible("[id^=tab-containers]", () => update_stats());
   // start polling container stats if collapse is active
@@ -1303,9 +1313,9 @@ function update_stats(timeout=5){
       if (mem_chart.data.labels.length > 30) mem_chart.data.labels.shift();
 
       cpu_chart.data.datasets[0].data.push(data.cpu.usage);
-      if (cpu_chart.data.datasets[0].data.length > 30)  cpu_chart.data.datasets[0].data.shift();
+      if (cpu_chart.data.datasets[0].data.length > 30) cpu_chart.data.datasets[0].data.shift();
       mem_chart.data.datasets[0].data.push(data.memory.usage);
-      if (mem_chart.data.datasets[0].data.length > 30)  mem_chart.data.datasets[0].data.shift();
+      if (mem_chart.data.datasets[0].data.length > 30) mem_chart.data.datasets[0].data.shift();
 
       cpu_chart.update();
       mem_chart.update();
@@ -1464,23 +1474,23 @@ function createReadWriteChart(chart_id, read_lable, write_lable){
   };
   var optionsNet = {
     interaction: {
-        mode: 'index'
+      mode: 'index'
     },
     scales: {
       yAxis: {
         min: 0,
         grid: {
-            display: false
+          display: false
         },
         ticks: {
           callback: function(i, index, ticks) {
-             return formatBytes(i);
+            return formatBytes(i);
           }
         }
       },
       xAxis: {
         grid: {
-            display: false
+          display: false
         }
       }
     }
@@ -1528,13 +1538,13 @@ function createHostCpuAndMemChart(){
   };
   var optionsCpu = {
     interaction: {
-        mode: 'index'
+      mode: 'index'
     },
     scales: {
       yAxis: {
         min: 0,
         grid: {
-            display: false
+          display: false
         },
         ticks: {
           callback: function(i, index, ticks) {
@@ -1544,7 +1554,7 @@ function createHostCpuAndMemChart(){
       },
       xAxis: {
         grid: {
-            display: false
+          display: false
         }
       }
     }
@@ -1566,13 +1576,13 @@ function createHostCpuAndMemChart(){
   };
   var optionsMem = {
     interaction: {
-        mode: 'index'
+      mode: 'index'
     },
     scales: {
       yAxis: {
         min: 0,
         grid: {
-            display: false
+          display: false
         },
         ticks: {
           callback: function(i, index, ticks) {
@@ -1582,7 +1592,7 @@ function createHostCpuAndMemChart(){
       },
       xAxis: {
         grid: {
-            display: false
+          display: false
         }
       }
     }
@@ -1678,22 +1688,22 @@ function parseGithubMarkdownLinks(inputText) {
 
   replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
   replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
-      if (matched.includes('github.com')){
-        // return short link if it's github link
-        last_uri_path = matched.split('/');
-        last_uri_path = last_uri_path[last_uri_path.length - 1];
+    if (matched.includes('github.com')){
+      // return short link if it's github link
+      last_uri_path = matched.split('/');
+      last_uri_path = last_uri_path[last_uri_path.length - 1];
 
-        // adjust Full Changelog link to match last git version and new git version, if link is a compare link
-        if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){
-          matched = matched.replace(last_uri_path,  mailcow_info.last_version_tag + '...' + mailcow_info.version_tag);
-          last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag;
-        }
+      // adjust Full Changelog link to match last git version and new git version, if link is a compare link
+      if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){
+        matched = matched.replace(last_uri_path,  mailcow_info.last_version_tag + '...' + mailcow_info.version_tag);
+        last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag;
+      }
 
-        return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>';
-      };
+      return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>';
+    };
 
-      // if it's not a github link, return complete link
-      return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
+    // if it's not a github link, return complete link
+    return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
   });
 
   return replacedText;
diff --git a/data/web/js/site/edit.js b/data/web/js/site/edit.js
index 4c57b35e..4680bdfa 100644
--- a/data/web/js/site/edit.js
+++ b/data/web/js/site/edit.js
@@ -1,220 +1,222 @@
-$(document).ready(function() {
-  $(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
-  $("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
-  $(".goto_checkbox").click(function( event ) {
-   $("form[data-id='editalias'] .goto_checkbox").not(this).prop('checked', false);
-    if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
-      $('#textarea_alias_goto').prop('disabled', true);
-    }
-    else {
-      $("#textarea_alias_goto").removeAttr('disabled');
-    }
-  });
-  $("#disable_sender_check").click(function( event ) {
-    if ($("form[data-id='editmailbox'] #disable_sender_check:checked").length > 0) {
-      $('#editSelectSenderACL').prop('disabled', true);
-      $('#editSelectSenderACL').selectpicker('refresh');
-    }
-    else {
-      $('#editSelectSenderACL').prop('disabled', false);
-      $('#editSelectSenderACL').selectpicker('refresh');
-    }
-  });
-  if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
-    $('#textarea_alias_goto').prop('disabled', true);
-  }
-
-  $("#mailbox-password-warning-close").click(function( event ) {
-    $('#mailbox-passwd-hidden-info').addClass('hidden');
-    $('#mailbox-passwd-form-groups').removeClass('hidden');
-  });
-  // Sender ACL
-  if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
-    $("#sender_acl_disabled").show();
-  }
-  $('#editSelectSenderACL').change(function() {
-    if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
-      $("#sender_acl_disabled").show();
-    }
-    else {
-      $("#sender_acl_disabled").hide();
-    }
-  });
-  // Resources
-  if ($("#editSelectMultipleBookings").val() == "custom") {
-    $("#multiple_bookings_custom_div").show();
-    $('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
-  }
-  $("#editSelectMultipleBookings").change(function() {
-    $('input[name=multiple_bookings]').val($("#editSelectMultipleBookings").val());
-    if ($('input[name=multiple_bookings]').val() == "custom") {
-      $("#multiple_bookings_custom_div").show();
-    }
-    else {
-      $("#multiple_bookings_custom_div").hide();
-    }
-  });
-  $("#multiple_bookings_custom").bind("change keypress keyup blur", function() {
-    $('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
-  });
-
-  // load tags
-  if ($('#tags').length){
-    var tagsEl = $('#tags').parent().find('.tag-values')[0];
-    console.log($(tagsEl).val())
-    var tags = JSON.parse($(tagsEl).val());
-    $(tagsEl).val("");
-    
-    for (var i = 0; i < tags.length; i++)
-      addTag($('#tags'), tags[i]);
-  }
-});
-
-jQuery(function($){
-  // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
-  function validateEmail(email) {
-    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-    return re.test(email);
-  }
-  function draw_wl_policy_domain_table() {
-    $('#wl_policy_domain_table').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: '/api/v1/get/policy_wl_domain/' + table_for_domain,
-        dataSrc: function(data){
-          $.each(data, function (i, item) {
-            if (!validateEmail(item.object)) {
-              item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
-            }
-            else {
-              item.chkbox = '<input type="checkbox" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />';
-            }
-          });
-
-          return data;
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: 'ID',
-            data: 'prefid',
-            defaultContent: ''
-          },
-          {
-            title: lang_user.spamfilter_table_rule,
-            data: 'value',
-            defaultContent: ''
-          },
-          {
-            title: 'Scope',
-            data: 'object',
-            defaultContent: ''
-          }
-      ]
-    });
-  }
-  function draw_bl_policy_domain_table() {
-    $('#bl_policy_domain_table').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: '/api/v1/get/policy_bl_domain/' + table_for_domain,
-        dataSrc: function(data){
-          $.each(data, function (i, item) {
-            if (!validateEmail(item.object)) {
-              item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
-            }
-            else {
-              item.chkbox = '<input type="checkbox" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />';
-            }
-          });
-
-          return data;
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: 'ID',
-            data: 'prefid',
-            defaultContent: ''
-          },
-          {
-            title: lang_user.spamfilter_table_rule,
-            data: 'value',
-            defaultContent: ''
-          },
-          {
-            title: 'Scope',
-            data: 'object',
-            defaultContent: ''
-          }
-      ]
-    });
-  }
-
-  
-  // detect element visibility changes
-  function onVisible(element, callback) {
-    $(document).ready(function() {
-      element_object = document.querySelector(element);
-      if (element_object === null) return;
-
-      new IntersectionObserver((entries, observer) => {
-        entries.forEach(entry => {
-          if(entry.intersectionRatio > 0) {
-            callback(element_object);
-            observer.disconnect();
-          }
-        });
-      }).observe(element_object);
-    });
-  }
-  // Draw Table if tab is active
-  onVisible("[id^=wl_policy_domain_table]", () => draw_wl_policy_domain_table());
-  onVisible("[id^=bl_policy_domain_table]", () => draw_bl_policy_domain_table());
-});
+$(document).ready(function() {
+  $(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
+  $("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
+  $(".goto_checkbox").click(function( event ) {
+    $("form[data-id='editalias'] .goto_checkbox").not(this).prop('checked', false);
+    if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
+      $('#textarea_alias_goto').prop('disabled', true);
+    }
+    else {
+      $("#textarea_alias_goto").removeAttr('disabled');
+    }
+  });
+  $("#disable_sender_check").click(function( event ) {
+    if ($("form[data-id='editmailbox'] #disable_sender_check:checked").length > 0) {
+      $('#editSelectSenderACL').prop('disabled', true);
+      $('#editSelectSenderACL').selectpicker('refresh');
+    }
+    else {
+      $('#editSelectSenderACL').prop('disabled', false);
+      $('#editSelectSenderACL').selectpicker('refresh');
+    }
+  });
+  if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
+    $('#textarea_alias_goto').prop('disabled', true);
+  }
+
+  $("#mailbox-password-warning-close").click(function( event ) {
+    $('#mailbox-passwd-hidden-info').addClass('hidden');
+    $('#mailbox-passwd-form-groups').removeClass('hidden');
+  });
+  // Sender ACL
+  if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
+    $("#sender_acl_disabled").show();
+  }
+  $('#editSelectSenderACL').change(function() {
+    if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
+      $("#sender_acl_disabled").show();
+    }
+    else {
+      $("#sender_acl_disabled").hide();
+    }
+  });
+  // Resources
+  if ($("#editSelectMultipleBookings").val() == "custom") {
+    $("#multiple_bookings_custom_div").show();
+    $('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
+  }
+  $("#editSelectMultipleBookings").change(function() {
+    $('input[name=multiple_bookings]').val($("#editSelectMultipleBookings").val());
+    if ($('input[name=multiple_bookings]').val() == "custom") {
+      $("#multiple_bookings_custom_div").show();
+    }
+    else {
+      $("#multiple_bookings_custom_div").hide();
+    }
+  });
+  $("#multiple_bookings_custom").bind("change keypress keyup blur", function() {
+    $('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
+  });
+
+  // load tags
+  if ($('#tags').length){
+    var tagsEl = $('#tags').parent().find('.tag-values')[0];
+    console.log($(tagsEl).val())
+    var tags = JSON.parse($(tagsEl).val());
+    $(tagsEl).val("");
+
+    for (var i = 0; i < tags.length; i++)
+      addTag($('#tags'), tags[i]);
+  }
+});
+
+jQuery(function($){
+  // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
+  function validateEmail(email) {
+    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+    return re.test(email);
+  }
+  function draw_wl_policy_domain_table() {
+    $('#wl_policy_domain_table').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: '/api/v1/get/policy_wl_domain/' + table_for_domain,
+        dataSrc: function(data){
+          $.each(data, function (i, item) {
+            if (!validateEmail(item.object)) {
+              item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />';
+            }
+          });
+
+          return data;
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'prefid',
+          defaultContent: ''
+        },
+        {
+          title: lang_user.spamfilter_table_rule,
+          data: 'value',
+          defaultContent: ''
+        },
+        {
+          title: 'Scope',
+          data: 'object',
+          defaultContent: ''
+        }
+      ]
+    });
+  }
+  function draw_bl_policy_domain_table() {
+    $('#bl_policy_domain_table').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: '/api/v1/get/policy_bl_domain/' + table_for_domain,
+        dataSrc: function(data){
+          $.each(data, function (i, item) {
+            if (!validateEmail(item.object)) {
+              item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />';
+            }
+          });
+
+          return data;
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'prefid',
+          defaultContent: ''
+        },
+        {
+          title: lang_user.spamfilter_table_rule,
+          data: 'value',
+          defaultContent: ''
+        },
+        {
+          title: 'Scope',
+          data: 'object',
+          defaultContent: ''
+        }
+      ]
+    });
+  }
+
+
+  // detect element visibility changes
+  function onVisible(element, callback) {
+    $(document).ready(function() {
+      element_object = document.querySelector(element);
+      if (element_object === null) return;
+
+      new IntersectionObserver((entries, observer) => {
+        entries.forEach(entry => {
+          if(entry.intersectionRatio > 0) {
+            callback(element_object);
+            observer.disconnect();
+          }
+        });
+      }).observe(element_object);
+    });
+  }
+  // Draw Table if tab is active
+  onVisible("[id^=wl_policy_domain_table]", () => draw_wl_policy_domain_table());
+  onVisible("[id^=bl_policy_domain_table]", () => draw_bl_policy_domain_table());
+});
diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js
index b93b1819..49cce1b2 100644
--- a/data/web/js/site/mailbox.js
+++ b/data/web/js/site/mailbox.js
@@ -77,7 +77,7 @@ $(document).ready(function() {
         $('.dns-modal-body').html(xhr.responseText);
       }
     });
-  }); 
+  });
   // @Open Domain add modal
   $('#addDomainModal').on('show.bs.modal', function(e) {
     $.ajax({
@@ -85,24 +85,24 @@ $(document).ready(function() {
       data: {},
       dataType: 'json',
       success: async function(data){
-        $('#domain_templates').find('option').remove(); 
+        $('#domain_templates').find('option').remove();
         $('#domain_templates').selectpicker('destroy');
         $('#domain_templates').selectpicker();
         for (var i = 0; i < data.length; i++){
           if (data[i].template === "Default"){
-            $('#domain_templates').prepend($('<option>', { 
-                'value': data[i].id,
-                'text': data[i].template,
-                'data-attributes': JSON.stringify(data[i].attributes),
-                'selected': true
+            $('#domain_templates').prepend($('<option>', {
+              'value': data[i].id,
+              'text': data[i].template,
+              'data-attributes': JSON.stringify(data[i].attributes),
+              'selected': true
             }));
             setDomainTemplateData(data[i].attributes);
           } else {
-            $('#domain_templates').append($('<option>', { 
-                'value': data[i].id,
-                'text': data[i].template,
-                'data-attributes': JSON.stringify(data[i].attributes),
-                'selected': false
+            $('#domain_templates').append($('<option>', {
+              'value': data[i].id,
+              'text': data[i].template,
+              'data-attributes': JSON.stringify(data[i].attributes),
+              'selected': false
             }));
           }
         };
@@ -127,24 +127,24 @@ $(document).ready(function() {
       data: {},
       dataType: 'json',
       success: async function(data){
-        $('#mailbox_templates').find('option').remove(); 
+        $('#mailbox_templates').find('option').remove();
         $('#mailbox_templates').selectpicker('destroy');
         $('#mailbox_templates').selectpicker();
         for (var i = 0; i < data.length; i++){
           if (data[i].template === "Default"){
-            $('#mailbox_templates').prepend($('<option>', { 
-                'value': data[i].id,
-                'text': data[i].template,
-                'data-attributes': JSON.stringify(data[i].attributes),
-                'selected': true
+            $('#mailbox_templates').prepend($('<option>', {
+              'value': data[i].id,
+              'text': data[i].template,
+              'data-attributes': JSON.stringify(data[i].attributes),
+              'selected': true
             }));
             setMailboxTemplateData(data[i].attributes);
           } else {
-            $('#mailbox_templates').append($('<option>', { 
-                value: data[i].id,
-                text : data[i].template,
-                'data-attributes': JSON.stringify(data[i].attributes),
-                'selected': false
+            $('#mailbox_templates').append($('<option>', {
+              value: data[i].id,
+              text : data[i].template,
+              'data-attributes': JSON.stringify(data[i].attributes),
+              'selected': false
             }));
           }
         };
@@ -229,20 +229,20 @@ $(document).ready(function() {
     } else {
       $('#addDomain_gal').prop('checked', false);
     }
-    
+
     if (template.active == 1){
       $('#addDomain_active').prop('checked', true);
     } else {
       $('#addDomain_active').prop('checked', false);
     }
-    
+
     $("#addDomain_rl_value").val(template.rl_value);
     $('#addDomain_rl_frame').selectpicker('val', template.rl_frame);
     $("#dkim_selector").val(template.dkim_selector);
     if (!template.key_size)
       template.key_size = 2048;
     $('#key_size').selectpicker('val', template.key_size.toString());
-    
+
     if (template.backupmx == 1){
       $('#addDomain_relay_domain').prop('checked', true);
     } else {
@@ -259,7 +259,7 @@ $(document).ready(function() {
       $('#addDomain_relay_unknown_only').prop('checked', false);
     }
 
-    
+
     // load tags
     $('#addDomain_tags').val("");
     $($('#addDomain_tags').parent().find(".tag-values")[0]).val("");
@@ -404,7 +404,7 @@ $(document).ready(function() {
     } else {
       $('#sogo_access').prop('checked', false);
     }
-    
+
     // load tags
     $('#addMailbox_tags').val("");
     $($('#addMailbox_tags').parent().find(".tag-values")[0]).val("");
@@ -417,11 +417,11 @@ jQuery(function($){
   // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
   function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
   function unix_time_format(i){return""==i?'<i class="bi bi-x"></i>':new Date(i?1e3*i:0).toLocaleDateString(void 0,{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"})}
-  
+
   $(".refresh_table").on('click', function(e) {
     e.preventDefault();
     var table_name = $(this).data('table');
-    
+
     if ($.fn.DataTable.isDataTable('#' + table_name))
       $('#' + table_name).DataTable().ajax.reload();
   });
@@ -433,10 +433,11 @@ jQuery(function($){
     }
 
     var table = $('#domain_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -617,8 +618,8 @@ jQuery(function($){
           defaultContent: ''
         },
       ]
-    });      
-    
+    });
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-domains', '#domain_table');
     });
@@ -631,15 +632,16 @@ jQuery(function($){
     }
 
     var table = $('#templates_domain_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
-      order:[[2, 'desc']],
+      order: [[2, 'desc']],
       initComplete: function(){
         hideTableExpandCollapseBtn('#tab-templates-domains', '#templates_domain_table');
       },
@@ -666,13 +668,13 @@ jQuery(function($){
             }
             item.attributes.rl_value = escapeHtml(item.attributes.rl_value);
 
-            
+
             if (item.template.toLowerCase() == "default"){
               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>' +
               '</div>';
             }
-            else{
+            else {
               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="#" data-action="delete_selected" data-id="single-template" data-api-url="delete/domain/template" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
@@ -693,138 +695,138 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: "ID",
-            data: 'id',
-            responsivePriority: 2,
-            defaultContent: ''
-          },
-          {
-            title: lang.template,
-            data: 'template',
-            responsivePriority: 3,
-            defaultContent: ''
-          },              
-          {
-            title: lang.max_aliases,
-            data: 'attributes.max_num_aliases_for_domain',
-            defaultContent: '',
-          },             
-          {
-            title: lang.max_mailboxes,
-            data: 'attributes.max_num_mboxes_for_domain',
-            defaultContent: '',
-          },             
-          {
-            title: lang.mailbox_defquota,
-            data: 'attributes.def_quota_for_mbox',
-            defaultContent: '',
-          },               
-          {
-            title: lang.max_quota,
-            data: 'attributes.max_quota_for_mbox',
-            defaultContent: '',
-          },            
-          {
-            title: lang.domain_quota_total,
-            data: 'attributes.max_quota_for_domain',
-            defaultContent: '',
-          },          
-          {
-            title: lang.gal,
-            data: 'attributes.gal',
-            defaultContent: '',
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
-            }
-          },           
-          {
-            title: lang.backup_mx,
-            data: 'attributes.backupmx',
-            defaultContent: '',
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
-            }
-          },           
-          {
-            title: lang.relay_all,
-            data: 'attributes.relay_all_recipients',
-            defaultContent: '',
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
-            }
-          },           
-          {
-            title: lang.relay_unknown,
-            data: 'attributes.relay_unknown_only',
-            defaultContent: '',
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
-            }
-          },           
-          {
-            title: lang.active,
-            data: 'attributes.active',
-            defaultContent: '',
-            responsivePriority: 4,
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
-            }
-          },               
-          {
-            title: 'rl_frame',
-            data: 'attributes.rl_frame',
-            defaultContent: '',
-            class: 'none',
-          },             
-          {
-            title: 'rl_value',
-            data: 'attributes.rl_value',
-            defaultContent: '',
-            class: 'none',
-          },            
-          {
-            title: lang.dkim_domains_selector,
-            data: 'attributes.dkim_selector',
-            defaultContent: '',
-            class: 'none',
-          },            
-          {
-            title: lang.dkim_key_length,
-            data: 'attributes.key_size',
-            defaultContent: '',
-            class: 'none',
-          }, 
-          {
-            title: 'Tags',
-            data: 'attributes.tags',
-            defaultContent: '',
-            className: 'none'
-          },    
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md',
-            responsivePriority: 6,
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: "ID",
+          data: 'id',
+          responsivePriority: 2,
+          defaultContent: ''
+        },
+        {
+          title: lang.template,
+          data: 'template',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.max_aliases,
+          data: 'attributes.max_num_aliases_for_domain',
+          defaultContent: '',
+        },
+        {
+          title: lang.max_mailboxes,
+          data: 'attributes.max_num_mboxes_for_domain',
+          defaultContent: '',
+        },
+        {
+          title: lang.mailbox_defquota,
+          data: 'attributes.def_quota_for_mbox',
+          defaultContent: '',
+        },
+        {
+          title: lang.max_quota,
+          data: 'attributes.max_quota_for_mbox',
+          defaultContent: '',
+        },
+        {
+          title: lang.domain_quota_total,
+          data: 'attributes.max_quota_for_domain',
+          defaultContent: '',
+        },
+        {
+          title: lang.gal,
+          data: 'attributes.gal',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.backup_mx,
+          data: 'attributes.backupmx',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.relay_all,
+          data: 'attributes.relay_all_recipients',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.relay_unknown,
+          data: 'attributes.relay_unknown_only',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.active,
+          data: 'attributes.active',
+          defaultContent: '',
+          responsivePriority: 4,
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: 'rl_frame',
+          data: 'attributes.rl_frame',
+          defaultContent: '',
+          class: 'none',
+        },
+        {
+          title: 'rl_value',
+          data: 'attributes.rl_value',
+          defaultContent: '',
+          class: 'none',
+        },
+        {
+          title: lang.dkim_domains_selector,
+          data: 'attributes.dkim_selector',
+          defaultContent: '',
+          class: 'none',
+        },
+        {
+          title: lang.dkim_key_length,
+          data: 'attributes.key_size',
+          defaultContent: '',
+          class: 'none',
+        },
+        {
+          title: 'Tags',
+          data: 'attributes.tags',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md',
+          responsivePriority: 6,
+          defaultContent: ''
+        },
       ]
     });
 
@@ -840,10 +842,11 @@ jQuery(function($){
     }
 
     var table = $('#mailbox_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -927,7 +930,7 @@ jQuery(function($){
               '<div class="progress-bar-mailbox progress-bar progress-bar-' + item.percent_class + '" role="progressbar" aria-valuenow="' + item.percent_in_use + '" aria-valuemin="0" aria-valuemax="100" ' +
               'style="min-width:2em;width:' + item.percent_in_use + '%">' + item.percent_in_use + '%' + '</div></div>';
             item.username = escapeHtml(item.username);
-            
+
             if (Array.isArray(item.tags)){
               var tags = '';
               for (var i = 0; i < item.tags.length; i++)
@@ -942,167 +945,167 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: lang.username,
-            data: 'username',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: lang.domain_quota,
-            data: 'quota.value',
-            responsivePriority: 8,
-            defaultContent: '',  
-            orderData: 23
-          },
-          {
-            title: lang.last_mail_login,
-            data: 'last_mail_login',
-            defaultContent: '',
-            responsivePriority: 7,
-            render: function (data, type) {
-              res = data.split("/");
-              return '<div class="badge bg-info mb-2">IMAP @ ' + unix_time_format(Number(res[0])) + '</div><br>' +
-                '<div class="badge bg-info mb-2">POP3 @ ' + unix_time_format(Number(res[1])) + '</div><br>' +
-                '<div class="badge bg-info">SMTP @ ' + unix_time_format(Number(res[2])) + '</div>';
-            }
-          },
-          {
-            title: lang.last_pw_change,
-            data: 'last_pw_change',
-            defaultContent: ''
-          },
-          {
-            title: lang.in_use,
-            data: 'in_use',
-            defaultContent: '',
-            responsivePriority: 9,
-            className: 'dt-data-w100'
-          },
-          {
-            title: lang.fname,
-            data: 'name',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.domain,
-            data: 'domain',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.tls_enforce_in,
-            data: 'tls_enforce_in',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.tls_enforce_out,
-            data: 'tls_enforce_out',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: 'SMTP',
-            data: 'smtp_access',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: 'IMAP',
-            data: 'imap_access',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: 'POP3',
-            data: 'pop3_access',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: 'SIEVE',
-            data: 'sieve_access',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.quarantine_notification,
-            data: 'quarantine_notification',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.quarantine_category,
-            data: 'quarantine_category',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.msg_num,
-            data: 'messages',
-            defaultContent: '',
-            responsivePriority: 5
-          },
-          {
-            title: lang.created_on,
-            data: 'created',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.last_modified,
-            data: 'modified',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: 'Tags',
-            data: 'tags',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            responsivePriority: 4,
-            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&&'&#8212;');
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md',
-            responsivePriority: 6,
-            defaultContent: ''
-          },
-          {
-            title: "",
-            data: 'quota.sortBy',
-            responsivePriority: 8,
-            defaultContent: '',
-            className: "d-none"
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: lang.username,
+          data: 'username',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.domain_quota,
+          data: 'quota.value',
+          responsivePriority: 8,
+          defaultContent: '',
+          orderData: 23
+        },
+        {
+          title: lang.last_mail_login,
+          data: 'last_mail_login',
+          defaultContent: '',
+          responsivePriority: 7,
+          render: function (data, type) {
+            res = data.split("/");
+            return '<div class="badge bg-info mb-2">IMAP @ ' + unix_time_format(Number(res[0])) + '</div><br>' +
+              '<div class="badge bg-info mb-2">POP3 @ ' + unix_time_format(Number(res[1])) + '</div><br>' +
+              '<div class="badge bg-info">SMTP @ ' + unix_time_format(Number(res[2])) + '</div>';
+          }
+        },
+        {
+          title: lang.last_pw_change,
+          data: 'last_pw_change',
+          defaultContent: ''
+        },
+        {
+          title: lang.in_use,
+          data: 'in_use',
+          defaultContent: '',
+          responsivePriority: 9,
+          className: 'dt-data-w100'
+        },
+        {
+          title: lang.fname,
+          data: 'name',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.domain,
+          data: 'domain',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.tls_enforce_in,
+          data: 'tls_enforce_in',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.tls_enforce_out,
+          data: 'tls_enforce_out',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: 'SMTP',
+          data: 'smtp_access',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: 'IMAP',
+          data: 'imap_access',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: 'POP3',
+          data: 'pop3_access',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: 'SIEVE',
+          data: 'sieve_access',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.quarantine_notification,
+          data: 'quarantine_notification',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.quarantine_category,
+          data: 'quarantine_category',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.msg_num,
+          data: 'messages',
+          defaultContent: '',
+          responsivePriority: 5
+        },
+        {
+          title: lang.created_on,
+          data: 'created',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.last_modified,
+          data: 'modified',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: 'Tags',
+          data: 'tags',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          responsivePriority: 4,
+          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&&'&#8212;');
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md',
+          responsivePriority: 6,
+          defaultContent: ''
+        },
+        {
+          title: "",
+          data: 'quota.sortBy',
+          responsivePriority: 8,
+          defaultContent: '',
+          className: "d-none"
+        },
       ]
     });
 
@@ -1118,15 +1121,16 @@ jQuery(function($){
     }
 
     var table = $('#templates_mbox_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
-      order:[[2, 'desc']],
+      order: [[2, 'desc']],
       initComplete: function(){
         hideTableExpandCollapseBtn('#tab-templates-mbox', '#templates_mbox_table');
       },
@@ -1175,17 +1179,17 @@ jQuery(function($){
               item.attributes.quarantine_category = lang.q_all;
             }
 
-            
+
             if (item.template.toLowerCase() == "default"){
-                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>' +
-                  '</div>';
+              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>' +
+                '</div>';
             }
             else {
-                  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="#" data-action="delete_selected" data-id="single-template" data-api-url="delete/mailbox/template" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-                  '</div>';              
+              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="#" data-action="delete_selected" data-id="single-template" data-api-url="delete/mailbox/template" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+                '</div>';
             }
 
             if (Array.isArray(item.attributes.tags)){
@@ -1230,12 +1234,12 @@ jQuery(function($){
           data: 'template',
           responsivePriority: 3,
           defaultContent: ''
-        },              
+        },
         {
           title: lang.domain_quota,
           data: 'attributes.quota',
           defaultContent: '',
-        },             
+        },
         {
           title: lang.tls_enforce_in,
           data: 'attributes.tls_enforce_in',
@@ -1282,7 +1286,7 @@ jQuery(function($){
           data: 'attributes.quarantine_category',
           defaultContent: '',
           className: 'none'
-        },            
+        },
         {
           title: lang.force_pw_update,
           data: 'attributes.force_pw_update',
@@ -1291,25 +1295,25 @@ jQuery(function($){
           render: function (data, type) {
             return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
           }
-        },            
+        },
         {
           title: "rl_frame",
           data: 'attributes.rl_frame',
           defaultContent: '',
           class: 'none',
-        },           
+        },
         {
           title: 'rl_value',
           data: 'attributes.rl_value',
           defaultContent: '',
           class: 'none',
-        }, 
+        },
         {
           title: 'Tags',
           data: 'attributes.tags',
           defaultContent: '',
           className: 'none'
-        },           
+        },
         {
           title: lang.active,
           data: 'attributes.active',
@@ -1318,7 +1322,7 @@ jQuery(function($){
           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&&'&#8212;');
           }
-        },     
+        },
         {
           title: lang.action,
           data: 'action',
@@ -1328,7 +1332,7 @@ jQuery(function($){
         },
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-templates-mbox', '#templates_mbox_table');
     });
@@ -1341,10 +1345,11 @@ jQuery(function($){
     }
 
     var table = $('#resource_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -1377,65 +1382,65 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: lang.description,
-            data: 'description',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: lang.alias,
-            data: 'name',
-            defaultContent: ''
-          },
-          {
-            title: lang.kind,
-            data: 'kind',
-            defaultContent: ''
-          },
-          {
-            title: lang.domain,
-            data: 'domain',
-            responsivePriority: 4,
-            defaultContent: ''
-          },
-          {
-            title: lang.multiple_bookings,
-            data: 'multiple_bookings',
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            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&&'&#8212;');
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            responsivePriority: 5,
-            defaultContent: '',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right'
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: lang.description,
+          data: 'description',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.alias,
+          data: 'name',
+          defaultContent: ''
+        },
+        {
+          title: lang.kind,
+          data: 'kind',
+          defaultContent: ''
+        },
+        {
+          title: lang.domain,
+          data: 'domain',
+          responsivePriority: 4,
+          defaultContent: ''
+        },
+        {
+          title: lang.multiple_bookings,
+          data: 'multiple_bookings',
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          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&&'&#8212;');
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          responsivePriority: 5,
+          defaultContent: '',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right'
+        },
       ]
     });
 
@@ -1448,14 +1453,14 @@ jQuery(function($){
       // Domains
       var optgroup = "<optgroup label='" + lang.domains + "'>";
       $.each(data.domains, function(index, domain){
-        optgroup += "<option value='" + domain + "'>" + domain + "</option>"
+        optgroup += "<option value='" + domain + "'>" + domain + "</option>";
       });
-      optgroup += "</optgroup>"
+      optgroup += "</optgroup>";
       $('#bcc-local-dest').append(optgroup);
       // Alias domains
       var optgroup = "<optgroup label='" + lang.domain_aliases + "'>";
       $.each(data.alias_domains, function(index, alias_domain){
-        optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>"
+        optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>";
       });
       optgroup += "</optgroup>"
       $('#bcc-local-dest').append(optgroup);
@@ -1463,9 +1468,9 @@ jQuery(function($){
       $.each(data.mailboxes, function(mailbox, aliases){
         var optgroup = "<optgroup label='" + mailbox + "'>";
         $.each(aliases, function(index, alias){
-          optgroup += "<option value='" + alias + "'>" + alias + "</option>"
+          optgroup += "<option value='" + alias + "'>" + alias + "</option>";
         });
-        optgroup += "</optgroup>"
+        optgroup += "</optgroup>";
         $('#bcc-local-dest').append(optgroup);
       });
       // Finish
@@ -1477,17 +1482,18 @@ jQuery(function($){
       $('#bcc_table').DataTable().columns.adjust().responsive.recalc();
       return;
     }
-    
+
     var table = $('#bcc_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
-      order:[[2, 'desc']],
+      order: [[2, 'desc']],
       initComplete: function(){
         hideTableExpandCollapseBtn('#collapse-tab-bcc', '#bcc_table');
       },
@@ -1514,65 +1520,65 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: lang.bcc_type,
-            data: 'type',
-            defaultContent: ''
-          },
-          {
-            title: lang.bcc_local_dest,
-            data: 'local_dest',
-            defaultContent: ''
-          },
-          {
-            title: lang.bcc_destinations,
-            data: 'bcc_dest',
-            defaultContent: ''
-          },
-          {
-            title: lang.domain,
-            data: 'domain',
-            responsivePriority: 4,
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            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&&'&#8212;');
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
-            responsivePriority: 5,
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.bcc_type,
+          data: 'type',
+          defaultContent: ''
+        },
+        {
+          title: lang.bcc_local_dest,
+          data: 'local_dest',
+          defaultContent: ''
+        },
+        {
+          title: lang.bcc_destinations,
+          data: 'bcc_dest',
+          defaultContent: ''
+        },
+        {
+          title: lang.domain,
+          data: 'domain',
+          responsivePriority: 4,
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          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&&'&#8212;');
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
 
@@ -1588,15 +1594,16 @@ jQuery(function($){
     }
 
     var table = $('#recipient_map_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
-      order:[[2, 'desc']],
+      order: [[2, 'desc']],
       initComplete: function(){
         hideTableExpandCollapseBtn('#collapse-tab-bcc-filters', '#recipient_map_table');
       },
@@ -1605,7 +1612,7 @@ jQuery(function($){
         url: "/api/v1/get/recipient_map/all",
         dataSrc: function(json){
           if (role !== "admin") return null;
-          
+
           $.each(json, function (i, item) {
             item.recipient_map_old = escapeHtml(item.recipient_map_old);
             item.recipient_map_new = escapeHtml(item.recipient_map_new);
@@ -1620,58 +1627,58 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: lang.recipient_map_old,
-            data: 'recipient_map_old',
-            defaultContent: ''
-          },
-          {
-            title: lang.recipient_map_new,
-            data: 'recipient_map_new',
-            defaultContent: '',
-            responsivePriority: 4
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
-            responsivePriority: 5,
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.recipient_map_old,
+          data: 'recipient_map_old',
+          defaultContent: ''
+        },
+        {
+          title: lang.recipient_map_new,
+          data: 'recipient_map_new',
+          defaultContent: '',
+          responsivePriority: 4
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#collapse-tab-bcc-filters', '#recipient_map_table');
     });
@@ -1684,15 +1691,16 @@ jQuery(function($){
     }
 
     var table = $('#tls_policy_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
-      order:[[2, 'desc']],
+      order: [[2, 'desc']],
       initComplete: function(){
         hideTableExpandCollapseBtn('#tab-tls-policy', '#tls_policy_table');
       },
@@ -1701,7 +1709,7 @@ jQuery(function($){
         url: "/api/v1/get/tls-policy-map/all",
         dataSrc: function(json){
           if (role !== "admin") return null;
-          
+
           $.each(json, function (i, item) {
             item.dest = escapeHtml(item.dest);
             item.policy = '<b>' + escapeHtml(item.policy) + '</b>';
@@ -1721,63 +1729,63 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: lang.tls_map_dest,
-            data: 'dest',
-            defaultContent: '',
-            responsivePriority: 4
-          },
-          {
-            title: lang.tls_map_policy,
-            data: 'policy',
-            defaultContent: ''
-          },
-          {
-            title: lang.tls_map_parameters,
-            data: 'parameters',
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
-            responsivePriority: 5,
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.tls_map_dest,
+          data: 'dest',
+          defaultContent: '',
+          responsivePriority: 4
+        },
+        {
+          title: lang.tls_map_policy,
+          data: 'policy',
+          defaultContent: ''
+        },
+        {
+          title: lang.tls_map_parameters,
+          data: 'parameters',
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-tls-policy', '#tls_policy_table');
     });
@@ -1790,15 +1798,16 @@ jQuery(function($){
     }
 
     var table = $('#alias_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
-      order:[[2, 'desc']],
+      order: [[2, 'desc']],
       initComplete: function(){
         hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table');
       },
@@ -1849,88 +1858,88 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: lang.alias,
-            data: 'address',
-            responsivePriority: 4,
-            defaultContent: ''
-          },
-          {
-            title: lang.target_address,
-            data: 'goto',
-            defaultContent: ''
-          },
-          {
-            title: lang.domain,
-            data: 'domain',
-            defaultContent: '',
-            responsivePriority: 5,
-          },
-          {
-            title: lang.bcc_destinations,
-            data: 'bcc_dest',
-            defaultContent: ''
-          },
-          {
-            title: lang.sogo_visible,
-            data: 'sogo_visible',
-            defaultContent: '',
-            render: function(data, type){
-              return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
-            }
-          },
-          {
-            title: lang.public_comment,
-            data: 'public_comment',
-            defaultContent: ''
-          },
-          {
-            title: lang.private_comment,
-            data: 'private_comment',
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            responsivePriority: 6,
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
-            responsivePriority: 5,
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.alias,
+          data: 'address',
+          responsivePriority: 4,
+          defaultContent: ''
+        },
+        {
+          title: lang.target_address,
+          data: 'goto',
+          defaultContent: ''
+        },
+        {
+          title: lang.domain,
+          data: 'domain',
+          defaultContent: '',
+          responsivePriority: 5,
+        },
+        {
+          title: lang.bcc_destinations,
+          data: 'bcc_dest',
+          defaultContent: ''
+        },
+        {
+          title: lang.sogo_visible,
+          data: 'sogo_visible',
+          defaultContent: '',
+          render: function(data, type){
+            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.public_comment,
+          data: 'public_comment',
+          defaultContent: ''
+        },
+        {
+          title: lang.private_comment,
+          data: 'private_comment',
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          responsivePriority: 6,
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
-    
+
     table.on('responsive-resize', function (e, datatable, columns){
       hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table');
     });
@@ -1943,10 +1952,11 @@ jQuery(function($){
     }
 
     var table = $('#aliasdomain_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@@ -1978,50 +1988,50 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: lang.alias,
-            data: 'alias_domain',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: lang.target_domain,
-            data: 'target_domain',
-            responsivePriority: 4,
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
-            responsivePriority: 5,
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: lang.alias,
+          data: 'alias_domain',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.target_domain,
+          data: 'target_domain',
+          responsivePriority: 4,
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
 
@@ -2037,15 +2047,16 @@ jQuery(function($){
     }
 
     var table = $('#sync_job_table').DataTable({
-			responsive: true,
+      responsive: true,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
-      order:[[2, 'desc']],
+      order: [[2, 'desc']],
       initComplete: function(){
         hideTableExpandCollapseBtn('#tab-syncjobs', '#sync_job_table');
       },
@@ -2082,9 +2093,9 @@ jQuery(function($){
               item.success = '<i class="text-' + (item.success == 1 ? 'success' : 'danger') + ' bi bi-' + (item.success == 1 ? 'check-lg' : 'x-lg') + '"></i>';
             }
             if (lang['syncjob_'+item.exit_status]) {
-	            item.exit_status = lang['syncjob_'+item.exit_status];
+              item.exit_status = lang['syncjob_'+item.exit_status];
             } else if (item.success != '-') {
-	            item.exit_status = lang.syncjob_check_log;
+              item.exit_status = lang.syncjob_check_log;
             }
             item.exit_status = item.success + ' ' + item.exit_status;
           });
@@ -2093,87 +2104,87 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: lang.owner,
-            data: 'user2',
-            responsivePriority: 4,
-            defaultContent: ''
-          },
-          {
-            title: 'Server',
-            data: 'server_w_port',
-            defaultContent: ''
-          },
-          {
-            title: lang.last_run,
-            data: 'last_run',
-            defaultContent: ''
-          },
-          {
-            title: lang.syncjob_last_run_result,
-            data: 'exit_status',
-            defaultContent: ''
-          },
-          {
-            title: 'Log',
-            data: 'log',
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            defaultContent: '',
-            render: function (data, type) {
-              return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
-            }
-          },
-          {
-            title: lang.status,
-            data: 'is_running',
-            defaultContent: ''
-          },
-          {
-            title: lang.excludes,
-            data: 'exclude',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.mins_interval,
-            data: 'mins_interval',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
-            responsivePriority: 5,
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: lang.owner,
+          data: 'user2',
+          responsivePriority: 4,
+          defaultContent: ''
+        },
+        {
+          title: 'Server',
+          data: 'server_w_port',
+          defaultContent: ''
+        },
+        {
+          title: lang.last_run,
+          data: 'last_run',
+          defaultContent: ''
+        },
+        {
+          title: lang.syncjob_last_run_result,
+          data: 'exit_status',
+          defaultContent: ''
+        },
+        {
+          title: 'Log',
+          data: 'log',
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
+          }
+        },
+        {
+          title: lang.status,
+          data: 'is_running',
+          defaultContent: ''
+        },
+        {
+          title: lang.excludes,
+          data: 'exclude',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.mins_interval,
+          data: 'mins_interval',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
 
@@ -2189,16 +2200,17 @@ jQuery(function($){
     }
 
     var table = $('#filter_table').DataTable({
-			responsive: true,
+      responsive: true,
       autoWidth: false,
       processing: true,
       serverSide: false,
       stateSave: true,
+      pageLength: pagination_size,
       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
-      order:[[2, 'desc']],
+      order: [[2, 'desc']],
       initComplete: function(){
         hideTableExpandCollapseBtn('#tab-filters', '#filter_table');
       },
@@ -2226,64 +2238,64 @@ jQuery(function($){
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 1
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: '',
-            responsivePriority: 2
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            responsivePriority: 2,
-            defaultContent: ''
-          },
-          {
-            title: lang.active,
-            data: 'active',
-            responsivePriority: 3,
-            defaultContent: ''
-          },
-          {
-            title: 'Type',
-            data: 'filter_type',
-            responsivePriority: 4,
-            defaultContent: ''
-          },
-          {
-            title: lang.owner,
-            data: 'username',
-            defaultContent: ''
-          },
-          {
-            title: lang.description,
-            data: 'script_desc',
-            defaultContent: ''
-          },
-          {
-            title: 'Script',
-            data: 'script_data',
-            defaultContent: '',
-            className: 'none'
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
-            responsivePriority: 5,
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          responsivePriority: 2,
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          responsivePriority: 3,
+          defaultContent: ''
+        },
+        {
+          title: 'Type',
+          data: 'filter_type',
+          responsivePriority: 4,
+          defaultContent: ''
+        },
+        {
+          title: lang.owner,
+          data: 'username',
+          defaultContent: ''
+        },
+        {
+          title: lang.description,
+          data: 'script_desc',
+          defaultContent: ''
+        },
+        {
+          title: 'Script',
+          data: 'script_data',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-body-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
 
@@ -2294,11 +2306,11 @@ jQuery(function($){
 
   function hideTableExpandCollapseBtn(tab, table){
     if ($(table).hasClass('collapsed'))
-      $(tab).find(".table_collapse_option").show(); 
+      $(tab).find(".table_collapse_option").show();
     else
-      $(tab).find(".table_collapse_option").hide(); 
+      $(tab).find(".table_collapse_option").hide();
   }
-  
+
   // detect element visibility changes
   function onVisible(element, callback) {
     $(document).ready(function() {
diff --git a/data/web/js/site/qhandler.js b/data/web/js/site/qhandler.js
index 4fcc9634..8a8471f2 100644
--- a/data/web/js/site/qhandler.js
+++ b/data/web/js/site/qhandler.js
@@ -1,71 +1,71 @@
-jQuery(function($){
-  var qitem = $('legend').data('hash');
-  var qError = $("#qid_error");
-  $.ajax({
-    url: '/inc/ajax/qitem_details.php',
-    data: { hash: qitem },
-    dataType: 'json',
-    success: function(data){
-      $('[data-id="qitems_single"]').each(function(index) {
-        $(this).attr("data-item", qitem);
-      });
-      $('#qid_detail_subj').text(data.subject);
-      $('#qid_detail_hfrom').text(data.header_from);
-      $('#qid_detail_efrom').text(data.env_from);
-      $('#qid_detail_score').html('');
-      $('#qid_detail_symbols').html('');
-      $('#qid_detail_recipients').html('');
-      $('#qid_detail_fuzzy').html('');
-      if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
-        $.each(data.fuzzy_hashes, function (index, value) {
-          $('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
-        });
-      } else {
-        $('#qid_detail_fuzzy').append('-');
-      }
-      if (typeof data.symbols !== 'undefined') {
-        data.symbols.sort(function (a, b) {
-          if (a.score === 0) return 1
-          if (b.score === 0) return -1
-          if (b.score < 0 && a.score < 0) {
-            return a.score - b.score
-          }
-          if (b.score > 0 && a.score > 0) {
-            return b.score - a.score
-          }
-          return b.score - a.score
-        })
-        $.each(data.symbols, function (index, value) {
-          var highlightClass = ''
-          if (value.score > 0) highlightClass = 'negative'
-          else if (value.score < 0) highlightClass = 'positive'
-          else highlightClass = 'neutral'
-          $('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
-        });
-        $('[data-bs-toggle="tooltip"]').tooltip()
-      }
-      if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
-        if (data.action === "add header") {
-          $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
-        } else if (data.action === "reject") {
-          $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
-        } else if (data.action === "rewrite subject") {
-          $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
-        }
-      }
-      if (typeof data.recipients !== 'undefined') {
-        $.each(data.recipients, function(index, value) {
-          var elem = $('<span class="mail-address-item"></span>');
-          elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
-          $('#qid_detail_recipients').append(elem);
-        });
-      }
-    },
-    error: function(data){
-      if (typeof data.error !== 'undefined') {
-        qError.text("Error loading quarantine item");
-        qError.show();
-      }
-    }
-  });
-});
+jQuery(function($){
+  var qitem = $('legend').data('hash');
+  var qError = $("#qid_error");
+  $.ajax({
+    url: '/inc/ajax/qitem_details.php',
+    data: { hash: qitem },
+    dataType: 'json',
+    success: function(data){
+      $('[data-id="qitems_single"]').each(function(index) {
+        $(this).attr("data-item", qitem);
+      });
+      $('#qid_detail_subj').text(data.subject);
+      $('#qid_detail_hfrom').text(data.header_from);
+      $('#qid_detail_efrom').text(data.env_from);
+      $('#qid_detail_score').html('');
+      $('#qid_detail_symbols').html('');
+      $('#qid_detail_recipients').html('');
+      $('#qid_detail_fuzzy').html('');
+      if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
+        $.each(data.fuzzy_hashes, function (index, value) {
+          $('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
+        });
+      } else {
+        $('#qid_detail_fuzzy').append('-');
+      }
+      if (typeof data.symbols !== 'undefined') {
+        data.symbols.sort(function (a, b) {
+          if (a.score === 0) return 1;
+          if (b.score === 0) return -1;
+          if (b.score < 0 && a.score < 0) {
+            return a.score - b.score;
+          }
+          if (b.score > 0 && a.score > 0) {
+            return b.score - a.score;
+          }
+          return b.score - a.score;
+        })
+        $.each(data.symbols, function (index, value) {
+          var highlightClass = '';
+          if (value.score > 0) highlightClass = 'negative';
+          else if (value.score < 0) highlightClass = 'positive';
+          else highlightClass = 'neutral';
+          $('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
+        });
+        $('[data-bs-toggle="tooltip"]').tooltip();
+      }
+      if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
+        if (data.action === "add header") {
+          $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
+        } else if (data.action === "reject") {
+          $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
+        } else if (data.action === "rewrite subject") {
+          $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
+        }
+      }
+      if (typeof data.recipients !== 'undefined') {
+        $.each(data.recipients, function(index, value) {
+          var elem = $('<span class="mail-address-item"></span>');
+          elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
+          $('#qid_detail_recipients').append(elem);
+        });
+      }
+    },
+    error: function(data){
+      if (typeof data.error !== 'undefined') {
+        qError.text("Error loading quarantine item");
+        qError.show();
+      }
+    }
+  });
+});
diff --git a/data/web/js/site/quarantine.js b/data/web/js/site/quarantine.js
index 7da3a7dd..e69863f7 100644
--- a/data/web/js/site/quarantine.js
+++ b/data/web/js/site/quarantine.js
@@ -1,286 +1,297 @@
-// Base64 functions
-var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
-
-jQuery(function($){
-  acl_data = JSON.parse(acl);
-  // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
-  var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
-  function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
-  function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
-  $(".refresh_table").on('click', function(e) {
-    e.preventDefault();
-    var table_name = $(this).data('table');
-    $('#' + table_name).DataTable().ajax.reload();
-  });
-  function draw_quarantine_table() {
-    var table = $('#quarantinetable').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      initComplete: function(){
-        hideTableExpandCollapseBtn('#quarantinetable');
-      },
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/quarantine/all",
-        dataSrc: function(data){
-          $.each(data, function (i, item) {
-            if (item.subject === null) {
-              item.subject = '';
-            } else {
-              item.subject = escapeHtml(item.subject);
-            }
-            if (item.score === null) {
-              item.score = '-';
-            }
-            if (item.virus_flag > 0) {
-              item.virus = '<span class="badge fs-6 bg-danger">' + lang.high_danger + '</span>';
-            } else {
-              item.virus = '<span class="badge fs-6 bg-secondary">' + lang.neutral_danger + '</span>';
-            }
-            if (item.action === "reject") {
-              item.rspamdaction = '<span class="badge fs-6 bg-danger">' + lang.rejected + '</span>';
-            } else if (item.action === "add header") {
-              item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.junk_folder + '</span>';
-            } else if (item.action === "rewrite subject") {
-              item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.rewrite_subject + '</span>';
-            }
-            if(item.notified > 0) {
-              item.notified = '&#10004;';
-            } else {
-              item.notified = '&#10006;';
-            }
-            if (acl_data.login_as === 1) {
-            item.action = '<div class="btn-group">' +
-              '<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-info show_qid_info"><i class="bi bi-box-arrow-up-right"></i> ' + lang.show_item + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs  btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-              '</div>';
-            }
-            else {
-            item.action = '<div class="btn-group">' +
-              '<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><i class="bi bi-file-earmark-text"></i> ' + lang.show_item + '</a>' +
-              '</div>';
-            }
-            item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
-          });
-
-          return data;
-        }
-      },
-      columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: 'ID',
-            data: 'id',
-            defaultContent: ''
-          },
-          {
-            title: lang.qid,
-            data: 'qid',
-            defaultContent: ''
-          },
-          {
-            title: lang.sender,
-            data: 'sender',
-            defaultContent: ''
-          },
-          {
-            title: lang.subj,
-            data: 'subject',
-            defaultContent: ''
-          },
-          {
-            title: lang.rspamd_result,
-            data: 'rspamdaction',
-            defaultContent: ''
-          },
-          {
-            title: lang.rcpt,
-            data: 'rcpt',
-            defaultContent: ''
-          },
-          {
-            title: lang.danger,
-            data: 'virus',
-            defaultContent: ''
-          },
-          {
-            title: lang.spam_score,
-            data: 'score',
-            defaultContent: ''
-          },
-          {
-            title: lang.notified,
-            data: 'notified',
-            defaultContent: ''
-          },
-          {
-            title: lang.received,
-            data: 'created',
-            defaultContent: '',
-            createdCell: function(td, cellData) {    
-              $(td).attr({
-                "data-order": cellData,
-                "data-sort": cellData
-              });
-              
-              var date = new Date(cellData ? cellData * 1000 : 0); 
-              var dateString = date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
-              $(td).html(dateString);
-            }
-          },
-          {
-            title: lang.action,
-            data: 'action',
-            className: 'text-md-end dt-sm-head-hidden dt-body-right',
-            defaultContent: ''
-          },
-      ]
-    });
-
-    table.on('responsive-resize', function (e, datatable, columns){
-      hideTableExpandCollapseBtn('#quarantinetable');
-    });
-  }
-
-  $('body').on('click', '.show_qid_info', function (e) {
-    e.preventDefault();
-    var qitem = $(this).attr('data-item');
-    var qError = $("#qid_error");
-
-    $('#qidDetailModal').modal('show');
-    qError.hide();
-
-    $.ajax({
-      url: '/inc/ajax/qitem_details.php',
-      data: { id: qitem },
-      dataType: 'json',
-      success: function(data){
-
-        $('[data-id="qitems_single"]').each(function(index) {
-          $(this).attr("data-item", qitem);
-        });
-
-        $("#quick_download_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&eml', '_blank')");
-        $("#quick_release_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_release', '_blank')");
-        $("#quick_delete_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_delete', '_blank')");
-
-        $('#qid_detail_subj').text(data.subject);
-        $('#qid_detail_hfrom').text(data.header_from);
-        $('#qid_detail_efrom').text(data.env_from);
-        $('#qid_detail_score').html('');
-        $('#qid_detail_recipients').html('');
-        $('#qid_detail_symbols').html('');
-        $('#qid_detail_fuzzy').html('');
-        if (typeof data.symbols !== 'undefined') {
-          data.symbols.sort(function (a, b) {
-            if (a.score === 0) return 1
-            if (b.score === 0) return -1
-            if (b.score < 0 && a.score < 0) {
-              return a.score - b.score
-            }
-            if (b.score > 0 && a.score > 0) {
-              return b.score - a.score
-            }
-            return b.score - a.score
-          })
-          $.each(data.symbols, function (index, value) {
-            var highlightClass = ''
-            if (value.score > 0) highlightClass = 'negative'
-            else if (value.score < 0) highlightClass = 'positive'
-            else highlightClass = 'neutral'
-            $('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
-          });
-          $('[data-bs-toggle="tooltip"]').tooltip()
-        }
-        if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
-          $.each(data.fuzzy_hashes, function (index, value) {
-            $('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
-          });
-        } else {
-          $('#qid_detail_fuzzy').append('-');
-        }
-        if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
-          if (data.action == "add header") {
-            $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
-          } else if (data.action == "reject") {
-            $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
-          } else if (data.action == "rewrite subject") {
-            $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
-          }
-        }
-        if (typeof data.recipients !== 'undefined') {
-          $.each(data.recipients, function(index, value) {
-            var elem = $('<span class="mail-address-item"></span>');
-            elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
-            $('#qid_detail_recipients').append(elem);
-          });
-        }
-        $('#qid_detail_text').text(data.text_plain);
-        $('#qid_detail_text_from_html').text(data.text_html);
-        var qAtts = $("#qid_detail_atts");
-        if (typeof data.attachments !== 'undefined') {
-          qAtts.text('');
-          $.each(data.attachments, function(index, value) {
-            qAtts.append(
-              '<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
-              ' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
-            );
-          });
-        }
-        else {
-          qAtts.text('-');
-        }
-      },
-      error: function(data){
-        if (typeof data.error !== 'undefined') {
-          $('#qid_detail_subj').text('-');
-          $('#qid_detail_hfrom').text('-');
-          $('#qid_detail_efrom').text('-');
-          $('#qid_detail_score').html('-');
-          $('#qid_detail_recipients').html('-');
-          $('#qid_detail_symbols').html('-');
-          $('#qid_detail_fuzzy').html('-');
-          $('#qid_detail_text').text('-');
-          $('#qid_detail_text_from_html').text('-');
-          qError.text("Error loading quarantine item");
-          qError.show();
-        }
-      }
-    });
-  });
-
-  $('body').on('click', 'span.footable-toggle', function () {
-    event.stopPropagation();
-  })
-
-  // Initial table drawings
-  draw_quarantine_table();
-
-  
-  function hideTableExpandCollapseBtn(table){
-    if ($(table).hasClass('collapsed'))
-      $(".table_collapse_option").show(); 
-    else
-      $(".table_collapse_option").hide(); 
-  }
-});
+// Base64 functions
+var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
+
+jQuery(function($){
+  acl_data = JSON.parse(acl);
+  // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
+  var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
+  function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
+  function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
+  $(".refresh_table").on('click', function(e) {
+    e.preventDefault();
+    var table_name = $(this).data('table');
+    $('#' + table_name).DataTable().ajax.reload();
+  });
+  function draw_quarantine_table() {
+    var table = $('#quarantinetable').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      order: [[2, 'desc']],
+      lengthMenu: [
+        [10, 25, 50, 100, -1],
+        [10, 25, 50, 100, 'all']
+      ],
+      pagingType: 'first_last_numbers',
+      aColumns: [
+        { sWidth: '8.25%' },
+        { sClass: 'classDataTable' }
+      ],
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#quarantinetable');
+      },
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/quarantine/all",
+        dataSrc: function(data){
+          $.each(data, function (i, item) {
+            if (item.subject === null) {
+              item.subject = '';
+            } else {
+              item.subject = escapeHtml(item.subject);
+            }
+            if (item.score === null) {
+              item.score = '-';
+            }
+            if (item.virus_flag > 0) {
+              item.virus = '<span class="badge fs-6 bg-danger">' + lang.high_danger + '</span>';
+            } else {
+              item.virus = '<span class="badge fs-6 bg-secondary">' + lang.neutral_danger + '</span>';
+            }
+            if (item.action === "reject") {
+              item.rspamdaction = '<span class="badge fs-6 bg-danger">' + lang.rejected + '</span>';
+            } else if (item.action === "add header") {
+              item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.junk_folder + '</span>';
+            } else if (item.action === "rewrite subject") {
+              item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.rewrite_subject + '</span>';
+            }
+            if(item.notified > 0) {
+              item.notified = '&#10004;';
+            } else {
+              item.notified = '&#10006;';
+            }
+            if (acl_data.login_as === 1) {
+            item.action = '<div class="btn-group">' +
+              '<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-info show_qid_info"><i class="bi bi-box-arrow-up-right"></i> ' + lang.show_item + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs  btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '</div>';
+            }
+            else {
+            item.action = '<div class="btn-group">' +
+              '<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><i class="bi bi-file-earmark-text"></i> ' + lang.show_item + '</a>' +
+              '</div>';
+            }
+            item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
+          });
+
+          return data;
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          defaultContent: ''
+        },
+        {
+          title: lang.qid,
+          data: 'qid',
+          defaultContent: ''
+        },
+        {
+          title: lang.sender,
+          data: 'sender',
+          className: 'senders-mw220',
+          defaultContent: ''
+        },
+        {
+          title: lang.subj,
+          data: 'subject',
+          defaultContent: ''
+        },
+        {
+          title: lang.rspamd_result,
+          data: 'rspamdaction',
+          defaultContent: ''
+        },
+        {
+          title: lang.rcpt,
+          data: 'rcpt',
+          defaultContent: ''
+        },
+        {
+          title: lang.danger,
+          data: 'virus',
+          defaultContent: ''
+        },
+        {
+          title: lang.spam_score,
+          data: 'score',
+          defaultContent: ''
+        },
+        {
+          title: lang.notified,
+          data: 'notified',
+          defaultContent: ''
+        },
+        {
+          title: lang.received,
+          data: 'created',
+          defaultContent: '',
+          createdCell: function(td, cellData) {
+            $(td).attr({
+              "data-order": cellData,
+              "data-sort": cellData
+            });
+
+            var date = new Date(cellData ? cellData * 1000 : 0);
+            var dateString = date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+            $(td).html(dateString);
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        },
+      ]
+    });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#quarantinetable');
+    });
+  }
+
+  $('body').on('click', '.show_qid_info', function (e) {
+    e.preventDefault();
+    var qitem = $(this).attr('data-item');
+    var qError = $("#qid_error");
+
+    $('#qidDetailModal').modal('show');
+    qError.hide();
+
+    $.ajax({
+      url: '/inc/ajax/qitem_details.php',
+      data: { id: qitem },
+      dataType: 'json',
+      success: function(data){
+
+        $('[data-id="qitems_single"]').each(function(index) {
+          $(this).attr("data-item", qitem);
+        });
+
+        $("#quick_download_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&eml', '_blank')");
+        $("#quick_release_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_release', '_blank')");
+        $("#quick_delete_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_delete', '_blank')");
+
+        $('#qid_detail_subj').text(data.subject);
+        $('#qid_detail_hfrom').text(data.header_from);
+        $('#qid_detail_efrom').text(data.env_from);
+        $('#qid_detail_score').html('');
+        $('#qid_detail_recipients').html('');
+        $('#qid_detail_symbols').html('');
+        $('#qid_detail_fuzzy').html('');
+        if (typeof data.symbols !== 'undefined') {
+          data.symbols.sort(function (a, b) {
+            if (a.score === 0) return 1;
+            if (b.score === 0) return -1;
+            if (b.score < 0 && a.score < 0) {
+              return a.score - b.score;
+            }
+            if (b.score > 0 && a.score > 0) {
+              return b.score - a.score;
+            }
+            return b.score - a.score;
+          })
+          $.each(data.symbols, function (index, value) {
+            var highlightClass = '';
+            if (value.score > 0) highlightClass = 'negative';
+            else if (value.score < 0) highlightClass = 'positive';
+            else highlightClass = 'neutral';
+            $('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
+          });
+          $('[data-bs-toggle="tooltip"]').tooltip();
+        }
+        if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
+          $.each(data.fuzzy_hashes, function (index, value) {
+            $('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
+          });
+        } else {
+          $('#qid_detail_fuzzy').append('-');
+        }
+        if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
+          if (data.action == "add header") {
+            $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
+          } else if (data.action == "reject") {
+            $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
+          } else if (data.action == "rewrite subject") {
+            $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
+          }
+        }
+        if (typeof data.recipients !== 'undefined') {
+          $.each(data.recipients, function(index, value) {
+            var elem = $('<span class="mail-address-item"></span>');
+            elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
+            $('#qid_detail_recipients').append(elem);
+          });
+        }
+        $('#qid_detail_text').text(data.text_plain);
+        $('#qid_detail_text_from_html').text(data.text_html);
+        var qAtts = $("#qid_detail_atts");
+        if (typeof data.attachments !== 'undefined') {
+          qAtts.text('');
+          $.each(data.attachments, function(index, value) {
+            qAtts.append(
+              '<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
+              ' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
+            );
+          });
+        }
+        else {
+          qAtts.text('-');
+        }
+      },
+      error: function(data){
+        if (typeof data.error !== 'undefined') {
+          $('#qid_detail_subj').text('-');
+          $('#qid_detail_hfrom').text('-');
+          $('#qid_detail_efrom').text('-');
+          $('#qid_detail_score').html('-');
+          $('#qid_detail_recipients').html('-');
+          $('#qid_detail_symbols').html('-');
+          $('#qid_detail_fuzzy').html('-');
+          $('#qid_detail_text').text('-');
+          $('#qid_detail_text_from_html').text('-');
+          qError.text("Error loading quarantine item");
+          qError.show();
+        }
+      }
+    });
+  });
+
+  $('body').on('click', 'span.footable-toggle', function () {
+    event.stopPropagation();
+  })
+
+  // Initial table drawings
+  draw_quarantine_table();
+
+  function hideTableExpandCollapseBtn(table){
+    if ($(table).hasClass('collapsed'))
+      $(".table_collapse_option").show();
+    else
+      $(".table_collapse_option").hide();
+  }
+});
diff --git a/data/web/js/site/queue.js b/data/web/js/site/queue.js
index ebebaff8..bc4c7369 100644
--- a/data/web/js/site/queue.js
+++ b/data/web/js/site/queue.js
@@ -1,127 +1,128 @@
 jQuery(function($){
 
-    $(".refresh_table").on('click', function(e) {
-      e.preventDefault();
-      var table_name = $(this).data('table');
-      $('#' + table_name).DataTable().ajax.reload();
-    });
+  $(".refresh_table").on('click', function(e) {
+    e.preventDefault();
+    var table_name = $(this).data('table');
+    $('#' + table_name).DataTable().ajax.reload();
+  });
 
 
-    function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
+  function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
 
-    // Queue item
-    $('#showQueuedMsg').on('show.bs.modal', function (e) {
-      $('#queue_msg_content').text(lang.loading);
-      button = $(e.relatedTarget)
-      if (button != null) {
-        $('#queue_id').text(button.data('queue-id'));
-      }
-      $.ajax({
-          type: 'GET',
-          url: '/api/v1/get/postcat/' + button.data('queue-id'),
-          dataType: 'text',
-          complete: function (data) {
-            $('#queue_msg_content').text(data.responseText);
-          }
-      });
-    })
-
-    function draw_queue() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#queuetable') ) {
-      $('#queuetable').DataTable().columns.adjust().responsive.recalc();
-      return;
+  // Queue item
+  $('#showQueuedMsg').on('show.bs.modal', function (e) {
+    $('#queue_msg_content').text(lang.loading);
+    button = $(e.relatedTarget)
+    if (button != null) {
+      $('#queue_id').text(button.data('queue-id'));
     }
+    $.ajax({
+      type: 'GET',
+      url: '/api/v1/get/postcat/' + button.data('queue-id'),
+      dataType: 'text',
+      complete: function (data) {
+        $('#queue_msg_content').text(data.responseText);
+      }
+    });
+  })
 
-    $('#queuetable').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/mailq/all",
-        dataSrc: function(data){
-          $.each(data, function (i, item) {
-            item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
-            rcpts = $.map(item.recipients, function(i) {
-              return escapeHtml(i);
-            });
-            item.recipients = rcpts.join('<hr style="margin:1px!important">');
-            item.action = '<div class="btn-group">' +
-              '<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
+  function draw_queue() {
+  // just recalc width if instance already exists
+  if ($.fn.DataTable.isDataTable('#queuetable') ) {
+    $('#queuetable').DataTable().columns.adjust().responsive.recalc();
+    return;
+  }
+
+  $('#queuetable').DataTable({
+    responsive: true,
+    processing: true,
+    serverSide: false,
+    stateSave: true,
+    pageLength: pagination_size,
+    dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+         "tr" +
+         "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+    language: lang_datatables,
+    ajax: {
+      type: "GET",
+      url: "/api/v1/get/mailq/all",
+      dataSrc: function(data){
+        $.each(data, function (i, item) {
+          item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
+          rcpts = $.map(item.recipients, function(i) {
+            return escapeHtml(i);
+          });
+          item.recipients = rcpts.join('<hr style="margin:1px!important">');
+          item.action = '<div class="btn-group">' +
+            '<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
             '</div>';
           });
           return data;
         }
       },
       columns: [
-          {
-            // placeholder, so checkbox will not block child row toggle
-            title: '',
-            data: null,
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: '',
-            data: 'chkbox',
-            searchable: false,
-            orderable: false,
-            defaultContent: ''
-          },
-          {
-            title: 'QID',
-            data: 'queue_id',
-            defaultContent: ''
-          },
-          {
-            title: 'Queue',
-            data: 'queue_name',
-            defaultContent: ''
-          },
-          {
-            title: lang_admin.arrival_time,
-            data: 'arrival_time',
-            defaultContent: '',
-            render: function (data, type){
-              var date = new Date(data ? data * 1000 : 0); 
-              return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
-            }
-          },
-          {
-            title: lang_admin.message_size,
-            data: 'message_size',
-            defaultContent: '',
-            render: function (data, type){
-              return humanFileSize(data);
-            }
-          },
-          {
-            title: lang_admin.sender,
-            data: 'sender',
-            defaultContent: ''
-          },
-          {
-            title: lang_admin.recipients,
-            data: 'recipients',
-            defaultContent: ''
-          },
-          {
-            title: lang_admin.action,
-            data: 'action',
-            className: 'text-md-end dt-sm-head-hidden dt-body-right',
-            defaultContent: ''
-          },
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'QID',
+          data: 'queue_id',
+          defaultContent: ''
+        },
+        {
+          title: 'Queue',
+          data: 'queue_name',
+          defaultContent: ''
+        },
+        {
+          title: lang_admin.arrival_time,
+          data: 'arrival_time',
+          defaultContent: '',
+          render: function (data, type){
+            var date = new Date(data ? data * 1000 : 0);
+            return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+          }
+        },
+        {
+          title: lang_admin.message_size,
+          data: 'message_size',
+          defaultContent: '',
+          render: function (data, type){
+            return humanFileSize(data);
+          }
+        },
+        {
+          title: lang_admin.sender,
+          data: 'sender',
+          defaultContent: ''
+        },
+        {
+          title: lang_admin.recipients,
+          data: 'recipients',
+          defaultContent: ''
+        },
+        {
+          title: lang_admin.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        },
       ]
     });
   }
 
   draw_queue();
 
-})
\ No newline at end of file
+})
diff --git a/data/web/js/site/user.js b/data/web/js/site/user.js
index d1b9780f..d93e692f 100644
--- a/data/web/js/site/user.js
+++ b/data/web/js/site/user.js
@@ -1,662 +1,667 @@
-// Base64 functions
-var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
-$(document).ready(function() {
-  // Spam score slider
-  var spam_slider = $('#spam_score')[0];
-  if (typeof spam_slider !== 'undefined') {
-    noUiSlider.create(spam_slider, {
-      start: user_spam_score,
-      connect: [true, true, true],
-      range: {
-        'min': [0], //stepsize is 50.000
-        '50%': [10],
-        '70%': [20, 5],
-        '80%': [50, 10],
-        '90%': [100, 100],
-        '95%': [1000, 1000],
-        'max': [5000]
-      },
-    });
-    var connect = spam_slider.querySelectorAll('.noUi-connect');
-    var classes = ['c-1-color', 'c-2-color', 'c-3-color'];
-    for (var i = 0; i < connect.length; i++) {
-      connect[i].classList.add(classes[i]);
-    }
-    spam_slider.noUiSlider.on('update', function (values, handle) {
-      $('.spam-ham-score').text('< ' + Math.round(values[0] * 10) / 10);
-      $('.spam-spam-score').text(Math.round(values[0] * 10) / 10 + ' - ' + Math.round(values[1] * 10) / 10);
-      $('.spam-reject-score').text('> ' + Math.round(values[1] * 10) / 10);
-      $('#spam_score_value').val((Math.round(values[0] * 10) / 10) + ',' + (Math.round(values[1] * 10) / 10));
-    });
-  }
-  // syncjobLogModal
-  $('#syncjobLogModal').on('show.bs.modal', function(e) {
-    var syncjob_id = $(e.relatedTarget).data('syncjob-id');
-    $.ajax({
-      url: '/inc/ajax/syncjob_logs.php',
-      data: { id: syncjob_id },
-      dataType: 'text',
-      success: function(data){
-        $(e.currentTarget).find('#logText').text(data);
-      },
-      error: function(xhr, status, error) {
-        $(e.currentTarget).find('#logText').text(xhr.responseText);
-      }
-    });
-  });
-  $(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
-  $("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
-
-});
-jQuery(function($){
-  // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
-  var entityMap = {
-  '&': '&amp;',
-  '<': '&lt;',
-  '>': '&gt;',
-  '"': '&quot;',
-  "'": '&#39;',
-  '/': '&#x2F;',
-  '`': '&#x60;',
-  '=': '&#x3D;'
-  };
-  function escapeHtml(string) {
-    return String(string).replace(/[&<>"'`=\/]/g, function (s) {
-      return entityMap[s];
-    });
-  }
-  // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
-  function validateEmail(email) {
-    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-    return re.test(email);
-  }
-  function unix_time_format(tm) {
-    var date = new Date(tm ? tm * 1000 : 0);
-    return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
-  }
-  acl_data = JSON.parse(acl);
-
-  $('.clear-last-logins').on('click', function () {if (confirm(lang.delete_ays)) {last_logins('reset');}})
-  $(".login-history").on('click', function(e) {e.preventDefault(); last_logins('get', $(this).data('days'));$(this).addClass('active').siblings().removeClass('active');});
-
-  function last_logins(action, days = 7) {
-    if (action == 'get') {
-      $('.last-login').html('<i class="bi bi-hourglass"></i>' +  lang.waiting);
-      $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/last-login/' + encodeURIComponent(mailcow_cc_username) + '/' + days,
-        jsonp: false,
-        error: function () {
-          console.log('error reading last logins');
-        },
-        success: function (data) {
-          $('.last-login').html();
-          if (data.ui.time) {
-            $('.last-login').html('<i class="bi bi-person-fill"></i> ' + lang.last_ui_login + ': ' + unix_time_format(data.ui.time));
-          } else {
-            $('.last-login').text(lang.no_last_login);
-          }
-          if (data.sasl) {
-            $('.last-login').append('<ul class="list-group">');
-            $.each(data.sasl, function (i, item) {
-              var datetime = new Date(item.datetime.replace(/-/g, "/"));
-              var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
-              var service = '<div class="badge fs-6 bg-secondary">' + item.service.toUpperCase() + '</div>';
-              var app_password = item.app_password ? ' <a href="/edit/app-passwd/' + item.app_password + '"><i class="bi bi-app-indicator"></i> ' + escapeHtml(item.app_password_name || "App") + '</a>' : '';
-              var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.he.net/ip/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>";
-              var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + item.location.toLowerCase() + '"></span>' : '';
-              var ip_data = real_rip + ip_location + app_password;
-              $(".last-login").append('<li class="list-group-item">' + local_datetime + " " + service + " " + lang.from + " " + ip_data + "</li>");
-            })
-            $('.last-login').append('</ul>');
-          }
-        }
-      })
-    } else if (action == 'reset') {
-      $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/reset-last-login/' + encodeURIComponent(mailcow_cc_username),
-        jsonp: false,
-        error: function () {
-          console.log('cannot reset last logins');
-        },
-        success: function (data) {
-          last_logins('get');
-        }
-      })
-    }
-  }
-
-  function draw_tla_table() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#tla_table') ) {
-      $('#tla_table').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#tla_table').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/time_limited_aliases",
-        dataSrc: function(data){
-          console.log(data);
-          $.each(data, function (i, item) {
-            if (acl_data.spam_alias === 1) {
-              item.action = '<div class="btn-group">' +
-                '<a href="#" data-action="delete_selected" data-id="single-tla" data-api-url="delete/time_limited_alias" data-item="' + encodeURIComponent(item.address) + '" class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-                '</div>';
-              item.chkbox = '<input type="checkbox" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />';
-              item.address = escapeHtml(item.address);
-            }
-            else {
-              item.chkbox = '<input type="checkbox" disabled />';
-              item.action = '<span>-</span>';
-            }
-          });
-
-          return data;
-        }
-      },
-      columns: [          
-        {
-          // placeholder, so checkbox will not block child row toggle
-          title: '',
-          data: null,
-          searchable: false,
-          orderable: false,
-          defaultContent: ''
-        },
-        {
-          title: '',
-          data: 'chkbox',
-          searchable: false,
-          orderable: false,
-          defaultContent: ''
-        },
-        {
-          title: lang.alias,
-          data: 'address',
-          defaultContent: ''
-        },
-        {
-          title: lang.alias_valid_until,
-          data: 'validity',
-          defaultContent: '',
-          render: function (data, type) {
-            var date = new Date(data ? data * 1000 : 0); 
-            return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
-          }
-        },
-        {
-          title: lang.created_on,
-          data: 'created',
-          defaultContent: '',
-          render: function (data, type) {
-            var date = new Date(data.replace(/-/g, "/"));
-            return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
-          }
-        },
-        {
-          title: lang.action,
-          data: 'action',
-          className: 'text-md-end dt-sm-head-hidden dt-body-right',
-          defaultContent: ''
-        }
-      ]
-    });
-  }
-  function draw_sync_job_table() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#sync_job_table') ) {
-      $('#sync_job_table').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#sync_job_table').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: '/api/v1/get/syncjobs/' + encodeURIComponent(mailcow_cc_username) + '/no_log',
-        dataSrc: function(data){
-          console.log(data);
-          $.each(data, function (i, item) {
-            item.user1 = escapeHtml(item.user1);
-            item.log = '<a href="#syncjobLogModal" data-bs-toggle="modal" data-syncjob-id="' + item.id + '">' + lang.open_logs + '</a>'
-            if (!item.exclude > 0) {
-              item.exclude = '-';
-            } else {
-              item.exclude  = '<code>' + escapeHtml(item.exclude) + '</code>';
-            }
-            item.server_w_port = escapeHtml(item.user1 + '@' + item.host1 + ':' + item.port1);
-            if (acl_data.syncjobs === 1) {
-              item.action = '<div class="btn-group">' +
-                '<a href="/edit/syncjob/' + item.id + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-                '<a href="#" data-action="delete_selected" data-id="single-syncjob" data-api-url="delete/syncjob" data-item="' + item.id + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-                '</div>';
-              item.chkbox = '<input type="checkbox" data-id="syncjob" name="multi_select" value="' + item.id + '" />';
-            }
-            else {
-              item.action = '<span>-</span>';
-              item.chkbox = '<input type="checkbox" disabled />';
-            }
-            if (item.is_running == 1) {
-              item.is_running = '<span id="active-script" class="badge fs-6 bg-success">' + lang.running + '</span>';
-            } else {
-              item.is_running = '<span id="inactive-script" class="badge fs-6 bg-warning">' + lang.waiting + '</span>';
-            }
-            if (!item.last_run > 0) {
-              item.last_run = lang.waiting;
-            }
-            if (item.success == null) {
-              item.success = '-';
-              item.exit_status = '';
-            } else {
-              item.success = '<i class="text-' + (item.success == 1 ? 'success' : 'danger') + ' bi bi-' + (item.success == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            }
-            if (lang['syncjob_'+item.exit_status]) {
-	            item.exit_status = lang['syncjob_'+item.exit_status];
-            } else if (item.success != '-') {
-	            item.exit_status = lang.syncjob_check_log;
-            }
-            item.exit_status = item.success + ' ' + item.exit_status;
-          });
-
-          return data;
-        }
-      },
-      columns: [          
-        {
-          // placeholder, so checkbox will not block child row toggle
-          title: '',
-          data: null,
-          searchable: false,
-          orderable: false,
-          defaultContent: '',
-          responsivePriority: 1
-        },
-        {
-          title: '',
-          data: 'chkbox',
-          searchable: false,
-          orderable: false,
-          defaultContent: '',
-          responsivePriority: 2
-        },
-        {
-          title: 'ID',
-          data: 'id',
-          defaultContent: '',
-          responsivePriority: 3
-        },
-        {
-          title: 'Server',
-          data: 'server_w_port',
-          defaultContent: ''
-        },
-        {
-          title: lang.username,
-          data: 'user1',
-          defaultContent: '',
-          responsivePriority: 3
-        },
-        {
-          title: lang.last_run,
-          data: 'last_run',
-          defaultContent: ''
-        },
-        {
-          title: lang.syncjob_last_run_result,
-          data: 'exit_status',
-          defaultContent: ''
-        },
-        {
-          title: 'Log',
-          data: 'log',
-          defaultContent: ''
-        },
-        {
-          title: lang.active,
-          data: 'active',
-          defaultContent: '',
-          render: function (data, type) {
-            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'
-          }
-        },
-        {
-          title: lang.status,
-          data: 'is_running',
-          defaultContent: '',
-          responsivePriority: 5
-        },
-        {
-          title: lang.encryption,
-          data: 'enc1',
-          defaultContent: ''
-        },
-        {
-          title: lang.excludes,
-          data: 'exclude',
-          defaultContent: ''
-        },
-        {
-          title: lang.interval + " (min)",
-          data: 'mins_interval',
-          defaultContent: ''
-        },
-        {
-          title: lang.action,
-          data: 'action',
-          className: 'text-md-end dt-sm-head-hidden dt-body-right',
-          defaultContent: '',
-          responsivePriority: 5
-        }
-      ]
-    });
-  }
-  function draw_app_passwd_table() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#app_passwd_table') ) {
-      $('#app_passwd_table').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#app_passwd_table').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: '/api/v1/get/app-passwd/all',
-        dataSrc: function(data){
-          console.log(data);
-          $.each(data, function (i, item) {
-            item.name = escapeHtml(item.name)
-            item.protocols = []
-            if (item.imap_access == 1) { item.protocols.push("<code>IMAP</code>"); }
-            if (item.smtp_access == 1) { item.protocols.push("<code>SMTP</code>"); }
-            if (item.eas_access == 1) { item.protocols.push("<code>EAS/ActiveSync</code>"); }
-            if (item.dav_access == 1) { item.protocols.push("<code>DAV</code>"); }
-            if (item.pop3_access == 1) { item.protocols.push("<code>POP3</code>"); }
-            if (item.sieve_access == 1) { item.protocols.push("<code>Sieve</code>"); }
-            item.protocols = item.protocols.join(" ")
-            if (acl_data.app_passwds === 1) {
-              item.action = '<div class="btn-group">' +
-                '<a href="/edit/app-passwd/' + item.id + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-                '<a href="#" data-action="delete_selected" data-id="single-apppasswd" data-api-url="delete/app-passwd" data-item="' + item.id + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-                '</div>';
-              item.chkbox = '<input type="checkbox" data-id="apppasswd" name="multi_select" value="' + item.id + '" />';
-            }
-            else {
-              item.action = '<span>-</span>';
-              item.chkbox = '<input type="checkbox" disabled />';
-            }
-          });
-
-          return data;
-        }
-      },
-      columns: [          
-        {
-          // placeholder, so checkbox will not block child row toggle
-          title: '',
-          data: null,
-          searchable: false,
-          orderable: false,
-          defaultContent: ''
-        },
-        {
-          title: '',
-          data: 'chkbox',
-          searchable: false,
-          orderable: false,
-          defaultContent: ''
-        },
-        {
-          title: 'ID',
-          data: 'id',
-          defaultContent: ''
-        },
-        {
-          title: lang.app_name,
-          data: 'name',
-          defaultContent: ''
-        },
-        {
-          title: lang.allowed_protocols,
-          data: 'protocols',
-          defaultContent: ''
-        },
-        {
-          title: lang.active,
-          data: 'active',
-          defaultContent: '',
-          render: function (data, type) {
-            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'
-          }
-        },
-        {
-          title: lang.action,
-          data: 'action',
-          className: 'text-md-end dt-sm-head-hidden dt-body-right',
-          defaultContent: ''
-        }
-      ]
-    });
-  }
-  function draw_wl_policy_mailbox_table() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#wl_policy_mailbox_table') ) {
-      $('#wl_policy_mailbox_table').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#wl_policy_mailbox_table').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: '/api/v1/get/policy_wl_mailbox',
-        dataSrc: function(data){
-          console.log(data);
-          $.each(data, function (i, item) {
-            if (validateEmail(item.object)) {
-              item.chkbox = '<input type="checkbox" data-id="policy_wl_mailbox" name="multi_select" value="' + item.prefid + '" />';
-            }
-            else {
-              item.chkbox = '<input type="checkbox" disabled title="' + lang.spamfilter_table_domain_policy + '" />';
-            }
-            if (acl_data.spam_policy === 0) {
-              item.chkbox = '<input type="checkbox" disabled />';
-            }
-          });
-
-          return data;
-        }
-      },
-      columns: [          
-        {
-          // placeholder, so checkbox will not block child row toggle
-          title: '',
-          data: null,
-          searchable: false,
-          orderable: false,
-          defaultContent: ''
-        },
-        {
-          title: '',
-          data: 'chkbox',
-          searchable: false,
-          orderable: false,
-          defaultContent: ''
-        },
-        {
-          title: 'ID',
-          data: 'prefid',
-          defaultContent: ''
-        },
-        {
-          title: lang.spamfilter_table_rule,
-          data: 'value',
-          defaultContent: ''
-        },
-        {
-          title:'Scope',
-          data: 'object',
-          defaultContent: ''
-        }
-      ]
-    });
-  }
-  function draw_bl_policy_mailbox_table() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#bl_policy_mailbox_table') ) {
-      $('#bl_policy_mailbox_table').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#bl_policy_mailbox_table').DataTable({
-			responsive: true,
-      processing: true,
-      serverSide: false,
-      stateSave: true,
-      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
-           "tr" +
-           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: '/api/v1/get/policy_bl_mailbox',
-        dataSrc: function(data){
-          console.log(data);
-          $.each(data, function (i, item) {
-            if (validateEmail(item.object)) {
-              item.chkbox = '<input type="checkbox" data-id="policy_bl_mailbox" name="multi_select" value="' + item.prefid + '" />';
-            }
-            else {
-              item.chkbox = '<input type="checkbox" disabled tooltip="' + lang.spamfilter_table_domain_policy + '" />';
-            }
-            if (acl_data.spam_policy === 0) {
-              item.chkbox = '<input type="checkbox" disabled />';
-            }
-          });
-
-          return data;
-        }
-      },
-      columns: [          
-        {
-          // placeholder, so checkbox will not block child row toggle
-          title: '',
-          data: null,
-          searchable: false,
-          orderable: false,
-          defaultContent: ''
-        },
-        {
-          title: '',
-          data: 'chkbox',
-          searchable: false,
-          orderable: false,
-          defaultContent: ''
-        },
-        {
-          title: 'ID',
-          data: 'prefid',
-          defaultContent: ''
-        },
-        {
-          title: lang.spamfilter_table_rule,
-          data: 'value',
-          defaultContent: ''
-        },
-        {
-          title:'Scope',
-          data: 'object',
-          defaultContent: ''
-        }
-      ]
-    });
-  }
-
-  // FIDO2 friendly name modal
-  $('#fido2ChangeFn').on('show.bs.modal', function (e) {
-    rename_link = $(e.relatedTarget)
-    if (rename_link != null) {
-      $('#fido2_cid').val(rename_link.data('cid'));
-      $('#fido2_subject_desc').text(Base64.decode(rename_link.data('subject')));
-    }
-  })
-
-  // Sieve data modal
-  $('#userFilterModal').on('show.bs.modal', function(e) {
-    $('#user_sieve_filter').text(lang.loading);
-    $.ajax({
-      dataType: 'json',
-      url: '/api/v1/get/active-user-sieve/' + encodeURIComponent(mailcow_cc_username),
-      jsonp: false,
-      error: function () {
-        console.log('Cannot get active sieve script');
-      },
-      complete: function (data) {
-        if (data.responseText == '{}') {
-          $('#user_sieve_filter').text(lang.no_active_filter);
-        } else {
-          $('#user_sieve_filter').text(JSON.parse(data.responseText));
-        }
-      }
-    })
-  });
-  $('#userFilterModal').on('hidden.bs.modal', function () {
-    $('#user_sieve_filter').text(lang.loading);
-  });
-
-  // detect element visibility changes
-  function onVisible(element, callback) {
-    $(document).ready(function() {
-      element_object = document.querySelector(element);
-      if (element_object === null) return;
-
-      new IntersectionObserver((entries, observer) => {
-        entries.forEach(entry => {
-          if(entry.intersectionRatio > 0) {
-            callback(element_object);
-          }
-        });
-      }).observe(element_object);
-    });
-  }
-
-  // Load only if the tab is visible
-  onVisible("[id^=tla_table]", () => draw_tla_table());
-  onVisible("[id^=bl_policy_mailbox_table]", () => draw_bl_policy_mailbox_table());
-  onVisible("[id^=wl_policy_mailbox_table]", () => draw_wl_policy_mailbox_table());
-  onVisible("[id^=sync_job_table]", () => draw_sync_job_table());
-  onVisible("[id^=app_passwd_table]", () => draw_app_passwd_table());
-  last_logins('get');
-});
+// Base64 functions
+var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
+$(document).ready(function() {
+  // Spam score slider
+  var spam_slider = $('#spam_score')[0];
+  if (typeof spam_slider !== 'undefined') {
+    noUiSlider.create(spam_slider, {
+      start: user_spam_score,
+      connect: [true, true, true],
+      range: {
+        'min': [0], //stepsize is 50.000
+        '50%': [10],
+        '70%': [20, 5],
+        '80%': [50, 10],
+        '90%': [100, 100],
+        '95%': [1000, 1000],
+        'max': [5000]
+      },
+    });
+    var connect = spam_slider.querySelectorAll('.noUi-connect');
+    var classes = ['c-1-color', 'c-2-color', 'c-3-color'];
+    for (var i = 0; i < connect.length; i++) {
+      connect[i].classList.add(classes[i]);
+    }
+    spam_slider.noUiSlider.on('update', function (values, handle) {
+      $('.spam-ham-score').text('< ' + Math.round(values[0] * 10) / 10);
+      $('.spam-spam-score').text(Math.round(values[0] * 10) / 10 + ' - ' + Math.round(values[1] * 10) / 10);
+      $('.spam-reject-score').text('> ' + Math.round(values[1] * 10) / 10);
+      $('#spam_score_value').val((Math.round(values[0] * 10) / 10) + ',' + (Math.round(values[1] * 10) / 10));
+    });
+  }
+  // syncjobLogModal
+  $('#syncjobLogModal').on('show.bs.modal', function(e) {
+    var syncjob_id = $(e.relatedTarget).data('syncjob-id');
+    $.ajax({
+      url: '/inc/ajax/syncjob_logs.php',
+      data: { id: syncjob_id },
+      dataType: 'text',
+      success: function(data){
+        $(e.currentTarget).find('#logText').text(data);
+      },
+      error: function(xhr, status, error) {
+        $(e.currentTarget).find('#logText').text(xhr.responseText);
+      }
+    });
+  });
+  $(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
+  $("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
+
+});
+jQuery(function($){
+  // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
+  var entityMap = {
+  '&': '&amp;',
+  '<': '&lt;',
+  '>': '&gt;',
+  '"': '&quot;',
+  "'": '&#39;',
+  '/': '&#x2F;',
+  '`': '&#x60;',
+  '=': '&#x3D;'
+  };
+  function escapeHtml(string) {
+    return String(string).replace(/[&<>"'`=\/]/g, function (s) {
+      return entityMap[s];
+    });
+  }
+  // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
+  function validateEmail(email) {
+    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+    return re.test(email);
+  }
+  function unix_time_format(tm) {
+    var date = new Date(tm ? tm * 1000 : 0);
+    return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+  }
+  acl_data = JSON.parse(acl);
+
+  $('.clear-last-logins').on('click', function () {if (confirm(lang.delete_ays)) {last_logins('reset');}})
+  $(".login-history").on('click', function(e) {e.preventDefault(); last_logins('get', $(this).data('days'));$(this).addClass('active').siblings().removeClass('active');});
+
+  function last_logins(action, days = 7) {
+    if (action == 'get') {
+      $('.last-login').html('<i class="bi bi-hourglass"></i>' +  lang.waiting);
+      $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/last-login/' + encodeURIComponent(mailcow_cc_username) + '/' + days,
+        jsonp: false,
+        error: function () {
+          console.log('error reading last logins');
+        },
+        success: function (data) {
+          $('.last-login').html();
+          if (data.ui.time) {
+            $('.last-login').html('<i class="bi bi-person-fill"></i> ' + lang.last_ui_login + ': ' + unix_time_format(data.ui.time));
+          } else {
+            $('.last-login').text(lang.no_last_login);
+          }
+          if (data.sasl) {
+            $('.last-login').append('<ul class="list-group">');
+            $.each(data.sasl, function (i, item) {
+              var datetime = new Date(item.datetime.replace(/-/g, "/"));
+              var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+              var service = '<div class="badge fs-6 bg-secondary">' + item.service.toUpperCase() + '</div>';
+              var app_password = item.app_password ? ' <a href="/edit/app-passwd/' + item.app_password + '"><i class="bi bi-app-indicator"></i> ' + escapeHtml(item.app_password_name || "App") + '</a>' : '';
+              var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.he.net/ip/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>";
+              var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + item.location.toLowerCase() + '"></span>' : '';
+              var ip_data = real_rip + ip_location + app_password;
+              $(".last-login").append('<li class="list-group-item">' + local_datetime + " " + service + " " + lang.from + " " + ip_data + "</li>");
+            })
+            $('.last-login').append('</ul>');
+          }
+        }
+      })
+    } else if (action == 'reset') {
+      $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/reset-last-login/' + encodeURIComponent(mailcow_cc_username),
+        jsonp: false,
+        error: function () {
+          console.log('cannot reset last logins');
+        },
+        success: function (data) {
+          last_logins('get');
+        }
+      })
+    }
+  }
+
+  function draw_tla_table() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#tla_table') ) {
+      $('#tla_table').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#tla_table').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/time_limited_aliases",
+        dataSrc: function(data){
+          console.log(data);
+          $.each(data, function (i, item) {
+            if (acl_data.spam_alias === 1) {
+              item.action = '<div class="btn-group">' +
+                '<a href="#" data-action="delete_selected" data-id="single-tla" data-api-url="delete/time_limited_alias" data-item="' + encodeURIComponent(item.address) + '" class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+                '</div>';
+              item.chkbox = '<input type="checkbox" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />';
+              item.address = escapeHtml(item.address);
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled />';
+              item.action = '<span>-</span>';
+            }
+          });
+
+          return data;
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: lang.alias,
+          data: 'address',
+          defaultContent: ''
+        },
+        {
+          title: lang.alias_valid_until,
+          data: 'validity',
+          defaultContent: '',
+          render: function (data, type) {
+            var date = new Date(data ? data * 1000 : 0);
+            return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+          }
+        },
+        {
+          title: lang.created_on,
+          data: 'created',
+          defaultContent: '',
+          render: function (data, type) {
+            var date = new Date(data.replace(/-/g, "/"));
+            return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        }
+      ]
+    });
+  }
+  function draw_sync_job_table() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#sync_job_table') ) {
+      $('#sync_job_table').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#sync_job_table').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: '/api/v1/get/syncjobs/' + encodeURIComponent(mailcow_cc_username) + '/no_log',
+        dataSrc: function(data){
+          console.log(data);
+          $.each(data, function (i, item) {
+            item.user1 = escapeHtml(item.user1);
+            item.log = '<a href="#syncjobLogModal" data-bs-toggle="modal" data-syncjob-id="' + item.id + '">' + lang.open_logs + '</a>'
+            if (!item.exclude > 0) {
+              item.exclude = '-';
+            } else {
+              item.exclude  = '<code>' + escapeHtml(item.exclude) + '</code>';
+            }
+            item.server_w_port = escapeHtml(item.user1 + '@' + item.host1 + ':' + item.port1);
+            if (acl_data.syncjobs === 1) {
+              item.action = '<div class="btn-group">' +
+                '<a href="/edit/syncjob/' + item.id + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+                '<a href="#" data-action="delete_selected" data-id="single-syncjob" data-api-url="delete/syncjob" data-item="' + item.id + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+                '</div>';
+              item.chkbox = '<input type="checkbox" data-id="syncjob" name="multi_select" value="' + item.id + '" />';
+            }
+            else {
+              item.action = '<span>-</span>';
+              item.chkbox = '<input type="checkbox" disabled />';
+            }
+            if (item.is_running == 1) {
+              item.is_running = '<span id="active-script" class="badge fs-6 bg-success">' + lang.running + '</span>';
+            } else {
+              item.is_running = '<span id="inactive-script" class="badge fs-6 bg-warning">' + lang.waiting + '</span>';
+            }
+            if (!item.last_run > 0) {
+              item.last_run = lang.waiting;
+            }
+            if (item.success == null) {
+              item.success = '-';
+              item.exit_status = '';
+            } else {
+              item.success = '<i class="text-' + (item.success == 1 ? 'success' : 'danger') + ' bi bi-' + (item.success == 1 ? 'check-lg' : 'x-lg') + '"></i>';
+            }
+            if (lang['syncjob_'+item.exit_status]) {
+              item.exit_status = lang['syncjob_'+item.exit_status];
+            } else if (item.success != '-') {
+              item.exit_status = lang.syncjob_check_log;
+            }
+            item.exit_status = item.success + ' ' + item.exit_status;
+          });
+
+          return data;
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 1
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: '',
+          responsivePriority: 2
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          defaultContent: '',
+          responsivePriority: 3
+        },
+        {
+          title: 'Server',
+          data: 'server_w_port',
+          defaultContent: ''
+        },
+        {
+          title: lang.username,
+          data: 'user1',
+          defaultContent: '',
+          responsivePriority: 3
+        },
+        {
+          title: lang.last_run,
+          data: 'last_run',
+          defaultContent: ''
+        },
+        {
+          title: lang.syncjob_last_run_result,
+          data: 'exit_status',
+          defaultContent: ''
+        },
+        {
+          title: 'Log',
+          data: 'log',
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'
+          }
+        },
+        {
+          title: lang.status,
+          data: 'is_running',
+          defaultContent: '',
+          responsivePriority: 5
+        },
+        {
+          title: lang.encryption,
+          data: 'enc1',
+          defaultContent: ''
+        },
+        {
+          title: lang.excludes,
+          data: 'exclude',
+          defaultContent: ''
+        },
+        {
+          title: lang.interval + " (min)",
+          data: 'mins_interval',
+          defaultContent: ''
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: '',
+          responsivePriority: 5
+        }
+      ]
+    });
+  }
+  function draw_app_passwd_table() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#app_passwd_table') ) {
+      $('#app_passwd_table').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#app_passwd_table').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: '/api/v1/get/app-passwd/all',
+        dataSrc: function(data){
+          console.log(data);
+          $.each(data, function (i, item) {
+            item.name = escapeHtml(item.name)
+            item.protocols = []
+            if (item.imap_access == 1) { item.protocols.push("<code>IMAP</code>"); }
+            if (item.smtp_access == 1) { item.protocols.push("<code>SMTP</code>"); }
+            if (item.eas_access == 1) { item.protocols.push("<code>EAS/ActiveSync</code>"); }
+            if (item.dav_access == 1) { item.protocols.push("<code>DAV</code>"); }
+            if (item.pop3_access == 1) { item.protocols.push("<code>POP3</code>"); }
+            if (item.sieve_access == 1) { item.protocols.push("<code>Sieve</code>"); }
+            item.protocols = item.protocols.join(" ")
+            if (acl_data.app_passwds === 1) {
+              item.action = '<div class="btn-group">' +
+                '<a href="/edit/app-passwd/' + item.id + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+                '<a href="#" data-action="delete_selected" data-id="single-apppasswd" data-api-url="delete/app-passwd" data-item="' + item.id + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+                '</div>';
+              item.chkbox = '<input type="checkbox" data-id="apppasswd" name="multi_select" value="' + item.id + '" />';
+            }
+            else {
+              item.action = '<span>-</span>';
+              item.chkbox = '<input type="checkbox" disabled />';
+            }
+          });
+
+          return data;
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'id',
+          defaultContent: ''
+        },
+        {
+          title: lang.app_name,
+          data: 'name',
+          defaultContent: ''
+        },
+        {
+          title: lang.allowed_protocols,
+          data: 'protocols',
+          defaultContent: ''
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>'
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'text-md-end dt-sm-head-hidden dt-body-right',
+          defaultContent: ''
+        }
+      ]
+    });
+  }
+  function draw_wl_policy_mailbox_table() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#wl_policy_mailbox_table') ) {
+      $('#wl_policy_mailbox_table').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#wl_policy_mailbox_table').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: '/api/v1/get/policy_wl_mailbox',
+        dataSrc: function(data){
+          console.log(data);
+          $.each(data, function (i, item) {
+            if (validateEmail(item.object)) {
+              item.chkbox = '<input type="checkbox" data-id="policy_wl_mailbox" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled title="' + lang.spamfilter_table_domain_policy + '" />';
+            }
+            if (acl_data.spam_policy === 0) {
+              item.chkbox = '<input type="checkbox" disabled />';
+            }
+          });
+
+          return data;
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'prefid',
+          defaultContent: ''
+        },
+        {
+          title: lang.spamfilter_table_rule,
+          data: 'value',
+          defaultContent: ''
+        },
+        {
+          title:'Scope',
+          data: 'object',
+          defaultContent: ''
+        }
+      ]
+    });
+  }
+  function draw_bl_policy_mailbox_table() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#bl_policy_mailbox_table') ) {
+      $('#bl_policy_mailbox_table').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    $('#bl_policy_mailbox_table').DataTable({
+      responsive: true,
+      processing: true,
+      serverSide: false,
+      stateSave: true,
+      pageLength: pagination_size,
+      dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
+           "tr" +
+           "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: '/api/v1/get/policy_bl_mailbox',
+        dataSrc: function(data){
+          console.log(data);
+          $.each(data, function (i, item) {
+            if (validateEmail(item.object)) {
+              item.chkbox = '<input type="checkbox" data-id="policy_bl_mailbox" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled tooltip="' + lang.spamfilter_table_domain_policy + '" />';
+            }
+            if (acl_data.spam_policy === 0) {
+              item.chkbox = '<input type="checkbox" disabled />';
+            }
+          });
+
+          return data;
+        }
+      },
+      columns: [
+        {
+          // placeholder, so checkbox will not block child row toggle
+          title: '',
+          data: null,
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: '',
+          data: 'chkbox',
+          searchable: false,
+          orderable: false,
+          defaultContent: ''
+        },
+        {
+          title: 'ID',
+          data: 'prefid',
+          defaultContent: ''
+        },
+        {
+          title: lang.spamfilter_table_rule,
+          data: 'value',
+          defaultContent: ''
+        },
+        {
+          title:'Scope',
+          data: 'object',
+          defaultContent: ''
+        }
+      ]
+    });
+  }
+
+  // FIDO2 friendly name modal
+  $('#fido2ChangeFn').on('show.bs.modal', function (e) {
+    rename_link = $(e.relatedTarget)
+    if (rename_link != null) {
+      $('#fido2_cid').val(rename_link.data('cid'));
+      $('#fido2_subject_desc').text(Base64.decode(rename_link.data('subject')));
+    }
+  })
+
+  // Sieve data modal
+  $('#userFilterModal').on('show.bs.modal', function(e) {
+    $('#user_sieve_filter').text(lang.loading);
+    $.ajax({
+      dataType: 'json',
+      url: '/api/v1/get/active-user-sieve/' + encodeURIComponent(mailcow_cc_username),
+      jsonp: false,
+      error: function () {
+        console.log('Cannot get active sieve script');
+      },
+      complete: function (data) {
+        if (data.responseText == '{}') {
+          $('#user_sieve_filter').text(lang.no_active_filter);
+        } else {
+          $('#user_sieve_filter').text(JSON.parse(data.responseText));
+        }
+      }
+    })
+  });
+  $('#userFilterModal').on('hidden.bs.modal', function () {
+    $('#user_sieve_filter').text(lang.loading);
+  });
+
+  // detect element visibility changes
+  function onVisible(element, callback) {
+    $(document).ready(function() {
+      element_object = document.querySelector(element);
+      if (element_object === null) return;
+
+      new IntersectionObserver((entries, observer) => {
+        entries.forEach(entry => {
+          if(entry.intersectionRatio > 0) {
+            callback(element_object);
+          }
+        });
+      }).observe(element_object);
+    });
+  }
+
+  // Load only if the tab is visible
+  onVisible("[id^=tla_table]", () => draw_tla_table());
+  onVisible("[id^=bl_policy_mailbox_table]", () => draw_bl_policy_mailbox_table());
+  onVisible("[id^=wl_policy_mailbox_table]", () => draw_wl_policy_mailbox_table());
+  onVisible("[id^=sync_job_table]", () => draw_sync_job_table());
+  onVisible("[id^=app_passwd_table]", () => draw_app_passwd_table());
+  last_logins('get');
+});
diff --git a/data/web/templates/admin.twig b/data/web/templates/admin.twig
index 863f87e9..33f2422b 100644
--- a/data/web/templates/admin.twig
+++ b/data/web/templates/admin.twig
@@ -57,7 +57,7 @@
       </div>
     </div> <!-- /col-md-12 -->
   </div> <!-- /row -->
-</div> 
+</div>
 
 {% include 'modals/admin.twig' %}
 
@@ -66,7 +66,7 @@ var lang = {{ lang_admin|raw }};
 var lang_datatables = {{ lang_datatables|raw }};
 var admin_username = '{{ mailcow_cc_username }}';
 var csrf_token = '{{ csrf_token }}';
-var pagination_size = '{{ pagination_size }}';
-var log_pagination_size = '{{ log_pagination_size }}';
+var pagination_size = Math.trunc('{{ pagination_size }}');
+var log_pagination_size = Math.trunc('{{ log_pagination_size }}');
 </script>
 {% endblock %}
diff --git a/data/web/templates/debug.twig b/data/web/templates/debug.twig
index 1e011c8b..006703fb 100644
--- a/data/web/templates/debug.twig
+++ b/data/web/templates/debug.twig
@@ -41,7 +41,7 @@
               </div>
               <div class="col-sm-12 col-md-8">
                 <div class="table-responsive" style="margin-top: 10px;">
-                  <table class="table table-striped table-condensed">
+                  <table class="table table-striped table-condensed w-100">
                     <tbody>
                       <tr>
                         <td>Hostname</td>
@@ -59,7 +59,7 @@
                               <span class="text">{{ lang.debug.show_ip }}</span>
                               <div class="spinner-border spinner-border-sm d-none" role="status">
                                 <span class="visually-hidden">Loading...</span>
-                              </div>  
+                              </div>
                             </button>
                           {% else %}
                             <span class="d-block">{{ lang.admin.ip_check_disabled|raw }}</span>
@@ -71,7 +71,7 @@
                         <td class="text-break">
                           <div class="fw-bolder">
                             <p ><a href="#" id="maiclow_version">{{ mailcow_info.version_tag }}</a></p>
-                            <p id="mailcow_update"></p> 
+                            <p id="mailcow_update"></p>
                           </div>
                         </td>
                       </tr>
@@ -198,10 +198,10 @@
                         <i class="bi bi-caret-down-fill caret"></i>
                       </button>
                     </div>
-                  {% endif %}  
+                  {% endif %}
                   </div>
                   {% if containers["solr-mailcow"].State.Running == 1 %}
-                  <div class="collapse p-0 list-group-details container-details-collapse" id="solr-mailcowCollapse" data-id="{{ containers["solr-mailcow"].Id }}">    
+                  <div class="collapse p-0 list-group-details container-details-collapse" id="solr-mailcowCollapse" data-id="{{ containers["solr-mailcow"].Id }}">
                     <div class="row p-2 pt-4">
                       <div class="col-sm-3">
                         <p><img class="img-responsive" alt="Solr Logo" width="128px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAABlCAYAAAAI2qyuAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAXEUAAFxFAbktYiwAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuNWRHWFIAABv7SURBVHhe7V0JuBxVlX4JMy6jM4qOC4rp6qWq+jW+ruoXkIjoS1d3EhYXRvNkFHFBYECUAXGAASEOyiLIrigOEpDoACIqwogLiwMSkIghYF4viSFhiUGWCAQIyXuZ/9w+3V3VdXtfee/+33e+16/uqXtv1T2n7j33nnvukIJCP7BjbOzvsgt0O5cyjsymzG9mU8YvcylzIucYT2Ydczt+7xDkGFuR/hf8XplxzHG6d20qauSSkfCSJUOzRWYKCtMBE3uZ/5hxjE9A4K+FEmwuKUFj9Nd1Y9brdwwNzco6xr10DXk8DAX77kTaXEQKx8UoKLz8AGE+D73BcxVC3zBl09HPUz6ZpOHI0kEboXhnrU3H5ogCFRQGHSvmzv17+stf/ZaVA71EppgXFO0Xcp4CYai2LZeKLs2m9BDxKygMHEg4Mynz+kzK2IP+X52KvlEmzI1SPml+kPLB8CyOXmJKxuMnYyvoG7l9Iv9E9yoo9B3Xjg/thC/8cegtnsffFyGcr6TrBWNcJsQNkGPeIjIHkO8yKU9teiSXiryfs1BQ6A8eWhTdBV/3W4qCid/3c9IQ9QAugW2YoGSTeSc6Snn8KTkcKAyf5Ly1iHudi9eNBV4lKqSg0Evk0tF5EN7HPILpmN/l5CEMdY71pDVKjnkFZ0HDqwulPE1QJmneM5E238ZZKih0H3ln+AB8oV/wC6TxOWYhBfmGP702offYknX0t9P9Dy6MvaE9I99Fjrk+50RiomIKCt0EBO7jENzyop6LVqNXYTbiu6YyvR5NpMz/4tuH8PvLMp6WyTFO5awVFLoDfIkXQ9CkygHatuL9c/+BWYfQwyyX8FQlKN1jGxfEX0P3blg879XoTTbJ+JolskUySeNEUae0PkxEvxUUOgoMfd4rZqkkQkgEAV/FrALgfVzGV41gb3yWbx3KYKgm42mWqKfLpqKHU54Fg9/cgHqtp8kFUZCCQidAK9UQrpoCD2H8HrMPUU9SbRhWhVZeOz6+E927A39h/K+R8DRHjrGVejzKM7dP5E3IM1NMQ92WF6ejFRTawoPjsVdA0O72CJ+E0AMcxbcMkVOhjEdGYgiUNlJ861DeMcdlfM2R8SyM8gWUHy0aQllWSPguFgUqKLSDfMrYRyJcPppImnvxLSTkSRmPlBzjRr5NAMr4eylfo+QYT2A4+C7Ki9ZAsk70NhmfUEw8myhUQaFV0PBKJmBuwvDlpXVjY6UFOVw7pJKnCm3LObHS9Cv1JBKeZuiRfHJ4N8qLPH2hbD+V8JTJMdY/OBZ7rShcQaEV7FgyNBtj9iekAsaE9AeYXSDjRJfI+CoJBvO3+BYBKNrNMr5GCHXIToyZGuVDjpIZx7xCxucjxzhbFK6g0CogSP/rEywXQThLBjoBw5fLZHxugnJsJuOZbxnKLtQtGvbIeOsRyr9vrfPOt1A+pBzoOc6V8ckIivRiUbEUFFoCBPcsmXAVKTvfPJpZBSCgv5bxecgxjmd2gXzKvErKV4egHLevTYdex9lQXZteYMykyi4yCgp1UXT3KAJC9JFKoXJTZoHxHmYVgPBnZXxFwlDqIbcTYatOidmkccNd83Z9NWczlEubR0BBpLy1iNZ31NqIQsOgIRL/FPgzBFgmWET4gm9fySvgBLFRSuqnVSYo4L8yuwAUqmmnxIxjXFncUEWgPFHupIy3EZpwzNM4KwWF6kBvEKRZqdtc+70L43q5oQ6h/BOzCZAtIOMrEgR7OeXH7IWNVU06JaJ+F7jzyM83F1GdZbyNEnqRh4uLlQoKVTGRLIzhK/d5QwApAolfuBzzSmYRoB2FUj4QlGkqM987HAP/KTJeGdH9EOST+VaBNRjeNatg1YjWbzhbBQU5MGQS0UMyaWNvviQAwTyzUqCIILReA72GvQJl+BGzCWyA/YB8G3JKLAyfyu70hDUpcwQK+pSMvxWinomzVlDwY82C0JsLgigE5iC+LABBXOwWpiKREyOzCOTmG8fL+cwXK4MqgLdBp0Rjaz41fCDfJiDcWRzjUTl/a4Q65jl7BQU/3EpQdBEvYt0iU3MLExG+uNsedbm4E6oZ3Og9zmEWAbJxoIx1nRIhtFvI3YVvE1i9KLpLxjHXyvjbIRrCqdkshapAb1BaYIOCeJz5eEX9abdAQaFWc3IJuH69h6dAIgAcswhAYQ6U8HkICviU28eLsGr/kZ0hyPfL+Nsmx9iu7BCFqoDg/colMDfx5RIgQN4FwLTXQCdgOOTznEW+HjuFAOWq45RoPDYBG4PZBWg6GUp6l5y/daLVdDzbpVBGk4tSUPAjk6RwOQWhwdf7Qb5cAoTobLdg5dLGMZwkUFgDqexlygHgiqgRKVEQ8siTjcHsAsLtPlnb5aVZwvANBr5x+rqx2Fu5mKHMmPHP/FNBoYyCa3h5oQ2/N7vXGgj5tPFRj4Alh9/HSQIUh9edhyDH/AAnl4BeoJZT4v3r9isLLKEwvDOvlvC2RJmU+TCGeF/c5PLkLYQwMi6nHoovKSiUwTv5HigKEQRyigSekwUecG2EQvp2t4ARMguNaDFd8KSMWyuVjALKkTHs5ivxO+Ydq/Ye2ZlZBUSv5JiXyPibJsdYlU1FP7XD1aPRPviC97HxLPFAQR7iJAUFL/BV9WyOyiZ1i5ME+EsuIrRDkLJ8uYTcgujC0r2pcgA4N6A0PyjyeMgxbnIHfSgCw7ivSvmbINTldjzLfm5lpWeBjXMInsMzVYz/NzOLgoIfEJDS8CefDIv4uG5gyPQbTv8hXyoBaUcU7804RikAXBHVnBJJacjGYLYSYKu0FnwOhOfYDrpudTpSCkVURGa+kULafbL7yGBnNgUFPwqr04WACxh6lPaZF4Gv8ddFGsbwfKkECPTXhKA5xnOVHsEEKMIFRUEsEuyBi+lrziwl4Iv/yWpDsVqEe15AOd9ekx7WOasSKOQP6nYjeKT3EikFeZkgEom8skixmP/r2k3Q/ggWljP4UgklQz0dHeNLJaAHuZLSMAzzecZWOiWSkIJKgeLcgPJ9CDxNub+jR3gyl4qevsoJio1TbhSimujfRHl184RyPcu3KQwq5u0679VG0NpWJF2z/8JJPYHwyHWMZyDwV/OlEvILYxHqYSoNeAKE9DbQY7J93rjn1LIQmpMQ2C9wkgf5VHg+0mu6y7sJ5a3HcO4YWZk0M4e0E4p2UyOEsh/m29vB+E6RyGjYDCY+YASsYwzN+oYRtJfi73X4eyPoZ3rQvhZ/L0cDnwOeY00t8UG6Z2hoiTpjrg4KCmLvKBIU5AlO6hkgVCeDlvO/JYjZLsdYwf96AIVam0uah/C/JRQiJRocV8t4CcryCU7yYCIZnYsy/1YptDJCfqvy6eGDZcew0TmGtFIPnnWye2tRNmn+gbNpDqY2YhqB+IkQ/JuhCJvdDdgMobGfMjT7el2zDo9G93gjZ6/gwiAoCPlY5R3DpyAECHgpCmIRPMP1e5k9AWE9UggfnSEyX9+fL3uQGTOiULCanr34uk9hCHQreoV9K6ePiyAvZFJs2f0N0vWcVX0EAmOvigTjn4Uwr3A3WKcIDb8VtAzkmU6c6RgEBSFMpMyDZLNLlQ6KBDLKi8Ha3Cg6JUJoN1d6/hbxwMLd3gHlWS8RVkFI2w7F+NFaPr1KBvIURjnXgqR5NEpQ5tM5y+oYw0OZocQR+NI/4m6oLtIkhOCqUCj+Zq7CjMagKAgNVXYc7nUTqYZqoTzFUCdl/qVyTaWI3N6RN6HnWC0TVtHjpMxLyO5hdh/W7z2yM5TvXFDVuMHNUDYZ/RhnLcdw2NpND1p/cDdQJWGYtR22xWr8vUYPJk6LBOJHmEF7cVSzFpphK4nr6TBsDj1gfRrDsRNwz6XohX6HfLdU5uUhzd5EeXBVZiwGRUHahVgFx5c/vzAsFXAy9KEcPqdFKMaTGMZ9dc2C6h9MWg0nQx+8NWN2NUu05ZiL8ANCPg7BlwoxhP5p/P0uGeaRyLtaOiSRpiuFAmn2+aQMlWUI0mjmxj6Ub5mRmC4KQj5VsvUQgvD9gj3hFk78/xB6jKNrRTskpYMNcgAUo2bklFYo45gbqtk2QwZsDTTGlLthBIlhlnXU3F384852QMoiytSsh31loh5kxDPrjMN0UZBqoJkwCGRp3wiU4n6yd9yBImQQs1wp8/bifR0nx7yUi/IirI18sDBscgupNUlTtPF4OaRLNxB/S/w1EIBzC+V5hGJ7JBj3eYLOBExnBSnMdhnfg6BTAIZbaLdg1a82Iw8jHsL7ffC3HNanESJ/LS6yDGNOPOiftrWep3ULZukJxJpK0H7OXQ8IxjM0vcwsMwbTWUHyKf0sCPo1E2Pm7nypKoSNkjK+Rsa6TKA7SajT47IZu1lQjl9VNMbWfhnKEc0eI+V01wdDvPt67WrRb0xXBaHTZnPJ3TwboWSgoVY+qR8GxdgoE+ZuEG035uLLMEKJlLshiCCk/87JfYEZtD/uqZNm5aPh0bmcPCMw3W2QWshgyAXFKO1L6RTxkG4T8qYtwT/OOcY55KKC3x9ZkzZ3l7nawzC3r/c2hEW7qfruCoJeYyno50bI3hf/zjjXlJmoIGsWRN8JO+MXbqFujjAMc8y1UIDb8HspeoTToACH0h4V2sjljgXcEDBseS2E8EV3Q0QGZw2iptE23TGTFOTBsdhbs0nzvyHYVc80LHz9yVvXXJlNGTeA//y8ox8HA//A1anou8m5kmbGOMumUHWCgBbz3I0AZSEvxhktmIOCmaAghYM+zZMg+M9guLMVtB49wB15x1iWSRtnkP8WzSpRz1I8HrpdkBLRPpGsE6VA17Sv5TcoV36ADnoLWuEuK0jQvpyTFPqMmaAgFPCBfLNoIXFFg+4szYCUgcL4YJj1CSjiebQoib+VnsJ/rRrFBC/+Ik8jBOPHcZJCnzGTjfRWQLNeHDDioGxKPx/KcHsxCEMtQg9V3e/KCFpXeBohYNd20poBGMdXJxqMG7QGZGr20REtfooesL5Cbv6RgPUZXUu8zzDmdj1uklKQ6qAYW3TwJ3qDT2N4dCGGSndimNZ0dHcM4f6Hs5QDNscyTyMEEh/mpBkFWsk3g9ZBeB8/9i+YSmkK9tsEhPZcnn7uuN3WDQWJBO3FyOvuIpG3NifVw6xhLRGIzME7ClrnmIWNcLdRHnrQuh3pXZtlJGWg6IprUvqncmnjIgj2negdOrFouJrOT+di5MADXgoqN7yWOJiTZgTItR7PfTaInDDL76FZ0ux7IsEEucR0TFG6oSAQ5s+788THoOa+B+opjVD8RJS9ynOfi5Dn80uWdGaHKLnLk0t8Nq0flqP94465HNT5FXTHeGJ1KmpwsdUBm+Msz8Nq9pc4aVpjLr5KRsg6XtesZ9zP3wG6VQ/sNszFtIV+Kkhh+t86A71F7a0JoHYURNgNwitXBIW7G387sp+jJlGk+Ipg2FUBAfm3igdeyknTFhhGDuM5a+11mYIwrscQ6ic0nMA7+rIRsk/EfafBLrkM/9+L9K2S+5isF0DHoqi2epN+KYgZsOaD7yEPXw3qQA8yi06yyiajH86n9K9AiG/CEGoj7Aq5gLdBUMAtFBOYy60PjEF39zysZq3H5Wm7DmIErAMg+M+6n7lEmrUBdsjJ4XCCNvbUfAf8hf0o7rsZ5N8eANJD9nXtbA/oh4IYwcQX/R7dLtJs2pawVOQTju+jByw7FprrOaatE6CFO/LZghH9IQyHTsVX/0YIeFs+WVC4pzNj3hOz6oK21eKBPeNvchbk5GmFQm/pdacnguD9FX8/R8MuZm0K4fDIKITql5X5EuH68tiu897ArE2hxwoyC/mf50ljwvWt+LBcYYZtGpb09eO5Hkoz4ZgfEEqTMn4GwX+EVtgrlcFHjpGjgHGcTXPAV2FpxQu5BZenVS8C5TgMz+b70uP6dZGI/SZmawez0GN8DArhM/YhlPe2svuylwqC3zRRUb5eTr+OtkKImwcTs2hLbjYV3R/Dp5PR0/wUfze4lQP//3T9/t5g2E1B10b3lLycabPdVQ/Y+0G4vMMGzdrWDY9lfc7uISjd/Z6yCnQz9dbM1hB6pSAUS8BzTVy3/6aHEp5zzF9OEEqT1PeDwizGv+1/7PFCbqh4SS/Qghgnv2yh67uH8CyerzoE+CU9EO/aeg/1FhjK3e4uU5QbtM9ilobQIwX5Fb0P9zXwPBoMjnpOdeoGyM7IJaMLs46xDMOkSzBkOoW23NKRzrR7sPLAnb6CDC28LM8CGRrkWSOQ8Byc+HICrYgbWvx3Fc9EW3g/wixdAxnxELzfu8sGTVKwCmapi54oSCVRRJlgvP76QAfB8XovhIJs9Q6NyLPXeAi/f5tPmVdRzN1M0jgy4xj75pxIjBb5KCQRZ9N90FcVL6linG5NRoLWOdTgzPaygVQYQtYJnNx1vDO451t41qdUPuqUp0B8zFITvVYQ6knYEO8LaOEOCnF9o9O7ZJzDvtgMRbor6+j/wtl0FzTNh5clmba0NqKHOb4XPkidQGzX2BtQ76fczwAB+AWSejr5wNuHPTNn5EHNyTXRawUxNevLzNZX5NKRMfQo98qUokjkdwXl+Dl+H7I2HXod39ob4GUdWjkuLRIaaSsU5SY0+rG0hjKoPYseTJxZUe9nIpGRXTm5p0AvcrG7LqCnGpnV6qmCaPYDzU4idBMU9YRc1KEo69BTPJ6lU3Md88pMKvrFtWljb1lghZ4iHEi8Gw2Slb5ML01BYTCMsH4LI/RqNMAluHY2rp2O31+BDXAKudCbgfghwjsWSsWhRbs2dhRGsmb9zVPPBr/a3UAgYL2ehNtdH12L13Xp6aWC4Lo0mHS/US8UUF9Bh7agUb6E3oQW0nwvtS0iAdbse4xg/DvkQt7JrzuU9ShvWfYm8tbl5L6AFNRTp6C1BpdrfiR6qCB/RNK0WvfqKchdAkpyGBr1LrxM30p0h4iiKN5LaxNkP3DRLQH5eGaPIBTSE4x6Ce5FPPG+jFC8pttDDxVkRod47SjMdyTeRsGoIYTfxstegb+d9oYFkQepdVEssIfnnOxGQHsWKvKa7IavUCvQtcRl7rrh3fljMLnQCwXB/1tM039KlELnMDsOm2JYi+8Z1uwPmSHr0xDKo0wtfjSdKBWhma+QvQSNcRFslGvQyHdiuLGBBNfdUDIC/7NmSKx2N2yvwNbxrApDAAbmQHg8jydABuo2wUlS9KQH0ezGD4pR6B12LTT+HjSkgsLcBPKEHvIQ0kOhuQ1N5UEIv+++Vw+OnMRJfUcsFnsFhNztSTxZa9q8Nwoi3w+iMGAYmTOys67FvwAh+LOnAYsUsFY2clQbeB903zdo7jIQyF+768dB8aRQCqLgg9jpR7NQ0n3h1l21VqHFvZ4geNakae41UONrPBcddFp+poB1DCf5oBREoSpEtPmgfZ+nMQv0HWbxgfeXl3ghUD09NrkRROhIO3cdQ/Z5nOSDUhCFmqCvv65Zd3gaNGhPVdvQJU7g9fBaKzlpYKCHhL9buY6avYyTfFAKolAXBdvEyrsbFYJyJyd7EAnEEx6+oJyvnzC1+CJ3HSl8Dif5oBREoSGE59h7oTErHCgTcU4uga55eDS7tcPguwgjYO/rrqNSEIWOAML+c3fDoqF9q+O6PkKbo8o8mvUoJw0MaFuuu46gqlFklIIoNAxDix/oadig9VtOKqGwk6/c00AYttN6CycPBMTCqes5UMczOckHpSAKDYPcTtwNC/viKU5yYzZdd/OR9zCnDQRQp8vd9cOQ60hO8kEpiEIzmI0hk3ttZErmQ0Q9i4sHZB3FSQMBDBU9C5kUpI2TfFAKotAUIOye2SxynOSkEiBEnthO6FF+wkl9R3TO6C6oU3kIqFkv1dpwphREoSlAQf7kblzy3OWkEiAA+7t5IFTPDYodEtWsw911A93NSVIoBVFoCmjcR92NKwv4Vti74t13Yc6xDuLkvgIK7gkFhN7tPzlJCqUgCg2DegE07rZy41ovVdtDjbQfuIUAgnUHJ/UNw2FrN9TFvZYzxTGAq0IpiELDoN137obF1zfDST4YoVHHzUsUDSfezcl9AQSzwg1fhHetCaUgAwLybepFJL12gF7hQk/D1j5slGa8POE/9WD8Vlzvy75rCLaF+ns2iOkhez9OrgqlIAOAsJY4WLiIa/amMIYBfHmgQF66EHjP1t560REjQXvczU+kB+I9jzUrojsW9vK760LGeV1lVQrSZ0ApTsVLKo+LNetx/N2DkwcFs1CvH5XqKMjaSLvzOL0aZlUKJglYr2NjkSHurgNoSg+OvpeTa0IpSJ8BBaG94uWXJcjaMkhRvSmmVmUdaZsuJ9cEHe4CgfAGZobBTuGMmKWrQC+2AALomlggsq7g5LpQCjIAwAs72fPCCkQnuH47Hu9vLCnagkt1cdcN9V3RTARA2UcAz3b1+ND4TszSFaCcPSB8nuB1KHcdhf9hlrpQCjIgwIv6LK3qel6ceHn2WjYme2rc0rZalF0ZrpME7MlIZDTMbA2BbAAI1m8q84po1g8bGKa1BD048l6UUXmQzgsQzncxS0PwKYjc/6wpKAVpETSNipfnWYgrkhCwOkHOOgU6bgGCMOGvh7Wl1TrQEWh4Nn+emvV/MneVNjArEkgcgfdVccCnNUneyMzTMArH49GBoKW8JqNvqx+4ohaUgrQBsae7Yt+Fl6zlaPxPxmJjHQ1YLcLhhOIfhmLcKS1XszbXcuprBBQ8Dvn7I6Zo1uN6wP4YWNrqJUX+/kOIQNakGUwcwmxNA3n80Z0fnuE4TmoJSkHaxyxDSxyMF1k1Hi/StuDFXodh2WF8fl3TwhWNju5iBu1x5EHHKlcvS7MfMAJzo3xbW9B16+0Q2JXScqCctB0WbE0F1aYeCHmeA3rel69mvdju1DIJsDtPvI9nYnPsGCdLEQvEqkakVArSIVBXjsb4ptQ2qaTC9PAvhd2gWSfga/8ZCP9iI2wdEC4clXwoBPAkPWR9C79vBs9jpXurEMreijy+3mlHQ3KRp22usjIFafYaPRj/uqmNLiqEPvUY87PI10vsfZ8jQhNRwDvpkcl4b+t1Lb4n39cywuHd34E6eYLq4V0+jWunGKFEKkrnj9BmMjFlb/0Q6WtBNLEhna5XCtJh0HFceKnfx8uvmK7sDqGsbaZmX9XtY8AovjDK82yukpP1PIT9cSjsJiGY9YN3k3AupWATXFTbQLmVaymNkDSkqFKQLoGGUpGgfTZe8EbPC+4UafFHIIhn9vLIYfIGpl6toV6yAcK7+Z0Rst7D2XcSs2nWTVZmdbIm9UDCdy64UpAug2ZWCt164gx06/fgBbtnWRomNNQTuPfXES1+SiQwOm9oaEnvDmOsQOEA08QZqJN0Fq8OPY8v/LUYVjZ8OGdrWDLbLCya+s5h95Nok7vRC7+fby5BKUiPQeE+zeDICPlGURhN9DJfZVvjcijQMnydL6PogRjSnGwER8gWSfPUal8cB2tjyWzy+kUdT4Lg3ADKg9zj/yk80yYMt+7E81xAdlavQ5vSDkTU6aOgM+g4BROE+pyH9/wfZO+R9wC1CbP7QDOVtE+/SDRxwUkKHgwN/T/fvy7K4dvMgwAAAABJRU5ErkJggg==" /></p>
@@ -238,10 +238,10 @@
                         <canvas class="d-none" id="solr-mailcow_NetIOChart" width="400" height="200"></canvas>
                       </div>
                       <div class="col-sm-12 d-flex" style="height: 40px">
-                        <a href data-bs-toggle="modal" 
-                          data-container="solr-mailcow" 
-                          data-bs-target="#RestartContainer" 
-                          class="btn btn-sm btn-secondary d-flex align-items-center justify-content-center mb-2 ms-auto" 
+                        <a href data-bs-toggle="modal"
+                          data-container="solr-mailcow"
+                          data-bs-target="#RestartContainer"
+                          class="btn btn-sm btn-secondary d-flex align-items-center justify-content-center mb-2 ms-auto"
                           style="height: 30px;">{{ lang.debug.restart_container }}
                             <i class="ms-1 bi
                             {% if containers["solr-mailcow"].State.Running == 1 %}
@@ -255,7 +255,7 @@
                           ></i>
                         </a>
                       </div>
-                    </div>                  
+                    </div>
                   </div>
                   {% endif %}
                 </div>
@@ -291,8 +291,8 @@
                           </button>
                         </div>
                       </div>
-                      <div class="collapse p-0 list-group-details container-details-collapse" id="{{ container }}Collapse" data-id="{{ container_info.Id }}">   
-                        <div class="row p-2 pt-4">   
+                      <div class="collapse p-0 list-group-details container-details-collapse" id="{{ container }}Collapse" data-id="{{ container_info.Id }}">
+                        <div class="row p-2 pt-4">
                           <div class="mt-4 col-sm-12 col-md-6 d-flex flex-column">
                             <h6>Disk I/O</h6>
                             <div class="spinner-border my-4 mx-auto" role="status">
@@ -306,12 +306,12 @@
                               <span class="visually-hidden">Loading...</span>
                             </div>
                             <canvas class="d-none" id="{{ container }}_NetIOChart" width="400" height="200"></canvas>
-                          </div>   
-                          <div class="col-12 d-flex" style="height: 40px">             
-                            <a href data-bs-toggle="modal" 
-                              data-container="{{ container }}" 
-                              data-bs-target="#RestartContainer" 
-                              class="btn btn-sm btn-secondary d-flex align-items-center justify-content-center mb-2 ms-auto" 
+                          </div>
+                          <div class="col-12 d-flex" style="height: 40px">
+                            <a href data-bs-toggle="modal"
+                              data-container="{{ container }}"
+                              data-bs-target="#RestartContainer"
+                              class="btn btn-sm btn-secondary d-flex align-items-center justify-content-center mb-2 ms-auto"
                               style="height: 30px;">{{ lang.debug.restart_container }}
                                 <i class="ms-1 bi
                                 {% if container_info.State.Running == 1 %}
@@ -340,7 +340,7 @@
         <div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
         <div class="card">
           <div class="card-header d-flex align-items-center fs-5">
-            <span class="mt-2 ms-2">Postfix</span>      
+            <span class="mt-2 ms-2">Postfix</span>
             <div class="btn-group ms-auto">
               <button class="btn btn-sm btn-secondary refresh_table" data-draw="draw_postfix_logs" data-table="postfix_log">{{ lang.admin.refresh }}</button>
             </div>
@@ -627,6 +627,6 @@
   var lang_debug = {{ lang_debug|raw }};
   var lang_datatables = {{ lang_datatables|raw }};
   var csrf_token = '{{ csrf_token }}';
-  var log_pagination_size = '{{ log_pagination_size }}';
+  var log_pagination_size = Math.trunc('{{ log_pagination_size }}');
 </script>
 {% endblock %}
diff --git a/data/web/templates/domainadmin.twig b/data/web/templates/domainadmin.twig
index 56f5e75f..070bf00c 100644
--- a/data/web/templates/domainadmin.twig
+++ b/data/web/templates/domainadmin.twig
@@ -46,7 +46,7 @@
       <div class="col-sm-3 col-5 text-end">{{ lang.fido2.known_ids }}:</div>
       <div class="col-sm-9 col-7">
         <div class="table-responsive">
-          <table class="table table-striped table-hover table-condensed" id="fido2_keys">
+          <table class="table table-striped table-hover table-condensed w-100" id="fido2_keys">
             <tr>
               <th>ID</th>
               <th style="min-width:240px;text-align: right">{{ lang.admin.action }}</th>
diff --git a/data/web/templates/edit.twig b/data/web/templates/edit.twig
index 29f36435..af83a31d 100644
--- a/data/web/templates/edit.twig
+++ b/data/web/templates/edit.twig
@@ -26,7 +26,7 @@
   var lang_user = {{ lang_user|raw }};
   var lang_datatables = {{ lang_datatables|raw }};
   var csrf_token = '{{ csrf_token }}';
-  var pagination_size = '{{ pagination_size }}';
+  var pagination_size = Math.trunc('{{ pagination_size }}');
   var table_for_domain = '{{ domain }}';
 </script>
 {% endblock %}
diff --git a/data/web/templates/mailbox.twig b/data/web/templates/mailbox.twig
index d1044288..1312441c 100644
--- a/data/web/templates/mailbox.twig
+++ b/data/web/templates/mailbox.twig
@@ -58,7 +58,7 @@
   var lang_rl = {{ lang_rl|raw }};
   var lang_datatables = {{ lang_datatables|raw }};
   var csrf_token = '{{ csrf_token }}';
-  var pagination_size = '{{ pagination_size }}';
+  var pagination_size = Math.trunc('{{ pagination_size }}');
   var role = '{{ role }}';
   var is_dual = {{ is_dual }};
   var ALLOW_ADMIN_EMAIL_LOGIN = {{ allow_admin_email_login }};
diff --git a/data/web/templates/quarantine.twig b/data/web/templates/quarantine.twig
index 5ff7fe66..79b5ea16 100644
--- a/data/web/templates/quarantine.twig
+++ b/data/web/templates/quarantine.twig
@@ -37,7 +37,7 @@
           </p>
           {% endif %}
         </p>
-        <table id="quarantinetable" class="table table-striped"></table>
+        <table id="quarantinetable" class="table table-striped w-100"></table>
         <div class="mass-actions-quarantine mt-4">
           <div class="btn-group" data-acl="{{ acl.quarantine }}">
             <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="qitems" href="#"><i class="bi bi-check-all"></i> {{ lang.quarantine.toggle_all }}</a>
@@ -66,7 +66,7 @@ var acl = '{{ acl_json|raw }}';
 var lang = {{ lang_quarantine|raw }};
 var lang_datatables = {{ lang_datatables|raw }};
 var csrf_token = '{{ csrf_token }}';
-var pagination_size = '{{ pagination_size }}';
+var pagination_size = Math.trunc('{{ pagination_size }}');
 var role = '{{ role }}';
 </script>
 {% endblock %}
diff --git a/data/web/templates/queue.twig b/data/web/templates/queue.twig
index 1a5d4ff9..e843c18e 100644
--- a/data/web/templates/queue.twig
+++ b/data/web/templates/queue.twig
@@ -55,7 +55,7 @@
   var lang = {{ lang_queue|raw }};
   var lang_datatables = {{ lang_datatables|raw }};
   var csrf_token = '{{ csrf_token }}';
-  var pagination_size = '{{ pagination_size }}';
+  var pagination_size = Math.trunc('{{ pagination_size }}');
   var table_for_domain = '{{ domain }}';
 </script>
 {% endblock %}
diff --git a/data/web/templates/user_domainadmin_common.twig b/data/web/templates/user_domainadmin_common.twig
index 8a7ace39..64a2205d 100644
--- a/data/web/templates/user_domainadmin_common.twig
+++ b/data/web/templates/user_domainadmin_common.twig
@@ -4,7 +4,7 @@
   var acl = '{{ acl_json|raw }}';
   var lang = {{ lang_user|raw }};
   var csrf_token = '{{ csrf_token }}';
-  var pagination_size = '{{ pagination_size }}';
+  var pagination_size = Math.trunc('{{ pagination_size }}');
   var mailcow_cc_username = '{{ mailcow_cc_username }}';
   var user_spam_score = [{{ user_spam_score }}];
   var lang_datatables = {{ lang_datatables|raw }};