Browse Source

Merge pull request #133 from andryyy/footable

Footable
André Peters 8 years ago
parent
commit
aa31769e92

+ 1 - 1
.gitignore

@@ -4,7 +4,7 @@ data/conf/dovecot/dovecot-master.passwd
 mailcow.conf
 mailcow.conf_backup
 data/conf/nginx/listen*active
-data/conf/nginx/server_name*active
+data/conf/nginx/server_name.active
 data/conf/postfix/sql
 data/conf/dovecot/sql
 data/web/inc/vars.local.inc.php

+ 0 - 1
data/conf/nginx/server_name.active

@@ -1 +0,0 @@
-server_name logs.servercow.de autodiscover.* autoconfig.*;

+ 8 - 0
data/conf/rspamd/local.d/ratelimit.conf

@@ -0,0 +1,8 @@
+rates {
+  to = [100, 0.033333333];
+  to_ip = [30, 0.025];
+  to_ip_from = [20, 0.01666666667];
+  bounce_to = [10, 0.000555556];
+  bounce_to_ip = [5, 0.000277778];
+  user = [20, 0.01666666667];
+}

File diff suppressed because it is too large
+ 0 - 0
data/web/css/footable.bootstrap.min.css


+ 1 - 1
data/web/inc/functions.inc.php

@@ -4422,7 +4422,7 @@ function mailbox_get_resource_details($resource) {
     $resourcedata['name'] = $row['username'];
     $resourcedata['kind'] = $row['kind'];
     $resourcedata['multiple_bookings'] = $row['multiple_bookings'];
-    $resourcedata['multiple_bookings_int'] = $row['multiple_bookings'];
+    $resourcedata['multiple_bookings_int'] = $row['multiple_bookings_int'];
     $resourcedata['description'] = $row['name'];
     $resourcedata['active'] = $row['active'];
     $resourcedata['active_int'] = $row['active_int'];

+ 1 - 0
data/web/inc/header.inc.php

@@ -15,6 +15,7 @@
 <link rel="stylesheet" href="/css/bootstrap-select.min.css">
 <link rel="stylesheet" href="/css/bootstrap-slider.min.css">
 <link rel="stylesheet" href="/css/bootstrap-switch.min.css">
+<link rel="stylesheet" href="/css/footable.bootstrap.min.css">
 <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700&subset=latin,latin-ext">
 <link rel="stylesheet" href="/inc/languages.min.css">
 <link rel="stylesheet" href="/css/mailcow.css">

File diff suppressed because it is too large
+ 7 - 0
data/web/js/footable.min.js


+ 237 - 61
data/web/js/mailbox.js

@@ -1,66 +1,242 @@
 $(document).ready(function() {
 	// Show element counter for tables
 	$('[data-toggle="tooltip"]').tooltip();
-	var rowCountDomainAlias = $('#domainaliastable >tbody >#data').length;
-	var rowCountDomain = $('#domaintable >tbody >#data').length;
-	var rowCountMailbox = $('#mailboxtable >tbody >#data').length;
-	var rowCountAlias = $('#aliastable >tbody >#data').length;
-	var rowCountResource = $('#resourcetable >tbody >#data').length;
-	$("#numRowsDomainAlias").text(rowCountDomainAlias);
-	$("#numRowsDomain").text(rowCountDomain);
-	$("#numRowsMailbox").text(rowCountMailbox);
-	$("#numRowsAlias").text(rowCountAlias);
-	$("#numRowsResource").text(rowCountResource);
+  function humanFileSize(bytes) {
+    if(Math.abs(bytes) < 1024) {
+        return bytes + ' B';
+    }
+    var units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
+    var u = -1;
+    do {
+        bytes /= 1024;
+        ++u;
+    } while(Math.abs(bytes) >= 1024 && u < units.length - 1);
+    return bytes.toFixed(1)+' '+units[u];
+  }
+  
+  $.ajax({
+    dataType: 'json',
+    url: '/json_api.php?action=domain_table_data',
+    jsonp: false,
+    error: function () {
+      alert('Cannot receive history');
+    },
+    success: function (data) {
+      $.each(data, function (i, item) {
+        item.aliases = item.aliases_in_domain + " / " + item.max_num_aliases_for_domain;
+        item.mailboxes = item.mboxes_in_domain + " / " + item.max_num_mboxes_for_domain;
+        item.quota = humanFileSize(item.quota_used_in_domain) + " / " + humanFileSize(item.max_quota_for_domain);
+        item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
+        item.action = '<div class="btn-group">' +
+          '<a href="/edit.php?domain=' + item.domain_name + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
+          '<a href="/delete.php?domain=' + item.domain_name + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
+					'</div>';
+      });
+      $('#domain_table').footable({
+        "columns": [
+          {"sorted": true,"name":"domain_name","title":lang.domain,"style":{"width":"250px"}},
+          {"name":"aliases","title":lang.aliases,"breakpoints":"xs sm"},
+          {"name":"mailboxes","title":lang.mailboxes},
+          {"name":"quota","title":lang.domain_quota},
+          {"name":"max_quota_for_mbox","title":lang.mailbox_quota},
+          {"name":"backupmx","title":lang.backup_mx,"breakpoints":"xs sm"},
+          {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
+          {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
+        ],
+        "rows": data,
+        "empty": lang.empty,
+        "paging": {
+          "enabled": true,
+          "limit": 5,
+          "size": 25
+        },
+        "filtering": {
+          "enabled": true,
+          "position": "left",
+          "placeholder": lang.search
+        },
+        "sorting": {
+          "enabled": true
+        }
+      });
+    }
+  });
 
-	// Filter table function
-	$.fn.extend({
-		filterTable: function(){
-			return this.each(function(){
-				$(this).on('keyup', function(e){
-					var $this = $(this),
-            search = $this.val().toLowerCase(),
-            target = $this.attr('data-filters'),
-            $target = $(target),
-            $rows = $target.find('tbody #data');
-					$target.find('tbody .filterTable_no_results').remove();
-					if(search == '') {
-						$target.find('tbody #no-data').show();
-						$rows.show();
-					} else {
-						$target.find('tbody #no-data').hide();
-						$rows.each(function(){
-							var $this = $(this);
-							$this.text().toLowerCase().indexOf(search) === -1 ? $this.hide() : $this.show();
-						})
-						if($target.find('tbody #data:visible').size() === 0) {
-							var col_count = $target.find('#data').first().find('td').size();
-							var no_results = $('<tr class="filterTable_no_results"><td colspan="100%">-</td></tr>')
-							$target.find('tbody').prepend(no_results);
-						}
-					}
-				});
-			});
-		}
-	});
-	$('[data-action="filter"]').filterTable();
-	$('.container').on('click', '.panel-heading span.filter', function(e){
-		var $this = $(this),
-		$panel = $this.parents('.panel');
-		$panel.find('.panel-body').slideToggle("fast");
-		if($this.css('display') != 'none') {
-			$panel.find('.panel-body input').focus();
-		}
-	});
-	$('.container').on('click', '.panel-heading .panel-title', function(e){
-		var $this = $(this),
-			$panel = $this.parents('.panel');
-		$panel.find('.table-responsive').slideToggle("fast");
-	});
-	$('.panel-heading .panel-title').addClass('clickable');
-	$('.panel .table-responsive').each(function() {
-		if ($(this).height() > 550) {
-			// If one is too large initially hide all
-			$('.panel .table-responsive').slideUp("fast");
-		}
-	})
+  $.ajax({
+    dataType: 'json',
+    url: '/json_api.php?action=mailbox_table_data',
+    jsonp: false,
+    error: function () {
+      alert('Cannot receive history');
+    },
+    success: function (data) {
+      $.each(data, function (i, item) {
+        item.quota = humanFileSize(item.quota_used) + " / " + humanFileSize(item.quota);
+        item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
+        item.action = '<div class="btn-group">' +
+          '<a href="/edit.php?mailbox=' + item.username + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
+          '<a href="/delete.php?mailbox=' + item.username + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
+					'</div>';
+        item.in_use = '<div class="progress">' +
+				  '<div class="progress-bar progress-bar-' + item.percent_class + ' role="progressbar" aria-valuenow="' + item.percent_in_use + '" aria-valuemin="0" aria-valuemax="100" ' +
+          'style="min-width:2em;width:' + item.percent_in_use + '%">' + item.percent_in_use + '%' + '</div></div>';
+
+      });
+      $('#mailbox_table').footable({
+        "columns": [
+          {"sorted": true,"name":"username","title":lang.username,"style":{"width":"250px"}},
+          {"name":"name","title":lang.fname,"breakpoints":"xs sm"},
+          {"name":"domain","title":lang.domain},
+          {"name":"quota","title":lang.domain_quota},
+          {"name":"spam_aliases","title":lang.spam_aliases},
+          {"name":"in_use","type":"html","title":lang.in_use},
+          {"name":"messages","title":lang.msg_num,"breakpoints":"xs sm"},
+          {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
+          {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
+        ],
+        "empty": lang.empty,
+        "rows": data,
+        "paging": {
+          "enabled": true,
+          "limit": 5,
+          "size": 25
+        },
+        "filtering": {
+          "enabled": true,
+          "position": "left",
+          "placeholder": lang.search
+        },
+        "sorting": {
+          "enabled": true
+        }
+      });
+    }
+  });
+
+  $.ajax({
+    dataType: 'json',
+    url: '/json_api.php?action=resource_table_data',
+    jsonp: false,
+    error: function () {
+      alert('Cannot receive history');
+    },
+    success: function (data) {
+      $.each(data, function (i, item) {
+        item.action = '<div class="btn-group">' +
+          '<a href="/edit.php?resource=' + item.name + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
+          '<a href="/delete.php?resource=' + item.name + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
+					'</div>';
+      });
+      $('#resources_table').footable({
+        "columns": [
+          {"sorted": true,"name":"description","title":lang.description,"style":{"width":"250px"}},
+          {"name":"kind","title":lang.kind,"breakpoints":"xs sm"},
+          {"name":"domain","title":lang.domain},
+          {"name":"multiple_bookings","title":lang.multiple_bookings},
+          {"name":"domain","title":lang.domain},
+          {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
+          {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
+        ],
+        "empty": lang.empty,
+        "rows": data,
+        "paging": {
+          "enabled": true,
+          "limit": 5,
+          "size": 25
+        },
+        "filtering": {
+          "enabled": true,
+          "position": "left",
+          "placeholder": lang.search
+        },
+        "sorting": {
+          "enabled": true
+        }
+      });
+    }
+  });
+
+  $.ajax({
+    dataType: 'json',
+    url: '/json_api.php?action=domain_alias_table_data',
+    jsonp: false,
+    error: function () {
+      alert('Cannot receive history');
+    },
+    success: function (data) {
+      $.each(data, function (i, item) {
+        item.action = '<div class="btn-group">' +
+          '<a href="/edit.php?aliasdomain=' + item.alias_domain + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
+          '<a href="/delete.php?aliasdomain=' + item.alias_domain + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
+					'</div>';
+      });
+      $('#aliasdomain_table').footable({
+        "columns": [
+          {"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}},
+          {"name":"target_domain","title":lang.target_domain,"breakpoints":"xs sm"},
+          {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
+          {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
+        ],
+        "empty": lang.empty,
+        "rows": data,
+        "paging": {
+          "enabled": true,
+          "limit": 5,
+          "size": 25
+        },
+        "filtering": {
+          "enabled": true,
+          "position": "left",
+          "placeholder": lang.search
+        },
+        "sorting": {
+          "enabled": true
+        }
+      });
+    }
+  });
+
+  $.ajax({
+    dataType: 'json',
+    url: '/json_api.php?action=alias_table_data',
+    jsonp: false,
+    error: function () {
+      alert('Cannot receive history');
+    },
+    success: function (data) {
+      $.each(data, function (i, item) {
+        if (item.is_catch_all == 1) {
+          item.address = '<div class="label label-default">Catch-All</div> ' + item.address;
+        }
+        item.action = '<div class="btn-group">' +
+          '<a href="/edit.php?alias=' + item.address + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
+          '<a href="/delete.php?alias=' + item.address + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
+					'</div>';
+      });
+      $('#alias_table').footable({
+        "columns": [
+          {"sorted": true,"name":"address","title":lang.alias,"style":{"width":"250px"}},
+          {"name":"goto","title":lang.target_address},
+          {"name":"domain","title":lang.domain},
+          {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
+          {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
+        ],
+        "empty": lang.empty,
+        "rows": data,
+        "paging": {
+          "enabled": true,
+          "limit": 5,
+          "size": 5
+        },
+        "filtering": {
+          "enabled": true,
+          "position": "left",
+          "placeholder": lang.search
+        },
+        "sorting": {
+          "enabled": true
+        }
+      });
+    }
+  });
 });

+ 101 - 4
data/web/json_api.php

@@ -2,13 +2,104 @@
 require_once 'inc/prerequisites.inc.php';
 error_reporting(E_ALL);
 if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) {
-  if ($_GET['action'] && $_GET['object']) {
+  if (isset($_GET['action'])) {
     $action = $_GET['action'];
-    $object = $_GET['object'];
     switch ($action) {
+      case "domain_table_data":
+        $domains = mailbox_get_domains();
+        if (!empty($domains)) {
+          foreach ($domains as $domain) {
+            $data[] = mailbox_get_domain_details($domain);
+          }
+          if (!isset($data) || empty($data)) {
+            echo '{}';
+          }
+          else {
+            echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+          }
+        }
+        break;
+      case "mailbox_table_data":
+        $domains = mailbox_get_domains();
+        if (!empty($domains)) {
+          foreach ($domains as $domain) {
+            $mailboxes = mailbox_get_mailboxes($domain);
+            if (!empty($mailboxes)) {
+              foreach ($mailboxes as $mailbox) {
+                $data[] = mailbox_get_mailbox_details($mailbox);
+              }
+            }
+          }
+          if (!isset($data) || empty($data)) {
+            echo '{}';
+          }
+          else {
+            echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+          }
+        }
+        break;
+      case "resource_table_data":
+        $domains = mailbox_get_domains();
+        if (!empty($domains)) {
+          foreach ($domains as $domain) {
+            $resources = mailbox_get_resources($domain);
+            if (!empty($resources)) {
+              foreach ($resources as $resource) {
+                $data[] = mailbox_get_resource_details($resource);
+              }
+            }
+          }
+          if (!isset($data) || empty($data)) {
+            echo '{}';
+          }
+          else {
+            echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+          }
+        }
+        break;
+      case "domain_alias_table_data":
+        $domains = mailbox_get_domains();
+        if (!empty($domains)) {
+          foreach ($domains as $domain) {
+            $alias_domains = mailbox_get_alias_domains($domain);
+            if (!empty($alias_domains)) {
+              foreach ($alias_domains as $alias_domain) {
+                $data[] = mailbox_get_alias_domain_details($alias_domain);
+              }
+            }
+          }
+          if (!isset($data) || empty($data)) {
+            echo '{}';
+          }
+          else {
+            echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+          }
+        }
+        break;
+      case "alias_table_data":
+        $domains = array_merge(mailbox_get_domains(), mailbox_get_alias_domains());
+        if (!empty($domains)) {
+          foreach ($domains as $domain) {
+            $aliases = mailbox_get_aliases($domain);
+            if (!empty($aliases)) {
+              foreach ($aliases as $alias) {
+                $data[] = mailbox_get_alias_details($alias);
+              }
+            }
+          }
+          if (!isset($data) || empty($data)) {
+            echo '{}';
+          }
+          else {
+            echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+          }
+        }
+        break;
       case "get_mailbox_details":
+        if (!isset($_GET['object'])) { return false; }
+        $object = $_GET['object'];
         $data = mailbox_get_mailbox_details($object);
-        if (!$data || empty($data)) {
+        if (!isset($data) || empty($data)) {
           echo '{}';
         }
         else {
@@ -16,8 +107,10 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
         }
         break;
       case "get_domain_details":
+        if (!isset($_GET['object'])) { return false; }
+        $object = $_GET['object'];
         $data = mailbox_get_domain_details($object);
-        if (!$data || empty($data)) {
+        if (!isset($data) || empty($data)) {
           echo '{}';
         }
         else {
@@ -25,6 +118,8 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
         }
         break;
       case "get_u2f_reg_challenge":
+        if (!isset($_GET['object'])) { return false; }
+        $object = $_GET['object'];
         if (
           ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin")
           &&
@@ -40,6 +135,8 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
         }
         break;
       case "get_u2f_auth_challenge":
+        if (!isset($_GET['object'])) { return false; }
+        $object = $_GET['object'];
         if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) {
           $reqs = json_encode($u2f->getAuthenticateData(get_u2f_registrations($object)));
           $_SESSION['authReq']  = $reqs;

+ 36 - 373
data/web/mailbox.php

@@ -5,16 +5,22 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 require_once "inc/header.inc.php";
 $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
 ?>
+<style>
+table.footable>tbody>tr.footable-empty>td {
+  font-size:15px !important;
+  font-style:italic;
+}
+.pagination a {
+  text-decoration: none !important;
+}
+</style>
 <div class="container">
 	<div class="row">
 		<div class="col-md-12">
 			<div class="panel panel-default">
 				<div class="panel-heading">
-				<h3 class="panel-title"><?=$lang['mailbox']['domains'];?> <span class="badge" id="numRowsDomain"></span></h3>
+				<h3 class="panel-title"><?=$lang['mailbox']['domains'];?></h3>
 				<div class="pull-right">
-					<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
-						<i class="glyphicon glyphicon-filter"></i>
-					</span>
 				<?php
 				if ($_SESSION['mailcow_cc_role'] == "admin"):
 				?>
@@ -24,93 +30,9 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
 				?>
 				</div>
 				</div>
-				<div class="panel-body">
-					<input type="text" class="form-control" id="domaintable-filter" data-action="filter" data-filters="#domaintable" placeholder="Filter" />
-				</div>
-				<div class="table-responsive">
-				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domaintable">
-					<thead>
-						<tr>
-							<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
-							<th class="sort-table" style="min-width: 81px;"><?=$lang['mailbox']['aliases'];?></th>
-							<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['mailboxes'];?></th>
-							<th class="sort-table" style="min-width: 172px;"><?=$lang['mailbox']['mailbox_quota'];?></th>
-							<th class="sort-table" style="min-width: 117px;"><?=$lang['mailbox']['domain_quota'];?></th>
-							<?php
-							if ($_SESSION['mailcow_cc_role'] == "admin"):
-							?>
-								<th class="sort-table" style="min-width: 105px;"><?=$lang['mailbox']['backup_mx'];?></th>
-							<?php
-							endif;
-							?>
-							<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
-							<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
-						</tr>
-					</thead>
-					<tbody>
-					<?php
-          $domains = mailbox_get_domains();
-	        if (!empty($domains)):
-					foreach ($domains as $domain):
-            $domaindata = mailbox_get_domain_details($domain);
-					?>
-						<tr id="data">
-							<td><?=htmlspecialchars($domaindata['domain_name']);?></td>
-							<td><?=$domaindata['aliases_in_domain'];?> / <?=$domaindata['max_num_aliases_for_domain'];?></td>
-							<td><?=$domaindata['mboxes_in_domain'];?> / <?=$domaindata['max_num_mboxes_for_domain'];?></td>
-							<td><?=formatBytes($domaindata['max_quota_for_mbox']);?></td>
-							<td><?=formatBytes($domaindata['quota_used_in_domain'], 2);?> / <?=formatBytes($domaindata['max_quota_for_domain'], 2);?></td>
-							<?php
-							if ($_SESSION['mailcow_cc_role'] == "admin"):
-							?>
-								<td><?=$domaindata['backupmx'];?></td>
-							<?php
-							endif;
-							?>
-							<td><?=$domaindata['active'];?></td>
-							<?php
-							if ($_SESSION['mailcow_cc_role'] == "admin"):
-							?>
-								<td style="text-align: right;">
-									<div class="btn-group">
-										<a href="/edit.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
-										<a href="/delete.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
-									</div>
-								</td>
-							<?php
-							else:
-							?>
-								<td style="text-align: right;">
-									<div class="btn-group">
-										<a href="/edit.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
-									</div>
-								</td>
-						</tr>
-              <?php
-              endif;
-            endforeach;
-            else:
-							?>
-              <tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record_single'];?></td></tr>
-            <?php
-            endif;
-            ?>
-					</tbody>
-						<?php
-						if ($_SESSION['mailcow_cc_role'] == "admin"):
-						?>
-					<tfoot>
-						<tr id="no-data">
-							<td colspan="999" style="text-align: center; font-style: normal; border-top: 1px solid #e7e7e7;">
-								<a href="/add.php?domain"><?=$lang['mailbox']['add_domain'];?></a>
-							</td>
-						</tr>
-					</tfoot>
-						<?php
-						endif;
-						?>
-				</table>
-				</div>
+          <div class="table-responsive">
+            <table id="domain_table" class="table table-striped"></table>
+          </div>
 			</div>
 		</div>
 	</div>
@@ -118,91 +40,14 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
 		<div class="col-md-12">
 			<div class="panel panel-default">
 				<div class="panel-heading">
-					<h3 class="panel-title"><?=$lang['mailbox']['mailboxes'];?> <span class="badge" id="numRowsMailbox"></span></h3>
+					<h3 class="panel-title"><?=$lang['mailbox']['mailboxes'];?></h3>
 					<div class="pull-right">
-						<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
-							<i class="glyphicon glyphicon-filter"></i>
-						</span>
 						<a href="/add.php?mailbox"><span class="glyphicon glyphicon-plus"></span></a>
 					</div>
 				</div>
-				<div class="panel-body">
-					<input type="text" class="form-control" id="mailboxtable-filter" data-action="filter" data-filters="#mailboxtable" placeholder="Filter" />
-				</div>
-				<div class="table-responsive">
-				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="mailboxtable">
-					<thead>
-						<tr>
-							<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['username'];?></th>
-							<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['fname'];?></th>
-							<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
-							<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['quota'];?></th>
-							<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['spam_aliases'];?></th>
-							<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['in_use'];?></th>
-							<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['msg_num'];?></th>
-							<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
-							<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
-						</tr>
-					</thead>
-					<tbody>
-						<?php
-					if (!empty($domains)) {
-            foreach (mailbox_get_domains() as $domain) {
-              $mailboxes = mailbox_get_mailboxes($domain);
-              if (!empty($mailboxes)) {
-                foreach ($mailboxes as $mailbox) {
-                  $mailboxdata = mailbox_get_mailbox_details($mailbox);
-						?>
-						<tr id="data">
-							<td><?=($mailboxdata['is_relayed'] == "0") ? htmlspecialchars($mailboxdata['username']) : '<span data-toggle="tooltip" title="Relayed"><i class="glyphicon glyphicon-forward"></i>' . htmlspecialchars($mailboxdata['username']) . '</span>';?></td>
-							<td><?=htmlspecialchars($mailboxdata['name'], ENT_QUOTES, 'UTF-8');?></td>
-							<td><?=htmlspecialchars($mailboxdata['domain']);?></td>
-							<td><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?></td>
-							<td><?=$mailboxdata['spam_aliases'];?></td>
-							<td style="min-width:120px;">
-								<div class="progress">
-									<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
-										<?=$mailboxdata['percent_in_use'];?>%
-									</div>
-								</div>
-							</td>
-							<td><?=$mailboxdata['messages'];?></td>
-							<td><?=$mailboxdata['active'];?></td>
-							<td style="text-align: right;">
-								<div class="btn-group">
-									<a href="/edit.php?mailbox=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
-									<a href="/delete.php?mailbox=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
-									<?php if ($_SESSION['mailcow_cc_role'] == "admin"): ?>
-                  <a href="/index.php?duallogin=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-success"><span class="glyphicon glyphicon-user"></span> Login</a>
-                  <?php endif; ?>
-								</div>
-							</td>
-						</tr>
-						<?php
-                }
-              }
-              else {
-                  ?>
-                  <tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
-                  <?php
-              }
-            }
-					} else {
-					?>
-						<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
-						<?php
-					}
-						?>
-					</tbody>
-					<tfoot>
-						<tr id="no-data">
-							<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
-								<a href="/add.php?mailbox"><?=$lang['mailbox']['add_mailbox'];?></a>
-							</td>
-						</tr>
-					</tfoot>
-				</table>
-				</div>
+        <div class="table-responsive">
+          <table id="mailbox_table" class="table table-striped"></table>
+        </div>
 			</div>
 		</div>
 	</div>
@@ -210,76 +55,14 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
 		<div class="col-md-12">
 			<div class="panel panel-default">
 				<div class="panel-heading">
-					<h3 class="panel-title"><?=$lang['mailbox']['resources'];?> <span class="badge" id="numRowsResource"></span></h3>
+					<h3 class="panel-title"><?=$lang['mailbox']['resources'];?></h3>
 					<div class="pull-right">
-						<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
-							<i class="glyphicon glyphicon-filter"></i>
-						</span>
 						<a href="/add.php?resource"><span class="glyphicon glyphicon-plus"></span></a>
 					</div>
 				</div>
-				<div class="panel-body">
-					<input type="text" class="form-control" id="resourcetable-filter" data-action="filter" data-filters="#resourcetable" placeholder="Filter" />
-				</div>
-				<div class="table-responsive">
-				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="resourcetable">
-					<thead>
-						<tr>
-							<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['description'];?></th>
-							<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['kind'];?></th>
-							<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
-							<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['multiple_bookings'];?></th>
-							<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
-							<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
-						</tr>
-					</thead>
-					<tbody>
-						<?php
-					if (!empty($domains)) {
-            foreach (mailbox_get_domains() as $domain) {
-              $resources = mailbox_get_resources($domain);
-              if (!empty($resources)) {
-                foreach ($resources as $resource) {
-                  $resourcedata = mailbox_get_resource_details($resource);
-						?>
-						<tr id="data">
-							<td><?=htmlspecialchars($resourcedata['description'], ENT_QUOTES, 'UTF-8');?></td>
-							<td><?=$resourcedata['kind'];?></td>
-							<td><?=htmlspecialchars($resourcedata['domain']);?></td>
-							<td><?=$resourcedata['multiple_bookings'];?></td>
-							<td><?=$resourcedata['active'];?></td>
-							<td style="text-align: right;">
-								<div class="btn-group">
-									<a href="/edit.php?resource=<?=urlencode($resourcedata['name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
-									<a href="/delete.php?resource=<?=urlencode($resourcedata['name']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
-								</div>
-							</td>
-						</tr>
-						<?php
-                }
-              }
-              else {
-                  ?>
-                  <tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
-                  <?php
-              }
-            }
-					} else {
-						?>
-						<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
-						<?php
-					}
-						?>
-					</tbody>
-					<tfoot>
-						<tr id="no-data">
-							<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
-								<a href="/add.php?resource"><?=$lang['mailbox']['add_resource'];?></a>
-							</td>
-						</tr>
-					</tfoot>
-				</table>
-				</div>
+        <div class="table-responsive">
+          <table id="resources_table" class="table table-striped"></table>
+        </div>
 			</div>
 		</div>
 	</div>
@@ -287,72 +70,14 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
 		<div class="col-md-12">
 			<div class="panel panel-default">
 				<div class="panel-heading">
-					<h3 class="panel-title"><?=$lang['mailbox']['domain_aliases'];?> <span class="badge" id="numRowsDomainAlias"></span></h3>
+					<h3 class="panel-title"><?=$lang['mailbox']['domain_aliases'];?></h3>
 					<div class="pull-right">
-						<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
-							<i class="glyphicon glyphicon-filter"></i>
-						</span>
 						<a href="/add.php?aliasdomain"><span class="glyphicon glyphicon-plus"></span></a>
 					</div>
 				</div>
-				<div class="panel-body">
-					<input type="text" class="form-control" id="domainaliastable-filter" data-action="filter" data-filters="#domainaliastable" placeholder="Filter" />
-				</div>
-				<div class="table-responsive">
-				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainaliastable">
-					<thead>
-						<tr>
-							<th class="sort-table" style="min-width: 67px;"><?=$lang['mailbox']['alias'];?></th>
-							<th class="sort-table" style="min-width: 127px;"><?=$lang['mailbox']['target_domain'];?></th>
-							<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
-							<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
-						</tr>
-					</thead>
-					<tbody>
-					<?php
-				if (!empty($domains)) {
-          foreach (mailbox_get_domains() as $domain) {
-            $alias_domains = mailbox_get_alias_domains($domain);
-            if (!empty($alias_domains)) {
-              foreach ($alias_domains as $alias_domain) {
-                $aliasdomaindata = mailbox_get_alias_domain_details($alias_domain);
-                ?>
-                <tr id="data">
-                  <td><?=htmlspecialchars($aliasdomaindata['alias_domain']);?></td>
-                  <td><?=htmlspecialchars($aliasdomaindata['target_domain']);?></td>
-                  <td><?=$aliasdomaindata['active'];?></td>
-                  <td style="text-align: right;">
-                    <div class="btn-group">
-                      <a href="/edit.php?aliasdomain=<?=urlencode($aliasdomaindata['alias_domain']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
-                      <a href="/delete.php?aliasdomain=<?=urlencode($aliasdomaindata['alias_domain']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
-                    </div>
-                  </td>
-                </tr>
-                <?php
-              }
-            }
-            else {
-	        ?>
-                  <tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
-	        <?php
-            }
-          }
-				} else {
-          ?>
-						<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
-					<?php
-				}
-					?>
-					</tbody>
-					<tfoot>
-						<tr id="no-data">
-							<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
-								<a href="/add.php?aliasdomain"><?=$lang['mailbox']['add_domain_alias'];?></a>
-							</td>
-						</tr>
-					</tfoot>
-				</table>
-				</div>
+        <div class="table-responsive">
+          <table id="aliasdomain_table" class="table table-striped"></table>
+        </div>
 			</div>
 		</div>
 	</div>
@@ -361,87 +86,25 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
 		<div class="col-md-12">
 			<div class="panel panel-default">
 				<div class="panel-heading">
-					<h3 class="panel-title"><?=$lang['mailbox']['aliases'];?> <span class="badge" id="numRowsAlias"></span></h3>
+					<h3 class="panel-title"><?=$lang['mailbox']['aliases'];?></h3>
 					<div class="pull-right">
-						<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
-							<i class="glyphicon glyphicon-filter"></i>
-						</span>
 						<a href="/add.php?alias"><span class="glyphicon glyphicon-plus"></span></a>
 					</div>
 				</div>
-				<div class="panel-body">
-					<input type="text" class="form-control" id="aliastable-filter" data-action="filter" data-filters="#aliastable" placeholder="Filter" />
-				</div>
-				<div class="table-responsive">
-				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="aliastable">
-					<thead>
-						<tr>
-							<th class="sort-table" style="min-width: 67px;"><?=$lang['mailbox']['alias'];?></th>
-							<th class="sort-table" style="min-width: 119px;"><?=$lang['mailbox']['target_address'];?></th>
-							<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
-							<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
-							<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
-						</tr>
-					</thead>
-					<tbody>
-					<?php
-				if (!empty($domains)) {
-          foreach (array_merge(mailbox_get_domains(), mailbox_get_alias_domains()) as $domain) {
-            $aliases = mailbox_get_aliases($domain);
-            if (!empty($aliases)) {
-              foreach ($aliases as $alias) {
-                $aliasdata = mailbox_get_alias_details($alias);
-					?>
-						<tr id="data">
-							<td>
-							<?= ($aliasdata['is_catch_all'] == "1") ? '<span class="glyphicon glyphicon-pushpin" aria-hidden="true"></span> Catch-all ' . htmlspecialchars($aliasdata['address']) : htmlspecialchars($aliasdata['address']); ?>
-							</td>
-							<td>
-							<?php
-							foreach(explode(",", $aliasdata['goto']) as $goto) {
-								echo nl2br(htmlspecialchars($goto.PHP_EOL));
-							}
-							?>
-							</td>
-							<td><?=htmlspecialchars($aliasdata['domain']);?></td>
-							<td><?=$aliasdata['active'];?></td>
-							<td style="text-align: right;">
-								<div class="btn-group">
-									<a href="/edit.php?alias=<?=urlencode($aliasdata['address']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
-									<a href="/delete.php?alias=<?=urlencode($aliasdata['address']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
-								</div>
-							</td>
-						</tr>
-						<?php
-							}
-						}
-						else {
-								?>
-								<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
-								<?php
-						}
-          }
-				} else {
-						?>
-						<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
-						<?php
-				}
-						?>
-					</tbody>
-					<tfoot>
-						<tr id="no-data">
-							<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
-								<a href="/add.php?alias"><?=$lang['mailbox']['add_alias'];?></a>
-							</td>
-						</tr>
-					</tfoot>
-				</table>
-				</div>
+        <div class="table-responsive">
+          <table id="alias_table" class="table table-striped"></table>
+        </div>
 			</div>
 		</div>
 	</div>
 </div> <!-- /container -->
-<script src="js/sorttable.js"></script>
+<script type='text/javascript'>
+<?php
+$lang_mailbox = json_encode($lang['mailbox']);
+echo "var lang = ". $lang_mailbox . ";\n";
+?>
+</script>
+<script src="js/footable.min.js"></script>
 <script src="js/mailbox.js"></script>
 <?php
 require_once("inc/footer.inc.php");

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