More work in progress of mail tree:

-Initiate first level of mailboxes in subscription dialog, and let autoloading do the rest of expensive operation
-Save last state of tree in subscription dialog
-Uses new approach for comparing subscribed and unsubscribed folders
This commit is contained in:
Hadi Nategh 2015-07-27 17:46:45 +00:00
parent a0c38411e9
commit 0c7e104027
4 changed files with 126 additions and 43 deletions

View File

@ -97,20 +97,36 @@ class mail_tree
* @param string $path a node path * @param string $path a node path
* @return array returns an array of data extracted from given node path * @return array returns an array of data extracted from given node path
*/ */
static function getFolderData ($_path, $_hDelimiter) static function pathToFolderData ($_path, $_hDelimiter)
{ {
list(,$path) = explode(self::$delimiter, $_path); list(,$path) = explode(self::$delimiter, $_path);
$parts = explode($_hDelimiter, $path); $path_chain = $parts = explode($_hDelimiter, $path);
$name = array_pop($parts); $name = array_pop($parts);
return array ( return array (
'name' => $name, 'name' => $name,
'mailbox' => $path, 'mailbox' => $path,
'parent' => implode($_hDelimiter, $parts), 'parent' => implode($_hDelimiter, $parts),
'text' => $name, 'text' => $name,
'tooltip' => $name 'tooltip' => $name,
'path' => $path_chain
); );
} }
/**
* Check if a given node has children attribute set
*
* @param array $_node array of a node
* @return int returns 1 if it has children flag set otherwise 0
*/
private static function nodeHasChildren ($_node)
{
$hasChildren = 0;
if (in_array('\haschildren', $_node['ATTRIBUTES']) ||
in_array('\Haschildren', $_node['ATTRIBUTES']) ||
in_array('\HasChildren', $_node['ATTRIBUTES'])) $hasChildren = 1;
return $hasChildren;
}
/** /**
* getTree provides tree structure regarding to selected node * getTree provides tree structure regarding to selected node
* *
@ -118,22 +134,15 @@ class mail_tree
* @param string $_profileID = '' icServer id * @param string $_profileID = '' icServer id
* @param int|boolean $_openTopLevel = 1 Open top level folders on load if it's set to 1|true, * @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 * false|0 leaves them in closed state
* @param $_noCheckboxNS = false no checkbox for namesapaces makes sure to not put checkbox for namespaces node
* *
* @return array returns an array of mail tree structure according to provided node * @return array returns an array of mail tree structure according to provided node
*/ */
function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1) function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_noCheckboxNS = false)
{ {
//Init mail folders //Init mail folders
$tree = array(tree::ID=> $_parent?$_parent:0,tree::CHILDREN => array()); $tree = array(tree::ID=> $_parent?$_parent:0,tree::CHILDREN => array());
$hDelimiter = $this->ui->mail_bo->getHierarchyDelimiter(); $hDelimiter = $this->ui->mail_bo->getHierarchyDelimiter();
$fn_nodeHasChildren = function ($_node)
{
$hasChildren = 0;
if (in_array('\haschildren', $_node['ATTRIBUTES']) ||
in_array('\Haschildren', $_node['ATTRIBUTES']) ||
in_array('\HasChildren', $_node['ATTRIBUTES'])) $hasChildren = 1;
return $hasChildren;
};
if ($_parent) list($_profileID) = explode(self::$delimiter, $_parent); if ($_parent) list($_profileID) = explode(self::$delimiter, $_parent);
@ -151,8 +160,8 @@ class mail_tree
{ {
try try
{ {
$nodeInfo = self::getFolderData($_parent, $hDelimiter); $nodeInfo = self::pathToFolderData($_parent, $hDelimiter);
$folders = $this->ui->mail_bo->getFolderArray($nodeInfo['mailbox']); $folders = $this->ui->mail_bo->getFolderArray($nodeInfo['mailbox'],false,2);
} catch (Exception $ex) { } catch (Exception $ex) {
return self::treeLeafNoConnectionArray($_profileID, $ex->getMessage(),array($_profileID), ''); return self::treeLeafNoConnectionArray($_profileID, $ex->getMessage(),array($_profileID), '');
} }
@ -161,10 +170,10 @@ class mail_tree
foreach ($folders as &$node) foreach ($folders as &$node)
{ {
$nodeId = $_profileID.self::$delimiter.$node['MAILBOX']; $nodeId = $_profileID.self::$delimiter.$node['MAILBOX'];
$nodeData = self::getFolderData($nodeId, $node['delimiter']); $nodeData = self::pathToFolderData($nodeId, $node['delimiter']);
$childrenNode[] = array( $childrenNode[] = array(
tree::ID=> $nodeId, tree::ID=> $nodeId,
tree::AUTOLOAD_CHILDREN => $fn_nodeHasChildren($node), tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($node),
tree::CHILDREN =>array(), tree::CHILDREN =>array(),
tree::LABEL => $nodeData['text'], tree::LABEL => $nodeData['text'],
tree::TOOLTIP => $nodeData['tooltip'], tree::TOOLTIP => $nodeData['tooltip'],
@ -201,6 +210,7 @@ class mail_tree
'sieve' => $accObj->imapServer()->acc_sieve_enabled, 'sieve' => $accObj->imapServer()->acc_sieve_enabled,
'spamfolder'=> $accObj->imapServer()->acc_folder_junk?true:false 'spamfolder'=> $accObj->imapServer()->acc_folder_junk?true:false
), ),
tree::NOCHECKBOX => $_noCheckboxNS
); );
self::setOutStructure($baseNode, $tree,self::$delimiter); self::setOutStructure($baseNode, $tree,self::$delimiter);
} }
@ -212,14 +222,21 @@ class mail_tree
foreach ($foldersList as $index => $topFolder) 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( $parentNode = array(
tree::ID=>$_profileID.self::$delimiter.$topFolder[$index]['MAILBOX'], tree::ID=>$_profileID.self::$delimiter.$topFolder[$index]['MAILBOX'],
tree::AUTOLOAD_CHILDREN => $fn_nodeHasChildren($topFolder[$index]), tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($topFolder[$index]),
tree::CHILDREN =>array(), tree::CHILDREN =>array(),
tree::LABEL =>lang($topFolder[$index]['MAILBOX']), tree::LABEL =>lang($topFolder[$index]['MAILBOX']),
tree::OPEN => $_openTopLevel, tree::OPEN => $_openTopLevel,
tree::TOOLTIP => lang($topFolder[$index]['MAILBOX']), tree::TOOLTIP => lang($topFolder[$index]['MAILBOX']),
tree::CHECKED => $topFolder[$index]['SUBSCRIBED'] tree::CHECKED => $topFolder[$index]['SUBSCRIBED'],
tree::NOCHECKBOX => $noCheckbox
); );
if ($index === "INBOX") if ($index === "INBOX")
{ {
@ -267,7 +284,7 @@ class mail_tree
$childNode = array( $childNode = array(
tree::ID => $nodeId, tree::ID => $nodeId,
tree::AUTOLOAD_CHILDREN => $fn_nodeHasChildren($node), tree::AUTOLOAD_CHILDREN => self::nodeHasChildren($node),
tree::CHILDREN => array(), tree::CHILDREN => array(),
tree::LABEL => lang($folderName), tree::LABEL => lang($folderName),
'parent' => $parentPath, 'parent' => $parentPath,

View File

@ -238,10 +238,11 @@ class mail_ui
/** /**
* Ajax function to request next branch of a tree branch * Ajax function to request next branch of a tree branch
*/ */
static function ajax_tree_autoloading () static function ajax_tree_autoloading ($_id = null)
{ {
$mail_ui = new mail_ui(); $mail_ui = new mail_ui();
etemplate_widget_tree::send_quote_json($mail_ui->mail_tree->getTree($_GET['id'])); $_id = $_id? $_id:$_GET['id'];
etemplate_widget_tree::send_quote_json($mail_ui->mail_tree->getTree($_id,'',1,false));
} }
/** /**
@ -262,21 +263,30 @@ class mail_ui
{ {
egw_framework::window_close('Missing acc_id!'); egw_framework::window_close('Missing acc_id!');
} }
$sel_options['foldertree'] = $this->getFolderTree(false, $profileId, false, false); // Initial tree's options, the rest would be loaded dynamicaly by autoloading,
// triggered from client-side. Also, we keep this here as
$sel_options['foldertree'] = $this->mail_tree->getTree(null,$profileId,1,true);
//Get all subscribed folder
// as getting all subscribed folder is very fast operation
// we can use it to get a comparison base for folders which
// got subscribed or unsubscribed by the user
try {
$subscribed = $this->mail_bo->icServer->listSubscribedMailboxes('',0,true);
} catch (Exception $ex) {
egw_framework::message($ex->getMessage());
}
if (!is_array($content)) if (!is_array($content))
{ {
$content['foldertree'] = array(); $content['foldertree'] = array();
$allFolders = $this->mail_bo->getFolderObjects(false,false,false,false);
foreach ($allFolders as $folder) foreach ($subscribed as $folder)
{
$folderName = $profileId . self::$delimiter . $folder->folderName;
if ($folder->subscribed)
{ {
$folderName = $profileId . self::$delimiter . $folder['MAILBOX'];
array_push($content['foldertree'], $folderName); array_push($content['foldertree'], $folderName);
} }
} }
}
else else
{ {
list($button) = @each($content['button']); list($button) = @each($content['button']);
@ -291,13 +301,30 @@ class mail_ui
{ {
$namespace_roots[] = $profileId . self::$delimiter . str_replace($namespace['delimiter'], '', $namespace['prefix']); $namespace_roots[] = $profileId . self::$delimiter . str_replace($namespace['delimiter'], '', $namespace['prefix']);
} }
error_log(__METHOD__."() namespace_roots=".array2string($namespace_roots)); $to_unsubscribe = $to_subscribe = array();
$to_subscribe = array_diff($content['foldertree'], $content['current_subscribed'], $namespace_roots); //$allFoldersData = $this->mail_bo->getFolderArray(null,false,0);
$to_unsubscribe = array_diff($content['current_subscribed'], $content['foldertree'], $namespace_roots); foreach ($content['foldertree'] as $path => $value)
{
list(,$node) = explode($profileId.self::$delimiter, $path);
if ($node)
{
if (is_array($subscribed) && $subscribed[$node] && !$value['value']) $to_unsubscribe []= $node;
if (is_array($subscribed) && !$subscribed[$node] && $value['value']) $to_subscribe [] = $node;
if ($value['value']) $cont[] = $path;
}
}
$content['foldertree'] = $cont;
// set foldertree options to basic node in order to avoid initial autoloading
// from client side, as no options would trigger that.
$sel_options['foldertree'] = array('id' => '0', 'item'=> array());
foreach(array_merge($to_subscribe, $to_unsubscribe) as $mailbox) foreach(array_merge($to_subscribe, $to_unsubscribe) as $mailbox)
{ {
if (in_array($profileId.self::$delimiter.$mailbox, $namespace_roots, true))
{
continue;
}
$subscribe = in_array($mailbox, $to_subscribe); $subscribe = in_array($mailbox, $to_subscribe);
list(,$mailbox) = explode(self::$delimiter, $mailbox); // remove profileId and delimiter
try { try {
$this->mail_bo->icServer->subscribeMailbox($mailbox, $subscribe); $this->mail_bo->icServer->subscribeMailbox($mailbox, $subscribe);
} }
@ -329,7 +356,9 @@ class mail_ui
// update foldertree in main window // update foldertree in main window
$parentFolder='INBOX'; $parentFolder='INBOX';
$refreshData = array( $refreshData = array(
$profileId => lang($parentFolder) $profileId => lang($parentFolder),
'subscribed' => $to_subscribe,
'unsubscribed' => $to_unsubscribe
); );
$response = egw_json_response::get(); $response = egw_json_response::get();
foreach($refreshData as $folder => &$name) foreach($refreshData as $folder => &$name)
@ -337,7 +366,9 @@ class mail_ui
$name = $this->getFolderTree(true, $folder, true, true, false); $name = $this->getFolderTree(true, $folder, true, true, false);
} }
// give success/error message to opener and popup itself // give success/error message to opener and popup itself
//$response->call('opener.app.mail.subscription_refresh',$refreshData);
$response->call('opener.app.mail.mail_reloadNode',$refreshData); $response->call('opener.app.mail.mail_reloadNode',$refreshData);
egw_framework::refresh_opener($msg, 'mail', null, null, null, null, null, $msg_type); egw_framework::refresh_opener($msg, 'mail', null, null, null, null, null, $msg_type);
if ($button == 'apply') if ($button == 'apply')
{ {
@ -353,7 +384,7 @@ class mail_ui
} }
$preserv['profileId'] = $profileId; $preserv['profileId'] = $profileId;
$preserv['current_subscribed'] = $content['foldertree'];
$readonlys = array(); $readonlys = array();
$stmpl->exec('mail.mail_ui.subscription', $content,$sel_options,$readonlys,$preserv,2); $stmpl->exec('mail.mail_ui.subscription', $content,$sel_options,$readonlys,$preserv,2);

View File

@ -44,6 +44,10 @@ app.classes.mail = AppJS.extend(
timeout: null, timeout: null,
request: null request: null
}, },
/**
*
*/
subscription_treeLastState : "",
/** /**
* abbrevations for common access rights * abbrevations for common access rights
@ -277,6 +281,15 @@ app.classes.mail = AppJS.extend(
jQuery('input',to.node).focus(); jQuery('input',to.node).focus();
} }
break; break;
case 'mail.subscribe':
if (this.subscription_treeLastState != "")
{
var tree = this.et2.getWidgetById('foldertree');
//Saved state of tree
var state = jQuery.parseJSON(this.subscription_treeLastState);
tree.input.loadJSONObject(tree._htmlencode_node(state));
}
} }
}, },
@ -3655,6 +3668,28 @@ app.classes.mail = AppJS.extend(
this.egw.open_link('mail.mail_sieve.editVacation&acc_id='+acc_id,'_blank','700x480'); this.egw.open_link('mail.mail_sieve.editVacation&acc_id='+acc_id,'_blank','700x480');
}, },
subscription_refresh: function(_data)
{
console.log(_data);
},
/**
* Submit on apply button and save current tree state
*
* @param {type} _egw
* @param {type} _widget
* @returns {undefined}
*/
subscription_apply: function (_egw, _widget)
{
var tree = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('foldertree');
if (tree)
{
tree.input._xfullXML = true;
this.subscription_treeLastState = tree.input.serializeTreeToJSON();
}
this.et2._inst.submit(_widget);
},
/** /**
* Popup the subscription dialog * Popup the subscription dialog
* *

View File

@ -13,14 +13,14 @@
</row> </row>
<row> <row>
<box scrolling="auto"> <box scrolling="auto">
<tree id="foldertree" onclick="app.mail.subscribe_onclick" multiple="true"/> <tree id="foldertree" multiple="true" autoloading="mail_ui::ajax_tree_autoloading"/>
</box> </box>
</row> </row>
</rows> </rows>
</grid> </grid>
<hbox class="dialogFooterToolbar"> <hbox class="dialogFooterToolbar">
<button statustext="Saves subscription changes" label="Save" id="button[save]"/> <button statustext="Saves subscription changes" label="Save" id="button[save]"/>
<button statustext="Applies the changes made" label="Apply" id="button[apply]"/> <button statustext="Applies the changes made" label="Apply" id="button[apply]" onclick="app.mail.subscription_apply"/>
<button label="Cancel" id="button[cancel]" onclick="window.close()"/> <button label="Cancel" id="button[cancel]" onclick="window.close()"/>
</hbox> </hbox>
</template> </template>