From 0c7e1040274bd06fea61fb0a48cd74fa277aa844 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Mon, 27 Jul 2015 17:46:45 +0000 Subject: [PATCH] More work in progress of mail tree: -Initiate first level of mailboxes in subscription dialog, and let autoloading do the rest of expensive operation -Save last state of tree in subscription dialog -Uses new approach for comparing subscribed and unsubscribed folders --- mail/inc/class.mail_tree.inc.php | 61 ++++++++++++++++---------- mail/inc/class.mail_ui.inc.php | 65 ++++++++++++++++++++-------- mail/js/app.js | 39 ++++++++++++++++- mail/templates/default/subscribe.xet | 4 +- 4 files changed, 126 insertions(+), 43 deletions(-) diff --git a/mail/inc/class.mail_tree.inc.php b/mail/inc/class.mail_tree.inc.php index 1b8f7c945a..b7d43cfdd4 100644 --- a/mail/inc/class.mail_tree.inc.php +++ b/mail/inc/class.mail_tree.inc.php @@ -97,20 +97,36 @@ class mail_tree * @param string $path a node path * @return array returns an array of data extracted from given node path */ - static function getFolderData ($_path, $_hDelimiter) + static function pathToFolderData ($_path, $_hDelimiter) { list(,$path) = explode(self::$delimiter, $_path); - $parts = explode($_hDelimiter, $path); + $path_chain = $parts = explode($_hDelimiter, $path); $name = array_pop($parts); return array ( 'name' => $name, 'mailbox' => $path, 'parent' => implode($_hDelimiter, $parts), 'text' => $name, - 'tooltip' => $name + 'tooltip' => $name, + 'path' => $path_chain ); } + /** + * Check if a given node has children attribute set + * + * @param array $_node array of a node + * @return int returns 1 if it has children flag set otherwise 0 + */ + private static function nodeHasChildren ($_node) + { + $hasChildren = 0; + if (in_array('\haschildren', $_node['ATTRIBUTES']) || + in_array('\Haschildren', $_node['ATTRIBUTES']) || + in_array('\HasChildren', $_node['ATTRIBUTES'])) $hasChildren = 1; + return $hasChildren; + } + /** * getTree provides tree structure regarding to selected node * @@ -118,22 +134,15 @@ class mail_tree * @param string $_profileID = '' icServer id * @param int|boolean $_openTopLevel = 1 Open top level folders on load if it's set to 1|true, * false|0 leaves them in closed state + * @param $_noCheckboxNS = false no checkbox for namesapaces makes sure to not put checkbox for namespaces node * * @return array returns an array of mail tree structure according to provided node */ - function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1) + function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_noCheckboxNS = false) { //Init mail folders $tree = array(tree::ID=> $_parent?$_parent:0,tree::CHILDREN => array()); $hDelimiter = $this->ui->mail_bo->getHierarchyDelimiter(); - $fn_nodeHasChildren = function ($_node) - { - $hasChildren = 0; - if (in_array('\haschildren', $_node['ATTRIBUTES']) || - in_array('\Haschildren', $_node['ATTRIBUTES']) || - in_array('\HasChildren', $_node['ATTRIBUTES'])) $hasChildren = 1; - return $hasChildren; - }; if ($_parent) list($_profileID) = explode(self::$delimiter, $_parent); @@ -151,20 +160,20 @@ class mail_tree { try { - $nodeInfo = self::getFolderData($_parent, $hDelimiter); - $folders = $this->ui->mail_bo->getFolderArray($nodeInfo['mailbox']); + $nodeInfo = self::pathToFolderData($_parent, $hDelimiter); + $folders = $this->ui->mail_bo->getFolderArray($nodeInfo['mailbox'],false,2); } catch (Exception $ex) { return self::treeLeafNoConnectionArray($_profileID, $ex->getMessage(),array($_profileID), ''); } - + $childrenNode = array(); foreach ($folders as &$node) { $nodeId = $_profileID.self::$delimiter.$node['MAILBOX']; - $nodeData = self::getFolderData($nodeId, $node['delimiter']); + $nodeData = self::pathToFolderData($nodeId, $node['delimiter']); $childrenNode[] = array( tree::ID=> $nodeId, - tree::AUTOLOAD_CHILDREN => $fn_nodeHasChildren($node), + tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($node), tree::CHILDREN =>array(), tree::LABEL => $nodeData['text'], tree::TOOLTIP => $nodeData['tooltip'], @@ -201,6 +210,7 @@ class mail_tree 'sieve' => $accObj->imapServer()->acc_sieve_enabled, 'spamfolder'=> $accObj->imapServer()->acc_folder_junk?true:false ), + tree::NOCHECKBOX => $_noCheckboxNS ); self::setOutStructure($baseNode, $tree,self::$delimiter); } @@ -212,14 +222,21 @@ class mail_tree foreach ($foldersList as $index => $topFolder) { + $nameSpaces = $this->ui->mail_bo->_getNameSpaces(); + $noCheckbox = false; + foreach ($nameSpaces as &$ns) + { + if($_noCheckboxNS && $ns['prefix'] === $index.$hDelimiter) $noCheckbox = true; + } $parentNode = array( tree::ID=>$_profileID.self::$delimiter.$topFolder[$index]['MAILBOX'], - tree::AUTOLOAD_CHILDREN => $fn_nodeHasChildren($topFolder[$index]), + tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($topFolder[$index]), tree::CHILDREN =>array(), tree::LABEL =>lang($topFolder[$index]['MAILBOX']), tree::OPEN => $_openTopLevel, tree::TOOLTIP => lang($topFolder[$index]['MAILBOX']), - tree::CHECKED => $topFolder[$index]['SUBSCRIBED'] + tree::CHECKED => $topFolder[$index]['SUBSCRIBED'], + tree::NOCHECKBOX => $noCheckbox ); if ($index === "INBOX") { @@ -267,7 +284,7 @@ class mail_tree $childNode = array( tree::ID => $nodeId, - tree::AUTOLOAD_CHILDREN => $fn_nodeHasChildren($node), + tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($node), tree::CHILDREN => array(), tree::LABEL => lang($folderName), 'parent' => $parentPath, @@ -387,5 +404,5 @@ class mail_tree $insert['item'][] = $data; //error_log(__METHOD__."() leaving with out=".array2string($out)); } - -} \ No newline at end of file + +} diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index d03db7002a..322f55b3a7 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -238,10 +238,11 @@ class mail_ui /** * Ajax function to request next branch of a tree branch */ - static function ajax_tree_autoloading () + static function ajax_tree_autoloading ($_id = null) { $mail_ui = new mail_ui(); - etemplate_widget_tree::send_quote_json($mail_ui->mail_tree->getTree($_GET['id'])); + $_id = $_id? $_id:$_GET['id']; + etemplate_widget_tree::send_quote_json($mail_ui->mail_tree->getTree($_id,'',1,false)); } /** @@ -262,19 +263,28 @@ class mail_ui { egw_framework::window_close('Missing acc_id!'); } - $sel_options['foldertree'] = $this->getFolderTree(false, $profileId, false, false); - + // Initial tree's options, the rest would be loaded dynamicaly by autoloading, + // triggered from client-side. Also, we keep this here as + $sel_options['foldertree'] = $this->mail_tree->getTree(null,$profileId,1,true); + + //Get all subscribed folder + // as getting all subscribed folder is very fast operation + // we can use it to get a comparison base for folders which + // got subscribed or unsubscribed by the user + try { + $subscribed = $this->mail_bo->icServer->listSubscribedMailboxes('',0,true); + } catch (Exception $ex) { + egw_framework::message($ex->getMessage()); + } + if (!is_array($content)) { $content['foldertree'] = array(); - $allFolders = $this->mail_bo->getFolderObjects(false,false,false,false); - foreach ($allFolders as $folder) + + foreach ($subscribed as $folder) { - $folderName = $profileId . self::$delimiter . $folder->folderName; - if ($folder->subscribed) - { - array_push($content['foldertree'], $folderName); - } + $folderName = $profileId . self::$delimiter . $folder['MAILBOX']; + array_push($content['foldertree'], $folderName); } } else @@ -291,13 +301,30 @@ class mail_ui { $namespace_roots[] = $profileId . self::$delimiter . str_replace($namespace['delimiter'], '', $namespace['prefix']); } - error_log(__METHOD__."() namespace_roots=".array2string($namespace_roots)); - $to_subscribe = array_diff($content['foldertree'], $content['current_subscribed'], $namespace_roots); - $to_unsubscribe = array_diff($content['current_subscribed'], $content['foldertree'], $namespace_roots); + $to_unsubscribe = $to_subscribe = array(); + //$allFoldersData = $this->mail_bo->getFolderArray(null,false,0); + foreach ($content['foldertree'] as $path => $value) + { + list(,$node) = explode($profileId.self::$delimiter, $path); + if ($node) + { + if (is_array($subscribed) && $subscribed[$node] && !$value['value']) $to_unsubscribe []= $node; + if (is_array($subscribed) && !$subscribed[$node] && $value['value']) $to_subscribe [] = $node; + if ($value['value']) $cont[] = $path; + } + + } + $content['foldertree'] = $cont; + // set foldertree options to basic node in order to avoid initial autoloading + // from client side, as no options would trigger that. + $sel_options['foldertree'] = array('id' => '0', 'item'=> array()); foreach(array_merge($to_subscribe, $to_unsubscribe) as $mailbox) { + if (in_array($profileId.self::$delimiter.$mailbox, $namespace_roots, true)) + { + continue; + } $subscribe = in_array($mailbox, $to_subscribe); - list(,$mailbox) = explode(self::$delimiter, $mailbox); // remove profileId and delimiter try { $this->mail_bo->icServer->subscribeMailbox($mailbox, $subscribe); } @@ -329,7 +356,9 @@ class mail_ui // update foldertree in main window $parentFolder='INBOX'; $refreshData = array( - $profileId => lang($parentFolder) + $profileId => lang($parentFolder), + 'subscribed' => $to_subscribe, + 'unsubscribed' => $to_unsubscribe ); $response = egw_json_response::get(); foreach($refreshData as $folder => &$name) @@ -337,7 +366,9 @@ class mail_ui $name = $this->getFolderTree(true, $folder, true, true, false); } // give success/error message to opener and popup itself + //$response->call('opener.app.mail.subscription_refresh',$refreshData); $response->call('opener.app.mail.mail_reloadNode',$refreshData); + egw_framework::refresh_opener($msg, 'mail', null, null, null, null, null, $msg_type); if ($button == 'apply') { @@ -353,7 +384,7 @@ class mail_ui } $preserv['profileId'] = $profileId; - $preserv['current_subscribed'] = $content['foldertree']; + $readonlys = array(); $stmpl->exec('mail.mail_ui.subscription', $content,$sel_options,$readonlys,$preserv,2); diff --git a/mail/js/app.js b/mail/js/app.js index 12ad6597a2..8f19df9c03 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -44,7 +44,11 @@ app.classes.mail = AppJS.extend( timeout: null, request: null }, - + /** + * + */ + subscription_treeLastState : "", + /** * abbrevations for common access rights * @array @@ -277,6 +281,15 @@ app.classes.mail = AppJS.extend( jQuery('input',to.node).focus(); } break; + case 'mail.subscribe': + if (this.subscription_treeLastState != "") + { + var tree = this.et2.getWidgetById('foldertree'); + //Saved state of tree + var state = jQuery.parseJSON(this.subscription_treeLastState); + + tree.input.loadJSONObject(tree._htmlencode_node(state)); + } } }, @@ -3654,7 +3667,29 @@ app.classes.mail = AppJS.extend( var acc_id = parseInt(_senders[0].id); this.egw.open_link('mail.mail_sieve.editVacation&acc_id='+acc_id,'_blank','700x480'); }, - + + subscription_refresh: function(_data) + { + console.log(_data); + }, + + /** + * Submit on apply button and save current tree state + * + * @param {type} _egw + * @param {type} _widget + * @returns {undefined} + */ + subscription_apply: function (_egw, _widget) + { + var tree = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('foldertree'); + if (tree) + { + tree.input._xfullXML = true; + this.subscription_treeLastState = tree.input.serializeTreeToJSON(); + } + this.et2._inst.submit(_widget); + }, /** * Popup the subscription dialog * diff --git a/mail/templates/default/subscribe.xet b/mail/templates/default/subscribe.xet index 2b0107e6cc..8495bd8669 100755 --- a/mail/templates/default/subscribe.xet +++ b/mail/templates/default/subscribe.xet @@ -13,14 +13,14 @@ - +