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
This commit is contained in:
Hadi Nategh 2015-08-03 14:33:18 +00:00
parent 7bf6a497b0
commit 8096304c6a
2 changed files with 104 additions and 292 deletions

View File

@ -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;
}

View File

@ -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);
}