git subrepo commit (merge) mailcow/src/mailcow-dockerized

subrepo: subdir:   "mailcow/src/mailcow-dockerized"
  merged:   "c7b1dc37"
upstream: origin:   "https://github.com/mailcow/mailcow-dockerized.git"
  branch:   "master"
  commit:   "a366494c"
git-subrepo: version:  "0.4.6"
  origin:   "???"
  commit:   "???"
Change-Id: Id574ecd4e02e3c4fbf8a1efd49be11c0b6d19a3f
diff --git a/mailcow/src/mailcow-dockerized/data/web/js/build/004-datatables.js b/mailcow/src/mailcow-dockerized/data/web/js/build/004-datatables.js
index 9ece8ea..8c79171 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/build/004-datatables.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/build/004-datatables.js
@@ -15801,7 +15801,7 @@
 		paginationEl.empty();
 	}
 	else {
-		paginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination');
+		paginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination pagination-sm');
 	}
 
 	attach(
diff --git a/mailcow/src/mailcow-dockerized/data/web/js/build/013-mailcow.js b/mailcow/src/mailcow-dockerized/data/web/js/build/013-mailcow.js
index c734c82..fd60130 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/build/013-mailcow.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/build/013-mailcow.js
@@ -1,3 +1,13 @@
+const LOCALE = undefined;
+const DATETIME_FORMAT = {
+  year: "numeric",
+  month: "2-digit",
+  day: "2-digit",
+  hour: "2-digit",
+  minute: "2-digit",
+  second: "2-digit"
+};
+
 $(document).ready(function() {
   // mailcow alert box generator
   window.mailcow_alert_box = function(message, type) {
@@ -12,14 +22,22 @@
     $.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
   }
 
-  $(".generate_password").click(function( event ) {
+  $(".generate_password").click(async function( event ) {   
+    try { 
+      var password_policy = await window.fetch("/api/v1/get/passwordpolicy", { method:'GET', cache:'no-cache' });
+      var password_policy = await password_policy.json();
+      random_passwd_length = password_policy.length;
+    } catch(err) {
+      var random_passwd_length = 8;
+    }
+
     event.preventDefault();
     $('[data-hibp]').trigger('input');
     if (typeof($(this).closest("form").data('pwgen-length')) == "number") {
       var random_passwd = GPW.pronounceable($(this).closest("form").data('pwgen-length'))
     }
     else {
-      var random_passwd = GPW.pronounceable(8)
+      var random_passwd = GPW.pronounceable(random_passwd_length)
     }
     $(this).closest("form").find('[data-pwgen-field]').attr('type', 'text');
     $(this).closest("form").find('[data-pwgen-field]').val(random_passwd);
@@ -103,10 +121,21 @@
         if (lastTab) {
           $('[data-bs-target="#' + lastTab + '"]').click();
           var tab = $('[id^="' + lastTab + '"]');
-          $(tab).find('.card-body.collapse').collapse('show');
+          $(tab).find('.card-body.collapse:first').collapse('show');
         }
       });
   })();
+  
+  // responsive tabs, scroll to opened tab
+  $(document).on("shown.bs.collapse shown.bs.tab", function (e) {
+	  var target = $(e.target);
+	  if($(window).width() <= 767) {
+		  var offset = target.offset().top - 60;
+		  $("html, body").stop().animate({
+		    scrollTop: offset
+		  }, 100);
+	  }
+  });
 
   // IE fix to hide scrollbars when table body is empty
   $('tbody').filter(function (index) {
@@ -278,6 +307,8 @@
   $.extend($.fn.dataTable.defaults, {
     responsive: true
   });
+  // disable default datatable click listener
+  $(document).off('click', 'tbody>tr');
 
   // tag boxes
   $('.tag-box .tag-add').click(function(){
@@ -294,19 +325,28 @@
   $('#dark-mode-toggle').click(toggleDarkMode);
   if ($('#dark-mode-theme').length) {
     $('#dark-mode-toggle').prop('checked', true);
+    $('.main-logo').addClass('d-none');
+    $('.main-logo-dark').removeClass('d-none');
     if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png');
     if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png');
+  } else {
+    $('.main-logo').removeClass('d-none');
+    $('.main-logo-dark').addClass('d-none');
   }
   function toggleDarkMode(){
     if($('#dark-mode-theme').length){
       $('#dark-mode-theme').remove();
       $('#dark-mode-toggle').prop('checked', false);
+      $('.main-logo').removeClass('d-none');
+      $('.main-logo-dark').addClass('d-none');
       if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_dark.png');
       if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_dark.png');
       localStorage.setItem('theme', 'light');
     }else{
       $('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="/css/themes/mailcow-darkmode.css">');
       $('#dark-mode-toggle').prop('checked', true);
+      $('.main-logo').addClass('d-none');
+      $('.main-logo-dark').removeClass('d-none');
       if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png');
       if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png');
       localStorage.setItem('theme', 'dark');
diff --git a/mailcow/src/mailcow-dockerized/data/web/js/site/admin.js b/mailcow/src/mailcow-dockerized/data/web/js/site/admin.js
index d47a222..80da641 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/site/admin.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/site/admin.js
@@ -1,698 +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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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: ''

-          },

-          {

-            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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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"><span class="sorting-value">1</span></i>';
+            else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
+            else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-text-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: 'dt-sm-head-hidden dt-text-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"><span class="sorting-value">1</span></i>';
+            else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.active,
+          data: 'active',
+          defaultContent: '',
+          render: function (data, type) {
+            if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
+            else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          defaultContent: '',
+          className: 'dt-sm-head-hidden dt-text-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"><span class="sorting-value">yes</span></i>':'no'==data&&'<i class="bi bi-check-lg"><span class="sorting-value">no</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-text-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"><span class="sorting-value">1</span></i>';
+            else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-text-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"><span class="sorting-value">1</span></i>';
+            else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-text-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-lg 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-lg 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-lg 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" class="form-check-input" 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-lg 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-lg 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-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+          '</div>';
+        item.chkbox = '<input type="checkbox" class="form-check-input" data-id="transports" name="multi_select" value="' + item.id + '" />';
+      });
+    } else if (table == 'queuetable') {
+      $.each(data, function (i, item) {
+        item.chkbox = '<input type="checkbox" class="form-check-input" 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-xs-lg 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-xs-lg btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+          '</div>';
+        item.chkbox = '<input type="checkbox" class="form-check-input" 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-lg 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-lg 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" class="form-check-input" 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" class="form-check-input" 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-lg 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-lg 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-lg 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" class="form-check-input" 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-lg 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-lg 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/mailcow/src/mailcow-dockerized/data/web/js/site/debug.js b/mailcow/src/mailcow-dockerized/data/web/js/site/debug.js
index 85e6b78..10b1e8c 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/site/debug.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/site/debug.js
@@ -1,13 +1,3 @@
-const LOCALE = undefined;
-const DATETIME_FORMAT = {
-  year: "numeric",
-  month: "2-digit",
-  day: "2-digit",
-  hour: "2-digit",
-  minute: "2-digit",
-  second: "2-digit"
-};
-
 $(document).ready(function() {
   // Parse seconds ago to date
   // Get "now" timestamp
@@ -34,7 +24,7 @@
   });
 
   // set update loop container list
-  containersToUpdate = {}
+  containersToUpdate = {};
   // set default ChartJs Font Color
   Chart.defaults.color = '#999';
   // create host cpu and mem charts
@@ -43,15 +33,47 @@
   if (mailcow_info.branch === "master"){
     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")
+  $("#mailcow_version").click(function(){
+    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
-  get_public_ips();
+  $("#host_show_ip").click(function(){
+    $("#host_show_ip").find(".text").addClass("d-none");
+    $("#host_show_ip").find(".spinner-border").removeClass("d-none");
+
+    window.fetch("/api/v1/get/status/host/ip", { method:'GET', cache:'no-cache' }).then(function(response) {
+      return response.json();
+    }).then(function(data) {
+      console.log(data);
+
+      // display host ips
+      if (data.ipv4)
+        $("#host_ipv4").text(data.ipv4);
+      if (data.ipv6)
+        $("#host_ipv6").text(data.ipv6);
+
+      $("#host_show_ip").addClass("d-none");
+      $("#host_show_ip").find(".text").removeClass("d-none");
+      $("#host_show_ip").find(".spinner-border").addClass("d-none");
+      $("#host_ipv4").removeClass("d-none");
+      $("#host_ipv6").removeClass("d-none");
+      $("#host_ipv6").removeClass("text-danger");
+      $("#host_ipv4").addClass("d-block");
+      $("#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");
+      $("#host_ipv6").text(lang_debug.error_show_ip);
+      $("#host_show_ip").find(".text").removeClass("d-none");
+      $("#host_show_ip").find(".spinner-border").addClass("d-none");
+    });
+  });
   update_container_stats();
 });
 jQuery(function($){
@@ -85,11 +107,20 @@
       return;
     }
 
-    $('#autodiscover_log').DataTable({
+    var table = $('#autodiscover_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-autodiscover-logs', '#autodiscover_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/autodiscover/100",
@@ -134,6 +165,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-autodiscover-logs', '#autodiscover_log');
+    });
   }
   function draw_postfix_logs() {
     // just recalc width if instance already exists
@@ -142,11 +177,20 @@
       return;
     }
 
-    $('#postfix_log').DataTable({
+    var table = $('#postfix_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-postfix-logs', '#postfix_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/postfix",
@@ -176,6 +220,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-postfix-logs', '#postfix_log');
+    });
   }
   function draw_watchdog_logs() {
     // just recalc width if instance already exists
@@ -184,11 +232,20 @@
       return;
     }
 
-    $('#watchdog_log').DataTable({
+    var table = $('#watchdog_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-watchdog-logs', '#watchdog_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/watchdog",
@@ -222,6 +279,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-watchdog-logs', '#watchdog_log');
+    });
   }
   function draw_api_logs() {
     // just recalc width if instance already exists
@@ -230,11 +291,20 @@
       return;
     }
 
-    $('#api_log').DataTable({
+    var table =  $('#api_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/api",
@@ -275,6 +345,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
+    });
   }
   function draw_rl_logs() {
     // just recalc width if instance already exists
@@ -283,11 +357,20 @@
       return;
     }
 
-    $('#rl_log').DataTable({
+    var table = $('#rl_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/ratelimited",
@@ -366,6 +449,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
+    });
   }
   function draw_ui_logs() {
     // just recalc width if instance already exists
@@ -374,11 +461,20 @@
       return;
     }
 
-    $('#ui_logs').DataTable({
+    var table = $('#ui_logs').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_logs');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/ui",
@@ -437,6 +533,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log');
+    });
   }
   function draw_sasl_logs() {
     // just recalc width if instance already exists
@@ -445,11 +545,20 @@
       return;
     }
 
-    $('#sasl_logs').DataTable({
+    var table = $('#sasl_logs').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/sasl",
@@ -479,12 +588,16 @@
           data: 'datetime',
           defaultContent: '',
           createdCell: function(td, cellData) {
-            cellData = Math.floor((new Date(data.replace(/-/g, "/"))).getTime() / 1000);
+            cellData = Math.floor((new Date(cellData.replace(/-/g, "/"))).getTime() / 1000);
             createSortableDate(td, cellData)
           }
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
+    });
   }
   function draw_acme_logs() {
     // just recalc width if instance already exists
@@ -493,11 +606,20 @@
       return;
     }
 
-    $('#acme_log').DataTable({
+    var table = $('#acme_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/acme",
@@ -522,6 +644,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
+    });
   }
   function draw_netfilter_logs() {
     // just recalc width if instance already exists
@@ -530,11 +656,20 @@
       return;
     }
 
-    $('#netfilter_log').DataTable({
+    var table = $('#netfilter_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/netfilter",
@@ -564,6 +699,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
+    });
   }
   function draw_sogo_logs() {
     // just recalc width if instance already exists
@@ -572,11 +711,20 @@
       return;
     }
 
-    $('#sogo_log').DataTable({
+    var table = $('#sogo_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/sogo",
@@ -606,6 +754,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
+    });
   }
   function draw_dovecot_logs() {
     // just recalc width if instance already exists
@@ -614,11 +766,20 @@
       return;
     }
 
-    $('#dovecot_log').DataTable({
+    var table = $('#dovecot_log').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-dovecot-logs', '#dovecot_log');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/dovecot",
@@ -648,19 +809,20 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-dovecot-logs', '#dovecot_log');
+    });
   }
   function rspamd_pie_graph() {
     $.ajax({
       url: '/api/v1/get/rspamd/actions',
       async: true,
       success: function(data){
-        console.log(data);
-
         var total = 0;
         $(data).map(function(){total += this[1];});
         var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
         var values = $.makeArray($(data).map(function(){return this[1];}));
-        console.log(values);
 
         var graphdata = {
           labels: labels,
@@ -717,11 +879,20 @@
       return;
     }
 
-    $('#rspamd_history').DataTable({
+    var table = $('#rspamd_history').DataTable({
+      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>>",
       language: lang_datatables,
       order: [[0, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-rspamd-logs', '#rspamd_history');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/logs/rspamd-history",
@@ -767,12 +938,15 @@
           title: 'Score',
           data: 'score',
           defaultContent: '',
+          class: 'text-nowrap',
           createdCell: function(td, cellData) {
             $(td).attr({
               "data-order": cellData.sortBy,
               "data-sort": cellData.sortBy
             });
-            $(td).html(cellData.value);
+          },    
+          render: function (data) {
+            return data.value;
           }
         },
         {
@@ -795,7 +969,9 @@
               "data-order": cellData.sortBy,
               "data-sort": cellData.sortBy
             });
-            $(td).html(cellData.value);
+          },    
+          render: function (data) {
+            return data.value;
           }
         },
         {
@@ -810,6 +986,10 @@
         }
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history');
+    });
   }
   function process_table_data(data, table) {
     if (table == 'rspamd_history') {
@@ -821,31 +1001,31 @@
         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);
@@ -978,14 +1158,14 @@
         }
       });
     }
-    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;
@@ -993,7 +1173,7 @@
 
     if (table = $('#' + log_table).DataTable()) {
       var heading = $('#' + log_table).closest('.card').find('.card-header');
-      var load_rows = (table.page.len() + 1) + '-' + (table.page.len() + new_nrows)
+      var load_rows = (table.data().count() + 1) + '-' + (table.data().count() + new_nrows)
 
       $.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
         if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
@@ -1005,6 +1185,12 @@
       });
     }
   })
+  function hideTableExpandCollapseBtn(tab, table){
+    if ($(table).hasClass('collapsed'))
+      $(tab).find(".table_collapse_option").show();
+    else
+      $(tab).find(".table_collapse_option").hide();
+  }
 
   // detect element visibility changes
   function onVisible(element, callback) {
@@ -1037,7 +1223,6 @@
   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
@@ -1109,6 +1294,12 @@
       $("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
       $("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
       $("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
+      if (data.architecture == "aarch64"){
+        $("#host_architecture").html('<span data-bs-toggle="tooltip" data-bs-placement="top" title="' + lang_debug.wip +'">' + data.architecture + ' ⚠️</span>');
+      }
+      else {
+        $("#host_architecture").html(data.architecture);
+      }
 
       // update cpu and mem chart
       var cpu_chart = Chart.getChart("host_cpu_chart");
@@ -1120,9 +1311,9 @@
       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();
@@ -1224,20 +1415,6 @@
   // run again in n seconds
   setTimeout(update_container_stats, timeout * 1000);
 }
-// get public ips
-function get_public_ips(){
-  window.fetch("/api/v1/get/status/host/ip", {method:'GET',cache:'no-cache'}).then(function(response) {
-    return response.json();
-  }).then(function(data) {
-    console.log(data);
-
-    // display host ips
-    if (data.ipv4)
-      $("#host_ipv4").text(data.ipv4);
-    if (data.ipv6)
-      $("#host_ipv6").text(data.ipv6);
-  });
-}
 // format hosts uptime seconds to readable string
 function formatUptime(seconds){
   seconds = Number(seconds);
@@ -1295,23 +1472,23 @@
   };
   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
         }
       }
     }
@@ -1359,13 +1536,13 @@
   };
   var optionsCpu = {
     interaction: {
-        mode: 'index'
+      mode: 'index'
     },
     scales: {
       yAxis: {
         min: 0,
         grid: {
-            display: false
+          display: false
         },
         ticks: {
           callback: function(i, index, ticks) {
@@ -1375,7 +1552,7 @@
       },
       xAxis: {
         grid: {
-            display: false
+          display: false
         }
       }
     }
@@ -1397,13 +1574,13 @@
   };
   var optionsMem = {
     interaction: {
-        mode: 'index'
+      mode: 'index'
     },
     scales: {
       yAxis: {
         min: 0,
         grid: {
-            display: false
+          display: false
         },
         ticks: {
           callback: function(i, index, ticks) {
@@ -1413,7 +1590,7 @@
       },
       xAxis: {
         grid: {
-            display: false
+          display: false
         }
       }
     }
@@ -1509,22 +1686,22 @@
 
   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/mailcow/src/mailcow-dockerized/data/web/js/site/edit.js b/mailcow/src/mailcow-dockerized/data/web/js/site/edit.js
index 55a8e6b..cd938cd 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/site/edit.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/site/edit.js
@@ -1,210 +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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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" class="form-check-input" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" class="form-check-input" 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" class="form-check-input" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" class="form-check-input" 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/mailcow/src/mailcow-dockerized/data/web/js/site/mailbox.js b/mailcow/src/mailcow-dockerized/data/web/js/site/mailbox.js
index 12c4bb4..c2b1761 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/site/mailbox.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/site/mailbox.js
@@ -77,7 +77,7 @@
         $('.dns-modal-body').html(xhr.responseText);
       }
     });
-  }); 
+  });
   // @Open Domain add modal
   $('#addDomainModal').on('show.bs.modal', function(e) {
     $.ajax({
@@ -85,24 +85,24 @@
       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 @@
       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 @@
     } 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 @@
       $('#addDomain_relay_unknown_only').prop('checked', false);
     }
 
-    
+
     // load tags
     $('#addDomain_tags').val("");
     $($('#addDomain_tags').parent().find(".tag-values")[0]).val("");
@@ -404,7 +404,7 @@
     } else {
       $('#sogo_access').prop('checked', false);
     }
-    
+
     // load tags
     $('#addMailbox_tags').val("");
     $($('#addMailbox_tags').parent().find(".tag-values")[0]).val("");
@@ -417,11 +417,11 @@
   // 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,9 +433,18 @@
     }
 
     var table = $('#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,
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-domains', '#domain_table');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/domain/all",
@@ -457,16 +466,16 @@
 
             item.def_quota_for_mbox = humanFileSize(item.def_quota_for_mbox);
             item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
-            item.chkbox = '<input type="checkbox" data-id="domain" name="multi_select" value="' + encodeURIComponent(item.domain_name) + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="domain" name="multi_select" value="' + encodeURIComponent(item.domain_name) + '" />';
             item.action = '<div class="btn-group">';
             if (role == "admin") {
-              item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-                '<a href="#" data-action="delete_selected" data-id="single-domain" data-api-url="delete/domain" data-item="' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-                  '<a href="#dnsInfoModal" class="btn btn-sm btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>';
+              item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+                '<a href="#" data-action="delete_selected" data-id="single-domain" data-api-url="delete/domain" data-item="' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+                  '<a href="#dnsInfoModal" class="btn btn-sm btn-xs-lg btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>';
             }
             else {
-              item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#dnsInfoModal" class="btn btn-xs btn-xs-half btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>';
+              item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#dnsInfoModal" class="btn btn-sm btn-xs-lg btn-xs-half btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>';
             }
 
             if (Array.isArray(item.tags)){
@@ -598,18 +607,22 @@
           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>':2==data&&'&#8212;');
+            return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'&#8212;');
           }
         },
         {
           title: lang.action,
           data: 'action',
-          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right',
           responsivePriority: 5,
           defaultContent: ''
         },
       ]
-    });  
+    });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-domains', '#domain_table');
+    });
   }
   function draw_templates_domain_table() {
     // just recalc width if instance already exists
@@ -618,17 +631,26 @@
       return;
     }
 
-    $('#templates_domain_table').DataTable({
-			responsive : true,
+    var table = $('#templates_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,
+      order: [[2, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-templates-domains', '#templates_domain_table');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/domain/template/all",
         dataSrc: function(json){
           $.each(json, function (i, item) {
-            item.chkbox = '<input type="checkbox" data-id="domain_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="domain_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
 
             item.attributes.def_quota_for_mbox = humanFileSize(item.attributes.def_quota_for_mbox);
             item.attributes.max_quota_for_mbox = humanFileSize(item.attributes.max_quota_for_mbox);
@@ -646,495 +668,18 @@
             }
             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>' +
+              '<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg 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/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>' +
-              '</div>';
-            }
-
-            if (Array.isArray(item.attributes.tags)){
-              var tags = '';
-              for (var i = 0; i < item.attributes.tags.length; i++)
-                tags += '<span class="badge bg-primary tag-badge"><i class="bi bi-tag-fill"></i> ' + escapeHtml(item.attributes.tags[i]) + '</span>';
-              item.attributes.tags = tags;
-            } else {
-              item.attributes.tags = '';
-            }
-          });
-
-          return json;
-        }
-      },
-      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: ''
-          },
-      ]
-    });
-  }
-  function draw_mailbox_table() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#mailbox_table') ) {
-      $('#mailbox_table').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#mailbox_table').DataTable({
-			responsive : true,
-      processing: true,
-      serverSide: false,
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/mailbox/reduced",
-        dataSrc: function(json){
-          $.each(json, function (i, item) {
-            item.quota = item.quota_used + "/" + item.quota;
-            item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
-            item.last_mail_login = item.last_imap_login + '/' + item.last_pop3_login + '/' + item.last_smtp_login;
-            /*
-            if (!item.rl) {
-              item.rl = '∞';
-            } else {
-              item.rl = $.map(item.rl, function(e){
-                return e;
-              }).join('/1');
-              if (item.rl_scope === 'domain') {
-                item.rl = '<i class="bi bi-arrow-return-right"></i> ' + item.rl + ' (via ' + item.domain + ')';
-              }
-            }
-            */
-            item.chkbox = '<input type="checkbox" data-id="mailbox" name="multi_select" value="' + encodeURIComponent(item.username) + '" />';
-            if (item.attributes.passwd_update != '0') {
-              var last_pw_change = new Date(item.attributes.passwd_update.replace(/-/g, "/"));
-              item.last_pw_change = last_pw_change.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
-            } else {
-              item.last_pw_change = '-';
-            }
-            item.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>';
-            item.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>';
-            item.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            item.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            item.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            item.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            if (item.attributes.quarantine_notification === 'never') {
-              item.quarantine_notification = lang.never;
-            } else if (item.attributes.quarantine_notification === 'hourly') {
-              item.quarantine_notification = lang.hourly;
-            } else if (item.attributes.quarantine_notification === 'daily') {
-              item.quarantine_notification = lang.daily;
-            } else if (item.attributes.quarantine_notification === 'weekly') {
-              item.quarantine_notification = lang.weekly;
-            }
-            if (item.attributes.quarantine_category === 'reject') {
-              item.quarantine_category = '<span class="text-danger">' + lang.q_reject + '</span>';
-            } else if (item.attributes.quarantine_category === 'add_header') {
-              item.quarantine_category = '<span class="text-warning">' + lang.q_add_header + '</span>';
-            } else if (item.attributes.quarantine_category === 'all') {
-              item.quarantine_category = lang.q_all;
-            }
-            if (acl_data.login_as === 1) {
-
-              item.action = '<div class="btn-group">' +
-              '<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="single-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-              '<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="login_as btn btn-sm btn-xs-half btn-success"><i class="bi bi-person-fill"></i> Login</a>';
-              if (ALLOW_ADMIN_EMAIL_LOGIN) {
-                item.action += '<a href="/sogo-auth.php?login=' + encodeURIComponent(item.username) + '" class="login_as btn btn-sm btn-xs-half btn-primary" target="_blank"><i class="bi bi-envelope-fill"></i> SOGo</a>';
-              }
-              item.action += '</div>';
-            }
             else {
-            item.action = '<div class="btn-group">' +
-              '<a href="/edit/mailbox/' + encodeURIComponent(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-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              item.action = '<div class="btn-group">' +
+              '<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg 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-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
               '</div>';
             }
-            item.in_use = '<div class="progress">' +
-              '<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++)
-                tags += '<span class="badge bg-primary tag-badge"><i class="bi bi-tag-fill"></i> ' + escapeHtml(item.tags[i]) + '</span>';
-              item.tags = tags;
-            } else {
-              item.tags = '';
-            }
-          });
-
-          return json;
-        }
-      },
-      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',
-            responsivePriority: 8,
-            defaultContent: '',
-            render: function (data, type) {
-              data = data.split("/");
-              var of_q = (data[1] == 0 ? "∞" : humanFileSize(data[1]));
-              return humanFileSize(data[0]) + " / " + of_q;
-            }
-          },
-          {
-            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: ''
-          },
-      ]
-    });
-  }
-  function draw_templates_mbox_table() {
-    // just recalc width if instance already exists
-    if ($.fn.DataTable.isDataTable('#templates_mbox_table') ) {
-      $('#templates_mbox_table').DataTable().columns.adjust().responsive.recalc();
-      return;
-    }
-
-    $('#templates_mbox_table').DataTable({
-			responsive : true,
-      processing: true,
-      serverSide: false,
-      language: lang_datatables,
-      ajax: {
-        type: "GET",
-        url: "/api/v1/get/mailbox/template/all",
-        dataSrc: function(json){
-          $.each(json, function (i, item) {
-            item.chkbox = '<input type="checkbox" data-id="mailbox_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
-
-            item.template = escapeHtml(item.template);
-            if (item.attributes.rl_frame === "s"){
-              item.attributes.rl_frame = lang_rl.second;
-            } else if (item.attributes.rl_frame === "m"){
-              item.attributes.rl_frame = lang_rl.minute;
-            } else if (item.attributes.rl_frame === "h"){
-              item.attributes.rl_frame = lang_rl.hour;
-            } else if (item.attributes.rl_frame === "d"){
-              item.attributes.rl_frame = lang_rl.day;
-            }
-            item.attributes.rl_value = escapeHtml(item.attributes.rl_value);
-
-            item.attributes.quota = humanFileSize(item.attributes.quota);
-
-            item.attributes.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>';
-            item.attributes.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>';
-            item.attributes.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            item.attributes.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            item.attributes.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            item.attributes.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
-            if (item.attributes.quarantine_notification === 'never') {
-              item.attributes.quarantine_notification = lang.never;
-            } else if (item.attributes.quarantine_notification === 'hourly') {
-              item.attributes.quarantine_notification = lang.hourly;
-            } else if (item.attributes.quarantine_notification === 'daily') {
-              item.attributes.quarantine_notification = lang.daily;
-            } else if (item.attributes.quarantine_notification === 'weekly') {
-              item.attributes.quarantine_notification = lang.weekly;
-            }
-            if (item.attributes.quarantine_category === 'reject') {
-              item.attributes.quarantine_category = '<span class="text-danger">' + lang.q_reject + '</span>';
-            } else if (item.attributes.quarantine_category === 'add_header') {
-              item.attributes.quarantine_category = '<span class="text-warning">' + lang.q_add_header + '</span>';
-            } else if (item.attributes.quarantine_category === 'all') {
-              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>';
-            }
-            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>';              
-            }
 
             if (Array.isArray(item.attributes.tags)){
               var tags = '';
@@ -1178,12 +723,532 @@
           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"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.backup_mx,
+          data: 'attributes.backupmx',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.relay_all,
+          data: 'attributes.relay_all_recipients',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.relay_unknown,
+          data: 'attributes.relay_unknown_only',
+          defaultContent: '',
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.active,
+          data: 'attributes.active',
+          defaultContent: '',
+          responsivePriority: 4,
+          render: function (data, type) {
+            return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          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 dt-text-right',
+          responsivePriority: 6,
+          defaultContent: ''
+        },
+      ]
+    });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-templates-domains', '#templates_domain_table');
+    });
+  }
+  function draw_mailbox_table() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#mailbox_table') ) {
+      $('#mailbox_table').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    var table = $('#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,
+      initComplete: function(settings, json){
+        hideTableExpandCollapseBtn('#tab-mailboxes', '#mailbox_table');
+        filterByDomain(json, 8, table);
+      },
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/mailbox/reduced",
+        dataSrc: function(json){
+          $.each(json, function (i, item) {
+            item.quota = {
+              sortBy: item.quota_used,
+              value: item.quota
+            }
+            item.quota.value = (item.quota.value == 0 ? "∞" : humanFileSize(item.quota.value));
+            item.quota.value = humanFileSize(item.quota_used) + "/" + item.quota.value;
+
+            item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
+            item.last_mail_login = item.last_imap_login + '/' + item.last_pop3_login + '/' + item.last_smtp_login;
+            /*
+            if (!item.rl) {
+              item.rl = '∞';
+            } else {
+              item.rl = $.map(item.rl, function(e){
+                return e;
+              }).join('/1');
+              if (item.rl_scope === 'domain') {
+                item.rl = '<i class="bi bi-arrow-return-right"></i> ' + item.rl + ' (via ' + item.domain + ')';
+              }
+            }
+            */
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailbox" name="multi_select" value="' + encodeURIComponent(item.username) + '" />';
+            if (item.attributes.passwd_update != '0') {
+              var last_pw_change = new Date(item.attributes.passwd_update.replace(/-/g, "/"));
+              item.last_pw_change = last_pw_change.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+            } else {
+              item.last_pw_change = '-';
+            }
+            item.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>';
+            item.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>';
+            item.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
+            item.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
+            item.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
+            item.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
+            if (item.attributes.quarantine_notification === 'never') {
+              item.quarantine_notification = lang.never;
+            } else if (item.attributes.quarantine_notification === 'hourly') {
+              item.quarantine_notification = lang.hourly;
+            } else if (item.attributes.quarantine_notification === 'daily') {
+              item.quarantine_notification = lang.daily;
+            } else if (item.attributes.quarantine_notification === 'weekly') {
+              item.quarantine_notification = lang.weekly;
+            }
+            if (item.attributes.quarantine_category === 'reject') {
+              item.quarantine_category = '<span class="text-danger">' + lang.q_reject + '</span>';
+            } else if (item.attributes.quarantine_category === 'add_header') {
+              item.quarantine_category = '<span class="text-warning">' + lang.q_add_header + '</span>';
+            } else if (item.attributes.quarantine_category === 'all') {
+              item.quarantine_category = lang.q_all;
+            }
+            if (acl_data.login_as === 1) {
+
+              item.action = '<div class="btn-group">' +
+              '<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="login_as btn btn-sm btn-xs-lg btn-xs-half btn-success"><i class="bi bi-person-fill"></i> Login</a>';
+              if (ALLOW_ADMIN_EMAIL_LOGIN) {
+                item.action += '<a href="/sogo-auth.php?login=' + encodeURIComponent(item.username) + '" class="login_as btn btn-sm btn-xs-lg btn-xs-half btn-primary" target="_blank"><i class="bi bi-envelope-fill"></i> SOGo</a>';
+              }
+              item.action += '</div>';
+            }
+            else {
+            item.action = '<div class="btn-group">' +
+              '<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '</div>';
+            }
+            item.in_use = {
+              sortBy: item.percent_in_use,
+              value: '<div class="progress">' +
+              '<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++)
+                tags += '<span class="badge bg-primary tag-badge"><i class="bi bi-tag-fill"></i> ' + escapeHtml(item.tags[i]) + '</span>';
+              item.tags = tags;
+            } else {
+              item.tags = '';
+            }
+          });
+
+          return json;
+        }
+      },
+      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.value',
+          defaultContent: '',
+          responsivePriority: 9,
+          className: 'dt-data-w100',
+          orderData: 24
+        },
+        {
+          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"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'&#8212;');
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right',
+          responsivePriority: 6,
+          defaultContent: ''
+        },
+        {
+          title: "",
+          data: 'quota.sortBy',
+          defaultContent: '',
+          className: "d-none"
+        },
+        {
+          title: "",
+          data: 'in_use.sortBy',
+          defaultContent: '',
+          className: "d-none"
+        },
+      ]
+    });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-mailboxes', '#mailbox_table');
+    });
+  }
+  function draw_templates_mbox_table() {
+    // just recalc width if instance already exists
+    if ($.fn.DataTable.isDataTable('#templates_mbox_table') ) {
+      $('#templates_mbox_table').DataTable().columns.adjust().responsive.recalc();
+      return;
+    }
+
+    var table = $('#templates_mbox_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,
+      order: [[2, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-templates-mbox', '#templates_mbox_table');
+      },
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/mailbox/template/all",
+        dataSrc: function(json){
+          $.each(json, function (i, item) {
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailbox_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
+
+            item.template = escapeHtml(item.template);
+            if (item.attributes.rl_frame === "s"){
+              item.attributes.rl_frame = lang_rl.second;
+            } else if (item.attributes.rl_frame === "m"){
+              item.attributes.rl_frame = lang_rl.minute;
+            } else if (item.attributes.rl_frame === "h"){
+              item.attributes.rl_frame = lang_rl.hour;
+            } else if (item.attributes.rl_frame === "d"){
+              item.attributes.rl_frame = lang_rl.day;
+            }
+            item.attributes.rl_value = escapeHtml(item.attributes.rl_value);
+
+            item.attributes.quota = humanFileSize(item.attributes.quota);
+
+            item.attributes.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_in == 1 ? '1' : '0') + '</span></i>';
+            item.attributes.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_out == 1 ? '1' : '0') + '</span></i>';
+            item.attributes.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.pop3_access == 1 ? '1' : '0') + '</span></i>';
+            item.attributes.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.imap_access == 1 ? '1' : '0') + '</span></i>';
+            item.attributes.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.smtp_access == 1 ? '1' : '0') + '</span></i>';
+            item.attributes.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sieve_access == 1 ? '1' : '0') + '</span></i>';
+            item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sogo_access == 1 ? '1' : '0') + '</span></i>';
+            if (item.attributes.quarantine_notification === 'never') {
+              item.attributes.quarantine_notification = lang.never;
+            } else if (item.attributes.quarantine_notification === 'hourly') {
+              item.attributes.quarantine_notification = lang.hourly;
+            } else if (item.attributes.quarantine_notification === 'daily') {
+              item.attributes.quarantine_notification = lang.daily;
+            } else if (item.attributes.quarantine_notification === 'weekly') {
+              item.attributes.quarantine_notification = lang.weekly;
+            }
+            if (item.attributes.quarantine_category === 'reject') {
+              item.attributes.quarantine_category = '<span class="text-danger">' + lang.q_reject + '</span>';
+            } else if (item.attributes.quarantine_category === 'add_header') {
+              item.attributes.quarantine_category = '<span class="text-warning">' + lang.q_add_header + '</span>';
+            } else if (item.attributes.quarantine_category === 'all') {
+              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-sm btn-xs-lg 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-sm btn-xs-lg 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-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+                '</div>';
+            }
+
+            if (Array.isArray(item.attributes.tags)){
+              var tags = '';
+              for (var i = 0; i < item.attributes.tags.length; i++)
+                tags += '<span class="badge bg-primary tag-badge"><i class="bi bi-tag-fill"></i> ' + escapeHtml(item.attributes.tags[i]) + '</span>';
+              item.attributes.tags = tags;
+            } else {
+              item.attributes.tags = '';
+            }
+          });
+
+          return json;
+        }
+      },
+      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.domain_quota,
           data: 'attributes.quota',
           defaultContent: '',
-        },             
+        },
         {
           title: lang.tls_enforce_in,
           data: 'attributes.tls_enforce_in',
@@ -1230,7 +1295,7 @@
           data: 'attributes.quarantine_category',
           defaultContent: '',
           className: 'none'
-        },            
+        },
         {
           title: lang.force_pw_update,
           data: 'attributes.force_pw_update',
@@ -1239,43 +1304,47 @@
           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',
           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;');
+            return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'&#8212;');
           }
-        },     
+        },
         {
           title: lang.action,
           data: 'action',
-          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right',
           responsivePriority: 6,
           defaultContent: ''
         },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-templates-mbox', '#templates_mbox_table');
+    });
   }
   function draw_resource_table() {
     // just recalc width if instance already exists
@@ -1284,10 +1353,20 @@
       return;
     }
 
-    $('#resource_table').DataTable({
+    var table = $('#resource_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,
+      initComplete: function(settings, json){
+        hideTableExpandCollapseBtn('#tab-resources', '#resource_table');
+        filterByDomain(json, 5, table);
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/resource/all",
@@ -1301,10 +1380,10 @@
               item.multiple_bookings = '<span id="active-script" class="badge fs-6 bg-danger">' + lang.booking_custom_short + ' (' + item.multiple_bookings + ')</span>';
             }
             item.action = '<div class="btn-group">' +
-              '<a href="/edit/resource/' + encodeURIComponent(item.name) + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="single-resource" data-api-url="delete/resource" data-item="' + item.name + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="/edit/resource/' + encodeURIComponent(item.name) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-resource" data-api-url="delete/resource" data-item="' + item.name + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
               '</div>';
-            item.chkbox = '<input type="checkbox" data-id="resource" name="multi_select" value="' + encodeURIComponent(item.name) + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="resource" name="multi_select" value="' + encodeURIComponent(item.name) + '" />';
             item.name = escapeHtml(item.name);
             item.description = escapeHtml(item.description);
           });
@@ -1313,94 +1392,105 @@
         }
       },
       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"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'&#8212;');
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          responsivePriority: 5,
+          defaultContent: '',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right'
+        },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-resources', '#resource_table');
+    });
   }
   function draw_bcc_table() {
     $.get("/api/v1/get/bcc-destination-options", function(data){
+      var optgroup = "";
       // Domains
-      var optgroup = "<optgroup label='" + lang.domains + "'>";
-      $.each(data.domains, function(index, domain){
-        optgroup += "<option value='" + domain + "'>" + domain + "</option>"
-      });
-      optgroup += "</optgroup>"
-      $('#bcc-local-dest').append(optgroup);
+      if (data.domains && data.domains.length > 0) {
+        optgroup = "<optgroup label='" + lang.domains + "'>";
+        $.each(data.domains, function(index, domain){
+          optgroup += "<option value='" + domain + "'>" + domain + "</option>";
+        });
+        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 += "</optgroup>"
-      $('#bcc-local-dest').append(optgroup);
-      // Mailboxes and aliases
-      $.each(data.mailboxes, function(mailbox, aliases){
-        var optgroup = "<optgroup label='" + mailbox + "'>";
-        $.each(aliases, function(index, alias){
-          optgroup += "<option value='" + alias + "'>" + alias + "</option>"
+      if (data.alias_domains && data.alias_domains.length > 0) {
+        optgroup = "<optgroup label='" + lang.domain_aliases + "'>";
+        $.each(data.alias_domains, function(index, alias_domain){
+          optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>";
         });
         optgroup += "</optgroup>"
         $('#bcc-local-dest').append(optgroup);
-      });
-      // Finish
+      }
+      // Mailboxes and aliases
+      if (data.mailboxes && Object.keys(data.mailboxes).length > 0) {
+        $.each(data.mailboxes, function(mailbox, aliases){
+          optgroup = "<optgroup label='" + mailbox + "'>";
+          $.each(aliases, function(index, alias){
+            optgroup += "<option value='" + alias + "'>" + alias + "</option>";
+          });
+          optgroup += "</optgroup>";
+          $('#bcc-local-dest').append(optgroup);
+        });
+      }
+      // Recreate picker
       $('#bcc-local-dest').selectpicker('refresh');
     });
 
@@ -1409,21 +1499,32 @@
       $('#bcc_table').DataTable().columns.adjust().responsive.recalc();
       return;
     }
-    
-    $('#bcc_table').DataTable({
+
+    var table = $('#bcc_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,
+      order: [[2, 'desc']],
+      initComplete: function(settings, json){
+        hideTableExpandCollapseBtn('#collapse-tab-bcc', '#bcc_table');
+        filterByDomain(json, 6, table);
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/bcc/all",
         dataSrc: function(json){
           $.each(json, function (i, item) {
             item.action = '<div class="btn-group">' +
-              '<a href="/edit/bcc/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="single-bcc" data-api-url="delete/bcc" data-item="' + item.id + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="/edit/bcc/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-bcc" data-api-url="delete/bcc" data-item="' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
               '</div>';
-            item.chkbox = '<input type="checkbox" data-id="bcc" name="multi_select" value="' + item.id + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="bcc" name="multi_select" value="' + item.id + '" />';
             item.local_dest = escapeHtml(item.local_dest);
             item.bcc_dest = escapeHtml(item.bcc_dest);
             if (item.type == 'sender') {
@@ -1437,67 +1538,71 @@
         }
       },
       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"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'&#8212;');
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#collapse-tab-bcc', '#bcc_table');
+    });
   }
   function draw_recipient_map_table() {
     // just recalc width if instance already exists
@@ -1506,81 +1611,95 @@
       return;
     }
 
-    $('#recipient_map_table').DataTable({
+    var table = $('#recipient_map_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,
+      order: [[2, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#collapse-tab-bcc-filters', '#recipient_map_table');
+      },
       ajax: {
         type: "GET",
         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);
             item.action = '<div class="btn-group">' +
-              '<a href="/edit/recipient_map/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="single-recipient_map" data-api-url="delete/recipient_map" data-item="' + item.id + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="/edit/recipient_map/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-recipient_map" data-api-url="delete/recipient_map" data-item="' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
               '</div>';
-            item.chkbox = '<input type="checkbox" data-id="recipient_map" name="multi_select" value="' + item.id + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="recipient_map" name="multi_select" value="' + item.id + '" />';
           });
 
           return json;
         }
       },
       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"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#collapse-tab-bcc-filters', '#recipient_map_table');
+    });
   }
   function draw_tls_policy_table() {
     // just recalc width if instance already exists
@@ -1589,16 +1708,26 @@
       return;
     }
 
-    $('#tls_policy_table').DataTable({
+    var table = $('#tls_policy_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,
+      order: [[2, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-tls-policy', '#tls_policy_table');
+      },
       ajax: {
         type: "GET",
         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>';
@@ -1608,72 +1737,76 @@
               item.parameters = '<code>' + escapeHtml(item.parameters) + '</code>';
             }
             item.action = '<div class="btn-group">' +
-              '<a href="/edit/tls_policy_map/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="single-tls-policy-map" data-api-url="delete/tls-policy-map" data-item="' + item.id + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="/edit/tls_policy_map/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-tls-policy-map" data-api-url="delete/tls-policy-map" data-item="' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
               '</div>';
-            item.chkbox = '<input type="checkbox" data-id="tls-policy-map" name="multi_select" value="' + item.id + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="tls-policy-map" name="multi_select" value="' + item.id + '" />';
           });
 
           return json;
         }
       },
       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"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-tls-policy', '#tls_policy_table');
+    });
   }
   function draw_alias_table() {
     // just recalc width if instance already exists
@@ -1682,20 +1815,31 @@
       return;
     }
 
-    $('#alias_table').DataTable({
+    var table = $('#alias_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,
+      order: [[2, 'desc']],
+      initComplete: function(settings, json){
+        hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table');
+        filterByDomain(json, 5, table);
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/alias/all",
         dataSrc: function(json){
           $.each(json, function (i, item) {
             item.action = '<div class="btn-group">' +
-              '<a href="/edit/alias/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="single-alias" data-api-url="delete/alias" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="/edit/alias/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-alias" data-api-url="delete/alias" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
               '</div>';
-            item.chkbox = '<input type="checkbox" data-id="alias" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="alias" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
             item.goto = escapeHtml(item.goto.replace(/,/g, " "));
             if (item.public_comment !== null) {
               item.public_comment = escapeHtml(item.public_comment);
@@ -1733,87 +1877,95 @@
         }
       },
       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"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></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"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table');
+    });
+
+    table.on( 'draw', function (){
+        $('#alias_table [data-bs-toggle="tooltip"]').tooltip();
+    });
   }
   function draw_aliasdomain_table() {
     // just recalc width if instance already exists
@@ -1822,10 +1974,19 @@
       return;
     }
 
-    $('#aliasdomain_table').DataTable({
+    var table = $('#aliasdomain_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,
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-domain-aliases', '#aliasdomain_table');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/alias-domain/all",
@@ -1834,11 +1995,11 @@
             item.alias_domain = escapeHtml(item.alias_domain);
 
             item.action = '<div class="btn-group">' +
-              '<a href="/edit/aliasdomain/' + encodeURIComponent(item.alias_domain) + '" class="btn btn-sm btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="single-alias-domain" data-api-url="delete/alias-domain" data-item="' + encodeURIComponent(item.alias_domain) + '" class="btn btn-sm btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
-              '<a href="#dnsInfoModal" class="btn btn-sm btn-xs-third btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.alias_domain) + '"><i class="bi bi-globe2"></i> DNS</a></div>' +
+              '<a href="/edit/aliasdomain/' + encodeURIComponent(item.alias_domain) + '" class="btn btn-sm btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-alias-domain" data-api-url="delete/alias-domain" data-item="' + encodeURIComponent(item.alias_domain) + '" class="btn btn-sm btn-xs-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="#dnsInfoModal" class="btn btn-sm btn-xs-lg btn-xs-third btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.alias_domain) + '"><i class="bi bi-globe2"></i> DNS</a></div>' +
               '</div>';
-            item.chkbox = '<input type="checkbox" data-id="alias-domain" name="multi_select" value="' + encodeURIComponent(item.alias_domain) + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="alias-domain" name="multi_select" value="' + encodeURIComponent(item.alias_domain) + '" />';
             if(item.parent_is_backupmx == '1') {
               item.target_domain = '<span><a href="/edit/domain/' + item.target_domain + '">' + item.target_domain + '</a> <div class="badge fs-6 bg-warning">' + lang.alias_domain_backupmx + '</div></span>';
             } else {
@@ -1850,52 +2011,56 @@
         }
       },
       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"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-data-w100 dtr-col-md dt-text-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-domain-aliases', '#aliasdomain_table');
+    });
   }
   function draw_sync_job_table() {
     // just recalc width if instance already exists
@@ -1904,10 +2069,20 @@
       return;
     }
 
-    $('#sync_job_table').DataTable({
+    var table = $('#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,
+      order: [[2, 'desc']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-syncjobs', '#sync_job_table');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/syncjobs/all/no_log",
@@ -1922,10 +2097,10 @@
             }
             item.server_w_port = escapeHtml(item.user1) + '@' + escapeHtml(item.host1) + ':' + escapeHtml(item.port1);
             item.action = '<div class="btn-group">' +
-              '<a href="/edit/syncjob/' + item.id + '" class="btn btn-sm 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-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="/edit/syncjob/' + item.id + '" class="btn btn-sm btn-xs-lg 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-sm btn-xs-lg 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 + '" />';
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="syncjob" name="multi_select" value="' + item.id + '" />';
             if (item.is_running == 1) {
               item.is_running = '<span id="active-script" class="badge fs-6 bg-success">' + lang.running + '</span>';
             } else {
@@ -1941,9 +2116,9 @@
               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;
           });
@@ -1952,89 +2127,93 @@
         }
       },
       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"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></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-text-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-syncjobs', '#sync_job_table');
+    });
   }
   function draw_filter_table() {
     // just recalc width if instance already exists
@@ -2044,10 +2223,20 @@
     }
 
     var table = $('#filter_table').DataTable({
+      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']],
+      initComplete: function(){
+        hideTableExpandCollapseBtn('#tab-filters', '#filter_table');
+      },
       ajax: {
         type: "GET",
         url: "/api/v1/get/filters/all",
@@ -2062,91 +2251,139 @@
             item.script_data = '<pre class="text-break" style="margin:0px">' + escapeHtml(item.script_data) + '</pre>'
             item.filter_type = '<div class="badge fs-6 bg-secondary">' + item.filter_type.charAt(0).toUpperCase() + item.filter_type.slice(1).toLowerCase() + '</div>'
             item.action = '<div class="btn-group">' +
-              '<a href="/edit/filter/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
-              '<a href="#" data-action="delete_selected" data-id="single-filter" data-api-url="delete/filter" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
+              '<a href="/edit/filter/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
+              '<a href="#" data-action="delete_selected" data-id="single-filter" data-api-url="delete/filter" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
               '</div>';
-            item.chkbox = '<input type="checkbox" data-id="filter_item" name="multi_select" value="' + item.id + '" />'
+            item.chkbox = '<input type="checkbox" class="form-check-input" data-id="filter_item" name="multi_select" value="' + item.id + '" />'
           });
 
           return json;
         }
       },
       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-text-right',
+          responsivePriority: 5,
+          defaultContent: ''
+        },
       ]
     });
+
+    table.on('responsive-resize', function (e, datatable, columns){
+      hideTableExpandCollapseBtn('#tab-filters', '#filter_table');
+    });
   };
 
+  function hideTableExpandCollapseBtn(tab, table){
+    if ($(table).hasClass('collapsed'))
+      $(tab).find(".table_collapse_option").show();
+    else
+      $(tab).find(".table_collapse_option").hide();
+  }
+  
+  function filterByDomain(json, column, table){
+    var tableId = $(table.table().container()).attr('id');
+    // Create the `select` element
+    var select = $('<select class="btn btn-sm btn-xs-lg btn-light text-start mx-2"><option value="">'+lang.all_domains+'</option></select>')
+      .insertBefore(
+        $('#'+tableId+' .dataTables_filter > label > input')
+      )
+      .on( 'change', function(){
+        table.column(column)
+          .search($(this).val())
+          .draw();
+      });
+
+    // get all domains
+    var domains = [];
+    json.forEach(obj => {
+      Object.entries(obj).forEach(([key, value]) => {
+        if(key === 'domain') {
+          domains.push(value)
+        }
+      });
+    });
+    
+    // get unique domain list
+    domains = domains.filter(function(value, index, array) {
+      return array.indexOf(value) === index;
+    });
+    
+    // add domains to select
+    domains.forEach(function(domain) {
+        select.append($('<option>' + domain + '</option>'));
+    });
+  }
+
   // detect element visibility changes
   function onVisible(element, callback) {
     $(document).ready(function() {
-      element_object = document.querySelector(element);
+      let element_object = document.querySelector(element);
       if (element_object === null) return;
 
-      new IntersectionObserver((entries, observer) => {
+      let observer = new IntersectionObserver((entries, observer) => {
         entries.forEach(entry => {
           if(entry.intersectionRatio > 0) {
             callback(element_object);
+            observer.unobserve(element_object);
           }
         });
-      }).observe(element_object);
+      })
+
+      observer.observe(element_object);
     });
   }
 
diff --git a/mailcow/src/mailcow-dockerized/data/web/js/site/qhandler.js b/mailcow/src/mailcow-dockerized/data/web/js/site/qhandler.js
index 4fcc963..8a8471f 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/site/qhandler.js
+++ b/mailcow/src/mailcow-dockerized/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/mailcow/src/mailcow-dockerized/data/web/js/site/quarantine.js b/mailcow/src/mailcow-dockerized/data/web/js/site/quarantine.js
index 34531b0..8d7f222 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/site/quarantine.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/site/quarantine.js
@@ -1,260 +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() {

-    $('#quarantinetable').DataTable({

-      processing: true,

-      serverSide: false,

-      language: lang_datatables,

-      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: '',

-            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.action,

-            data: 'action',

-            className: 'text-md-end dt-sm-head-hidden dt-body-right',

-            defaultContent: ''

-          },

-      ]

-    });

-  }

-

-  $('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();

-});

+// 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" class="form-check-input" 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: 'dt-text-right dt-sm-head-hidden',
+          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/mailcow/src/mailcow-dockerized/data/web/js/site/queue.js b/mailcow/src/mailcow-dockerized/data/web/js/site/queue.js
index 057ad84..26c1509 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/site/queue.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/site/queue.js
@@ -1,123 +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) {
-            console.log(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({
-      processing: true,
-      serverSide: false,
-      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.queue_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" class="form-check-input" 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: 'dt-sm-head-hidden dt-text-right',
+          defaultContent: ''
+        },
       ]
     });
   }
 
   draw_queue();
 
-})
\ No newline at end of file
+})
diff --git a/mailcow/src/mailcow-dockerized/data/web/js/site/user.js b/mailcow/src/mailcow-dockerized/data/web/js/site/user.js
index 36bcfa6..088321c 100644
--- a/mailcow/src/mailcow-dockerized/data/web/js/site/user.js
+++ b/mailcow/src/mailcow-dockerized/data/web/js/site/user.js
@@ -1,637 +1,680 @@
-// 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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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({

-      processing: true,

-      serverSide: false,

-      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 createSortableDate(td, cellData, date_string = false) {
+    if (date_string)
+      var date = new Date(cellData);
+    else
+      var date = new Date(cellData ? cellData * 1000 : 0);
+
+    var timestamp = date.getTime();
+    $(td).attr({
+      "data-order": timestamp,
+      "data-sort": timestamp
+    });
+    $(td).html(date.toLocaleDateString(LOCALE, DATETIME_FORMAT));
+  }
+  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,
+      order: [[4, 'desc']],
+      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" class="form-check-input" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />';
+              item.address = escapeHtml(item.address);
+            }
+            else {
+              item.chkbox = '<input type="checkbox" class="form-check-input" 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: '',
+          createdCell: function(td, cellData) {
+            createSortableDate(td, cellData)
+          }
+        },
+        {
+          title: lang.created_on,
+          data: 'created',
+          defaultContent: '',
+          createdCell: function(td, cellData) {
+            createSortableDate(td, cellData, true)
+          }
+        },
+        {
+          title: lang.action,
+          data: 'action',
+          className: 'dt-sm-head-hidden dt-text-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" class="form-check-input" data-id="syncjob" name="multi_select" value="' + item.id + '" />';
+            }
+            else {
+              item.action = '<span>-</span>';
+              item.chkbox = '<input type="checkbox" class="form-check-input" 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: 'dt-sm-head-hidden dt-text-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" class="form-check-input" data-id="apppasswd" name="multi_select" value="' + item.id + '" />';
+            }
+            else {
+              item.action = '<span>-</span>';
+              item.chkbox = '<input type="checkbox" class="form-check-input" 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: 'dt-sm-head-hidden dt-text-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" class="form-check-input" data-id="policy_wl_mailbox" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" class="form-check-input" disabled title="' + lang.spamfilter_table_domain_policy + '" />';
+            }
+            if (acl_data.spam_policy === 0) {
+              item.chkbox = '<input type="checkbox" class="form-check-input" 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" class="form-check-input" data-id="policy_bl_mailbox" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" class="form-check-input" disabled tooltip="' + lang.spamfilter_table_domain_policy + '" />';
+            }
+            if (acl_data.spam_policy === 0) {
+              item.chkbox = '<input type="checkbox" class="form-check-input" 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');
+});