diff --git a/mail/inc/class.mail_tree.inc.php b/mail/inc/class.mail_tree.inc.php index ee87f870df..aa8ae7c36d 100644 --- a/mail/inc/class.mail_tree.inc.php +++ b/mail/inc/class.mail_tree.inc.php @@ -99,6 +99,7 @@ class mail_tree */ static function pathToFolderData ($_path, $_hDelimiter) { + if (!strpos($_path, self::$delimiter)) $_path = self::$delimiter.$_path; list(,$path) = explode(self::$delimiter, $_path); $path_chain = $parts = explode($_hDelimiter, $path); $name = array_pop($parts); @@ -126,7 +127,37 @@ class mail_tree in_array('\HasChildren', $_node['ATTRIBUTES'])) $hasChildren = 1; return $hasChildren; } - + + /** + * Check if the given tree id is account node (means root) + * + * @param type $_node a tree id node + * @return boolean returns true if the node is account node otherwise false + */ + private static function isAccountNode ($_node) + { + list(,$leaf) = explode(self::$delimiter, $_node); + if ($leaf || $_node == null) return false; + return true; + } + + /** + * Calculate node level form the root + * @param type $_path tree node full path, e.g. INBOX/Drafts + * + * @return int returns node level distance from the root, + * returns false if something goes wrong + */ + private static function getNodeLevel($_path, $_delimiter = '.') + { + $parts = explode($_delimiter, $_path); + if (is_array($parts)) + { + return count($parts); + } + return false; + } + /** * getTree provides tree structure regarding to selected node * @@ -135,10 +166,12 @@ class mail_tree * @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 + * @param boolean $_subscribedOnly = false get only subscribed folders + * @param boolean $_getThemAll = false, true will get all folders of the account * * @return array returns an array of mail tree structure according to provided node */ - function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_noCheckboxNS = false) + function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_noCheckboxNS = false, $_subscribedOnly= false, $_getThemAll = false) { //Init mail folders $tree = array(tree::ID=> $_parent?$_parent:0,tree::CHILDREN => array()); @@ -156,12 +189,21 @@ class mail_tree } } - if ($_parent) // Single node loader + // 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 { $nodeInfo = self::pathToFolderData($_parent, $hDelimiter); - $folders = $this->ui->mail_bo->getFolderArrays($nodeInfo['mailbox'],false,2); + $folders = $this->ui->mail_bo->getFolderArrays($nodeInfo['mailbox'],false,$_getThemAll?0:2, $_subscribedOnly); } catch (Exception $ex) { return self::treeLeafNoConnectionArray($_profileID, $ex->getMessage(),array($_profileID), ''); } @@ -188,132 +230,47 @@ class mail_tree } else //Top Level Nodes loader { - $baseNode = array('id' => 0); - foreach(emailadmin_account::search(true, false) as $acc_id => $accObj) + if (self::isAccountNode($_parent)) { - if (!$accObj->is_imap()|| $acc_id != $_profileID) continue; - $identity = emailadmin_account::identity_name($accObj,true,$GLOBALS['egw_info']['user']['acount_id']); - $baseNode = array( - tree::ID=> $acc_id, - tree::LABEL => str_replace(array('<','>'),array('[',']'),$identity), - tree::TOOLTIP => '('.$acc_id.') '.htmlspecialchars_decode($identity), - tree::IMAGE_LEAF => self::$leafImages['folderAccount'], - tree::IMAGE_FOLDER_OPEN => self::$leafImages['folderAccount'], - tree::IMAGE_FOLDER_CLOSED => self::$leafImages['folderAccount'], - 'path'=> array($acc_id), - tree::CHILDREN => array(), // dynamic loading on unfold - tree::AUTOLOAD_CHILDREN => true, - 'parent' => '', - tree::OPEN => $_openTopLevel, - // mark on account if Sieve is enabled - 'data' => array( - 'sieve' => $accObj->imapServer()->acc_sieve_enabled, - 'spamfolder'=> $accObj->imapServer()->acc_folder_junk?true:false - ), - tree::NOCHECKBOX => $_noCheckboxNS - ); - self::setOutStructure($baseNode, $tree,self::$delimiter); + $_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); - // Parent node arrays - $parentNode = $parentNodes = array(); - - foreach ($foldersList as $index => $topFolder) + $foldersList = $this->ui->mail_bo->getFolderArrays(null, true, $_getThemAll?0:2,$_subscribedOnly); + foreach ($foldersList as &$folder) { - $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 => self::nodeHasChildren($topFolder[$index]), + $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($topFolder[$index]['MAILBOX']), - tree::OPEN => $_openTopLevel, - tree::TOOLTIP => lang($topFolder[$index]['MAILBOX']), - tree::CHECKED => $topFolder[$index]['SUBSCRIBED'], - tree::NOCHECKBOX => $noCheckbox + 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 ); - if ($index === "INBOX") + // Set Acl capability for INBOX + if ($folder['MAILBOX'] === "INBOX") { - $parentNode[tree::IMAGE_LEAF] = self::$leafImages['folderHome']; - $parentNode[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderHome']; - $parentNode[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderHome']; + $data['data'] = array('acl' => $this->ui->mail_bo->icServer->queryCapability('ACL')); } - if(stripos(array2string($topFolder[$index]['ATTRIBUTES']),'\noselect')!== false) - { - $parentNode[tree::IMAGE_LEAF] = self::$leafImages['folderNoSelectClosed']; - $parentNode[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderNoSelectOpen']; - $parentNode[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderNoSelectClosed']; - } - // Save parentNodes - $parentNodes []= $index; - // Remove the parent nodes from the list - unset ($topFolder[$index]); - - //$parentNode[tree::CHILDREN][] =$childrenNode; - $baseNode[tree::CHILDREN][] = $parentNode; + self::setOutStructure($data, $tree, $folder['delimiter'], true, $this->ui->mail_bo->_getNameSpaces(), $definedFolders); } - foreach ($parentNodes as $pIndex => $parent) - { - $childrenNodes = $childNode = array(); - $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), - ); - // Iterate over childern of each top folder(namespaces) - foreach ($foldersList[$parent] as &$node) - { - // Skipe the parent node itself - if (is_array($foldersList[$parent][$parent]) && - $foldersList[$parent][$parent]['MAILBOX'] === $node['MAILBOX']) continue; - - $pathArr = explode($node['delimiter'], $node['MAILBOX']); - $folderName = array_pop($pathArr); - $parentPath = $_profileID.self::$delimiter.implode($pathArr,$node['delimiter']); - - $nodeId = $_profileID.self::$delimiter.$node['MAILBOX']; - - $childNode = array( - tree::ID => $nodeId, - tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($node), - tree::CHILDREN => array(), - tree::LABEL => lang($folderName), - 'parent' => $parentPath, - tree::CHECKED => $node['SUBSCRIBED'] - ); - - if (array_search($node['MAILBOX'], $definedFolders) !== false) - { - //User defined folders icons - $childNode[tree::IMAGE_LEAF] = - $childNode[tree::IMAGE_FOLDER_OPEN] = - $childNode [tree::IMAGE_FOLDER_CLOSED] = "MailFolder".$folderName.".png"; - } - elseif(stripos(array2string($node['ATTRIBUTES']),'\noselect')!== false) - { - $childNode[tree::IMAGE_LEAF] = self::$leafImages['folderNoSelectClosed']; - $childNode[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderNoSelectOpen']; - $childNode[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderNoSelectClosed']; - } - else - { - $childNode[tree::IMAGE_LEAF] = self::$leafImages['folderLeaf']; - $childNode[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderOpen']; - $childNode[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderClose']; - } - $childrenNodes[] = $childNode; - } - $baseNode[tree::CHILDREN][$pIndex][tree::CHILDREN] = $childrenNodes; - } - $tree[tree::CHILDREN][0] = $baseNode; + // 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]); } return $tree; } @@ -329,7 +286,7 @@ class mail_tree * as clearance for access may be limited to a single branch-node of a tree * @return void */ - static function setOutStructure($data, &$out, $del='.', $createMissingParents=true, $nameSpace=array()) + static function setOutStructure($data, &$out, $del='.', $createMissingParents=true, $nameSpace=array(), $definedFolders= array()) { //error_log(__METHOD__."(".array2string($data).', '.array2string($out).", '$del')"); $components = $data['path']; @@ -399,9 +356,92 @@ class mail_tree } $parents[] = $component; } + if ($data['folderarray']['delimiter'] && $data['folderarray']['MAILBOX']) + { + $path = explode($data['folderarray']['delimiter'], $data['folderarray']['MAILBOX']); + $folderName = array_pop($path); + + if ($data['folderarray']['MAILBOX'] === "INBOX") + { + $data[tree::IMAGE_LEAF] = self::$leafImages['folderHome']; + $data[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderHome']; + $data[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderHome']; + } + + // User defined folders may get different icons + // plus they need to be translated too + if (array_search($data['folderarray']['MAILBOX'], $definedFolders, true) !== false) + { + $data[tree::LABEL] = lang($folderName); + $data[tree::TOOLTIP] = lang($folderName); + //User defined folders icons + $data[tree::IMAGE_LEAF] = + $data[tree::IMAGE_FOLDER_OPEN] = + $data [tree::IMAGE_FOLDER_CLOSED] = "MailFolder".$folderName.".png"; + } + elseif(stripos(array2string($data['folderarray']['attributes']),'\noselect')!== false) + { + $data[tree::IMAGE_LEAF] = self::$leafImages['folderNoSelectClosed']; + $data[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderNoSelectOpen']; + $data[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderNoSelectClosed']; + } + elseif ($data['parent']) + { + $data[tree::LABEL] = $folderName; + $data[tree::TOOLTIP] = $folderName; + $data[tree::IMAGE_LEAF] = self::$leafImages['folderLeaf']; + $data[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderOpen']; + $data[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderClose']; + } + } + //Remove extra data from tree structure + unset($data['folderarray']); unset($data['path']); + $insert['item'][] = $data; - //error_log(__METHOD__."() leaving with out=".array2string($out)); } + /** + * Get accounts root node, fetches all or an accounts for a user + * + * @param type $_profileID = null Null means all accounts and giving profileid means fetches node for the account + * @param type $_noCheckbox = false option to switch checkbox of + * @param type $_openTopLevel = 0 option to either start the node opened (1) or closed (0) + * + * @return array an array of baseNodes of accounts + */ + static function getAccountsRootNode($_profileID = null, $_noCheckbox = false, $_openTopLevel = 0 ) + { + $roots = array(tree::ID => 0, tree::CHILDREN => array()); + + foreach(emailadmin_account::search(true, false) as $acc_id => $accObj) + { + if (!$accObj->is_imap()|| $_profileID && $acc_id != $_profileID) continue; + $identity = emailadmin_account::identity_name($accObj,true,$GLOBALS['egw_info']['user']['acount_id']); + // Open top level folders for active account + $openActiveAccount = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'] == $acc_id?1:0; + + $baseNode = array( + tree::ID=> $acc_id, + tree::LABEL => str_replace(array('<','>'),array('[',']'),$identity), + tree::TOOLTIP => '('.$acc_id.') '.htmlspecialchars_decode($identity), + tree::IMAGE_LEAF => self::$leafImages['folderAccount'], + tree::IMAGE_FOLDER_OPEN => self::$leafImages['folderAccount'], + tree::IMAGE_FOLDER_CLOSED => self::$leafImages['folderAccount'], + 'path'=> array($acc_id), + tree::CHILDREN => array(), // dynamic loading on unfold + tree::AUTOLOAD_CHILDREN => true, + 'parent' => '', + tree::OPEN => $_openTopLevel?$_openTopLevel:$openActiveAccount, + // mark on account if Sieve is enabled + 'data' => array( + 'sieve' => $accObj->imapServer()->acc_sieve_enabled, + 'spamfolder'=> $accObj->imapServer()->acc_folder_junk?true:false + ), + tree::NOCHECKBOX => $_noCheckbox + ); + self::setOutStructure($baseNode, $roots,self::$delimiter); + } + return $roots; + } } diff --git a/mail/templates/default/index.xet b/mail/templates/default/index.xet index a3af3042a3..e294d984ab 100644 --- a/mail/templates/default/index.xet +++ b/mail/templates/default/index.xet @@ -45,7 +45,7 @@