From c9184a9badfb1d90b679c7118d3d8fc0a6504f94 Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 17 May 2017 21:17:00 +0200 Subject: [PATCH] Various fixes and changes --- data/web/css/admin.css | 6 +- data/web/css/mailbox.css | 11 +- data/web/css/mailcow.css | 8 -- data/web/css/user.css | 24 ++++ data/web/inc/functions.inc.php | 212 +++++++++++++++------------------ data/web/inc/header.inc.php | 1 + data/web/js/admin.js | 2 +- data/web/js/mailbox.js | 1 - data/web/js/user.js | 157 +++++++++++++++++++++++- data/web/json_api.php | 62 +++++++++- data/web/mailbox.php | 2 +- data/web/user.php | 88 ++++---------- 12 files changed, 369 insertions(+), 205 deletions(-) create mode 100644 data/web/css/user.css diff --git a/data/web/css/admin.css b/data/web/css/admin.css index de6fbfa0..a8365f6d 100644 --- a/data/web/css/admin.css +++ b/data/web/css/admin.css @@ -18,4 +18,8 @@ body { body.modal-open { overflow-y:scroll; padding-right: inherit !important; -} \ No newline at end of file +} +.mass-actions-admin { + user-select: none; + padding:10px 0 10px 0; +} diff --git a/data/web/css/mailbox.css b/data/web/css/mailbox.css index b76f3f29..79e88255 100644 --- a/data/web/css/mailbox.css +++ b/data/web/css/mailbox.css @@ -18,15 +18,12 @@ table.footable>tbody>tr.footable-empty>td { padding: 10px; background: #F5F5F5; } - -#alias_table { - cursor:pointer; -} -#alias_table .footable-paging { - cursor: auto; -} @media (min-width: 992px) { .container { width: 80%; } } +.mass-actions-mailbox { + user-select: none; + padding:10px 0 10px 10px; +} diff --git a/data/web/css/mailcow.css b/data/web/css/mailcow.css index 3978fcd9..adfc063b 100644 --- a/data/web/css/mailcow.css +++ b/data/web/css/mailcow.css @@ -63,11 +63,3 @@ body.modal-open { max-width: 550px; z-index: 2000; } -.mass-actions-mailbox { - user-select: none; - padding:10px 0 10px 10px; -} -.mass-actions-admin { - user-select: none; - padding:10px 0 10px 0; -} diff --git a/data/web/css/user.css b/data/web/css/user.css new file mode 100644 index 00000000..605a1010 --- /dev/null +++ b/data/web/css/user.css @@ -0,0 +1,24 @@ +table.footable>tbody>tr.footable-empty>td { + font-size:15px !important; + font-style:italic; +} +.pagination a { + text-decoration: none !important; +} +.panel panel-default { + overflow: visible !important; +} +.table-responsive { + overflow: visible !important; +} +.footer-add-item { + display:block; + text-align: center; + font-style: italic; + padding: 10px; + background: #F5F5F5; +} +.mass-actions-user { + user-select: none; + padding:10px 0 10px 0; +} diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index a1a6d935..40e6a86a 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -867,7 +867,7 @@ function delete_policy_list_item($postarray) { return true; } function get_syncjobs($username = null) { - // 'username' can be be set, if not, default to mailcow_cc_username + // 'username' can be set, if not set, defaults to mailcow_cc_username global $lang; global $pdo; $data = array(); @@ -880,7 +880,10 @@ function get_syncjobs($username = null) { $username = $_SESSION['mailcow_cc_username']; } try { - $stmt = $pdo->prepare("SELECT *, CONCAT(LEFT(`password1`, 3), '…') as `password1_short` + $stmt = $pdo->prepare("SELECT *, + CONCAT(LEFT(`password1`, 3), '...') AS `password1_short`, + `active` AS `active_int`, + CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active` FROM `imapsync` WHERE `user2` = :username"); $stmt->execute(array(':username' => $username)); @@ -1096,126 +1099,107 @@ function add_syncjob($postarray) { } function edit_syncjob($postarray) { // Array items - // 'username' can be set, defaults to mailcow_cc_username global $lang; global $pdo; - if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) { - if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) { + if (!is_array($postarray['id'])) { + $ids = array(); + $ids[] = $postarray['id']; + } + else { + $ids = $postarray['id']; + } + + foreach ($ids as $id) { + $is_now = get_syncjob_details($id); + if (!empty($is_now)) { + $username = $is_now['user2']; + $user1 = (!empty($postarray['user1'])) ? $postarray['user1'] : $is_now['user1']; + $active = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int']; + $delete2duplicates = (isset($postarray['delete2duplicates'])) ? $postarray['delete2duplicates'] : $is_now['delete2duplicates']; + $delete1 = (isset($postarray['delete1'])) ? $postarray['delete1'] : $is_now['delete1']; + $port1 = (!empty($postarray['port1'])) ? $postarray['port1'] : $is_now['port1']; + $password1 = (!empty($postarray['password1'])) ? $postarray['password1'] : $is_now['password1']; + $host1 = (!empty($postarray['host1'])) ? $postarray['host1'] : $is_now['host1']; + $subfolder2 = (!empty($postarray['subfolder2'])) ? $postarray['subfolder2'] : $is_now['subfolder2']; + $enc1 = (!empty($postarray['enc1'])) ? $postarray['enc1'] : $is_now['enc1']; + $mins_interval = (!empty($postarray['mins_interval'])) ? $postarray['mins_interval'] : $is_now['mins_interval']; + $exclude = (!empty($postarray['exclude'])) ? $postarray['exclude'] : $is_now['exclude']; + $maxage = (!empty($postarray['maxage'])) ? $postarray['maxage'] : $is_now['maxage']; + } + else { $_SESSION['return'] = array( 'type' => 'danger', 'msg' => sprintf($lang['danger']['access_denied']) ); return false; } - else { - $username = $postarray['username']; + if (empty($subfolder2)) { + $subfolder2 = ""; + } + if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) { + $maxage = "0"; + } + if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['access_denied']) + ); + return false; + } + if (!filter_var($mins_interval, FILTER_VALIDATE_INT, array('options' => array('min_range' => 10, 'max_range' => 3600)))) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['access_denied']) + ); + return false; + } + if (!is_valid_domain_name($host1)) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['access_denied']) + ); + return false; + } + if ($enc1 != "TLS" && $enc1 != "SSL" && $enc1 != "PLAIN") { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['access_denied']) + ); + return false; + } + if (@preg_match("/" . $exclude . "/", null) === false) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['access_denied']) + ); + return false; + } + try { + $stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1, `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active + WHERE `id` = :id"); + $stmt->execute(array( + ':delete1' => $delete1, + ':id' => $id, + ':exclude' => $exclude, + ':maxage' => $maxage, + ':subfolder2' => $subfolder2, + ':host1' => $host1, + ':user1' => $user1, + ':password1' => $password1, + ':mins_interval' => $mins_interval, + ':port1' => $port1, + ':enc1' => $enc1, + ':delete2duplicates' => $delete2duplicates, + ':active' => $active, + )); + } + catch(PDOException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'MySQL: '.$e + ); + return false; } - } - else { - $username = $_SESSION['mailcow_cc_username']; - } - - $active = intval($postarray['active']); - $delete2duplicates = intval($postarray['delete2duplicates']); - $delete1 = intval($postarray['delete1']); - $id = $postarray['id']; - $port1 = $postarray['port1']; - $host1 = $postarray['host1']; - $password1 = $postarray['password1']; - $exclude = $postarray['exclude']; - $maxage = $postarray['maxage']; - $subfolder2 = $postarray['subfolder2']; - $user1 = $postarray['user1']; - $mins_interval = $postarray['mins_interval']; - $enc1 = $postarray['enc1']; - - if (empty($subfolder2)) { - $subfolder2 = ""; - } - if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) { - $maxage = "0"; - } - if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) - ); - return false; - } - if (!filter_var($mins_interval, FILTER_VALIDATE_INT, array('options' => array('min_range' => 10, 'max_range' => 3600)))) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) - ); - return false; - } - if (!is_valid_domain_name($host1)) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) - ); - return false; - } - if ($enc1 != "TLS" && $enc1 != "SSL" && $enc1 != "PLAIN") { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) - ); - return false; - } - if (@preg_match("/" . $exclude . "/", null) === false) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) - ); - return false; - } - try { - $stmt = $pdo->prepare("SELECT `user2` FROM `imapsync` - WHERE `user2` = :user2 AND `id` = :id"); - $stmt->execute(array(':user2' => $username, ':id' => $id)); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - } - catch(PDOException $e) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => 'MySQL: '.$e - ); - return false; - } - if (empty($num_results)) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) - ); - return false; - } - try { - $stmt = $pdo->prepare("UPDATE `imapsync` set `delete1` = :delete1, `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active - WHERE `user2` = :user2 AND `id` = :id"); - $stmt->execute(array( - ':user2' => $username, - ':delete1' => $delete1, - ':id' => $id, - ':exclude' => $exclude, - ':maxage' => $maxage, - ':subfolder2' => $subfolder2, - ':host1' => $host1, - ':user1' => $user1, - ':password1' => $password1, - ':mins_interval' => $mins_interval, - ':port1' => $port1, - ':enc1' => $enc1, - ':delete2duplicates' => $delete2duplicates, - ':active' => $active, - )); - } - catch(PDOException $e) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => 'MySQL: '.$e - ); - return false; } $_SESSION['return'] = array( 'type' => 'success', @@ -2479,7 +2463,7 @@ function dkim_delete_key($postarray) { $selector = $redis->hGet('DKIM_SELECTORS', $domain); $redis->hDel('DKIM_PUB_KEYS', $domain); $redis->hDel('DKIM_PRIV_KEYS', $selector . '.' . $domain); - $redis->hDel('DKIM_SELECTORS', $selector); + $redis->hDel('DKIM_SELECTORS', $domain); } catch (RedisException $e) { $_SESSION['return'] = array( diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php index e9240376..170335e3 100644 --- a/data/web/inc/header.inc.php +++ b/data/web/inc/header.inc.php @@ -21,6 +21,7 @@ ' : null; ?> ' : null; ?> +' : null; ?> diff --git a/data/web/js/admin.js b/data/web/js/admin.js index 711070dd..9d6642b9 100644 --- a/data/web/js/admin.js +++ b/data/web/js/admin.js @@ -56,7 +56,7 @@ $(document).ready(function() { url: '/api/v1/' + api_url, jsonp: false, complete: function (data) { - location.assign(window.location); + location.reload(true); } }); }) diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index 0ec0d5e2..100c9235 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -88,7 +88,6 @@ $(document).ready(function() { $('#ConfirmDeleteModal').modal('hide'); });; }); - }); jQuery(function($){ diff --git a/data/web/js/user.js b/data/web/js/user.js index 8d9450be..7959cc5a 100644 --- a/data/web/js/user.js +++ b/data/web/js/user.js @@ -22,12 +22,165 @@ $(document).ready(function() { // Init Bootstrap Switch $.fn.bootstrapSwitch.defaults.onColor = 'success'; - $("[name='tls_out']").bootstrapSwitch(); - $("[name='tls_in']").bootstrapSwitch(); + $("#tls_out").bootstrapSwitch(); + $("#tls_in").bootstrapSwitch(); // Log modal $('#logModal').on('show.bs.modal', function(e) { var logText = $(e.relatedTarget).data('log-text'); $(e.currentTarget).find('#logText').html('
' + logText + '
'); }); + + // Collect values of input fields with name multi_select with same data-id to js array multi_data[data-id] + var multi_data = []; + $(document).on('change', 'input[name=multi_select]:checkbox', function() { + if ($(this).is(':checked') && $(this).data('id')) { + var id = $(this).data('id'); + if (typeof multi_data[id] == "undefined") { + multi_data[id] = []; + } + multi_data[id].push($(this).val()); + } + else { + var id = $(this).data('id'); + multi_data[id].splice($.inArray($(this).val(), multi_data[id]),1); + } + }); + // Select checkbox by click on parent tr + $(document).on('click', 'tbody>tr', function(e) { + if (e.target.type == "checkbox") { + e.stopPropagation(); + } else { + var checkbox = $(this).find(':checkbox'); + checkbox.trigger('click'); + } + }); + // Select or deselect all checkboxes with same data-id + $(document).on('click', '#toggle_multi_select_all', function(e) { + e.preventDefault(); + id = $(this).data("id"); + multi_data[id] = []; + var all_checkboxes = $("input[data-id=" + id + "]:enabled"); + all_checkboxes.prop("checked", !all_checkboxes.prop("checked")).change(); + }); + // General API edit actions + $(document).on('click', '#edit_selected', function(e) { + e.preventDefault(); + var id = $(this).data('id'); + if (typeof multi_data[id] == "undefined") return; + data_array = multi_data[id]; + api_url = $(this).data('api-url'); + api_attr = $(this).data('api-attr'); + if (Object.keys(data_array).length !== 0) { + $.ajax({ + type: "POST", + dataType: "json", + data: { "items": JSON.stringify(data_array), "attr": JSON.stringify(api_attr), "csrf_token": csrf_token }, + url: '/api/v1/' + api_url, + jsonp: false, + complete: function (data) { + // var reponse = (JSON.parse(data.responseText)); + // console.log(reponse.type); + // console.log(reponse.msg); + location.assign(window.location); + } + }); + } + }); + // General API delete actions + $(document).on('click', '#delete_selected', function(e) { + e.preventDefault(); + var id = $(this).data('id'); + if (typeof multi_data[id] == "undefined" || multi_data[id] == "") return; + data_array = multi_data[id]; + api_url = $(this).data('api-url'); + $(document).on('show.bs.modal','#ConfirmDeleteModal', function () { + $("#ItemsToDelete").empty(); + for (var i in data_array) { + $("#ItemsToDelete").append("
  • " + data_array[i] + "
  • "); + } + }) + $('#ConfirmDeleteModal').modal({ + backdrop: 'static', + keyboard: false + }) + .one('click', '#IsConfirmed', function(e) { + $.ajax({ + type: "POST", + dataType: "json", + data: { "items": JSON.stringify(data_array), "csrf_token": csrf_token }, + url: '/api/v1/' + api_url, + jsonp: false, + complete: function (data) { + location.reload(true); + } + }); + }) + .one('click', '#isCanceled', function(e) { + $('#ConfirmDeleteModal').modal('hide'); + });; + }); +}); +jQuery(function($){ + // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery + var entityMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/', + '`': '`', + '=': '=' + }; + function escapeHtml(string) { + return String(string).replace(/[&<>"'`=\/]/g, function (s) { + return entityMap[s]; + }); + } + function draw_sync_job_table() { + ft_aliasdomain_table = FooTable.init('#sync_job_table', { + "columns": [ + {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"}, + {"sorted": true,"name":"server_w_port","title":"Server"}, + {"name":"enc1","title":lang.encryption}, + {"name":"user1","title":lang.username}, + {"name":"exclude","title":lang.excludes}, + {"name":"mins_interval","title":lang.interval + " (min)"}, + {"name":"last_run","title":lang.last_run}, + {"name":"log","title":"Log"}, + {"name":"active","filterable": false,"style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, + {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + ], + "empty": lang.empty, + "rows": $.ajax({ + dataType: 'json', + url: '/api/v1/get/syncjob', + jsonp: false, + error: function () { + console.log('Cannot draw sync job table'); + }, + success: function (data) { + $.each(data, function (i, item) { + item.log = 'Open logs' + item.exclude = '' + item.exclude + '' + item.server_w_port = item.host1 + ':' + item.port1; + item.action = '
    ' + + ' ' + lang.edit + '' + + '
    '; + item.chkbox = ''; + }); + } + }), + "paging": { + "enabled": true, + "limit": 5, + "size": pagination_size + }, + "sorting": { + "enabled": true + } + }); + } + draw_sync_job_table(); }); \ No newline at end of file diff --git a/data/web/json_api.php b/data/web/json_api.php index 0301659a..a2d08e39 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -151,7 +151,19 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } break; - + } + break; + case "syncjob": + switch ($object) { + default: + $data = get_syncjobs($object); + if (!isset($data) || empty($data)) { + echo '{}'; + } + else { + echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + } + break; } break; case "resource": @@ -183,7 +195,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo '{}'; } break; - default: $data = mailbox_get_resource_details($object); if (!isset($data) || empty($data)) { @@ -193,7 +204,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } break; - } break; case "fwdhost": @@ -226,7 +236,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } break; - } break; case "alias-domain": @@ -258,7 +267,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo '{}'; } break; - default: $data = mailbox_get_alias_domains($object); if (!isset($data) || empty($data)) { @@ -756,6 +764,50 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u )); } break; + case "syncjob": + if (isset($_POST['items']) && isset($_POST['attr'])) { + $items = (array)json_decode($_POST['items'], true); + $attr = (array)json_decode($_POST['attr'], true); + $postarray = array_merge(array('id' => $items), $attr); + if (is_array($postarray['id'])) { + if (edit_syncjob($postarray) === false) { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Edit failed' + )); + } + exit(); + } + else { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'success', + 'msg' => 'Task completed' + )); + } + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Incomplete post data' + )); + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Incomplete post data' + )); + } + break; case "resource": if (isset($_POST['items']) && isset($_POST['attr'])) { $items = (array)json_decode($_POST['items'], true); diff --git a/data/web/mailbox.php b/data/web/mailbox.php index b04ae968..b10bb414 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -152,7 +152,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];