From 8096304c6a8cd3608a638785a99fd9d98061239f Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Mon, 3 Aug 2015 14:33:18 +0000 Subject: [PATCH] Mail tree phase 2 W.I.P: -Remove depricated getFolderTree method and replace it with getTree from mail_tree class -Fix sorting folders in new folder arrays method -Fix subscription refresh --- mail/inc/class.mail_tree.inc.php | 173 +++++++++++++----------- mail/inc/class.mail_ui.inc.php | 223 ++----------------------------- 2 files changed, 104 insertions(+), 292 deletions(-) diff --git a/mail/inc/class.mail_tree.inc.php b/mail/inc/class.mail_tree.inc.php index aa8ae7c36d..5589a4682a 100644 --- a/mail/inc/class.mail_tree.inc.php +++ b/mail/inc/class.mail_tree.inc.php @@ -167,11 +167,11 @@ class mail_tree * false|0 leaves them in closed state * @param $_noCheckboxNS = false no checkbox for namesapaces makes sure to not put checkbox for namespaces node * @param boolean $_subscribedOnly = false get only subscribed folders - * @param boolean $_getThemAll = false, true will get all folders of the account + * @param boolean $_allInOneGo = false, true will get all folders (dependes on subscribedOnly option) of the account in one go * * @return array returns an array of mail tree structure according to provided node */ - function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_noCheckboxNS = false, $_subscribedOnly= false, $_getThemAll = false) + function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_noCheckboxNS = false, $_subscribedOnly= false, $_allInOneGo = false) { //Init mail folders $tree = array(tree::ID=> $_parent?$_parent:0,tree::CHILDREN => array()); @@ -189,89 +189,106 @@ class mail_tree } } - // User defined folders based on account - $definedFolders = array( - 'Trash' => $this->ui->mail_bo->getTrashFolder(false), - 'Templates' => $this->ui->mail_bo->getTemplateFolder(false), - 'Drafts' => $this->ui->mail_bo->getDraftFolder(false), - 'Sent' => $this->ui->mail_bo->getSentFolder(false), - 'Junk' => $this->ui->mail_bo->getJunkFolder(false), - 'Outbox' => $this->ui->mail_bo->getOutboxFolder(false), - ); - if ($_parent && !self::isAccountNode($_parent)) // Single node loader + try { - try + // User defined folders based on account + $definedFolders = array( + 'Trash' => $this->ui->mail_bo->getTrashFolder(false), + 'Templates' => $this->ui->mail_bo->getTemplateFolder(false), + 'Drafts' => $this->ui->mail_bo->getDraftFolder(false), + 'Sent' => $this->ui->mail_bo->getSentFolder(false), + 'Junk' => $this->ui->mail_bo->getJunkFolder(false), + 'Outbox' => $this->ui->mail_bo->getOutboxFolder(false), + ); + if ($_parent && !self::isAccountNode($_parent)) // Single node loader { $nodeInfo = self::pathToFolderData($_parent, $hDelimiter); - $folders = $this->ui->mail_bo->getFolderArrays($nodeInfo['mailbox'],false,$_getThemAll?0:2, $_subscribedOnly); - } 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::pathToFolderData($nodeId, $node['delimiter']); - $childrenNode[] = array( - tree::ID=> $nodeId, - tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($node), - tree::CHILDREN =>array(), - tree::LABEL => $nodeData['text'], - tree::TOOLTIP => $nodeData['tooltip'], - tree::IMAGE_LEAF => self::$leafImages['folderLeaf'], - tree::IMAGE_FOLDER_OPEN => self::$leafImages['folderOpen'], - tree::IMAGE_FOLDER_CLOSED => self::$leafImages['folderClose'], - tree::CHECKED => $node['SUBSCRIBED'], - 'parent' => $_parent - ); - } - $tree[tree::CHILDREN] = $childrenNode; - } - else //Top Level Nodes loader - { - if (self::isAccountNode($_parent)) - { - $_openTopLevel = 1; - } - else - { - $tree = self::getAccountsRootNode($_profileID, $_noCheckboxNS, $_openTopLevel); - if (!$_profileID && !$_openTopLevel) return $tree; - } - - //List of folders - $foldersList = $this->ui->mail_bo->getFolderArrays(null, true, $_getThemAll?0:2,$_subscribedOnly); - foreach ($foldersList as &$folder) - { - $path = $parent = $parts = explode($folder['delimiter'], $folder['MAILBOX']); - array_pop($parent); - - array_unshift($path, $_profileID); - - $data = array( - tree::ID=>$_profileID.self::$delimiter.$folder['MAILBOX'], - tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($folder), - tree::CHILDREN =>array(), - tree::LABEL =>lang($folder['MAILBOX']), - tree::OPEN => self::getNodeLevel($folder['MAILBOX'], $folder['delimiter']) <= $_openTopLevel?1:0, - tree::TOOLTIP => lang($folder['MAILBOX']), - tree::CHECKED => $folder['SUBSCRIBED'], - tree::NOCHECKBOX => 0, - 'parent' => $parent?$_profileID.self::$delimiter.implode($folder['delimiter'], $parent):$_profileID, - 'path' => $path, - 'folderarray' => $folder - ); - // Set Acl capability for INBOX - if ($folder['MAILBOX'] === "INBOX") + $folders = $this->ui->mail_bo->getFolderArrays($nodeInfo['mailbox'],false,$_allInOneGo?0:2, $_subscribedOnly); + + $childrenNode = array(); + foreach ($folders as &$node) { - $data['data'] = array('acl' => $this->ui->mail_bo->icServer->queryCapability('ACL')); + $nodeId = $_profileID.self::$delimiter.$node['MAILBOX']; + $nodeData = self::pathToFolderData($nodeId, $node['delimiter']); + $childrenNode[] = array( + tree::ID=> $nodeId, + tree::AUTOLOAD_CHILDREN => $_allInOneGo?false:self::nodeHasChildren($node), + tree::CHILDREN =>array(), + tree::LABEL => $nodeData['text'], + tree::TOOLTIP => $nodeData['tooltip'], + tree::IMAGE_LEAF => self::$leafImages['folderLeaf'], + tree::IMAGE_FOLDER_OPEN => self::$leafImages['folderOpen'], + tree::IMAGE_FOLDER_CLOSED => self::$leafImages['folderClose'], + tree::CHECKED => $node['SUBSCRIBED'], + 'parent' => $_parent + ); + } + $tree[tree::CHILDREN] = $childrenNode; + } + else //Top Level Nodes loader + { + if (self::isAccountNode($_parent)) // An account called for open + { + $_openTopLevel = 1; + $tree = self::getAccountsRootNode($_profileID, $_noCheckboxNS, $_openTopLevel); + } + else // Initial accounts|root nodes + { + $tree = self::getAccountsRootNode($_profileID, $_noCheckboxNS, $_openTopLevel); + if (!$_profileID && !$_openTopLevel) return $tree; + } + + //List of folders + $foldersList = $this->ui->mail_bo->getFolderArrays(null, true, $_allInOneGo?0:2,$_subscribedOnly); + foreach ($foldersList as &$folder) + { + $path = $parent = $parts = explode($folder['delimiter'], $folder['MAILBOX']); + array_pop($parent); + + array_unshift($path, $_profileID); + + $data = array( + tree::ID=>$_profileID.self::$delimiter.$folder['MAILBOX'], + tree::AUTOLOAD_CHILDREN => $_allInOneGo?false:self::nodeHasChildren($folder), + tree::CHILDREN =>array(), + tree::LABEL =>lang($folder['MAILBOX']), + tree::OPEN => self::getNodeLevel($folder['MAILBOX'], $folder['delimiter']) <= $_openTopLevel?1:0, + tree::TOOLTIP => lang($folder['MAILBOX']), + tree::CHECKED => $folder['SUBSCRIBED'], + tree::NOCHECKBOX => 0, + 'parent' => $parent?$_profileID.self::$delimiter.implode($folder['delimiter'], $parent):$_profileID, + 'path' => $path, + 'folderarray' => $folder + ); + // Set Acl capability for INBOX + if ($folder['MAILBOX'] === "INBOX") + { + $data['data'] = array('acl' => $this->ui->mail_bo->icServer->queryCapability('ACL')); + } + self::setOutStructure($data, $tree, $folder['delimiter'], true, $this->ui->mail_bo->_getNameSpaces(), $definedFolders); + } + // Structs children of account root node. Used for mail index tree when we do autoloading on account id + if (self::isAccountNode($_parent)) + { + $tree = array( + tree::ID => $_parent, + tree::CHILDREN => $tree[tree::CHILDREN][0][tree::CHILDREN], + tree::LABEL => $tree[tree::CHILDREN][0][tree::LABEL], + tree::IMAGE_LEAF => $tree[tree::CHILDREN][0][tree::IMAGE_LEAF], + tree::IMAGE_FOLDER_OPEN => $tree[tree::CHILDREN][0][tree::IMAGE_FOLDER_OPEN], + tree::IMAGE_FOLDER_CLOSED => $tree[tree::CHILDREN][0][tree::IMAGE_FOLDER_CLOSED], + tree::OPEN => 1, + tree::TOOLTIP => $tree[tree::CHILDREN][0][tree::TOOLTIP] + ); } - self::setOutStructure($data, $tree, $folder['delimiter'], true, $this->ui->mail_bo->_getNameSpaces(), $definedFolders); } - // Structs children of account root node. Used for mail index tree when we do autoloading on account id - if (self::isAccountNode($_parent)) $tree = array(tree::ID => $_parent, tree::CHILDREN => $tree[tree::CHILDREN][0][tree::CHILDREN]); } + catch (Exception $ex) // Catch exceptions + { + //mail_ui::callWizard($ex->getMessage(), false, 'error'); + return self::treeLeafNoConnectionArray($_profileID, $ex->getMessage(),array($_profileID), ''); + } + return $tree; } diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index b0f3a97152..a48021a4c1 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -112,6 +112,7 @@ class mail_ui */ function __construct($run_constructor=true) { + $this->mail_tree = new mail_tree($this); if (!$run_constructor) return; if (mail_bo::$debugTimes) $starttime = microtime (true); @@ -142,7 +143,6 @@ class mail_ui //openConnection gathers SpecialUseFolderInformation and Delimiter Info $this->mail_bo->openConnection(self::$icServerID); } - $this->mail_tree = new mail_tree($this); } catch (Exception $e) { @@ -362,7 +362,7 @@ class mail_ui $response = egw_json_response::get(); foreach($refreshData as $folder => &$name) { - $name = $this->getFolderTree(true, $folder, true, true, false); + $name = $this->mail_tree->getTree($folder, $profileId,1,true,true,true); } // give success/error message to opener and popup itself //$response->call('opener.app.mail.subscription_refresh',$refreshData); @@ -482,7 +482,7 @@ class mail_ui $content[self::$nm_index]['vacationrange'] = $sel_options[self::$nm_index]['vacationrange'] = ''; } //$zstarttime = microtime (true); - $sel_options[self::$nm_index]['foldertree'] = $this->getFolderTree('initial',null,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']); + $sel_options[self::$nm_index]['foldertree'] = $this->mail_tree->getTree(null, null, null, false, true,true ); //$zendtime = microtime(true) - $zstarttime; //error_log(__METHOD__.__LINE__. " time used: ".$zendtime); $content[self::$nm_index]['selectedFolder'] = $this->mail_bo->profileID.self::$delimiter.(!empty($this->mail_bo->sessionData['mailbox'])?$this->mail_bo->sessionData['mailbox']:'INBOX'); @@ -750,224 +750,19 @@ class mail_ui */ public function ajax_foldertree($_nodeID = null,$_subscribedOnly=null) { - //error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.$_subscribedOnly); $nodeID = $_GET['id']; if (!is_null($_nodeID)) $nodeID = $_nodeID; $subscribedOnly = (bool)(!is_null($_subscribedOnly)?$_subscribedOnly:!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']); $fetchCounters = !is_null($_nodeID); list($_profileID,$_folderName) = explode(self::$delimiter,$nodeID,2); - unset($_profileID); + if (!empty($_folderName)) $fetchCounters = true; - //error_log(__METHOD__.__LINE__.':'.$nodeID.'->'.array2string($fetchCounters)); - $data = $this->getFolderTree($fetchCounters, $nodeID, $subscribedOnly,true,true,false); - //error_log(__METHOD__.__LINE__.':'.$nodeID.'->'.array2string($data)); + + $data = $this->mail_tree->getTree($nodeID,$_profileID,0, false,$subscribedOnly,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']); if (!is_null($_nodeID)) return $data; etemplate_widget_tree::send_quote_json($data); } - /** - * getFolderTree, get folders from server and prepare the folder tree - * @param mixed bool/string $_fetchCounters, wether to fetch extended information on folders - * if set to initial, only for initial level of seen (unfolded) folders - * @param string $_nodeID nodeID to fetch and return - * @param boolean $_subscribedOnly flag to tell wether to fetch all or only subscribed (default) - * @param boolean $_returnNodeOnly only effective if $_nodeID is set, and $_nodeID is_nummeric - * @param boolean _useCacheIfPossible - if set to false cache will be ignored and reinitialized - * @param boolean $_popWizard Check if getFoldertree is called via open account (TRUE) or via tree interaction (FALSE), to control wizard popup - * @return array something like that: array('id'=>0, - * 'item'=>array( - * 'text'=>'INBOX', - * 'tooltip'=>'INBOX'.' '.lang('(not connected)'), - * 'im0'=>'kfm_home.png' - * 'item'=>array($MORE_ITEMS) - * ) - * ); - */ - function getFolderTree($_fetchCounters=false, $_nodeID=null, $_subscribedOnly=true, $_returnNodeOnly=true, $_useCacheIfPossible=true, $_popWizard=true) - { - if (mail_bo::$debugTimes) $starttime = microtime (true); - if (!is_null($_nodeID) && $_nodeID != 0) - { - list($_profileID,$_folderName) = explode(self::$delimiter,$_nodeID,2); - unset($_folderName); - if (is_numeric($_profileID)) - { - if ($_profileID && $_profileID != $this->mail_bo->profileID) - { - //error_log(__METHOD__.__LINE__.' change Profile to ->'.$_profileID); - try - { - $this->changeProfile($_profileID); - } - catch (Exception $e) - { - return mail_tree::treeLeafNoConnectionArray($_profileID, $e->getMessage(), array($_profileID), ''); - } - } - } - } - //$starttime = microtime(true); - $c = 0; - try - { - $folderObjects = $this->mail_bo->getFolderObjects($_subscribedOnly,false,false,$_useCacheIfPossible); - //$endtime = microtime(true) - $starttime; - //error_log(__METHOD__.__LINE__.' Fetching folderObjects took: '.$endtime); - $userDefinedFunctionFolders = array( - 'Trash' => $this->mail_bo->getTrashFolder(false), - 'Templates' => $this->mail_bo->getTemplateFolder(false), - 'Drafts' => $this->mail_bo->getDraftFolder(false), - 'Sent' => $this->mail_bo->getSentFolder(false), - 'Junk' => $this->mail_bo->getJunkFolder(false), - 'Outbox' => $this->mail_bo->getOutboxFolder(false), - ); - } - catch (Exception $e) - { - error_log(__LINE__.': '.__METHOD__."() ".$e->getMessage()); - $folderObjects=array(); - if ($_popWizard) - { - self::callWizard($e->getMessage(), false, 'error'); - } - $c = 1; - } - - $out = array('id' => 0); - - //$starttime = microtime(true); - foreach(emailadmin_account::search($only_current_user=true, false) as $acc_id => $accountObj) - { - if ($_profileID && $acc_id != $_profileID) - { - //error_log(__METHOD__.__LINE__.' Fetching accounts '." $acc_id != $_profileID ".'->'.$identity_name); - continue; - } - //error_log(__METHOD__.__LINE__.array2string($accountObj)); - if (!$accountObj->is_imap()) - { - // not to be used for IMAP Foldertree, as there is no Imap host - continue; - } - $identity_name = emailadmin_account::identity_name($accountObj,true,$GLOBALS['egw_info']['user']['acount_id']); - $oA = array('id' => $acc_id, - 'text' => str_replace(array('<','>'),array('[',']'),$identity_name),// as angle brackets are quoted, display in Javascript messages when used is ugly, so use square brackets instead - 'tooltip' => '('.$acc_id.') '.htmlspecialchars_decode($identity_name), - 'im0' => 'thunderbird.png', - 'im1' => 'thunderbird.png', - 'im2' => 'thunderbird.png', - 'path'=> array($acc_id), - 'child'=> (int)($acc_id != $_profileID || $folderObjects), // dynamic loading on unfold - 'parent' => '', - // mark on account if Sieve is enabled - 'data' => array('sieve' => $accountObj->imapServer()->acc_sieve_enabled,'spamfolder'=>($accountObj->imapServer()->acc_folder_junk?true:false)), - ); - mail_tree::setOutStructure($oA, $out, self::$delimiter); - - // create a fake INBOX folder showing connection error (necessary that client UI unlocks tree!) - if ($e && $acc_id == $_profileID && !$folderObjects) - { - $out = mail_tree::treeLeafNoConnectionArray($acc_id, lang($e->getMessage()), array($acc_id, 'INBOX'), $acc_id); - } - } - //$endtime = microtime(true) - $starttime; - - if (!empty($folderObjects)) - { - $delimiter = $this->mail_bo->getHierarchyDelimiter(); - $cmb = $this->mail_bo->icServer->getCurrentMailbox(); - $cmblevels = explode($delimiter,$cmb); - $cmblevelsCt = count($cmblevels); - } - //error_log(__METHOD__.__LINE__.function_backtrace()); - // needed in setOutStructure for namespace consistency in folderstructure of others and/or shared - $nameSpace = $this->mail_bo->_getNameSpaces(); - foreach($folderObjects as $key => $obj) - { - // A1. Comment this part out for the moment to not get performance issue and wierd error - // until we re-implement get folder status and mail_tree completely as getFolderTree - // method would go anyway. - //$fS = $this->mail_bo->getFolderStatus($key,false,($_fetchCounters?false:true)); - - //error_log(__METHOD__.__LINE__.array2string($key)); - $levels = explode($delimiter,$key); - $levelCt = count($levels); - $fetchCounters = (bool)$_fetchCounters; - if ($_fetchCounters==='initial') - { - if ($levelCt>$cmblevelsCt+1) $fetchCounters=false; - } - $fFP = $folderParts = explode($obj->delimiter, $key); - if (in_array($key,$userDefinedFunctionFolders)) $obj->shortDisplayName = lang($obj->shortDisplayName); - //get rightmost folderpart - array_pop($folderParts); - - // the rest of the array is the name of the parent - $parentName = implode((array)$folderParts,$obj->delimiter); - $parentName = $this->mail_bo->profileID.self::$delimiter.$parentName; - $oA =array('text'=> $obj->shortDisplayName, 'tooltip'=> $obj->folderName); - array_unshift($fFP,$this->mail_bo->profileID); - $oA['path'] = $fFP; - $path = $key; - if ($path=='INBOX') - { - $oA['im0'] = $oA['im1']= $oA['im2'] = "kfm_home.png"; - // mark on inbox if ACL is supported - $oA['data'] = array('acl' => $this->mail_bo->icServer->queryCapability('ACL')); - } - elseif (($_key = array_search($obj->folderName, $userDefinedFunctionFolders)) !== false) - { - $oA['text'] = lang($_key); - - $oA['im0'] = $oA['im1']= $oA['im2'] = "MailFolder".$_key.".png"; - } - else - { - $oA['im0'] = "MailFolderPlain.png"; // one Level - $oA['im1'] = "folderOpen.gif"; - $oA['im2'] = "MailFolderClosed.png"; // has Children - } - if ($fetchCounters) - { - $count = $this->mail_bo->getMailBoxCounters($key,true); - if($count->unseen) - { - $oA['text'] = $oA['text'].' ('.$count->unseen.')'; - $oA['style'] = 'font-weight: bold'; - } - } - $path = $this->mail_bo->profileID.self::$delimiter.$key; - $oA['id'] = $path; // ID holds the PATH - - // A2. This part needs to be commented out because of part A1 (see A1). as they are relative - /* - if (!empty($fS['attributes']) && stripos(array2string($fS['attributes']),'\noselect')!== false) - { - $oA['im0'] = "folderNoSelectClosed.gif"; // one Level - $oA['im1'] = "folderNoSelectOpen.gif"; - $oA['im2'] = "folderNoSelectClosed.gif"; // has Children - } - if (!empty($fS['attributes']) && stripos(array2string($fS['attributes']),'\hasnochildren')=== false) - { - $oA['child']=1; // translates to: hasChildren -> dynamicLoading - }*/ - - $oA['parent'] = $parentName; - - mail_tree::setOutStructure($oA,$out,$obj->delimiter,true,$nameSpace); - $c++; - } - if (!is_null($_nodeID) && $_nodeID !=0 && $_returnNodeOnly==true) - { - $node = self::findNode($out,$_nodeID); - //error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.array2string($node)); - if (mail_bo::$debugTimes) mail_bo::logRunTimes($starttime,null,'return subtree for:'.$_nodeID,__METHOD__.__LINE__); - return $node; - } - if (mail_bo::$debugTimes) mail_bo::logRunTimes($starttime,null,function_backtrace(),__METHOD__.__LINE__); - return ($c?$out:array('id'=>0, 'item'=>array('text'=>'INBOX','tooltip'=>'INBOX'.' '.lang('(not connected)'),'im0'=>'kfm_home.png'))); - } - /** * findNode - helper function to return only a branch of the tree * @@ -3776,7 +3571,7 @@ class mail_ui $response = egw_json_response::get(); foreach($refreshData as $folder => &$name) { - $name = $this->getFolderTree(true, $folder, $_subscribedOnly); + $name = $this->mail_tree->getTree($folder,$profileID,1,false, $_subscribedOnly,true); } $response->call('app.mail.mail_reloadNode',$refreshData); @@ -3906,7 +3701,7 @@ class mail_ui // Send full info back in the response foreach($refreshData as $folder => &$name) { - $name = $this->getFolderTree(true, $folder, !$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']); + $name = $this->mail_tree->getTree($folder,$profileID,1,false,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane'],true); } $response->call('app.mail.mail_reloadNode',$refreshData); @@ -4074,7 +3869,7 @@ class mail_ui translation::add_app('mail'); $refreshData = array( - $icServerID => $mail_ui->getFolderTree(true, $icServerID, !$mail_ui->mail_bo->mailPreferences['showAllFoldersInFolderPane'],true) + $icServerID => $mail_ui->mail_tree->getTree(null,$icServerID,1,false,!$mail_ui->mail_bo->mailPreferences['showAllFoldersInFolderPane'],!$mail_ui->mail_bo->mailPreferences['showAllFoldersInFolderPane']) ); $response->call('app.mail.mail_reloadNode',$refreshData); }