diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index 55295bc363..cb566db577 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -46,6 +46,7 @@ class mail_ui 'importMessage' => True, 'importMessageFromVFS2DraftAndDisplay'=>True, 'subscription' => True, + 'folderManagement' => true, ); /** @@ -592,6 +593,12 @@ class mail_ui 'onExecute' => 'javaScript:app.mail.unsubscribe_folder', 'group' => $group, ), + 'foldermanagement' => array( + 'caption' => 'Folder Management ...', + 'enabled' => 'javaScript:app.mail.mail_CheckFolderNoSelect', + 'onExecute' => 'javaScript:app.mail.folderManagement', + 'group' => $group, + ), 'sieve' => array( 'caption' => 'Mail filter', 'onExecute' => 'javaScript:app.mail.edit_sieve', @@ -697,6 +704,7 @@ class mail_ui unset($tree_actions['add']); unset($tree_actions['move']); unset($tree_actions['delete']); + unset($tree_actions['foldermanagement']); // manage folders should not affect the ability to subscribe or unsubscribe // to existing folders, it should only affect add/rename/move/delete } @@ -3725,9 +3733,10 @@ class mail_ui /** * ajax_deleteFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax) * @param string $_folderName folder to delete + * @param boolean $_return = false wheter return the success value (true) or send response to client (false) * @return nothing */ - function ajax_deleteFolder($_folderName) + function ajax_deleteFolder($_folderName, $_return = false) { //error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName)); $success = false; @@ -3805,22 +3814,10 @@ class mail_ui $msg = lang("refused to delete folder INBOX"); } } + if ($_return) return $success; $response = egw_json_response::get(); if ($success) { - $folders2return = egw_cache::getCache(egw_cache::INSTANCE,'email','folderObjects'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1); - if (isset($folders2return[$this->mail_bo->profileID])) - { - //error_log(__METHOD__.__LINE__.array2string($folders2return[$this->mail_bo->profileID])); - if (empty($subFolders)) $subFolders = array($folderName); - //error_log(__METHOD__.__LINE__.array2string($subFolders)); - foreach($subFolders as $i => $f) - { - //error_log(__METHOD__.__LINE__.$f.'->'.array2string($folders2return[$this->mail_bo->profileID][$f])); - if (isset($folders2return[$this->mail_bo->profileID][$f])) unset($folders2return[$this->mail_bo->profileID][$f]); - } - } - egw_cache::setCache(egw_cache::INSTANCE,'email','folderObjects'.trim($GLOBALS['egw_info']['user']['account_id']),$folders2return, $expiration=60*60*1); //error_log(__METHOD__.__LINE__.array2string($oA)); $response->call('app.mail.mail_removeLeaf',$oA); } @@ -4593,4 +4590,67 @@ class mail_ui if(mail_bo::$debug) error_log(__METHOD__."-> No messages selected."); } } + + /** + * Autoloading function to load branches of tree node + * of management folder tree + * + * @param type $_id + */ + function ajax_folderMgmtTree_autoloading ($_id = null) + { + $mail_ui = new mail_ui(); + $_id = $_id? $_id:$_GET['id']; + etemplate_widget_tree::send_quote_json($mail_ui->mail_tree->getTree($_id,'',1,true,false,false,false)); + } + + /** + * Main function to handle folder management dialog + * + * @param array $content content of dialog + */ + function folderManagement (array $content = null) + { + $dtmpl = new etemplate_new('mail.folder_management'); + $profileID = $_GET['acc_id']? $_GET['acc_id']: $content['acc_id']; + $sel_options['tree'] = $this->mail_tree->getTree(null,$profileID, 1, true, false, false); + + if (!is_array($content)) + { + $content = array ('acc_id' => $profileID); + } + + $readonlys = array(); + // Preserv + $preserv = array( + 'acc_id' => $content['acc_id'] // preserve acc id to be used in client-side + ); + $dtmpl->exec('mail.mail_ui.folderManagement', $content,$sel_options,$readonlys,$preserv,2); + } + + /** + * Function to delete folder for management longTask dialog + * it sends successfully deleted folder as response to be + * used in long task response handler. + * + * @param type $_folderName + */ + function ajax_folderMgmt_delete ($_folderName) + { + if ($_folderName) + { + $success = $this->ajax_deleteFolder($_folderName,true); + $response = egw_json_response::get(); + list(,$folderName) = explode(self::$delimiter, $_folderName); + if ($success) + { + $res = $folderName; + } + else + { + $res = lang("Failed to delete %1",$folderName); + } + $response->data($res); + } + } } diff --git a/mail/js/app.js b/mail/js/app.js index 847e166bca..82064bf1fb 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -272,6 +272,9 @@ app.classes.mail = AppJS.extend( tree.input.loadJSONObject(tree._htmlencode_node(state)); } + break; + case 'mail.folder_management': + this.egw.message('If you would like to select multiple folders in one action, you can hold ctrl key then select a folder as start range and another folder within a same level as end range, all folders in between will be selected or unselected based on their current status.','info',true); } }, @@ -1618,6 +1621,38 @@ app.classes.mail = AppJS.extend( // setting class of row, the old style }, + /** + * mail_emptySpam + * + * @param {object} action + * @param {object} _senders + */ + mail_emptySpam: function(action,_senders) { + var server = _senders[0].iface.id.split('::'); + var activeFilters = this.mail_getActiveFilters(); + var self = this; + + this.egw.message(this.egw.lang('empty junk')); + egw.json('mail.mail_ui.ajax_emptySpam',[server[0], activeFilters['selectedFolder']? activeFilters['selectedFolder']:null],function(){self.unlock_tree();}) + .sendRequest(true); + + // Directly delete any trash cache for selected server + if(window.localStorage) + { + for(var i = 0; i < window.localStorage.length; i++) + { + var key = window.localStorage.key(i); + + // Find directly by what the key would look like + if(key.indexOf('cached_fetch_mail::{"selectedFolder":"'+server[0]+'::') == 0 && + key.toLowerCase().indexOf(egw.lang('junk').toLowerCase()) > 0) + { + window.localStorage.removeItem(key); + } + } + } + }, + /** * mail_emptyTrash * @@ -4525,5 +4560,180 @@ app.classes.mail = AppJS.extend( { et2_dialog.alert('You need to save the message as draft first before to be able to save it into VFS','Save into VFS','info'); } + }, + + /** + * Folder Management, opens the folder magnt. dialog + * with the selected acc_id from index tree + * + * @param {egw action object} _action actions + * @param {object} _senders selected node + */ + folderManagement: function (_action,_senders) + { + var acc_id = parseInt(_senders[0].id); + this.egw.open_link('mail.mail_ui.folderManagement&acc_id='+acc_id, '_blank', '720x500'); + }, + + /** + * Show ajax-loader when the autoloading get started + * + * @param {type} _id item id + * @param {type} _widget tree widget + * @returns {Boolean} + */ + folderMgmt_autoloadingStart: function(_id, _widget) + { + return this.subscription_autoloadingStart (_id, _widget); + }, + + /** + * Revert back the icon after autoloading is finished + * @returns {Boolean} + */ + folderMgmt_autoloadingEnd: function(_id, _widget) + { + return true; + }, + + /** + * + * @param {type} _ids + * @param {type} _widget + * @returns {undefined} + */ + folderMgmt_onSelect: function(_ids, _widget) + { + // Flag to reset selected items + var resetSelection = false; + + var self = this; + + /** + * helper function to multiselect range of nodes in same level + * + * @param {string} _a start node id + * @param {string} _b end node id + * @param {string} _branch totall node ids in the level + */ + var rangeSelector = function(_a,_b, _branch) + { + var branchItems = _branch.split(_widget.input.dlmtr); + var _aIndex = _widget.input.getIndexById(_a); + var _bIndex = _widget.input.getIndexById(_b); + if (_bIndex<_aIndex) + { + var tmpIndex = _aIndex; + _aIndex = _bIndex; + _bIndex = tmpIndex; + } + for(var i =_aIndex;i<=_bIndex;i++) + { + self.folderMgmt_setCheckbox(_widget, branchItems[i], !_widget.input.isItemChecked(branchItems[i])); + } + }; + + // extract items ids + var itemIds = _ids.split(_widget.input.dlmtr); + + if(itemIds.length == 2) // there's a range selected + { + var branch = _widget.input.getSubItems(_widget.input.getParentId(itemIds[0])); + // Set range of selected/unselected + rangeSelector(itemIds[0], itemIds[1], branch); + } + else if(itemIds.length != 1) + { + resetSelection = true; + } + + if (resetSelection) + { + _widget.input._unselectItems(); + } + }, + + /** + * Set enable/disable checkbox + * + * @param {object} _widget tree widget + * @param {string} _itemId item tree id + * @param {boolean} _stat - status to be set on checkbox true/false + */ + folderMgmt_setCheckbox: function (_widget, _itemId, _stat) + { + if (_widget) + { + _widget.input.setCheck(_itemId, _stat); + _widget.input.setSubChecked(_itemId,_stat); + } + }, + + /** + * + * @param {type} _id + * @param {type} _widget + * @TODO: Implement onCheck handler in order to select or deselect subItems + * of a checked parent node + */ + folderMgmt_onCheck: function (_id, _widget) + { + var selected = _widget.input.getAllChecked(); + if (selected && selected.split(_widget.input.dlmtr).length > 5) + { + egw.message(egw.lang('If you would like to select multiple folders in one action, you can hold ctrl key then select a folder as start range and another folder within a same level as end range, all folders in between will be selected or unselected based on their current status.')); + } + }, + + /** + * Detele button handler + * triggers longTask dialog and send delete operation url + * + */ + folderMgmt_deleteBtn: function () + { + var tree = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('tree'); + var menuaction= 'mail.mail_ui.ajax_folderMgmt_delete'; + + var callbackDialog = function(_btn) + { + if (_btn === et2_dialog.YES_BUTTON) + { + if (tree) + { + var selFolders = tree.input.getAllChecked(); + if (selFolders) + { + var selFldArr = selFolders.split(tree.input.dlmtr); + var msg = egw.lang('Deleting %1 folders in progress ...', selFldArr.length); + et2_dialog.long_task(function(_val, _resp){ + console.log(_val, _resp); + if (_val && _resp.type !== 'error') + { + var stat = []; + var folderName = ''; + for(var i=0;i + + + + + diff --git a/mail/templates/pixelegg/app.css b/mail/templates/pixelegg/app.css index beebbd0489..e1c806715d 100755 --- a/mail/templates/pixelegg/app.css +++ b/mail/templates/pixelegg/app.css @@ -701,15 +701,18 @@ div.mailPreviewHeaders #mail-index_previewAttachmentArea.visible { background-position: left top; background-repeat: no-repeat; } -.mail_subscription_header { +.mail_subscription_header, +.mail_folder_management_header { font-weight: bold; font-size: 150%; padding-bottom: 20px; } -#mail-subscribe table.et2_grid tr td { +#mail-subscribe table.et2_grid tr td, +#mail-folder_management table.et2_grid tr td { padding: 0px; } -#mail-subscribe table.et2_grid tr td div.et2_box { +#mail-subscribe table.et2_grid tr td div.et2_box, +#mail-folder_management table.et2_grid tr td .mail_subscription_header { height: 500px; overflow: auto; } @@ -1080,10 +1083,12 @@ body { .egw_fw_ui_category_content .selectedTreeRow_lor { background-color: #ffc200; color: #1e1e1e; + text-decoration: none; } .egw_fw_ui_category_content .standartTreeRow_lor { - background-color: #f2f2f2; + background-color: rgba(103, 159, 210, 0.2); color: #1e1e1e; + text-decoration: none; } /* ################################################################################## * E-Mail Dialog "Compose" @@ -1670,11 +1675,13 @@ div#displayToolbar-menulist img { * * ################################################################################## */ -#mail-subscribe table.et2_grid tr td { +#mail-subscribe table.et2_grid tr td, +#mail-folder_management table.et2_grid tr td { padding: 0px; /*Label*/ } -#mail-subscribe table.et2_grid tr td .mail_subscription_header { +#mail-subscribe table.et2_grid tr td .mail_subscription_header, +#mail-folder_management table.et2_grid tr td .mail_subscription_header { /*line-height: 270%;*/ margin: 0.6em 0 0; font-weight: lighter; @@ -1683,12 +1690,28 @@ div#displayToolbar-menulist img { font-size: 150%; padding-bottom: 20px; } -#mail-subscribe table.et2_grid tr td div.et2_box { +#mail-subscribe table.et2_grid tr td div.et2_box, +#mail-folder_management table.et2_grid tr td div.et2_box { margin-top: 16px; height: 484px; overflow: auto; } -.mail_subscription_header { +#mail-subscribe .selectedTreeRow, +#mail-folder_management .selectedTreeRow, +#mail-subscribe .selectedTreeRow_lor, +#mail-folder_management .selectedTreeRow_lor { + background-color: #ffc200; + color: #1e1e1e; + text-decoration: none; +} +#mail-subscribe .standartTreeRow_lor, +#mail-folder_management .standartTreeRow_lor { + background-color: rgba(103, 159, 210, 0.2); + color: #1e1e1e; + text-decoration: none; +} +.mail_subscription_header, +.mail_folder_management_header { font-weight: bold; font-size: 150%; padding-bottom: 20px; diff --git a/mail/templates/pixelegg/app.less b/mail/templates/pixelegg/app.less index e448e8b13c..dbd8103be6 100755 --- a/mail/templates/pixelegg/app.less +++ b/mail/templates/pixelegg/app.less @@ -287,9 +287,9 @@ body { // color - selected tree item - .selectedTreeRow, .selectedTreeRow_lor {background-color: @egw_color_1_a; color: @gray_90;} + .selectedTreeRow, .selectedTreeRow_lor {background-color: @egw_color_1_a; color: @gray_90;text-decoration: none;} - .standartTreeRow_lor {background-color: @gray_5; color: @gray_90;} + .standartTreeRow_lor {background-color: rgba(103, 159, 210, 0.2); color: @gray_90;text-decoration: none;} /*new mail in FOlder span.standartTreeRow b {color: @egw_color_2_a;} */ @@ -863,7 +863,7 @@ div#displayToolbar-menulist{ * * ################################################################################## */ -#mail-subscribe{ +#mail-subscribe, #mail-folder_management{ table.et2_grid tr td { padding: 0px; @@ -891,13 +891,13 @@ div#displayToolbar-menulist{ } // color - selected tree item - .selectedTreeRow,.selectedTreeRow_lor {background-color: @egw_color_1_a; color: @gray_90;} + .selectedTreeRow,.selectedTreeRow_lor {background-color: @egw_color_1_a; color: @gray_90;text-decoration: none;} // color - hover over standard tree item - .standartTreeRow_lor {background-color: @gray_5; color: @gray_90;} + .standartTreeRow_lor {background-color: rgba(103, 159, 210, 0.2); color: @gray_90;text-decoration: none;} } // #mail-subscribe -.mail_subscription_header +.mail_subscription_header, .mail_folder_management_header { font-weight: bold; font-size: 150%;