Browse Source

[BS5] Replace FooTable with jquery Datatables

FreddleSpl0it 3 years ago
parent
commit
0ece065cb0

File diff suppressed because it is too large
+ 10 - 10
data/web/css/build/008-animate.min.css


+ 0 - 0
data/web/css/build/010-numberedtextarea.min.css → data/web/css/build/009-numberedtextarea.min.css


+ 0 - 0
data/web/css/build/011-jquery.jqplot.min.css → data/web/css/build/010-jquery.jqplot.min.css


+ 0 - 0
data/web/css/build/012-datatables.css → data/web/css/build/011-datatables.css


+ 0 - 0
data/web/css/build/013-bootstrap-icons.css → data/web/css/build/012-bootstrap-icons.css


+ 252 - 255
data/web/css/build/008-mailcow.css → data/web/css/build/013-mailcow.css

@@ -1,256 +1,253 @@
-@font-face {
-  font-family: 'Noto Sans';
-  font-style: normal;
-  font-weight: 400;
-  src: local(''),
-       url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff2') format('woff2'),
-       url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff') format('woff');
-}
-
-@font-face {
-  font-family: 'Noto Sans';
-  font-style: normal;
-  font-weight: 700;
-  src: local(''),
-       url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff2') format('woff2'),
-       url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff') format('woff');
-}
-
-@font-face {
-  font-family: 'Noto Sans';
-  font-style: italic;
-  font-weight: 400;
-  src: local(''),
-       url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff2') format('woff2'),
-       url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff') format('woff');
-}
-
-@font-face {
-  font-family: 'Noto Sans';
-  font-style: italic;
-  font-weight: 700;
-  src: local(''),
-       url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff2') format('woff2'),
-       url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff') format('woff');
-}
-#maxmsgsize { min-width: 80px; }
-#slider1 .slider-selection {
-	background: #FFD700;
-}
-#slider1 .slider-track-high {
-	background: #FF4500;
-}
-#slider1 .slider-track-low {
-  background: #66CD00;
-}
-.striped:nth-child(odd) {
-  background-color: #fff;
-}
-.striped:nth-child(even) {
-  background-color: #fafafa;
-  border:1px solid white;
-}
-.btn {
-  text-transform: none;
-}
-.btn * {
-  pointer-events: none;
-}
-.textarea-code {
-  font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
-  background:transparent !important;
-}
-.navbar-nav {
-  margin: 0;
-}
-.navbar-fixed-bottom .navbar-collapse, 
-.navbar-fixed-top .navbar-collapse {
-  max-height: 1000px
-}
-.bi {
-  display: inline-block;
-  font-size: 12pt;
-}
-.btn .bi {
-  display: inline-block;
-  font-size: inherit;
-}
-.btn-group-xs > .btn, .btn-xs {
-  padding: .25rem .4rem;
-  font-size: .875rem;
-  line-height: .5;
-  border-radius: .2rem;
-}
-.icon-spin {
-  animation-name: spin;
-  animation-duration: 2000ms;
-  animation-iteration-count: infinite;
-  animation-timing-function: linear;
-  -webkit-animation: spin 2000ms infinite linear;
-}
-.dropdown-menu {
-  font-size: 0.9rem;
-}
-@-webkit-keyframes spin {
-  0% {
-    -webkit-transform: rotate(0deg);
-    transform: rotate(0deg);
-  }
-  100% {
-    -webkit-transform: rotate(359deg);
-    transform: rotate(359deg);
-  }
-}
-@keyframes spin {
-  0% {
-    -webkit-transform: rotate(0deg);
-    transform: rotate(0deg);
-  }
-  100% {
-    -webkit-transform: rotate(359deg);
-    transform: rotate(359deg);
-  }
-}
-pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;}
-.footable-sortable {
-  -webkit-user-select: none;  
-  -moz-user-select: none;    
-  -ms-user-select: none;      
-  user-select: none;
-}
-/* Fix modal moving content left */
-body.modal-open {
-  overflow: inherit;
-  padding-right: inherit !important;
-}
-body {
-  font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
-  font-size: 10.5pt;
-  line-height: 1.5;
-}
-html {
-  font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
-  font-size: 10.5pt;
-  line-height: 1.5;
-}
-#mailcow-alert {
-  position: fixed;
-  bottom: 8px;
-  right: 25px;
-  min-width: 350px;
-  max-width: 550px;
-  z-index: 2000;
-}
-.input-group-sm .btn { margin-top: 0 !important }
-legend {
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  -ms-user-select: none;
-  -o-user-select: none;
-  user-select: none;
-  font-size: 1.2rem;
-}
-.navbar .navbar-brand {
-  padding-top: 5px;
-}
-.navbar .navbar-brand img {
-  height: 40px;
-}
-.mailcow-logo img {
-  max-width: 250px;
-}
-.lang-link-disabled a  {
-  pointer-events: none;
-}
-.lang-link-disabled  {
-  cursor: not-allowed;
-}
-.overlay {
-  background: #fff;
-  position: absolute;
-  z-index: 10000;
-  top: 0; right: 0; bottom: 0; left: 0;
-  opacity: 0.7;
-}
-.bootstrap-select.btn-group .no-results {
-  display: none;
-}
-.haveibeenpwned {
-  cursor: pointer;
-  -webkit-user-select: none;  
-  -moz-user-select: none;    
-  -ms-user-select: none;      
-  user-select: none;
-}
-.full-width-select {
-  width: 100%!important;  
-}
-.tooltip {
-  font-family: inherit;
-  font-size: 0.8rem;
-}
-.progress-bar {
-  font-size: 0.8rem;
-  line-height: 14px;
-}
-.footer {
-  margin-top: 27px;
-  margin-bottom: 20px;
-  color: #959595;
-  display: flex;
-  flex-direction: column;
-}
-.footer .version {
-  margin-left: auto;
-	margin-top: 20px;
-}
-.slave-info {
-  padding: 15px 0px 15px 15px;
-  font-weight: bold;
-}
-.alert-hr {
-  margin:3px 0px;
-  border-bottom:1px solid #f5f5f5!important;
-  opacity: 0.3;
-}
-.btn-input-missing,
-.btn-input-missing:hover,
-.btn-input-missing:active,
-.btn-input-missing:focus,
-.btn-input-missing:active:hover,
-.btn-input-missing:active:focus {
-  color: #000 !important;
-  background-color: #ff4136;
-  border-color: #ff291c;
-}
-table.footable>tbody>tr.footable-empty>td {
-  font-style:italic;
-  font-size: 1rem;
-}
-.navbar-nav > li {
-  font-size: 1rem !important;
-}
-.dropdown-menu > li > a {
-  font-size: 1rem !important;
-}
-.label {
-  font-size:inherit;
-}
-[class^="bi-"]::before, [class*=" bi-"]::before {
-  vertical-align: -0.2em !important;
-}
-legend > [class^="bi-"]::before, legend > [class*=" bi-"]::before {
-  vertical-align: 0em !important;
-}
-code {
-  font-size: inherit;
-}
-.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark {
-  margin-top: 0px;
-}
-.flag-icon {
-  margin-right: 5px;
-}
-.dropdown-header {
-  font-weight: 600;
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  src: local(''),
+       url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff2') format('woff2'),
+       url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff') format('woff');
+}
+
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 700;
+  src: local(''),
+       url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff2') format('woff2'),
+       url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff') format('woff');
+}
+
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: italic;
+  font-weight: 400;
+  src: local(''),
+       url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff2') format('woff2'),
+       url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff') format('woff');
+}
+
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: italic;
+  font-weight: 700;
+  src: local(''),
+       url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff2') format('woff2'),
+       url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff') format('woff');
+}
+#maxmsgsize { min-width: 80px; }
+#slider1 .slider-selection {
+	background: #FFD700;
+}
+#slider1 .slider-track-high {
+	background: #FF4500;
+}
+#slider1 .slider-track-low {
+  background: #66CD00;
+}
+.striped:nth-child(odd) {
+  background-color: #fff;
+}
+.striped:nth-child(even) {
+  background-color: #fafafa;
+  border:1px solid white;
+}
+.btn {
+  text-transform: none;
+}
+.btn * {
+  pointer-events: none;
+}
+.textarea-code {
+  font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
+  background:transparent !important;
+}
+.navbar-nav {
+  margin: 0;
+}
+.navbar-fixed-bottom .navbar-collapse, 
+.navbar-fixed-top .navbar-collapse {
+  max-height: 1000px
+}
+.bi {
+  display: inline-block;
+  font-size: 12pt;
+}
+.btn .bi {
+  display: inline-block;
+  font-size: inherit;
+}
+.btn-group-xs > .btn, .btn-xs {
+  padding: .25rem .4rem;
+  font-size: .875rem;
+  line-height: .5;
+  border-radius: .2rem;
+}
+.icon-spin {
+  animation-name: spin;
+  animation-duration: 2000ms;
+  animation-iteration-count: infinite;
+  animation-timing-function: linear;
+  -webkit-animation: spin 2000ms infinite linear;
+}
+.dropdown-menu {
+  font-size: 0.9rem;
+}
+@-webkit-keyframes spin {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@keyframes spin {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;}
+/* Fix modal moving content left */
+body.modal-open {
+  overflow: inherit;
+  padding-right: inherit !important;
+}
+body {
+  font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
+  font-size: 10.5pt;
+  line-height: 1.5;
+}
+html {
+  font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
+  font-size: 10.5pt;
+  line-height: 1.5;
+}
+#mailcow-alert {
+  position: fixed;
+  bottom: 8px;
+  right: 25px;
+  min-width: 350px;
+  max-width: 550px;
+  z-index: 2000;
+}
+.input-group-sm .btn { margin-top: 0 !important }
+legend {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  -o-user-select: none;
+  user-select: none;
+  font-size: 1.2rem;
+}
+.navbar .navbar-brand {
+  padding-top: 5px;
+}
+.navbar .navbar-brand img {
+  height: 40px;
+}
+.mailcow-logo img {
+  max-width: 250px;
+}
+.lang-link-disabled a  {
+  pointer-events: none;
+}
+.lang-link-disabled  {
+  cursor: not-allowed;
+}
+.overlay {
+  background: #fff;
+  position: absolute;
+  z-index: 10000;
+  top: 0; right: 0; bottom: 0; left: 0;
+  opacity: 0.7;
+}
+.bootstrap-select.btn-group .no-results {
+  display: none;
+}
+.haveibeenpwned {
+  cursor: pointer;
+  -webkit-user-select: none;  
+  -moz-user-select: none;    
+  -ms-user-select: none;      
+  user-select: none;
+}
+.full-width-select {
+  width: 100%!important;  
+}
+.tooltip {
+  font-family: inherit;
+  font-size: 0.8rem;
+}
+.progress-bar {
+  font-size: 0.8rem;
+  line-height: 14px;
+}
+.footer {
+  margin-top: 27px;
+  margin-bottom: 20px;
+  color: #959595;
+  display: flex;
+  flex-direction: column;
+}
+.footer .version {
+  margin-left: auto;
+	margin-top: 20px;
+}
+.slave-info {
+  padding: 15px 0px 15px 15px;
+  font-weight: bold;
+}
+.alert-hr {
+  margin:3px 0px;
+  border-bottom:1px solid #f5f5f5!important;
+  opacity: 0.3;
+}
+.btn-input-missing,
+.btn-input-missing:hover,
+.btn-input-missing:active,
+.btn-input-missing:focus,
+.btn-input-missing:active:hover,
+.btn-input-missing:active:focus {
+  color: #000 !important;
+  background-color: #ff4136;
+  border-color: #ff291c;
+}
+.navbar-nav > li {
+  font-size: 1rem !important;
+}
+.dropdown-menu > li > a {
+  font-size: 1rem !important;
+}
+.label {
+  font-size:inherit;
+}
+[class^="bi-"]::before, [class*=" bi-"]::before {
+  vertical-align: -0.2em !important;
+}
+legend > [class^="bi-"]::before, legend > [class*=" bi-"]::before {
+  vertical-align: 0em !important;
+}
+code {
+  font-size: inherit;
+}
+.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark {
+  margin-top: 0px;
+}
+.flag-icon {
+  margin-right: 5px;
+}
+.dropdown-header {
+  font-weight: 600;
+}
+.dataTables_info {
+  margin: 15px !important;
+  padding: 0px !important;
+}
+.dataTables_paginate, .dataTables_length, .dataTables_filter {
+  margin: 15px !important;
 }

+ 1 - 0
data/web/debug.php

@@ -53,6 +53,7 @@ $template_data = [
   'clamd_status' => $clamd_status,
   'containers' => $containers,
   'lang_admin' => json_encode($lang['admin']),
+  'lang_datatables' => json_encode($lang['datatables']),
 ];
 
 require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';

+ 423 - 374
data/web/js/site/debug.js

@@ -52,9 +52,7 @@ jQuery(function($){
   $(".refresh_table").on('click', function(e) {
     e.preventDefault();
     var table_name = $(this).data('table');
-    $('#' + table_name).find("tr.footable-empty").remove();
-    draw_table = $(this).data('draw');
-    eval(draw_table + '()');
+    $('#' + table_name).DataTable().ajax.reload();
   });
   function table_log_ready(ft, name) {
     heading = ft.$el.parents('.card').find('.card-header')
@@ -73,408 +71,435 @@ jQuery(function($){
     localStorage.setItem('current_page', JSON.stringify(current_page));
   }
   function draw_autodiscover_logs() {
-    ft_autodiscover_logs = FooTable.init('#autodiscover_log', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"ua","title":"User-Agent","style":{"min-width":"200px"}},
-        {"name":"user","title":"Username","style":{"min-width":"200px"}},
-        {"name":"ip","title":"IP","style":{"min-width":"200px"}},
-        {"name":"service","title":"Service"},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/autodiscover/100',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw autodiscover log table');
-        },
-        success: function (data) {
+    $('#autodiscover_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/autodiscover/100",
+        dataSrc: function(data){
           return process_table_data(data, 'autodiscover_log');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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: 'User-Agent',
+          data: 'ua'
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'autodiscover_logs');
+        {
+          title: 'Username',
+          data: 'user'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'autodiscover_logs');
+        {
+          title: 'IP',
+          data: 'ip'
+        },
+        {
+          title: 'Service',
+          data: 'service'
         }
-      }
+      ]
     });
   }
   function draw_postfix_logs() {
-    ft_postfix_logs = FooTable.init('#postfix_log', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"priority","title":lang.priority,"style":{"width":"80px"}},
-        {"name":"message","title":lang.message},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/postfix',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw postfix log table');
-        },
-        success: function (data) {
+    $('#postfix_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/postfix",
+        dataSrc: function(data){
           return process_table_data(data, 'general_syslog');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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"});
+          }
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'postfix_logs');
+        {
+          title: lang.priority,
+          data: 'priority'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'postfix_logs');
+        {
+          title: lang.message,
+          data: 'message'
         }
-      }
+      ]
     });
   }
   function draw_watchdog_logs() {
-    ft_watchdog_logs = FooTable.init('#watchdog_log', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"service","title":"Service"},
-        {"name":"trend","title":"Trend"},
-        {"name":"message","title":lang.message},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/watchdog',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw watchdog log table');
-        },
-        success: function (data) {
+    $('#watchdog_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/watchdog",
+        dataSrc: function(data){
           return process_table_data(data, 'watchdog');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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: 'Service',
+          data: 'service'
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'postfix_logs');
+        {
+          title: 'Trend',
+          data: 'trend'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'postfix_logs');
+        {
+          title: lang.message,
+          data: 'message'
         }
-      }
+      ]
     });
   }
   function draw_api_logs() {
-    ft_api_logs = FooTable.init('#api_log', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"uri","title":"URI","style":{"width":"310px"}},
-        {"name":"method","title":"Method","style":{"width":"80px"}},
-        {"name":"remote","title":"IP","style":{"width":"80px"}},
-        {"name":"data","title":"Data","breakpoints": "all","style":{"word-break":"break-all"}},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/api',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw api log table');
-        },
-        success: function (data) {
+    $('#api_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/api",
+        dataSrc: function(data){
           return process_table_data(data, 'apilog');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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: 'URI',
+          data: 'uri'
+        },
+        {
+          title: 'Method',
+          data: 'method'
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'api_logs');
+        {
+          title: 'IP',
+          data: 'remote'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'api_logs');
+        {
+          title: 'Data',
+          data: 'data'
         }
-      }
+      ]
     });
   }
   function draw_rl_logs() {
-    ft_rl_logs = FooTable.init('#rl_log', {
-      "columns": [
-        {"name":"indicator","title":" ","style":{"width":"50px"}},
-        {"name":"time","formatter":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"});},"title":lang.last_applied,"style":{"width":"170px"}},
-        {"name":"rl_name","title":lang.rate_name},
-        {"name":"from","title":lang.sender},
-        {"name":"rcpt","title":lang.recipients},
-        {"name":"user","title":lang.authed_user},
-        {"name":"message_id","title":"Msg ID","breakpoints": "all","style":{"word-break":"break-all"}},
-        {"name":"header_from","title":"Header From","breakpoints": "all","style":{"word-break":"break-all"}},
-        {"name":"header_subject","title":"Subject","breakpoints": "all","style":{"word-break":"break-all"}},
-        {"name":"rl_hash","title":"Hash","breakpoints": "all","style":{"word-break":"break-all"}},
-        {"name":"qid","title":"Rspamd QID","breakpoints": "all","style":{"word-break":"break-all"}},
-        {"name":"ip","title":"IP","breakpoints": "all","style":{"word-break":"break-all"}},
-        {"name":"action","title":lang.action,"breakpoints": "all","style":{"word-break":"break-all"}},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/ratelimited',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw rl log table');
-        },
-        success: function (data) {
+    $('#rl_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/ratelimited",
+        dataSrc: function(data){
           return process_table_data(data, 'rllog');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: ' ',
+          data: 'indicator'
+        },
+        {
+          title: lang.time,
+          data: 'time',
+          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.rate_name,
+          data: 'rl_name'
+        },
+        {
+          title: lang.sender,
+          data: 'from'
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'rl_logs');
+        {
+          title: lang.recipients,
+          data: 'rcpt'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'rl_logs');
+        {
+          title: lang.authed_user,
+          data: 'user'
+        },
+        {
+          title: 'Msg ID',
+          data: 'message_id'
+        },
+        {
+          title: 'Header From',
+          data: 'header_from'
+        },
+        {
+          title: 'Subject',
+          data: 'header_subject'
+        },
+        {
+          title: 'Hash',
+          data: 'rl_hash'
+        },
+        {
+          title: 'Rspamd QID',
+          data: 'qid'
+        },
+        {
+          title: 'IP',
+          data: 'ip'
+        },
+        {
+          title: lang.action,
+          data: 'action'
         }
-      }
+      ]
     });
   }
   function draw_ui_logs() {
-    ft_api_logs = FooTable.init('#ui_logs', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"type","title":"Type"},
-        {"name":"task","title":"Task"},
-        {"name":"user","title":"User"},
-        {"name":"role","title":"Role"},
-        {"name":"remote","title":"IP"},
-        {"name":"msg","title":lang.message,"style":{"word-break":"break-all"}},
-        {"name":"call","title":"Call","breakpoints": "all"}
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/ui',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw ui log table');
-        },
-        success: function (data) {
+    $('#ui_logs').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/ui",
+        dataSrc: function(data){
           return process_table_data(data, 'mailcow_ui');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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: 'Type',
+          data: 'type'
+        },
+        {
+          title: 'Task',
+          data: 'task'
+        },
+        {
+          title: 'User',
+          data: 'user'
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'ui_logs');
+        {
+          title: 'Role',
+          data: 'role'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'ui_logs');
+        {
+          title: 'IP',
+          data: 'remote'
+        },
+        {
+          title: lang.message,
+          data: 'msg'
+        },
+        {
+          title: 'Call',
+          data: 'call'
         }
-      }
+      ]
     });
   }
   function draw_sasl_logs() {
-    ft_api_logs = FooTable.init('#sasl_logs', {
-      "columns": [
-        {"name":"username","title":lang.username},
-        {"name":"service","title":lang.service},
-        {"name":"real_rip","title":"IP"},
-        {"sorted": true,"sortValue": function(value){res = new Date(value);return res.getTime();},"direction":"DESC","name":"datetime","formatter":function date_format(datetime) { var date = new Date(datetime.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.login_time,"style":{"width":"170px"}},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/sasl',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw sasl log table');
-        },
-        success: function (data) {
+    $('#sasl_logs').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/sasl",
+        dataSrc: function(data){
           return process_table_data(data, 'sasl_log_table');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'sasl_logs');
-        },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'sasl_logs');
+      },
+      columns: [
+        {
+          title: lang.username,
+          data: 'username'
+        },
+        {
+          title: lang.service,
+          data: 'service'
+        },
+        {
+          title: 'IP',
+          data: 'real_rip'
+        },
+        {
+          title: lang.login_time,
+          data: 'datetime',
+          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"});
+          }
         }
-      }
+      ]
     });
   }
   function draw_acme_logs() {
-    ft_acme_logs = FooTable.init('#acme_log', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"message","title":lang.message,"style":{"word-break":"break-all"}},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/acme',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw acme log table');
-        },
-        success: function (data) {
+    $('#acme_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/acme",
+        dataSrc: function(data){
           return process_table_data(data, 'general_syslog');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'acme_logs');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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"});
+          }
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'acme_logs');
+        {
+          title: lang.message,
+          data: 'message'
         }
-      }
+      ]
     });
   }
   function draw_netfilter_logs() {
-    ft_netfilter_logs = FooTable.init('#netfilter_log', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"priority","title":lang.priority,"style":{"width":"80px"}},
-        {"name":"message","title":lang.message},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/netfilter',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw netfilter log table');
-        },
-        success: function (data) {
+    $('#netfilter_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/netfilter",
+        dataSrc: function(data){
           return process_table_data(data, 'general_syslog');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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"});
+          }
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'netfilter_logs');
+        {
+          title: lang.priority,
+          data: 'priority'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'netfilter_logs');
+        {
+          title: lang.message,
+          data: 'message'
         }
-      }
+      ]
     });
   }
   function draw_sogo_logs() {
-    ft_sogo_logs = FooTable.init('#sogo_log', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"priority","title":lang.priority,"style":{"width":"80px"}},
-        {"name":"message","title":lang.message},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/sogo',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw sogo log table');
-        },
-        success: function (data) {
+    $('#sogo_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/sogo",
+        dataSrc: function(data){
           return process_table_data(data, 'general_syslog');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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"});
+          }
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'sogo_logs');
+        {
+          title: lang.priority,
+          data: 'priority'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'sogo_logs');
+        {
+          title: lang.message,
+          data: 'message'
         }
-      }
+      ]
     });
   }
   function draw_dovecot_logs() {
-    ft_dovecot_logs = FooTable.init('#dovecot_log', {
-      "columns": [
-        {"name":"time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name":"priority","title":lang.priority,"style":{"width":"80px"}},
-        {"name":"message","title":lang.message},
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/dovecot',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw dovecot log table');
-        },
-        success: function (data) {
+    $('#dovecot_log').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/dovecot",
+        dataSrc: function(data){
           return process_table_data(data, 'general_syslog');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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"});
+          }
         },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'dovecot_logs');
+        {
+          title: lang.priority,
+          data: 'priority'
         },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'dovecot_logs');
+        {
+          title: lang.message,
+          data: 'message'
         }
-      }
+      ]
     });
   }
   function rspamd_pie_graph() {
@@ -537,53 +562,75 @@ jQuery(function($){
     });
   }
   function draw_rspamd_history() {
-    ft_rspamd_history = FooTable.init('#rspamd_history', {
-      "columns": [
-        {"name":"unix_time","formatter":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"});},"title":lang.time,"style":{"width":"170px"}},
-        {"name": "ip","title": "IP address","breakpoints": "all","style": {"minWidth": 88}},
-        {"name": "sender_mime","title": "From","breakpoints": "xs sm md","style": {"minWidth": 100}},
-        {"name": "rcpt","title": "To","breakpoints": "xs sm md","style": {"minWidth": 100}},
-        {"name": "subject","title": "Subject","breakpoints": "all","style": {"word-break": "break-all","minWidth": 150}},
-        {"name": "action","title": "Action","style": {"minwidth": 82}},
-        {"name": "score","title": "Score","style": {"maxWidth": 110},},
-        {"name": "symbols","title": "Symbols","breakpoints": "all",},
-        {"name": "size","title": "Msg size","breakpoints": "all","style": {"minwidth": 50},"formatter": function(value){return humanFileSize(value);}},
-        {"name": "scan_time","title": "Scan time","breakpoints": "all","style": {"maxWidth": 72},},
-        {"name": "message-id","title": "ID","breakpoints": "all","style": {"minWidth": 130,"overflow": "hidden","textOverflow": "ellipsis","wordBreak": "break-all","whiteSpace": "normal"}},
-        {"name": "user","title": "Authenticated user","breakpoints": "xs sm md","style": {"minWidth": 100}}
-      ],
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/logs/rspamd-history',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw rspamd history table');
-        },
-        success: function (data) {
+    $('#rspamd_history').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/logs/rspamd-history",
+        dataSrc: function(data){
           return process_table_data(data, 'rspamd_history');
         }
-      }),
-      "empty": lang.empty,
-      "paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
-      "filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
-      "sorting": {"enabled": true},
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_log_ready(ft, 'rspamd_history');
-          heading = ft.$el.parents('.card').find('.card-header')
-          $(heading).children('.table-lines').text(function(){
-            var ft_paging = ft.use(FooTable.Paging)
-            return ft_paging.totalRows;
-          })
-          rspamd_pie_graph();
-        },
-        "after.ft.paging": function(e, ft){
-          table_log_paging(ft, 'rspamd_history');
+      },
+      columns: [
+        {
+          title: lang.time,
+          data: 'time',
+          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: 'IP address',
+          data: 'ip'
+        },
+        {
+          title: 'From',
+          data: 'sender_mime'
+        },
+        {
+          title: 'To',
+          data: 'rcpt'
+        },
+        {
+          title: 'Subject',
+          data: 'subject'
+        },
+        {
+          title: 'Action',
+          data: 'action'
+        },
+        {
+          title: 'Score',
+          data: 'score'
+        },
+        {
+          title: 'Subject',
+          data: 'header_subject'
+        },
+        {
+          title: 'Symbols',
+          data: 'symbols'
+        },
+        {
+          title: 'Msg size',
+          data: 'size'
+        },
+        {
+          title: 'Scan Time',
+          data: 'scan_time'
+        },
+        {
+          title: 'ID',
+          data: 'message-id'
+        },
+        {
+          title: 'Authenticated user',
+          data: 'user'
         }
-      }
+      ]
     });
   }
   function process_table_data(data, table) {
@@ -708,7 +755,7 @@ jQuery(function($){
         if (item === null) { return true; }
         item.username = escapeHtml(item.username);
         item.service = '<div class="badge fs-5 bg-secondary">' + item.service.toUpperCase() + '</div>';
-    });
+      });
     } else if (table == 'general_syslog') {
       $.each(data, function (i, item) {
         if (item === null) { return true; }
@@ -769,17 +816,19 @@ jQuery(function($){
       console.log("no data-table or data-nrows or log_url or data-post-process attr found");
       return;
     }
-    if (ft = FooTable.get($('#' + log_table))) {
-      var heading = ft.$el.parents('.card').find('.card-header')
-      var ft_paging = ft.use(FooTable.Paging)
-      var load_rows = (ft_paging.totalRows + 1) + '-' + (ft_paging.totalRows + new_nrows)
+
+    // BUG TODO: loading 100 results in loading 10 - loading 1000 results in loading 100
+    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)
+
       $.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; }
         var rows = process_table_data(data, post_process);
-        var rows_now = (ft_paging.totalRows + data.length);
+        var rows_now = (table.page.len() + data.length);
         $(heading).children('.table-lines').text(rows_now)
         mailcow_alert_box(data.length + lang.additional_rows, "success");
-        ft.rows.load(rows, true);
+        table.rows.add(rows).draw();
       });
     }
   })

+ 380 - 364
data/web/js/site/mailbox.js

@@ -54,22 +54,6 @@ $(document).ready(function() {
   //   }
   // });
   // Set paging
-  $('[data-page-size]').on('click', function(e){
-    e.preventDefault();
-    var new_size = $(this).data('page-size');
-    var parent_ul = $(this).closest('ul');
-    var table_id = $(parent_ul).data('table-id');
-    FooTable.get('#' + table_id).pageSize(new_size);
-    //$(this).parent().addClass('active').siblings().removeClass('active')
-    heading = $(this).parents('.card').find('.card-header')
-    var n_results = $(heading).children('.table-lines').text().split(' / ')[1];
-    $(heading).children('.table-lines').text(function(){
-      if (new_size > n_results) {
-        new_size = n_results;
-      }
-      return new_size + ' / ' + n_results;
-    })
-  });
   // Clone mailbox mass actions
   $("div").find("[data-actions-header='true'").each(function() {
     $(this).html($(this).nextAll('.mass-actions-mailbox:first').html());
@@ -242,9 +226,7 @@ jQuery(function($){
   $(".refresh_table").on('click', function(e) {
     e.preventDefault();
     var table_name = $(this).data('table');
-    $('#' + table_name).find("tr.footable-empty").remove();
-    draw_table = $(this).data('draw');
-    eval(draw_table + '()');
+    $('#' + table_name).DataTable().ajax.reload();
   });
   function table_mailbox_ready(ft, name) {
     if(is_dual) {
@@ -662,7 +644,7 @@ jQuery(function($){
       },
       columns: [
           {
-            title: "ID",
+            title: 'ID',
             data: 'id'
           },
           {
@@ -706,163 +688,152 @@ jQuery(function($){
     });
   }
   function draw_recipient_map_table() {
-    ft_recipient_map_table = FooTable.init('#recipient_map_table', {
-      "columns": [
-        {"name":"chkbox","title":"","style":{"min-width":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
-        {"sorted": true,"name":"id","title":"ID","style":{"min-width":"60px","width":"60px","text-align":"center"}},
-        {"name":"recipient_map_old","title":lang.recipient_map_old},
-        {"name":"recipient_map_new","title":lang.recipient_map_new},
-        {"name":"active","filterable": false,"style":{"min-width":"80px","width":"80px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
-        {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"180px","width":"180px"},"type":"html","title":(role == "admin" ? lang.action : ""),"breakpoints":"xs sm"}
-      ],
-      "empty": lang.empty,
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/recipient_map/all',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw recipient map table');
-        },
-        success: function (data) {
-          if (role == "admin") {
-            $.each(data, 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 footable-actions">' +
-                '<a href="/edit/recipient_map/' + 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-recipient_map" data-api-url="delete/recipient_map" 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="recipient_map" name="multi_select" value="' + item.id + '" />';
-            });
-          }
-        }
-      }),
-      "paging": {
-        "enabled": true,
-        "limit": 5,
-        "size": pagination_size
-      },
-      "state": {
-        "enabled": true
-      },
-      "filtering": {
-        "enabled": true,
-        "delay": 1200,
-        "position": "left",
-        "connectors": false,
-        "placeholder": lang.filter_table
-      },
-      "sorting": {
-        "enabled": true
-      },
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_mailbox_ready(ft, 'recipient_map_table');
-        },
-        "after.ft.filtering": function(e, ft){
-          table_mailbox_ready(ft, 'recipient_map_table');
+    $('#recipient_map_table').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      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 footable-actions">' +
+              '<a href="/edit/recipient_map/' + 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-recipient_map" data-api-url="delete/recipient_map" 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="recipient_map" name="multi_select" value="' + item.id + '" />';
+          });
+
+          console.log(json);
+          return json;
         }
       },
-      "toggleSelector": "table tbody span.footable-toggle"
+      columns: [
+          {
+            title: 'ID',
+            data: 'id'
+          },
+          {
+            title: lang.recipient_map_old,
+            data: 'recipient_map_old'
+          },
+          {
+            title: lang.recipient_map_new,
+            data: 'recipient_map_new'
+          },
+          {
+            title: lang.active,
+            data: 'active',
+            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: null,
+            render: function (data, type) {
+              return `<div class="btn-group">
+                <a href="/edit/admin/admin" class="btn btn-xs btn-xs-half btn-secondary">
+                  <i class="bi bi-pencil-fill"></i> Bearbeiten
+                </a>
+                <a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="admin" class="btn btn-xs btn-xs-half btn-danger">
+                  <i class="bi bi-trash"></i> Entfernen
+                </a>
+              </div>`;
+            }
+          },
+      ]
     });
   }
   function draw_tls_policy_table() {
-    ft_tls_policy_table = FooTable.init('#tls_policy_table', {
-      "columns": [
-        {"name":"chkbox","title":"","style":{"min-width":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
-        {"sorted": true,"name":"id","title":"ID","style":{"min-width":"60px","width":"60px","text-align":"center"}},
-        {"name":"dest","title":lang.tls_map_dest},
-        {"name":"policy","title":lang.tls_map_policy},
-        {"name":"parameters","title":lang.tls_map_parameters},
-        {"name":"active","filterable": false,"style":{"min-width":"80px","width":"80px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
-        {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"180px","width":"180px"},"type":"html","title":(role == "admin" ? lang.action : ""),"breakpoints":"xs sm"}
-      ],
-      "empty": lang.empty,
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/tls-policy-map/all',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw tls policy map table');
-        },
-        success: function (data) {
-          if (role == "admin") {
-            $.each(data, function (i, item) {
-              item.dest = escapeHtml(item.dest);
-              item.policy = '<b>' + escapeHtml(item.policy) + '</b>';
-              if (item.parameters == '') {
-                item.parameters = '<code>-</code>';
-              } else {
-                item.parameters = '<code>' + escapeHtml(item.parameters) + '</code>';
-              }
-              item.action = '<div class="btn-group footable-actions">' +
-                '<a href="/edit/tls_policy_map/' + 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-tls-policy-map" data-api-url="delete/tls-policy-map" 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="tls-policy-map" name="multi_select" value="' + item.id + '" />';
-            });
-          }
-        }
-      }),
-      "paging": {
-        "enabled": true,
-        "limit": 5,
-        "size": pagination_size
-      },
-      "state": {
-        "enabled": true
-      },
-      "filtering": {
-        "enabled": true,
-        "delay": 1200,
-        "position": "left",
-        "connectors": false,
-        "placeholder": lang.filter_table
-      },
-      "sorting": {
-        "enabled": true
-      },
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_mailbox_ready(ft, 'tls_policy_table');
-        },
-        "after.ft.filtering": function(e, ft){
-          table_mailbox_ready(ft, 'tls_policy_table');
+    $('#tls_policy_table').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      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>';
+            if (item.parameters == '') {
+              item.parameters = '<code>-</code>';
+            } else {
+              item.parameters = '<code>' + escapeHtml(item.parameters) + '</code>';
+            }
+            item.action = '<div class="btn-group footable-actions">' +
+              '<a href="/edit/tls_policy_map/' + 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-tls-policy-map" data-api-url="delete/tls-policy-map" 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="tls-policy-map" name="multi_select" value="' + item.id + '" />';
+          });
+
+          console.log(json);
+          return json;
         }
       },
-      "toggleSelector": "table tbody span.footable-toggle"
+      columns: [
+          {
+            title: 'ID',
+            data: 'id'
+          },
+          {
+            title: lang.tls_map_dest,
+            data: 'dest'
+          },
+          {
+            title: lang.tls_map_policy,
+            data: 'policy'
+          },
+          {
+            title: lang.tls_map_parameters,
+            data: 'parameters'
+          },
+          {
+            title: lang.domain,
+            data: 'domain'
+          },
+          {
+            title: lang.active,
+            data: 'active',
+            render: function (data, type) {
+              return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';
+            }
+          },
+          {
+            title: lang.action,
+            data: null,
+            render: function (data, type) {
+              return `<div class="btn-group">
+                <a href="/edit/admin/admin" class="btn btn-xs btn-xs-half btn-secondary">
+                  <i class="bi bi-pencil-fill"></i> Bearbeiten
+                </a>
+                <a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="admin" class="btn btn-xs btn-xs-half btn-danger">
+                  <i class="bi bi-trash"></i> Entfernen
+                </a>
+              </div>`;
+            }
+          },
+      ]
     });
   }
   function draw_alias_table() {
-    ft_alias_table = FooTable.init('#alias_table', {
-      "columns": [
-        {"name":"chkbox","title":"","style":{"min-width":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
-        {"name":"id","title":"ID","style":{"min-width":"60px","width":"60px","text-align":"center"}},
-        {"sorted": true,"name":"address","title":lang.alias,"style":{"width":"250px"}},
-        {"name":"goto","title":lang.target_address},
-        {"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
-        {"name":"public_comment","title":lang.public_comment,"breakpoints":"all"},
-        {"name":"private_comment","title":lang.private_comment,"breakpoints":"all"},
-        {"name":"sogo_visible","title":lang.sogo_visible,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';},"breakpoints":"all"},
-        {"name":"active","filterable": false,"style":{"min-width":"80px","width":"80px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
-        {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
-      ],
-      "empty": lang.empty,
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/alias/all',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw alias table');
-        },
-        success: function (data) {
-          $.each(data, function (i, item) {
+    $('#alias_table').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/alias/all",
+        dataSrc: function(json){
+          $.each(json, function (i, item) {
             item.action = '<div class="btn-group footable-actions">' +
               '<a href="/edit/alias/' + 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-alias" data-api-url="delete/alias" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
@@ -900,64 +871,78 @@ jQuery(function($){
               item.domain = '<i data-domainname="' + item.domain + '" class="bi bi-info-circle-fill alias-domain-info text-info" data-bs-toggle="tooltip" title="' + lang.target_domain + ': ' + item.in_primary_domain + '"></i> ' + item.domain;
             }
           });
-        }
-      }),
-      "paging": {
-        "enabled": true,
-        "limit": 5,
-        "size": pagination_size
-      },
-      "state": {
-        "enabled": true
-      },
-      "filtering": {
-        "enabled": true,
-        "delay": 1200,
-        "position": "left",
-        "connectors": false,
-        "placeholder": lang.filter_table
-      },
-      "components": {
-        "filtering": FooTable.domainFilter
-      },
-      "sorting": {
-        "enabled": true
-      },
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_mailbox_ready(ft, 'alias_table');
-          $('.alias-domain-info').tooltip();
-        },
-        "after.ft.filtering": function(e, ft){
-          table_mailbox_ready(ft, 'alias_table');
+
+          console.log(json);
+          return json;
         }
       },
-      "toggleSelector": "table tbody span.footable-toggle"
+      columns: [
+          {
+            title: 'ID',
+            data: 'id'
+          },
+          {
+            title: lang.alias,
+            data: 'address'
+          },
+          {
+            title: lang.target_address,
+            data: 'goto'
+          },
+          {
+            title: lang.bcc_destinations,
+            data: 'bcc_dest'
+          },
+          {
+            title: lang.domain,
+            data: 'domain'
+          },
+          {
+            title: lang.public_comment,
+            data: 'public_comment'
+          },
+          {
+            title: lang.private_comment,
+            data: 'private_comment'
+          },
+          {
+            title: lang.sogo_visible,
+            data: 'sogo_visible'
+          },
+          {
+            title: lang.active,
+            data: 'active',
+            render: function (data, type) {
+              return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';
+            }
+          },
+          {
+            title: lang.action,
+            data: null,
+            render: function (data, type) {
+              return `<div class="btn-group">
+                <a href="/edit/admin/admin" class="btn btn-xs btn-xs-half btn-secondary">
+                  <i class="bi bi-pencil-fill"></i> Bearbeiten
+                </a>
+                <a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="admin" class="btn btn-xs btn-xs-half btn-danger">
+                  <i class="bi bi-trash"></i> Entfernen
+                </a>
+              </div>`;
+            }
+          },
+      ]
     });
   }
-
   function draw_aliasdomain_table() {
-    ft_aliasdomain_table = FooTable.init('#aliasdomain_table', {
-      "columns": [
-        {"name":"chkbox","title":"","style":{"min-width":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
-        {"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}},
-        {"name":"target_domain","title":lang.target_domain,"type":"html"},
-        {"name":"active","filterable": false,"style":{"min-width":"80px","width":"80px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
-        {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"250px","width":"250px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
-      ],
-      "empty": lang.empty,
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/alias-domain/all',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw alias domain table');
-        },
-        success: function (data) {
-          $.each(data, function (i, item) {
+    $('#aliasdomain_table').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/alias-domain/all",
+        dataSrc: function(json){
+          $.each(json, function (i, item) {
             item.action = '<div class="btn-group footable-actions">' +
               '<a href="/edit/aliasdomain/' + encodeURIComponent(item.alias_domain) + '" 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-alias-domain" data-api-url="delete/alias-domain" data-item="' + encodeURIComponent(item.alias_domain) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
@@ -970,67 +955,66 @@ jQuery(function($){
               item.target_domain = '<span><a href="/edit/domain/' + item.target_domain + '">' + item.target_domain + '</a></span>';
             }
           });
-        }
-      }),
-      "paging": {
-        "enabled": true,
-        "limit": 5,
-        "size": pagination_size
-      },
-      "state": {
-        "enabled": true
-      },
-      "filtering": {
-        "enabled": true,
-        "delay": 1200,
-        "position": "left",
-        "connectors": false,
-        "placeholder": lang.filter_table
-      },
-      "sorting": {
-        "enabled": true
-      },
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_mailbox_ready(ft, 'aliasdomain_table');
-        },
-        "after.ft.filtering": function(e, ft){
-          table_mailbox_ready(ft, 'aliasdomain_table');
+
+          console.log(json);
+          return json;
         }
       },
-      "toggleSelector": "table tbody span.footable-toggle"
+      columns: [
+          {
+            title: lang.alias,
+            data: 'alias_domain'
+          },
+          {
+            title: lang.target_domain,
+            data: 'target_domain'
+          },
+          {
+            title: lang.bcc_local_dest,
+            data: 'local_dest'
+          },
+          {
+            title: lang.bcc_destinations,
+            data: 'bcc_dest'
+          },
+          {
+            title: lang.domain,
+            data: 'domain'
+          },
+          {
+            title: lang.active,
+            data: 'active',
+            render: function (data, type) {
+              return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';
+            }
+          },
+          {
+            title: lang.action,
+            data: null,
+            render: function (data, type) {
+              return `<div class="btn-group">
+                <a href="/edit/admin/admin" class="btn btn-xs btn-xs-half btn-secondary">
+                  <i class="bi bi-pencil-fill"></i> Bearbeiten
+                </a>
+                <a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="admin" class="btn btn-xs btn-xs-half btn-danger">
+                  <i class="bi bi-trash"></i> Entfernen
+                </a>
+              </div>`;
+            }
+          },
+      ]
     });
   }
-
   function draw_sync_job_table() {
-    ft_syncjob_table = FooTable.init('#sync_job_table', {
-      "columns": [
-        {"name":"chkbox","title":"","style":{"min-width":"60px","width":"60px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
-        {"sorted": true,"name":"id","title":"ID","style":{"min-width":"60px","width":"60px","text-align":"center"}},
-        {"name":"user2","title":lang.owner},
-        {"name":"server_w_port","title":"Server","breakpoints":"xs sm md","style":{"word-break":"break-all"}},
-        {"name":"exclude","title":lang.excludes,"breakpoints":"all"},
-        {"name":"mins_interval","title":lang.mins_interval,"breakpoints":"all"},
-        {"name":"last_run","title":lang.last_run,"breakpoints":"xs sm md"},
-        {"name":"exit_status","filterable": false,"title":lang.syncjob_last_run_result},
-        {"name":"log","title":"Log"},
-        {"name":"active","filterable": false,"style":{"min-width":"70px","width":"70px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
-        {"name":"is_running","filterable": false,"style":{"min-width":"120px","width":"100px"},"title":lang.status},
-        {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
-      ],
-      "empty": lang.empty,
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/syncjobs/all/no_log',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw sync job table');
-        },
-        success: function (data) {
-          $.each(data, function (i, item) {
+    $('#sync_job_table').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/syncjobs/all/no_log",
+        dataSrc: function(json){
+          $.each(json, function (i, item) {
             item.log = '<a href="#syncjobLogModal" data-bs-toggle="modal" data-syncjob-id="' + encodeURIComponent(item.id) + '">' + lang.open_logs + '</a>'
             item.user2 = escapeHtml(item.user2);
             if (!item.exclude > 0) {
@@ -1065,63 +1049,82 @@ jQuery(function($){
             }
             item.exit_status = item.success + ' ' + item.exit_status;
           });
-        }
-      }),
-      "paging": {
-        "enabled": true,
-        "limit": 5,
-        "size": pagination_size
-      },
-      "state": {
-        "enabled": true
-      },
-      "filtering": {
-        "enabled": true,
-        "delay": 1200,
-        "position": "left",
-        "connectors": false,
-        "placeholder": lang.filter_table
-      },
-      "sorting": {
-        "enabled": true
-      },
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_mailbox_ready(ft, 'sync_job_table');
-        },
-        "after.ft.filtering": function(e, ft){
-          table_mailbox_ready(ft, 'sync_job_table');
+
+          console.log(json);
+          return json;
         }
       },
-      "toggleSelector": "table tbody span.footable-toggle"
+      columns: [
+          {
+            title: 'ID',
+            data: 'id'
+          },
+          {
+            title: lang.owner,
+            data: 'user2'
+          },
+          {
+            title: 'Server',
+            data: 'server_w_port'
+          },
+          {
+            title: lang.excludes,
+            data: 'exclude'
+          },
+          {
+            title: lang.mins_interval,
+            data: 'mins_interval'
+          },
+          {
+            title: lang.last_run,
+            data: 'last_run'
+          },
+          {
+            title: lang.syncjob_last_run_result,
+            data: 'exit_status'
+          },
+          {
+            title: lang.status,
+            data: 'is_running'
+          },
+          {
+            title: lang.active,
+            data: 'active',
+            render: function (data, type) {
+              return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';
+            }
+          },
+          {
+            title: 'Log',
+            data: 'log'
+          },
+          {
+            title: lang.action,
+            data: null,
+            render: function (data, type) {
+              return `<div class="btn-group">
+                <a href="/edit/admin/admin" class="btn btn-xs btn-xs-half btn-secondary">
+                  <i class="bi bi-pencil-fill"></i> Bearbeiten
+                </a>
+                <a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="admin" class="btn btn-xs btn-xs-half btn-danger">
+                  <i class="bi bi-trash"></i> Entfernen
+                </a>
+              </div>`;
+            }
+          },
+      ]
     });
   }
-
   function draw_filter_table() {
-    ft_filter_table = FooTable.init('#filter_table', {
-      "columns": [
-        {"name":"chkbox","title":"","style":{"min-width":"60px","width":"60px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
-        {"name":"id","title":"ID","style":{"min-width":"60px","width":"60px","text-align":"center"}},
-        {"name":"active","style":{"min-width":"80px","width":"80px"},"title":lang.active},
-        {"name":"filter_type","style":{"min-width":"80px","width":"80px"},"title":"Type"},
-        {"sorted": true,"name":"username","title":lang.owner,"style":{"min-width":"550px","width":"350px"}},
-        {"name":"script_desc","title":lang.description,"breakpoints":"xs"},
-        {"name":"script_data","title":"Script","breakpoints":"all"},
-        {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
-      ],
-      "empty": lang.empty,
-      "rows": $.ajax({
-        dataType: 'json',
-        url: '/api/v1/get/filters/all',
-        jsonp: false,
-        error: function () {
-          console.log('Cannot draw filter table');
-        },
-        success: function (data) {
-          $.each(data, function (i, item) {
+    $('#filter_table').DataTable({
+      processing: true,
+      serverSide: false,
+      language: lang_datatables,
+      ajax: {
+        type: "GET",
+        url: "/api/v1/get/filters/all",
+        dataSrc: function(json){
+          $.each(json, function (i, item) {
             if (item.active == 1) {
               item.active = '<span id="active-script" class="badge fs-5 bg-success">' + lang.active + '</span>';
             } else {
@@ -1135,38 +1138,51 @@ jQuery(function($){
               '</div>';
             item.chkbox = '<input type="checkbox" data-id="filter_item" name="multi_select" value="' + item.id + '" />'
           });
-        }
-      }),
-      "paging": {
-        "enabled": true,
-        "limit": 5,
-        "size": pagination_size
-      },
-      "state": {
-        "enabled": true
-      },
-      "filtering": {
-        "enabled": true,
-        "delay": 1200,
-        "position": "left",
-        "connectors": false,
-        "placeholder": lang.filter_table
-      },
-      "sorting": {
-        "enabled": true
-      },
-      "on": {
-        "destroy.ft.table": function(e, ft){
-          $('.refresh_table').attr('disabled', 'true');
-        },
-        "ready.ft.table": function(e, ft){
-          table_mailbox_ready(ft, 'filter_table');
-        },
-        "after.ft.filtering": function(e, ft){
-          table_mailbox_ready(ft, 'filter_table');
+
+          console.log(json);
+          return json;
         }
       },
-      "toggleSelector": "table tbody span.footable-toggle"
+      columns: [
+          {
+            title: 'ID',
+            data: 'id'
+          },
+          {
+            title: lang.active,
+            data: 'active'
+          },
+          {
+            title: lang.filter_type,
+            data: 'Type'
+          },
+          {
+            title: lang.owner,
+            data: 'username'
+          },
+          {
+            title: lang.description,
+            data: 'script_desc'
+          },
+          {
+            title: 'Script',
+            data: 'script_data'
+          },
+          {
+            title: lang.action,
+            data: null,
+            render: function (data, type) {
+              return `<div class="btn-group">
+                <a href="/edit/admin/admin" class="btn btn-xs btn-xs-half btn-secondary">
+                  <i class="bi bi-pencil-fill"></i> Bearbeiten
+                </a>
+                <a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="admin" class="btn btn-xs btn-xs-half btn-danger">
+                  <i class="bi bi-trash"></i> Entfernen
+                </a>
+              </div>`;
+            }
+          },
+      ]
     });
   };
 
@@ -1177,12 +1193,12 @@ jQuery(function($){
   draw_domain_table();
   draw_mailbox_table();
   draw_resource_table();
-  // draw_alias_table();
-  // draw_aliasdomain_table();
-  // draw_sync_job_table();
-  // draw_filter_table();
+  draw_alias_table();
+  draw_aliasdomain_table();
+  draw_sync_job_table();
+  draw_filter_table();
   draw_bcc_table();
-  // draw_recipient_map_table();
-  // draw_tls_policy_table();
+  draw_recipient_map_table();
+  draw_tls_policy_table();
 
 });

+ 1 - 1
data/web/templates/admin.twig

@@ -63,7 +63,7 @@
 
 <script type='text/javascript'>
 var lang = {{ lang_admin|raw }};
-var lang_datatables = {{ lang_datatables|raw }}
+var lang_datatables = {{ lang_datatables|raw }};
 var admin_username = '{{ mailcow_cc_username }}';
 var csrf_token = '{{ csrf_token }}';
 var pagination_size = '{{ pagination_size }}';

+ 2 - 4
data/web/templates/admin/tab-config-admins.twig

@@ -1,9 +1,8 @@
 <div role="tabpanel" class="tab-pane fade show active" id="tab-config-admins" role="tabpanel" aria-labelledby="tab-config-admins">
     <div class="card mb-4">
       <div class="card-header bg-danger text-white">{{ lang.admin.admin_details }}</div>
+      <table id="adminstable" class="table table-striped dt-responsive nowrap w-100"></table>
       <div class="card-body">
-        <table id="adminstable" class="table table-striped dt-responsive nowrap" style="width:100%">
-        </table>
         <div class="mass-actions-admin">
           <div class="btn-group">
             <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="admins" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>
@@ -224,9 +223,8 @@
 
   <div class="card mb-4">
     <div class="card-header">{{ lang.admin.domain_admins }}</div>
+    <table id="domainadminstable" class="table table-striped dt-responsive nowrap w-100"></table>
     <div class="card-body">
-      <table id="domainadminstable" class="table table-striped dt-responsive nowrap" style="width:100%">
-      </table>
       <div class="mass-actions-admin">
         <div class="btn-group">
           <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="domain_admins" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>

+ 1 - 0
data/web/templates/debug.twig

@@ -322,6 +322,7 @@
 
 <script type='text/javascript'>
   var lang = {{ lang_admin|raw }};
+  var lang_datatables = {{ lang_datatables|raw }};
   var csrf_token = '{{ csrf_token }}';
   var log_pagination_size = '{{ log_pagination_size }}';
 </script>

+ 1 - 1
data/web/templates/mailbox.twig

@@ -47,7 +47,7 @@
 <script type='text/javascript'>
   var acl = '{{ acl_json|raw }}';
   var lang = {{ lang_mailbox|raw }};
-  var lang_datatables = {{ lang_datatables|raw }}
+  var lang_datatables = {{ lang_datatables|raw }};
   var csrf_token = '{{ csrf_token }}';
   var pagination_size = '{{ pagination_size }}';
   var role = '{{ role }}';

+ 0 - 6
data/web/templates/mailbox/page-size.twig

@@ -1,6 +0,0 @@
-<li><a class="dropdown-item" href="#" data-page-size="3">{{ lang.mailbox.table_size_show_n|format(3) }}</a></li>
-<li><a class="dropdown-item" href="#" data-page-size="10">{{ lang.mailbox.table_size_show_n|format(10) }}</a></li>
-<li><a class="dropdown-item" href="#" data-page-size="20">{{ lang.mailbox.table_size_show_n|format(20) }}</a></li>
-<li><a class="dropdown-item" href="#" data-page-size="50">{{ lang.mailbox.table_size_show_n|format(50) }}</a></li>
-<li><a class="dropdown-item" href="#" data-page-size="100">{{ lang.mailbox.table_size_show_n|format(100) }}</a></li>
-<li><a class="dropdown-item" href="#" data-page-size="200">{{ lang.mailbox.table_size_show_n|format(200) }}</a></li>

+ 0 - 12
data/web/templates/mailbox/tab-bcc.twig

@@ -5,12 +5,6 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button class="btn btn-xs btn-success" href="#" data-acl="{{ acl.bcc_maps }}" data-bs-toggle="modal" data-bs-target="#addBCCModalAdmin"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_bcc_entry }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_bcc_table" data-table="bcc_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}
-         
-        </button>
-        <ul class="dropdown-menu" data-table-id="bcc_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
     <p style="margin:10px" class="text-muted">{{ lang.mailbox.bcc_info|raw }}</p>
@@ -39,12 +33,6 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button class="btn btn-xs btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addRecipientMapModalAdmin"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_recipient_map_entry }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_recipient_map_table" data-table="recipient_map_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}
-         
-        </button>
-        <ul class="dropdown-menu" data-table-id="recipient_map_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
     <p style="margin:10px" class="text-muted">{{ lang.mailbox.recipient_map_info }}</p>

+ 1 - 8
data/web/templates/mailbox/tab-domain-aliases.twig

@@ -5,17 +5,10 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button class="btn btn-xs btn-success" href="#" data-acl="{{ acl.alias_domains }}" data-bs-toggle="modal" data-bs-target="#addAliasDomainModal"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_domain_alias }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_aliasdomain_table" data-table="aliasdomain_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}
-        </button>
-        <ul class="dropdown-menu" data-table-id="aliasdomain_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
 {#    <div class="mass-actions-mailbox" data-actions-header="true"></div>#}
-    <div class="table-responsive">
-      <table id="aliasdomain_table" class="table table-striped"></table>
-    </div>
+    <table id="aliasdomain_table" class="table table-striped dt-responsive nowrap w-100"></table>
     <div class="mass-actions-mailbox">
       <div class="btn-group">
         <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="alias-domain" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>

+ 1 - 5
data/web/templates/mailbox/tab-domains.twig

@@ -7,14 +7,10 @@
           <button class="btn btn-xs btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addDomainModal"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_domain }}</button>
         {% endif %}
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_domain_table" data-table="domain_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}</button>
-        <ul class="dropdown-menu" data-table-id="domain_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
 {#    <div class="mass-actions-mailbox" data-actions-header="true"></div>#}
-    <table id="domain_table" class="table table-striped dt-responsive nowrap" style="width:100%"></table>
+    <table id="domain_table" class="table table-striped dt-responsive nowrap w-100"></table>
     <div class="mass-actions-mailbox">
       <div class="btn-group">
         <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="domain" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>

+ 1 - 7
data/web/templates/mailbox/tab-filters.twig

@@ -5,19 +5,13 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button class="btn btn-xs btn-success" href="#" data-acl="{{ acl.filters }}" data-bs-toggle="modal" data-bs-target="#addFilterModalAdmin"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_filter }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_filter_table" data-table="filter_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}</button>
-        <ul class="dropdown-menu" data-table-id="filter_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
     <div class="card-body">
       <p class="text-muted">{{ lang.mailbox.sieve_info|raw }}</p><br>
     </div>
 {#    <div class="mass-actions-mailbox" data-actions-header="true"></div>#}
-    <div class="table-responsive">
-      <table class="table table-striped" id="filter_table"></table>
-    </div>
+    <table id="filter_table" class="table table-striped dt-responsive nowrap w-100"></table>
     <div class="mass-actions-mailbox">
       <div class="btn-group" data-acl="{{ acl.filters }}">
         <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="filter_item" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>

+ 0 - 4
data/web/templates/mailbox/tab-mailboxes.twig

@@ -5,10 +5,6 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button class="btn btn-xs btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addMailboxModal"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_mailbox }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_mailbox_table" data-table="mailbox_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}</button>
-        <ul class="dropdown-menu" data-table-id="mailbox_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
     <div class="mass-actions-mailbox d-none d-sm-flex" data-actions-header="true"></div>

+ 1 - 7
data/web/templates/mailbox/tab-mbox-aliases.twig

@@ -5,19 +5,13 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button class="btn btn-xs btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addAliasModal"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_alias }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_alias_table" data-table="alias_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}</button>
-        <ul class="dropdown-menu" data-table-id="alias_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
     <div class="card-body text-muted">
       {{ lang.mailbox.alias_domain_alias_hint|raw }}
     </div>
     <!-- <div class="mass-actions-mailbox" data-actions-header="true"></div> -->
-    <div class="table-responsive">
-      <table id="alias_table" class="table table-striped"></table>
-    </div>
+    <table id="alias_table" class="table table-striped dt-responsive nowrap w-100"></table>
     <div class="mass-actions-mailbox">
       <div class="btn-group">
         <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="alias" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>

+ 0 - 4
data/web/templates/mailbox/tab-resources.twig

@@ -5,10 +5,6 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button class="btn btn-xs btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addResourceModal"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_resource }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_resource_table" data-table="resource_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}</button>
-        <ul class="dropdown-menu" data-table-id="resource_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
     <div class="card-body text-muted">

+ 1 - 7
data/web/templates/mailbox/tab-syncjobs.twig

@@ -5,16 +5,10 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button data-acl="{{ acl.syncjobs }}" class="btn btn-xs btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addSyncJobModalAdmin"><i class="bi bi-plus-lg"></i> {{ lang.user.create_syncjob }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_sync_job_table" data-table="sync_job_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}</button>
-        <ul class="dropdown-menu" data-table-id="sync_job_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
     <!-- <div class="mass-actions-mailbox" data-actions-header="true"></div> -->
-    <div class="table-responsive">
-      <table class="table table-striped" id="sync_job_table"></table>
-    </div>
+    <table id="sync_job_table" class="table table-striped dt-responsive nowrap w-100"></table>
     <div class="mass-actions-mailbox">
       <div class="btn-group" data-acl="{{ acl.syncjobs }}">
         <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="syncjob" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>

+ 1 - 7
data/web/templates/mailbox/tab-tls-policy.twig

@@ -5,17 +5,11 @@
       <div class="btn-group ms-auto d-none d-sm-flex">
         <button class="btn btn-xs btn-success" href="#" data-bs-toggle="modal" data-bs-target="#addTLSPolicyMapAdmin"><i class="bi bi-plus-lg"></i> {{ lang.mailbox.add_tls_policy_map }}</button>
         <button class="btn btn-xs btn-secondary refresh_table" data-draw="draw_tls_policy_table" data-table="tls_policy_table">{{ lang.admin.refresh }}</button>
-        <button type="button" class="btn btn-xs btn-secondary dropdown-toggle" data-bs-toggle="dropdown">{{ lang.mailbox.table_size }}</button>
-        <ul class="dropdown-menu" data-table-id="tls_policy_table" role="menu">
-          {% include 'mailbox/page-size.twig' %}
-        </ul>
       </div>
     </div>
     <p style="margin:10px" class="text-muted">{{ lang.mailbox.tls_policy_maps_info|raw }}</p>
 {#    <div class="mass-actions-mailbox" data-actions-header="true"></div>#}
-    <div class="table-responsive">
-      <table class="table table-striped" id="tls_policy_table"></table>
-    </div>
+    <table id="tls_policy_table" class="table table-striped dt-responsive nowrap w-100"></table>
     <div class="mass-actions-mailbox">
       <div class="btn-group">
         <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="tls-policy-map" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>

Some files were not shown because too many files changed in this diff