diff --git a/emailadmin/inc/class.emailadmin_imapbase.inc.php b/emailadmin/inc/class.emailadmin_imapbase.inc.php
index 6eb44ee074..1674d43a1d 100644
--- a/emailadmin/inc/class.emailadmin_imapbase.inc.php
+++ b/emailadmin/inc/class.emailadmin_imapbase.inc.php
@@ -835,11 +835,10 @@ class emailadmin_imapbase
* openConnection
*
* @param int $_icServerID = 0
- * @param boolean $_adminConnection = false
* @throws Horde_Imap_Client_Exception on connection error or authentication failure
* @throws InvalidArgumentException on missing credentials
*/
- function openConnection($_icServerID=0, $_adminConnection=false)
+ function openConnection($_icServerID=0)
{
//error_log( "-------------------------->open connection ".function_backtrace());
//error_log(__METHOD__.' ('.__LINE__.') '.' ->'.array2string($this->icServer));
@@ -2622,7 +2621,69 @@ class emailadmin_imapbase
if (self::$debugTimes) self::logRunTimes($starttime,null,function_backtrace(),__METHOD__.' ('.__LINE__.') ');
return $folders2return[$this->icServer->ImapServerId];
}
-
+
+ /**
+ * Get IMAP folder for a mailbox
+ *
+ * @param $_nodePath = null folder name to fetch from IMAP,
+ * null means all folders
+ * @param $_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
+ */
+ function getFolderArray ($_nodePath = null, $_onlyTopLevel = false, $_search= 2)
+ {
+ // delimiter
+ $delimiter = $this->getHierarchyDelimiter();
+
+ $folders = array();
+
+ if ($_onlyTopLevel) // top level leaves
+ {
+ // Get top mailboxes of icServer
+ $topFolders = $this->icServer->getMailboxes("", 2, true);
+
+ foreach ($topFolders as &$node)
+ {
+ $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']]);
+ }
+ }
+ elseif ($_nodePath) // single node
+ {
+ switch ($_search)
+ {
+ // Including children
+ case 0:
+ case 2:
+ $path = $_nodePath.''.$delimiter;
+ break;
+ // Node itself
+ // shouldn't contain next level delimiter
+ case 1:
+ $path = $_nodePath;
+ break;
+ }
+ $folders = $this->icServer->getMailboxes($path, $_search, true);
+ ksort($folders);
+ return $folders;
+ }
+ elseif(!$_nodePath)
+ {
+ $folders = $this->icServer->getMailboxes('', 0, true);
+ }
+ return $folders;
+ }
+
+
/**
* Check if all automatic folders exist and create them if not
*
diff --git a/mail/inc/class.mail_tree.inc.php b/mail/inc/class.mail_tree.inc.php
new file mode 100644
index 0000000000..b7d43cfdd4
--- /dev/null
+++ b/mail/inc/class.mail_tree.inc.php
@@ -0,0 +1,408 @@
+
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @version $Id:$
+ */
+
+
+/**
+ * Define tree as class tree widget
+ */
+use \etemplate_widget_tree as tree;
+
+
+/**
+ * Mail tree worker class
+ * -provides backend functionality for folder tree
+ * -provides classes that may be used by other apps too
+ *
+ */
+class mail_tree
+{
+ /**
+ * delimiter - used to separate profileID from folder-tree-structure
+ *
+ * @var string
+ */
+ static $delimiter = '::';
+
+ /**
+ * Icons used for nodes different states
+ *
+ * @var array
+ */
+ static $leafImages = array(
+ 'folderNoSelectClosed' => "folderNoSelectClosed.gif",
+ 'folderNoSelectOpen' => "folderNoSelectOpen.gif",
+ 'folderOpen' => "folderOpen.gif",
+ 'folderClosed' => "MailFolderClosed.png",
+ 'folderLeaf' => "MailFolderPlain.png",
+ 'folderHome' => "kfm_home.png",
+ 'folderAccount' => "thunderbird.png",
+ );
+
+ /**
+ * Mail tree constructor
+ *
+ * @param object $mail_ui
+ */
+ function __construct($mail_ui) {
+ $this->ui = $mail_ui;
+ }
+
+ /**
+ *
+ * Structs an array of fake INBOX to show as an error node
+ * @param string $_profileID icServer profile id
+ * @param string $_err error message to be shown on tree node
+ * @param mixed $_path
+ * @param mixed $_parent
+ * @return array returns an array of tree node
+ */
+ static function treeLeafNoConnectionArray($_profileID, $_err, $_path, $_parent)
+ {
+ $baseNode = array('id' => $_profileID);
+ $leaf = array(
+ 'id' => $_profileID.self::$delimiter.'INBOX',
+ 'text' => $_err,
+ 'tooltip' => $_err,
+ 'im0' => self::$leafImages["folderNoSelectClosed"],
+ 'im1' => self::$leafImages["folderNoSelectOpen.gif"],
+ 'im2' => self::$leafImages["folderNoSelectClosed"],
+ 'path'=> $_path,
+ 'parent' => $_parent
+ );
+ self::setOutStructure($leaf, $baseNode, self::$delimiter);
+
+ return ($baseNode?$baseNode:array( // fallback not connected array
+ 'id'=>0,
+ 'item'=> array(
+ 'text'=>'INBOX',
+ 'tooltip'=>'INBOX'.' '.lang('(not connected)'),
+ 'im0'=> self::$leafImages['folderHome']
+ )
+ )
+ );
+ }
+
+ /**
+ * Get folder data from path
+ *
+ * @param string $path a node path
+ * @return array returns an array of data extracted from given node path
+ */
+ static function pathToFolderData ($_path, $_hDelimiter)
+ {
+ list(,$path) = explode(self::$delimiter, $_path);
+ $path_chain = $parts = explode($_hDelimiter, $path);
+ $name = array_pop($parts);
+ return array (
+ 'name' => $name,
+ 'mailbox' => $path,
+ 'parent' => implode($_hDelimiter, $parts),
+ 'text' => $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
+ *
+ * @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 $_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
+ */
+ function getTree ($_parent = null, $_profileID = '', $_openTopLevel = 1, $_noCheckboxNS = false)
+ {
+ //Init mail folders
+ $tree = array(tree::ID=> $_parent?$_parent:0,tree::CHILDREN => array());
+ $hDelimiter = $this->ui->mail_bo->getHierarchyDelimiter();
+
+ if ($_parent) list($_profileID) = explode(self::$delimiter, $_parent);
+
+ if (is_numeric($_profileID) && $_profileID != $this->ui->mail_bo->profileID)
+ {
+ try
+ {
+ $this->ui->changeProfile($_profileID);
+ } catch (Exception $ex) {
+ return self::treeLeafNoConnectionArray($_profileID, $ex->getMessage(),array($_profileID), '');
+ }
+ }
+
+ if ($_parent) // Single node loader
+ {
+ try
+ {
+ $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]);
+
+ //$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)
+ {
+ // 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;
+ }
+ return $tree;
+ }
+
+ /**
+ * setOutStructure - helper function to transform the folderObjectList to dhtmlXTreeObject requirements
+ *
+ * @param array $data data to be processed
+ * @param array &$out, out array
+ * @param string $del needed as glue for parent/child operation / comparsion
+ * @param boolean $createMissingParents a missing parent, instead of throwing an exception
+ * @param array $nameSpace used to check on creation of nodes in namespaces other than personal
+ * 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())
+ {
+ //error_log(__METHOD__."(".array2string($data).', '.array2string($out).", '$del')");
+ $components = $data['path'];
+ array_pop($components); // remove own name
+
+ $insert = &$out;
+ $parents = array();
+ foreach($components as $component)
+ {
+ if (count($parents)>1)
+ {
+ $helper = array_slice($parents,1,null,true);
+ $parent = $parents[0].self::$delimiter.implode($del, $helper);
+ if ($parent) $parent .= $del;
+ }
+ else
+ {
+ $parent = implode(self::$delimiter, $parents);
+ if ($parent) $parent .= self::$delimiter;
+ }
+
+ if (!is_array($insert) || !isset($insert['item']))
+ {
+ // throwing an exeption here seems to be unrecoverable,
+ // even if the cause is a something that can be handeled by the mailserver
+ if (mail_bo::$debug) error_log(__METHOD__.':'.__LINE__." id=$data[id]: Parent '$parent' of '$component' not found!");
+ // should we hit the break? if in personal: sure, something is wrong with the folderstructure
+ // if in shared or others we may proceed as access to folders may very well be limited to
+ // a single folder within the tree
+ $break = true;
+ foreach ($nameSpace as $nsp)
+ {
+ // if (appropriately padded) namespace prefix of (others or shared) is the leading part of parent
+ // we want to create the node in question as we meet the above considerations
+ if ($nsp['type']!='personal' && $nsp['prefix_present'] && stripos($parent,$data['path'][0].self::$delimiter.$nsp['prefix'])===0)
+ {
+ if (mail_bo::$debug) error_log(__METHOD__.__LINE__.' about to create:'.$parent.' in '.$data['path'][0].self::$delimiter.$nsp['prefix']);
+ $break=false;
+ }
+ }
+ if ($break) break;
+ }
+ if ($insert['item'])
+ {
+ foreach($insert['item'] as &$item)
+ {
+ if ($item['id'] == $parent.$component)
+ {
+ $insert =& $item;
+ break;
+ }
+ }
+ }
+ if ($item['id'] != $parent.$component)
+ {
+ if ($createMissingParents)
+ {
+ unset($item);
+ $item = array('id' => $parent.$component, 'text' => $component, 'im0' => "folderNoSelectClosed.gif",'im1' => "folderNoSelectOpen.gif",'im2' => "folderNoSelectClosed.gif",'tooltip' => lang('no access'));
+ $insert['item'][] =& $item;
+ $insert =& $item;
+ }
+ else
+ {
+ throw new egw_exception_assertion_failed(__METHOD__.':'.__LINE__.": id=$data[id]: Parent '$parent' '$component' not found!");
+ }
+ }
+ $parents[] = $component;
+ }
+ unset($data['path']);
+ $insert['item'][] = $data;
+ //error_log(__METHOD__."() leaving with out=".array2string($out));
+ }
+
+}
diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php
index 374eba7ed3..8850f047e4 100644
--- a/mail/inc/class.mail_ui.inc.php
+++ b/mail/inc/class.mail_ui.inc.php
@@ -23,7 +23,7 @@
* If no profile change is needed they just call:
* $mail_ui = new mail_ui();
* Afterwards they use $mail_ui instead of $this.
- */
+ */
class mail_ui
{
/**
@@ -142,6 +142,7 @@ 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)
{
@@ -233,7 +234,17 @@ class mail_ui
if (mail_bo::$debugTimes) mail_bo::logRunTimes($starttime,null,'',__METHOD__.__LINE__);
}
-
+
+ /**
+ * Ajax function to request next branch of a tree branch
+ */
+ static function ajax_tree_autoloading ($_id = null)
+ {
+ $mail_ui = new mail_ui();
+ $_id = $_id? $_id:$_GET['id'];
+ etemplate_widget_tree::send_quote_json($mail_ui->mail_tree->getTree($_id,'',1,false));
+ }
+
/**
* Subscription popup window
*
@@ -252,19 +263,28 @@ class mail_ui
{
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 folders
+ // as getting all subscribed folders 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))
{
$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)
- {
- array_push($content['foldertree'], $folderName);
- }
+ $folderName = $profileId . self::$delimiter . $folder['MAILBOX'];
+ array_push($content['foldertree'], $folderName);
}
}
else
@@ -281,13 +301,29 @@ class mail_ui
{
$namespace_roots[] = $profileId . self::$delimiter . str_replace($namespace['delimiter'], '', $namespace['prefix']);
}
- error_log(__METHOD__."() namespace_roots=".array2string($namespace_roots));
- $to_subscribe = array_diff($content['foldertree'], $content['current_subscribed'], $namespace_roots);
- $to_unsubscribe = array_diff($content['current_subscribed'], $content['foldertree'], $namespace_roots);
+ $to_unsubscribe = $to_subscribe = array();
+ 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)
{
+ if (in_array($profileId.self::$delimiter.$mailbox, $namespace_roots, true))
+ {
+ continue;
+ }
$subscribe = in_array($mailbox, $to_subscribe);
- list(,$mailbox) = explode(self::$delimiter, $mailbox); // remove profileId and delimiter
try {
$this->mail_bo->icServer->subscribeMailbox($mailbox, $subscribe);
}
@@ -319,7 +355,7 @@ class mail_ui
// update foldertree in main window
$parentFolder='INBOX';
$refreshData = array(
- $profileId => lang($parentFolder)
+ $profileId => lang($parentFolder),
);
$response = egw_json_response::get();
foreach($refreshData as $folder => &$name)
@@ -327,7 +363,9 @@ class mail_ui
$name = $this->getFolderTree(true, $folder, true, true, false);
}
// 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);
+
egw_framework::refresh_opener($msg, 'mail', null, null, null, null, null, $msg_type);
if ($button == 'apply')
{
@@ -343,7 +381,7 @@ class mail_ui
}
$preserv['profileId'] = $profileId;
- $preserv['current_subscribed'] = $content['foldertree'];
+
$readonlys = array();
$stmpl->exec('mail.mail_ui.subscription', $content,$sel_options,$readonlys,$preserv,2);
@@ -733,7 +771,7 @@ class mail_ui
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)
+ if (!is_null($_nodeID) && $_nodeID != 0)
{
list($_profileID,$_folderName) = explode(self::$delimiter,$_nodeID,2);
unset($_folderName);
@@ -748,18 +786,7 @@ class mail_ui
}
catch (Exception $e)
{
- $out = array('id' => $_profileID);
- $oA = array('id' => $_profileID.self::$delimiter.'INBOX',
- 'text' => $e->getMessage(),
- 'tooltip' => $e->getMessage(),
- 'im0' => "folderNoSelectClosed.gif",
- 'im1' => "folderNoSelectOpen.gif",
- 'im2' => "folderNoSelectClosed.gif",
- 'path'=> array($_profileID),
- 'parent' => ''
- );
- $this->setOutStructure($oA, $out, self::$delimiter);
- return ($out?$out:array('id'=>0, 'item'=>array('text'=>'INBOX','tooltip'=>'INBOX'.' '.lang('(not connected)'),'im0'=>'kfm_home.png')));
+ return self::treeLeafNoConnectionArray($_profileID, $e->getMessage(), array($_profileID), '');
}
}
}
@@ -820,22 +847,12 @@ class mail_ui
// mark on account if Sieve is enabled
'data' => array('sieve' => $accountObj->imapServer()->acc_sieve_enabled),
);
- $this->setOutStructure($oA, $out, self::$delimiter);
+ 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)
{
- $oA = array(
- 'id' => $acc_id.self::$delimiter.'INBOX',
- 'text' => lang($e->getMessage()),
- 'tooltip' => '('.$acc_id.') '.$e->getMessage(),
- 'im0' => "folderNoSelectClosed.gif",
- 'im1' => "folderNoSelectOpen.gif",
- 'im2' => "folderNoSelectClosed.gif",
- 'path'=> array($acc_id, 'INBOX'),
- 'parent' => $acc_id,
- );
- $this->setOutStructure($oA, $out, self::$delimiter);
+ $out = self::treeLeafNoConnectionArray($acc_id, lang($e->getMessage()), array($acc_id, 'INBOX'), $acc_id);
}
}
//$endtime = microtime(true) - $starttime;
@@ -852,6 +869,7 @@ class mail_ui
$nameSpace = $this->mail_bo->_getNameSpaces();
foreach($folderObjects as $key => $obj)
{
+ $fS = $this->mail_bo->getFolderStatus($key,false,($_fetchCounters?false:true));
//error_log(__METHOD__.__LINE__.array2string($key));
$levels = explode($delimiter,$key);
$levelCt = count($levels);
@@ -913,7 +931,7 @@ class mail_ui
}
$oA['parent'] = $parentName;
- $this->setOutStructure($oA,$out,$obj->delimiter,true,$nameSpace);
+ mail_tree::setOutStructure($oA,$out,$obj->delimiter,true,$nameSpace);
$c++;
}
if (!is_null($_nodeID) && $_nodeID !=0 && $_returnNodeOnly==true)
@@ -952,92 +970,7 @@ class mail_ui
}
}
- /**
- * setOutStructure - helper function to transform the folderObjectList to dhtmlXTreeObject requirements
- *
- * @param array $data data to be processed
- * @param array &$out, out array
- * @param string $del needed as glue for parent/child operation / comparsion
- * @param boolean $createMissingParents a missing parent, instead of throwing an exception
- * @param array $nameSpace used to check on creation of nodes in namespaces other than personal
- * as clearance for access may be limited to a single branch-node of a tree
- * @return void
- */
- function setOutStructure($data, &$out, $del='.', $createMissingParents=true, $nameSpace=array())
- {
- //error_log(__METHOD__."(".array2string($data).', '.array2string($out).", '$del')");
- $components = $data['path'];
- array_pop($components); // remove own name
-
- $insert = &$out;
- $parents = array();
- foreach($components as $component)
- {
- if (count($parents)>1)
- {
- $helper = array_slice($parents,1,null,true);
- $parent = $parents[0].self::$delimiter.implode($del, $helper);
- if ($parent) $parent .= $del;
- }
- else
- {
- $parent = implode(self::$delimiter, $parents);
- if ($parent) $parent .= self::$delimiter;
- }
-
- if (!is_array($insert) || !isset($insert['item']))
- {
- // throwing an exeption here seems to be unrecoverable,
- // even if the cause is a something that can be handeled by the mailserver
- if (mail_bo::$debug) error_log(__METHOD__.':'.__LINE__." id=$data[id]: Parent '$parent' of '$component' not found!");
- // should we hit the break? if in personal: sure, something is wrong with the folderstructure
- // if in shared or others we may proceed as access to folders may very well be limited to
- // a single folder within the tree
- $break = true;
- foreach ($nameSpace as $nsp)
- {
- // if (appropriately padded) namespace prefix of (others or shared) is the leading part of parent
- // we want to create the node in question as we meet the above considerations
- if ($nsp['type']!='personal' && $nsp['prefix_present'] && stripos($parent,$data['path'][0].self::$delimiter.$nsp['prefix'])===0)
- {
- if (mail_bo::$debug) error_log(__METHOD__.__LINE__.' about to create:'.$parent.' in '.$data['path'][0].self::$delimiter.$nsp['prefix']);
- $break=false;
- }
- }
- if ($break) break;
- }
- if ($insert['item'])
- {
- foreach($insert['item'] as &$item)
- {
- if ($item['id'] == $parent.$component)
- {
- $insert =& $item;
- break;
- }
- }
- }
- if ($item['id'] != $parent.$component)
- {
- if ($createMissingParents)
- {
- unset($item);
- $item = array('id' => $parent.$component, 'text' => $component, 'im0' => "folderNoSelectClosed.gif",'im1' => "folderNoSelectOpen.gif",'im2' => "folderNoSelectClosed.gif",'tooltip' => lang('no access'));
- $insert['item'][] =& $item;
- $insert =& $item;
- }
- else
- {
- throw new egw_exception_assertion_failed(__METHOD__.':'.__LINE__.": id=$data[id]: Parent '$parent' '$component' not found!");
- }
- }
- $parents[] = $component;
- }
- unset($data['path']);
- $insert['item'][] = $data;
- //error_log(__METHOD__."() leaving with out=".array2string($out));
- }
-
+
/**
* Get actions / context menu for index
*
@@ -2283,7 +2216,7 @@ class mail_ui
//error_log(__METHOD__.__LINE__.$linkView);
$attachmentHTML[$key]['link_view'] = ''.
- ($value['name'] ? ( $filename ? $filename : $value['name'] ) : lang('(no subject)')).
+ ($value['name'] ? $value['name'] : lang('(no subject)')).
'';
$linkData = array
diff --git a/mail/js/app.js b/mail/js/app.js
index 79b7f16129..b9651920b1 100644
--- a/mail/js/app.js
+++ b/mail/js/app.js
@@ -44,7 +44,11 @@ app.classes.mail = AppJS.extend(
timeout: null,
request: null
},
-
+ /**
+ *
+ */
+ subscription_treeLastState : "",
+
/**
* abbrevations for common access rights
* @array
@@ -259,6 +263,15 @@ app.classes.mail = AppJS.extend(
jQuery('input',to.node).focus();
}
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));
+ }
}
},
@@ -3565,7 +3578,57 @@ app.classes.mail = AppJS.extend(
var acc_id = parseInt(_senders[0].id);
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);
+ },
+
+ /**
+ * Show ajax-loader when the autoloading get started
+ *
+ * @param {type} _id item id
+ * @param {type} _widget tree widget
+ * @returns {Boolean}
+ */
+ subscription_autoloadingStart: function (_id, _widget)
+ {
+ var node = _widget.input._globalIdStorageFind(_id);
+ if (node && typeof node.htmlNode != 'undefined')
+ {
+ var img = jQuery('img',node.htmlNode)[0];
+ img.src = egw.image('ajax-loader', 'admin');
+ }
+ return true;
+ },
+
+ /**
+ * Revert back the icon after autoloading is finished
+ * @returns {Boolean}
+ */
+ subscription_autoloadingEnd: function ()
+ {
+ return true;
+ },
+
/**
* Popup the subscription dialog
*
diff --git a/mail/templates/default/app.css b/mail/templates/default/app.css
index 927a2106f4..0c6fe166a9 100644
--- a/mail/templates/default/app.css
+++ b/mail/templates/default/app.css
@@ -885,3 +885,7 @@ div.mailComposeHeaderSection>table {
width:100%;
height:100%;
}
+/*Avoid getting scrollbar on form area, let scrolling be handled by tree*/
+#mail-subscribe {
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/mail/templates/default/subscribe.xet b/mail/templates/default/subscribe.xet
index 2b0107e6cc..5fc5fedca3 100755
--- a/mail/templates/default/subscribe.xet
+++ b/mail/templates/default/subscribe.xet
@@ -13,14 +13,14 @@
-
+
diff --git a/mail/templates/pixelegg/app.css b/mail/templates/pixelegg/app.css
index 1f956be0fe..fd3337dd3d 100755
--- a/mail/templates/pixelegg/app.css
+++ b/mail/templates/pixelegg/app.css
@@ -873,6 +873,10 @@ div.mailComposeHeaderSection > table {
width: 100%;
height: 100%;
}
+/*Avoid getting scrollbar on form area, let scrolling be handled by tree*/
+#mail-subscribe {
+ overflow: hidden;
+}
#popupMainDiv {
padding: 5px;
}