From df179a385986ab935ba0fffd1d7109e54bb8444b Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Wed, 12 Aug 2015 13:27:57 +0000 Subject: [PATCH] * Mail/tree: Performance improvement for mail index folderTree - Autoloading mail folders when show all folders is on (performance improvement) - Get folder status for opened folders only (performance improvement) - Styling hovered over mail folder --- .../inc/class.emailadmin_imapbase.inc.php | 169 ++++++-- etemplate/js/et2_widget_tree.js | 19 + mail/inc/class.mail_tree.inc.php | 404 +++++++++++------- mail/inc/class.mail_ui.inc.php | 297 ++++--------- mail/js/app.js | 5 +- mail/templates/default/index.xet | 2 +- mail/templates/pixelegg/app.css | 7 +- mail/templates/pixelegg/app.less | 9 +- 8 files changed, 515 insertions(+), 397 deletions(-) diff --git a/emailadmin/inc/class.emailadmin_imapbase.inc.php b/emailadmin/inc/class.emailadmin_imapbase.inc.php index 1674d43a1d..0f2afc1846 100644 --- a/emailadmin/inc/class.emailadmin_imapbase.inc.php +++ b/emailadmin/inc/class.emailadmin_imapbase.inc.php @@ -1071,6 +1071,8 @@ class emailadmin_imapbase * @param ignoreStatusCache bool ignore the cache used for counters * * @return array + * + * @throws egw_exception */ function _getStatus($folderName,$ignoreStatusCache=false) { @@ -1133,7 +1135,7 @@ class emailadmin_imapbase // does the folder exist??? if (is_null($folderInfoCache) || !isset($folderInfoCache[$_folderName])) { - $ret = $this->icServer->getMailboxes('', $_folderName, true); + $ret = $this->icServer->getMailboxes($_folderName, 1, true); //error_log(__METHOD__.' ('.__LINE__.') '.$_folderName.' '.array2string($ret)); if (is_array($ret)) { @@ -1373,7 +1375,7 @@ class emailadmin_imapbase $sortResult = (is_array($_thisUIDOnly) ? $_thisUIDOnly:(array)$_thisUIDOnly); } - + // fetch the data for the selected messages if (self::$debug||self::$debugTimes) $starttime = microtime(true); try @@ -2254,6 +2256,7 @@ class emailadmin_imapbase * @param string _folderName the new foldername * * @return mixed name of the newly created folder or false on error + * @throws egw_exception */ function renameFolder($_oldFolderName, $_parent, $_folderName) { @@ -2289,6 +2292,7 @@ class emailadmin_imapbase * @param string _folderName the name of the folder to be deleted * * @return bool true on success, PEAR Error on failure + * @throws egw_exception */ function deleteFolder($_folderName) { @@ -2623,19 +2627,25 @@ class emailadmin_imapbase } /** - * Get IMAP folder for a mailbox + * Get IMAP folders for a mailbox * - * @param $_nodePath = null folder name to fetch from IMAP, + * @param string $_nodePath = null folder name to fetch from IMAP, * null means all folders - * @param $_onlyTopLevel if set to true only top level objects + * @param boolean $_onlyTopLevel if set to true only top level objects * will be return and nodePath would be ignored * @param int $_search = 2 search restriction in given mailbox * 0:All folders recursively from the $_nodePath * 1:Only folder of specified $_nodePath * 2:All folders of $_nodePath in the same heirachy level - * @return array an array of folder + * + * @param boolean $_subscribedOnly = false Command to fetch only the subscribed folders + * @param boolean $_getCounter = false Command to fetch mailbox counter + * + * @return array arrays of folders + * + * @todo Sorting autofolders and no autofolders */ - function getFolderArray ($_nodePath = null, $_onlyTopLevel = false, $_search= 2) + function getFolderArrays ($_nodePath = null, $_onlyTopLevel = false, $_search= 2, $_subscribedOnly = false, $_getCounter = false) { // delimiter $delimiter = $this->getHierarchyDelimiter(); @@ -2651,10 +2661,54 @@ class emailadmin_imapbase { $pattern = "/\\".$delimiter."/"; $reference = preg_replace($pattern, '', $node['MAILBOX']); - $mainFolder = $this->icServer->getMailboxes($reference, 1, true); - $subFolders = $this->icServer->getMailboxes($node['MAILBOX'].$node['delimiter'], 2, true); - $folders[$node['MAILBOX']] = array_merge((array)$mainFolder, (array)$subFolders); - ksort($folders[$node['MAILBOX']]); + $mainFolder = $subFolders = array(); + + // Get special use folders + if (!isset(self::$specialUseFolders)) $this->getSpecialUseFolders (); // Set self::$sepecialUseFolders + // Create autofolders if they all not created yet + if (count(self::$autoFolders) > count(self::$specialUseFolders)) $this->check_create_autofolders(self::$specialUseFolders); + // Merge of all auto folders and specialusefolders + $autoFoldersTmp = array_unique((array_merge(self::$autoFolders, array_values(self::$specialUseFolders)))); + + if ($_subscribedOnly) + { + $mainFolder = $this->icServer->listSubscribedMailboxes($reference, 1, true); + $subFolders = $this->icServer->listSubscribedMailboxes($node['MAILBOX'].$node['delimiter'], $_search, true); + } + else + { + $mainFolder = $this->icServer->getMailboxes($reference, 1, true); + $subFolders = $this->icServer->getMailboxes($node['MAILBOX'].$node['delimiter'], $_search, true); + } + if (is_array($mainFolder['INBOX'])) + { + // Array container of auto folders + $aFolders = array(); + + // Array container of non auto folders + $nFolders = array(); + + foreach ($subFolders as $path => $folder) + { + $folderInfo = mail_tree::pathToFolderData($folder['MAILBOX'], $folder['delimiter']); + if (in_array(trim($folderInfo['name']), $autoFoldersTmp)) + { + $aFolders [$path] = $folder; + } + else + { + $nFolders [$path] = $folder; + } + } + if (is_array($aFolders)) uasort ($aFolders, array($this,'sortByAutofolder')); + ksort($aFolders); + $subFolders = array_merge($aFolders,$nFolders); + } + else + { + if (is_array($subFolders)) ksort($subFolders); + } + $folders = array_merge($folders,(array)$mainFolder, (array)$subFolders); } } elseif ($_nodePath) // single node @@ -2672,13 +2726,36 @@ class emailadmin_imapbase $path = $_nodePath; break; } - $folders = $this->icServer->getMailboxes($path, $_search, true); + if ($_subscribedOnly) + { + $folders = $this->icServer->listSubscribedMailboxes($path, $_search, true); + } + else + { + $folders = $this->icServer->getMailboxes($path, $_search, true); + } + ksort($folders); - return $folders; } - elseif(!$_nodePath) + elseif(!$_nodePath) // all { - $folders = $this->icServer->getMailboxes('', 0, true); + if ($_subscribedOnly) + { + $folders = $this->icServer->listSubscribedMailboxes('', 0, true); + } + else + { + $folders = $this->icServer->getMailboxes('', 0, true); + } + } + + // Get counter information and add them to each fetched folders array + if ($_getCounter) + { + foreach ($folders as &$folder) + { + $folder['counter'] = $this->icServer->getMailboxCounters($folder['MAILBOX']); + } } return $folders; } @@ -2722,7 +2799,42 @@ class emailadmin_imapbase } return $rv; } - + + /** + * sortByName + * + * Helper function to sort folders array by name + * @param array $a + * @param array $b array of folders + * @return int expect values (0, 1 or -1) + */ + function sortByName($a,$b) + { + $a = mail_tree::pathToFolderData($a['MAILBOX'], $a['delimiter']); + $b = mail_tree::pathToFolderData($b['MAILBOX'], $b['delimiter']); + // 0, 1 und -1 + return strcasecmp($a['name'],$b['name']); + } + + /** + * sortByAutoFolderPos + * + * Helper function to sort folder-objects by auto Folder Position + * @param array $a + * @param array $b + * @return int expect values (0, 1 or -1) + */ + function sortByAutoFolder($a,$b) + { + // 0, 1 und -1 + $a = mail_tree::pathToFolderData($a['MAILBOX'], $a['delimiter']); + $b = mail_tree::pathToFolderData($b['MAILBOX'], $b['delimiter']); + $pos1 = array_search(trim($a['name']),self::$autoFolders); + $pos2 = array_search(trim($b['name']),self::$autoFolders); + if ($pos1 == $pos2) return 0; + return ($pos1 < $pos2) ? -1 : 1; + } + /** * sortByDisplayName * @@ -2819,7 +2931,7 @@ class emailadmin_imapbase if (is_array($mbx[$mbxkeys[0]]["ATTRIBUTES"]) && (in_array('\HasChildren',$mbx[$mbxkeys[0]]["ATTRIBUTES"]) || in_array('\Haschildren',$mbx[$mbxkeys[0]]["ATTRIBUTES"]) || in_array('\haschildren',$mbx[$mbxkeys[0]]["ATTRIBUTES"]))) { // if there are children fetch them //echo $mbx[$mbxkeys[0]]['MAILBOX']."
"; - + $buff = $this->icServer->getMailboxes($mbx[$mbxkeys[0]]['MAILBOX'].($mbx[$mbxkeys[0]]['MAILBOX'] == $prefix ? '':$delimiter),2,false); //$buff = $this->icServer->getMailboxes($mbx[$mbxkeys[0]]['MAILBOX'],2,false); //_debug_array($buff); @@ -3206,6 +3318,7 @@ class emailadmin_imapbase * @param string _forceDeleteMethod - "no", or deleteMethod like 'move_to_trash',"mark_as_deleted","remove_immediately" * * @return bool true, as we do not handle return values yet + * @throws egw_exception */ function deleteMessages($_messageUID, $_folder=NULL, $_forceDeleteMethod='no') { @@ -3529,6 +3642,7 @@ class emailadmin_imapbase * @param int $_targetProfileID - target profile ID, should only be handed over when target server is different from source * * @return mixed/bool true,false or new uid + * @throws egw_exception */ function moveMessages($_foldername, $_messageUID, $deleteAfterMove=true, $currentFolder = Null, $returnUIDs = false, $_sourceProfileID = Null, $_targetProfileID = Null) { @@ -3825,7 +3939,7 @@ class emailadmin_imapbase case 'BASE64': // use imap_base64 to decode, not any longer, as it is strict, and fails if it encounters invalid chars return base64_decode($_mimeMessage); - + case 'QUOTED-PRINTABLE': // use imap_qprint to decode return quoted_printable_decode($_mimeMessage); @@ -5386,6 +5500,7 @@ class emailadmin_imapbase * @param string _flags = '\\Recent'the imap flags to set for the saved message * * @return the id of the message appended or exception + * @throws egw_exception_wrong_userinput */ function appendMessage($_folderName, $_header, $_body, $_flags='\\Recent') { @@ -5774,6 +5889,8 @@ class emailadmin_imapbase * @param string $IDtoAddToFileName id to enrich the returned tmpfilename * @param string $reqMimeType /(default message/rfc822, if set to false, mimetype check will not be performed * @return mixed $fullPathtoFile or exception + * + * @throws egw_exception_wrong_userinput */ static function checkFileBasics(&$_formData, $IDtoAddToFileName='', $reqMimeType='message/rfc822') { @@ -5940,7 +6057,7 @@ class emailadmin_imapbase $basedir = 'vfs://default'; $needTempFile = false; } - + // If it is an inline image url, we need to fetch the actuall attachment // content and later on to be able to store its content as temp file if (strpos($myUrl, '/index.php?menuaction=mail.mail_ui.displayImage') !== false) @@ -5948,7 +6065,7 @@ class emailadmin_imapbase $URI_params = array(); // Strips the url and store it into a temp for further procss $tmp_url = html_entity_decode($myUrl); - + parse_str(parse_url($tmp_url, PHP_URL_QUERY),$URI_params); if ($URI_params['mailbox'] && $URI_params['uid'] && $URI_params['cid']) { @@ -5963,7 +6080,7 @@ class emailadmin_imapbase } } } - + if ( strlen($basedir) > 1 && substr($basedir,-1) != '/' && $myUrl[0]!='/') { $basedir .= '/'; } if ($needTempFile && !$attachment) $data = file_get_contents($basedir.urldecode($myUrl)); } @@ -6073,7 +6190,7 @@ class emailadmin_imapbase $AltBody = ($html_body = $mailObject->findBody('html')) ? $html_body->getContents() : null; //error_log(__METHOD__.' ('.__LINE__.') '.' AltBody:'.$AltBody); //error_log(__METHOD__.' ('.__LINE__.') '.array2string($mailObject->GetReplyTo())); - + // Fetch ReplyTo - Address if existing to check if we are to replace it $replyTo = $mailObject->getReplyTo(); if (isset($replyTo['replace@import.action'])) @@ -6289,7 +6406,7 @@ class emailadmin_imapbase * * @param egw_mailer $mailer instance of SMTP Mailer object * @param string|ressource|Horde_Mime_Part $message string or resource containing the RawMessage / object Mail_mimeDecoded message (part)) - * @throws egw_exception_assertion_failed when the required Horde_Mail_Part not found + * @throws egw_exception_wrong_parameter when the required Horde_Mail_Part not found */ function parseRawMessageIntoMailObject(egw_mailer $mailer, $message) { @@ -6396,7 +6513,7 @@ class emailadmin_imapbase function addAccount($_hookValues) { error_log(__METHOD__.' ('.__LINE__.') '.' NOT DONE YET!' . ' hookValue = '. $_hookValues); - + } /** @@ -6410,7 +6527,7 @@ class emailadmin_imapbase function deleteAccount($_hookValues) { error_log(__METHOD__.' ('.__LINE__.') '.' NOT DONE YET!' . ' hookValue = '. $_hookValues); - + } /** @@ -6424,6 +6541,6 @@ class emailadmin_imapbase function updateAccount($_hookValues) { error_log(__METHOD__.' ('.__LINE__.') '.' NOT DONE YET!' . ' hookValue = '. $_hookValues); - + } } diff --git a/etemplate/js/et2_widget_tree.js b/etemplate/js/et2_widget_tree.js index 684e2e73bd..b93fd2c5bd 100644 --- a/etemplate/js/et2_widget_tree.js +++ b/etemplate/js/et2_widget_tree.js @@ -101,6 +101,18 @@ var et2_tree = et2_inputWidget.extend( "type": "string", "default": "", "description": "comma-separated names of icons for a leaf, closed and opend folder (default: leaf.gif,folderClosed.gif,folderOpen.gif), images with extension get loaded from image_path, just 'image' or 'appname/image' are allowed too" + }, + "multimarking": { + "name": "multimarking", + "type": "any", + "default": false, + "description": "Allow marking multiple nodes, default is false which means disabled multiselection, true or 'strict' activates it and 'strict' makes it strick to only same level marking" + }, + highlighting:{ + "name": "highlighting", + "type": "boolean", + "default": false, + "description": "Add highlighting class on hovered over item, highlighting is disabled by default" } }, @@ -218,6 +230,13 @@ var et2_tree = et2_inputWidget.extend( widget.input.setXMLAutoLoading(egw.link(url)); widget.input.setDataMode('JSON'); } + + if (widget.options.multimarking) + { + widget.input.enableMultiselection(!!widget.options.multimarking, widget.options.multimarking === 'strict'); + } + // Enable/Disable highlighting + widget.input.enableHighlighting(widget.options.highlighting?true:false); }, /** diff --git a/mail/inc/class.mail_tree.inc.php b/mail/inc/class.mail_tree.inc.php index b7d43cfdd4..9b68e77b34 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,13 @@ 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 $_allInOneGo = false, true will get all folders (dependes on subscribedOnly option) of the account in one go + * @param boolean $_checkSubscribed = true, pre-check checkboxes of subscribed folders * * @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, $_allInOneGo = false, $_checkSubscribed = true) { //Init mail folders $tree = array(tree::ID=> $_parent?$_parent:0,tree::CHILDREN => array()); @@ -156,166 +190,114 @@ class mail_tree } } - if ($_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->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::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 - { - $baseNode = array('id' => 0); - foreach(emailadmin_account::search(true, false) as $acc_id => $accObj) - { - 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); - } - //List of folders - $foldersList = $this->ui->mail_bo->getFolderArray(null, true); - - // Parent node arrays - $parentNode = $parentNodes = array(); - - 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 => 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::NOCHECKBOX => $noCheckbox - ); - if ($index === "INBOX") - { - $parentNode[tree::IMAGE_LEAF] = self::$leafImages['folderHome']; - $parentNode[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderHome']; - $parentNode[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderHome']; - } - 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]); + $folders = $this->ui->mail_bo->getFolderArrays($nodeInfo['mailbox'],false,$_allInOneGo?0:2, $_subscribedOnly); - //$parentNode[tree::CHILDREN][] =$childrenNode; - $baseNode[tree::CHILDREN][] = $parentNode; - } - 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) + $childrenNode = array(); + foreach ($folders 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'] + $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 => $_checkSubscribed?$node['SUBSCRIBED']:false, + 'parent' => $_parent ); - - if (array_search($node['MAILBOX'], $definedFolders) !== false) + } + $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, true); + 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 => $_checkSubscribed?$folder['SUBSCRIBED']:false, + 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") { - //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']; + $data['data'] = array('acl' => $this->ui->mail_bo->icServer->queryCapability('ACL')); + $data[tree::NOCHECKBOX] = $_noCheckboxNS; } else { - $childNode[tree::IMAGE_LEAF] = self::$leafImages['folderLeaf']; - $childNode[tree::IMAGE_FOLDER_OPEN] = self::$leafImages['folderOpen']; - $childNode[tree::IMAGE_FOLDER_CLOSED] = self::$leafImages['folderClose']; + //Do not open Initially other folders but INBOX + $data[tree::OPEN] = 0; } - $childrenNodes[] = $childNode; + 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 => (string)$_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], + tree::AUTOLOAD_CHILDREN => 1, + 'data' => $tree[tree::CHILDREN][0]['data'] + ); } - $baseNode[tree::CHILDREN][$pIndex][tree::CHILDREN] = $childrenNodes; } - $tree[tree::CHILDREN][0] = $baseNode; } + catch (Exception $ex) // Catch exceptions + { + //mail_ui::callWizard($ex->getMessage(), false, 'error'); + return self::treeLeafNoConnectionArray($_profileID, $ex->getMessage(),array($_profileID), ''); + } + return $tree; } @@ -330,7 +312,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']; @@ -400,9 +382,133 @@ 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']; + $data[tree::LABEL] = lang($folderName); + $data[tree::TOOLTIP] = lang($folderName); + } + // User defined folders may get different icons + // plus they need to be translated too + elseif (($key = array_search($data['folderarray']['MAILBOX'], $definedFolders, true)) !== false) + { + $data[tree::LABEL] = lang($key); + $data[tree::TOOLTIP] = lang($key); + //User defined folders icons + $data[tree::IMAGE_LEAF] = + $data[tree::IMAGE_FOLDER_OPEN] = + $data [tree::IMAGE_FOLDER_CLOSED] = "MailFolder".$key.".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']; + } + + // Contains unseen mails for the folder + $unseen = $data['folderarray']['counter']['UNSEEN']; + + // if there's unseen mails then change the label and style + // accordingly to indicate useen mails + if ($unseen > 0) + { + $data[tree::LABEL] = $data[tree::LABEL].'('.$unseen.')'; + $data['style'] = 'font-weight: bold'; + } + } + //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=> (string)$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; + } + + /** + * Initialization tree for index sidebox menu + * + * This function gets all accounts root nodes and then + * fill the active accounts with its children. + * + * @param string $_parent = null no parent node means root with the first level of folders + * @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 boolean $_subscribedOnly = false get only subscribed folders + * @param boolean $_allInOneGo = false, true will get all folders (dependes on subscribedOnly option) of the account in one go + * @return type an array of tree + */ + function getInitialIndexTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_subscribedOnly= false, $_allInOneGo = false) + { + $tree = $this->getTree($_parent, $_profileID, $_openTopLevel, false, $_subscribedOnly, $_allInOneGo); + $activeAccount = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']; + $branches = $this->getTree($activeAccount, $activeAccount,1,false,$_subscribedOnly,$_allInOneGo); + foreach ($tree[tree::CHILDREN] as &$account) + { + if ($account[tree::ID] == $activeAccount) + { + $account = $branches; + } + } + return $tree; + } } diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index 89dc579efb..13f8c42100 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) { @@ -203,6 +203,8 @@ class mail_ui * * @param int $_icServerID * @param boolean $unsetCache + * + * @throws egw_exception */ function changeProfile($_icServerID,$unsetCache=false) { @@ -360,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); @@ -480,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->getInitialIndexTree(null, null, null, true,!$this->mail_bo->mailPreferences['showAllFoldersInFolderPane']); //$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'); @@ -663,6 +665,19 @@ class mail_ui ); break; } + ++$group; // put empty spam immediately in own group + $junkFolder = $this->mail_bo->getJunkFolder(); + //error_log(__METHOD__.__LINE__.$junkFolder); + if ($junkFolder && !empty($junkFolder)) + { + $tree_actions['empty_spam'] = array( + 'caption' => 'empty junk', + 'icon' => 'dhtmlxtree/MailFolderJunk', + 'enabled' => 'javaScript:app.mail.spamfolder_enabled', + 'onExecute' => 'javaScript:app.mail.mail_emptySpam', + 'group' => $group, + ); + } // enforce global (group-specific) ACL if (!mail_hooks::access('aclmanagement')) @@ -735,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), - ); - 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 * @@ -2129,7 +1939,10 @@ class mail_ui if (strtoupper($value['mimeType']=='APPLICATION/OCTET-STREAM')) $value['mimeType'] = mime_magic::filename2mime($attachmentHTML[$key]['filename']); $attachmentHTML[$key]['type']=$value['mimeType']; $attachmentHTML[$key]['mimetype']=mime_magic::mime2label($value['mimeType']); - list(, $acc_id) = explode(self::$delimiter, $rowID); + $hA = self::splitRowID($rowID); + $uid = $hA['msgUID']; + $mailbox = $hA['folder']; + $acc_id = $hA['profileID']; $attachmentHTML[$key]['mime_data'] = egw_link::set_data($value['mimeType'], 'emailadmin_imapbase::getAttachmentAccount', array( $acc_id, $mailbox, $uid, $value['partID'], $value['is_winmail'], true @@ -2774,7 +2587,7 @@ class mail_ui //error_log(__METHOD__.__LINE__.array2string($bodyParts)); // attachments here are only fetched to determine if there is a meeting request // and if. use the appropriate action. so we do not need embedded images - $fetchEmbeddedImages = false; + $fetchEmbeddedImages = false; $attachments = (array)$this->mail_bo->getMessageAttachments($uid, $partID, $structure, $fetchEmbeddedImages, true,true,$mailbox); //error_log(__METHOD__.__LINE__.array2string($attachments)); foreach ($attachments as &$attach) @@ -3102,7 +2915,7 @@ class mail_ui { if ($_type !="background") { - $bo = emailadmin_imapbase::getInstance(false, mail_ui::$icServerID); + $bo = emailadmin_imapbase::getInstance(false, self::$icServerID); $attachment = $bo->getAttachmentByCID($_uid, $CID, $_partID); // only use data uri for "smaller" images, as otherwise the first display of the mail takes to long @@ -3758,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); @@ -3888,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); @@ -4056,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); } @@ -4193,6 +4006,56 @@ class mail_ui $response->call('app.mail.mail_setQuotaDisplay',array('data'=>$content)); } + /** + * Empty spam/junk folder + * + * @param string $icServerID id of the server to empty its junkFolder + * @param string $selectedFolder seleted(active) folder by nm filter + * @return nothing + */ + function ajax_emptySpam($icServerID, $selectedFolder) + { + //error_log(__METHOD__.__LINE__.' '.$icServerID); + translation::add_app('mail'); + $response = egw_json_response::get(); + $rememberServerID = $this->mail_bo->profileID; + if ($icServerID && $icServerID != $this->mail_bo->profileID) + { + //error_log(__METHOD__.__LINE__.' change Profile to ->'.$icServerID); + $this->changeProfile($icServerID); + } + $junkFolder = $this->mail_bo->getJunkFolder(); + if(!empty($junkFolder)) { + if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder) + { + // Lock the tree if the active folder is junk folder + $response->call('app.mail.lock_tree'); + } + $this->mail_bo->deleteMessages('all',$junkFolder,'remove_immediately'); + + $heirarchyDelimeter = $this->mail_bo->getHierarchyDelimiter(true); + $fShortName = array_pop(explode($heirarchyDelimeter, $junkFolder)); + $fStatus = array( + $icServerID.self::$delimiter.$junkFolder => lang($fShortName) + ); + //Call to reset folder status counter, after junkFolder triggered not from Junk folder + //-as we don't have junk folder specific information available on client-side we need to deal with it on server + $response->call('app.mail.mail_setFolderStatus',$fStatus); + } + if ($rememberServerID != $this->mail_bo->profileID) + { + $oldFolderInfo = $this->mail_bo->getFolderStatus($junkFolder,false,false,false); + $response->call('egw.message',lang('empty junk')); + $response->call('app.mail.mail_reloadNode',array($icServerID.self::$delimiter.$junkFolder=>$oldFolderInfo['shortDisplayName'])); + //error_log(__METHOD__.__LINE__.' change Profile to ->'.$rememberServerID); + $this->changeProfile($rememberServerID); + } + else if ($selectedFolder == $icServerID.self::$delimiter.$junkFolder) + { + $response->call('egw.refresh',lang('empty junk'),'mail'); + } + } + /** * Empty trash folder * diff --git a/mail/js/app.js b/mail/js/app.js index 434bc34654..166ba0d140 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -1663,7 +1663,10 @@ app.classes.mail = AppJS.extend( } // alert(folder); this.egw.message(this.egw.lang('Connect to Profile %1',_widget.getSelectedLabel().replace(this._unseen_regexp, ''))); - + + //Open unloaded tree to get loaded + _widget.openItem(folder, true); + this.lock_tree(); egw.json('mail_ui::ajax_changeProfile',[folder, getFolders, this.et2._inst.etemplate_exec_id], jQuery.proxy(function() { // Profile changed, select inbox diff --git a/mail/templates/default/index.xet b/mail/templates/default/index.xet index a3af3042a3..46d7ee43af 100644 --- a/mail/templates/default/index.xet +++ b/mail/templates/default/index.xet @@ -45,7 +45,7 @@