* @category Horde
* @copyright 2000-2017 Horde LLC
* @license http://www.horde.org/licenses/gpl GPL
* @package IMP
*
* @property-read boolean $changed Has the tree changed?
* @property-read IMP_Ftree_Eltdiff $eltdiff Element diff tracker.
* @property-read IMP_FTree_Prefs_Expanded $expanded The expanded folders
* list.
* @property-read IMP_Ftree_Prefs_Poll $poll The poll list.
* @property-read boolean $subscriptions Whether IMAP subscriptions are
* enabled.
* @property-read boolean $unsubscribed_loaded True if unsubscribed mailboxes
* have been loaded.
*/
class IMP_Ftree
implements ArrayAccess, Countable, IteratorAggregate, Serializable
{
/* Constants for mailboxElt attributes. */
const ELT_NOSELECT = 1;
const ELT_NAMESPACE_OTHER = 2;
const ELT_NAMESPACE_SHARED = 4;
const ELT_IS_OPEN = 8;
const ELT_IS_SUBSCRIBED = 16;
const ELT_NOINFERIORS = 32;
const ELT_IS_POLLED = 64;
const ELT_NOT_POLLED = 128;
const ELT_VFOLDER = 256;
const ELT_NONIMAP = 512;
const ELT_INVISIBLE = 1024;
const ELT_NEED_SORT = 2048;
const ELT_REMOTE = 4096;
const ELT_REMOTE_AUTH = 8192;
const ELT_REMOTE_MBOX = 16384;
/* The string used to indicate the base of the tree. This must include
* null since this is the only 7-bit character not allowed in IMAP
* mailboxes (nulls allow us to sort by name but never conflict with an
* IMAP mailbox). */
const BASE_ELT = "base\0";
/**
* Account sources.
*
* @var array
*/
protected $_accounts;
/**
* Tree changed flag. Set when something in the tree has been altered.
*
* @var boolean
*/
protected $_changed = false;
/**
* Element diff tracking.
*
* @var IMP_Ftree_Eltdiff
*/
protected $_eltdiff;
/**
* Array containing the mailbox elements.
*
* @var array
*/
protected $_elts;
/**
* Parent/child list.
*
* @var array
*/
protected $_parent;
/**
* Temporary data that is not saved across serialization.
*
* @var array
*/
protected $_temp = array();
/**
* Constructor.
*/
public function __construct()
{
$this->init();
}
/**
*/
public function __get($name)
{
global $prefs;
switch ($name) {
case 'changed':
return ($this->_changed || $this->eltdiff->changed);
case 'expanded':
if (!isset($this->_temp['expanded'])) {
$this->_temp['expanded'] = new IMP_Ftree_Prefs_Expanded();
}
return $this->_temp['expanded'];
case 'eltdiff':
return $this->_eltdiff;
case 'poll':
if (!isset($this->_temp['poll'])) {
$this->_temp['poll'] = new IMP_Ftree_Prefs_Poll($this);
}
return $this->_temp['poll'];
case 'subscriptions':
return $prefs->getValue('subscribe');
case 'unsubscribed_loaded':
return $this[self::BASE_ELT]->subscribed;
}
}
/**
* Initialize the tree.
*/
public function init()
{
global $injector, $session;
$access_folders = $injector->getInstance('IMP_Factory_Imap')->create()->access(IMP_Imap::ACCESS_FOLDERS);
/* Reset class variables to the defaults. */
$this->_accounts = $this->_elts = $this->_parent = array();
$this->_changed = true;
$old_track = (isset($this->_eltdiff) && $this->_eltdiff->track);
$this->_eltdiff = new IMP_Ftree_Eltdiff();
/* Create a placeholder element to the base of the tree so we can
* keep track of whether the base level needs to be sorted. */
$this->_elts[self::BASE_ELT] = self::ELT_NEED_SORT | self::ELT_NONIMAP;
$this->_parent[self::BASE_ELT] = array();
$mask = IMP_Ftree_Account::INIT;
if (!$access_folders || !$this->subscriptions || $session->get('imp', 'showunsub')) {
$mask |= IMP_Ftree_Account::UNSUB;
$this->setAttribute('subscribed', self::BASE_ELT, true);
}
/* Add base account. */
$ob = $this->_accounts[self::BASE_ELT] = $access_folders
? new IMP_Ftree_Account_Imap()
: new IMP_Ftree_Account_Inboxonly();
array_map(array($this, '_insertElt'), $ob->getList(null, $mask));
if ($access_folders) {
/* Add remote servers. */
$this->insert(iterator_to_array(
$injector->getInstance('IMP_Remote')
));
/* Add virtual folders to the tree. */
$this->insert(iterator_to_array(
IMP_Search_IteratorFilter::create(
IMP_Search_IteratorFilter::VFOLDER
)
));
}
if ($old_track) {
$this->eltdiff->track = true;
}
}
/**
* Insert an element into the tree.
*
* @param mixed $id The name of the mailbox (or a list of mailboxes),
* an IMP_Search_Vfolder object, an IMP_Remote_Account
* object, or an array containing any mixture of these.
*/
public function insert($id)
{
foreach ((is_array($id) ? $id : array($id)) as $val) {
if (($val instanceof IMP_Search_Vfolder) &&
!isset($this->_accounts[strval($val)])) {
/* Virtual Folders. */
$account = $this->_accounts[strval($val)] = new IMP_Ftree_Account_Vfolder($val);
} elseif (($val instanceof IMP_Remote_Account) &&
!isset($this->_accounts[strval($val)])) {
/* Remote accounts. */
$account = $this->_accounts[strval($val)] = new IMP_Ftree_Account_Remote($val);
} else {
$account = $this->getAccount($val);
$val = $this->_normalize($val);
}
array_map(array($this, '_insertElt'), $account->getList(array($val)));
}
}
/**
* Expand an element.
*
* @param mixed $elts The element (or an array of elements) to
* expand.
* @param boolean $expandall Expand all subelements?
*/
public function expand($elts, $expandall = false)
{
foreach ((is_array($elts) ? $elts : array($elts)) as $val) {
if (($elt = $this[$val]) && $elt->children) {
if (!$elt->open) {
$elt->open = true;
}
/* Expand all children beneath this one. */
if ($expandall) {
$this->expand($this->_parent[strval($elt)]);
}
}
}
}
/**
* Expand all elements.
*/
public function expandAll()
{
$this->expand($this->_parent[self::BASE_ELT], true);
}
/**
* Collapse an element.
*
* @param mixed $elts The element (or an array of elements) to expand.
*/
public function collapse($elts)
{
foreach ((is_array($elts) ? $elts : array($elts)) as $val) {
if ($elt = $this[$val]) {
$elt->open = false;
}
}
}
/**
* Collapse all elements.
*/
public function collapseAll()
{
$this->collapse(
array_diff_key(array_keys($this->_elts), array(self::BASE_ELT))
);
}
/**
* Delete an element from the tree.
*
* @param mixed $elts The element (or an array of elements) to delete.
*/
public function delete($id)
{
if (is_array($id)) {
/* We want to delete from the TOP of the tree down to ensure that
* parents have an accurate view of what children are left. */
$this->sortList($id);
$id = array_reverse($id);
} else {
$id = array($id);
}
foreach (array_filter(array_map(array($this, 'offsetGet'), $id)) as $elt) {
$account = $this->getAccount($elt);
if (!($mask = $account->delete($elt))) {
continue;
}
$this->_changed = true;
if ($mask & IMP_Ftree_Account::DELETE_RECURSIVE) {
foreach (array_map('strval', iterator_to_array(new IMP_Ftree_Iterator($elt), false)) as $val) {
unset(
$this->_elts[$val],
$this->_parent[$val]
);
$this->eltdiff->delete($val);
}
unset($this->_parent[strval($elt)]);
}
if (strval($account) == strval($elt)) {
unset($this->_accounts[strval($elt)]);
}
if ($mask & IMP_Ftree_Account::DELETE_ELEMENT) {
/* Do not delete from tree if there are child elements -
* instead, convert to a container element. */
if ($elt->children) {
$elt->container = true;
continue;
}
/* Remove the mailbox from the expanded folders list. */
unset($this->expanded[$elt]);
/* Remove the mailbox from the polled list. */
$this->poll->removePollList($elt);
}
$parent = strval($elt->parent);
$this->eltdiff->delete($elt);
/* Delete the entry from the parent tree. */
unset(
$this->_elts[strval($elt)],
$this->_parent[$parent][array_search(strval($elt), $this->_parent[$parent], true)]
);
if (empty($this->_parent[$parent])) {
/* This mailbox is now completely empty (no children). */
unset($this->_parent[$parent]);
if ($p_elt = $this[$parent]) {
if ($p_elt->container && !$p_elt->namespace) {
$this->delete($p_elt);
} else {
$p_elt->open = false;
$this->eltdiff->change($p_elt);
}
}
}
if (!empty($this->_parent[$parent])) {
$this->_parent[$parent] = array_values($this->_parent[$parent]);
}
}
}
/**
* Rename a mailbox.
*
* @param string $old The old mailbox name.
* @param string $new The new mailbox name.
*/
public function rename($old, $new)
{
if (!($old_elt = $this[$old])) {
return;
}
$new_list = $polled = array();
$old_list = array_merge(
array($old),
iterator_to_array(new IMP_Ftree_IteratorFilter(new IMP_Ftree_Iterator($old_elt)), false)
);
foreach ($old_list as $val) {
$new_list[] = $new_name = substr_replace($val, $new, 0, strlen($old));
if ($val->polled) {
$polled[] = $new_name;
}
}
$this->insert($new_list);
$this->poll->addPollList($polled);
$this->delete($old_list);
}
/**
* Subscribe an element to the tree.
*
* @param mixed $id The element name or an array of element names.
*/
public function subscribe($id)
{
foreach ((is_array($id) ? $id : array($id)) as $val) {
$this->setAttribute('subscribed', $val, true);
$this->setAttribute('container', $val, false);
}
}
/**
* Unsubscribe an element from the tree.
*
* @param mixed $id The element name or an array of element names.
*/
public function unsubscribe($id)
{
if (is_array($id)) {
/* We want to delete from the TOP of the tree down to ensure that
* parents have an accurate view of what children are left. */
$this->sortList($id);
$id = array_reverse($id);
} else {
$id = array($id);
}
foreach ($id as $val) {
/* INBOX can never be unsubscribed to. */
if (($elt = $this[$val]) && !$elt->inbox) {
$this->_changed = true;
/* Do not delete from tree if there are child elements -
* instead, convert to a container element. */
if ($elt->children) {
$this->setAttribute('container', $elt, true);
}
/* Set as unsubscribed, add to unsubscribed list, and remove
* from subscribed list. */
$this->setAttribute('subscribed', $elt, false);
}
}
}
/**
* Load unsubscribed mailboxes.
*/
public function loadUnsubscribed()
{
/* If we are switching from unsubscribed to subscribed, no need
* to do anything (we just ignore unsubscribed stuff). */
if ($this->unsubscribed_loaded) {
return;
}
$this->_changed = true;
/* The BASE_ELT having the SUBSCRIBED mask indicates the unsubscribed
* mailboxes have been loaded into the object. */
$this->setAttribute('subscribed', self::BASE_ELT, true);
/* If we are switching from subscribed to unsubscribed, we need
* to add all unsubscribed elements that live in currently
* discovered items. */
$old_track = $this->eltdiff->track;
$this->eltdiff->track = false;
foreach ($this->_accounts as $val) {
array_map(array($this, '_insertElt'), $val->getList(array(), $val::UNSUB));
}
$this->eltdiff->track = $old_track;
}
/**
* Get an attribute value.
*
* @param string $type The attribute type.
* @param string $name The element name.
*
* @return mixed Boolean attribute result, or null if element or
* attribute doesn't exist
*/
public function getAttribute($type, $name)
{
if (!($elt = $this[$name])) {
return null;
}
$s_elt = strval($elt);
switch ($type) {
case 'children':
return isset($this->_parent[$s_elt]);
case 'container':
$attr = self::ELT_NOSELECT;
break;
case 'invisible':
$attr = self::ELT_INVISIBLE;
break;
case 'namespace_other':
$attr = self::ELT_NAMESPACE_OTHER;
break;
case 'namespace_shared':
$attr = self::ELT_NAMESPACE_SHARED;
break;
case 'needsort':
$attr = self::ELT_NEED_SORT;
break;
case 'nochildren':
$attr = self::ELT_NOINFERIORS;
break;
case 'nonimap':
$attr = self::ELT_NONIMAP;
break;
case 'open':
if (!$elt->children) {
return false;
}
$attr = self::ELT_IS_OPEN;
break;
case 'polled':
if ($this->_elts[$s_elt] & self::ELT_IS_POLLED) {
return true;
} elseif ($this->_elts[$s_elt] & self::ELT_NOT_POLLED) {
return false;
}
$polled = $this->poll[$elt];
$this->setAttribute('polled', $elt, $polled);
return $polled;
case 'remote':
$attr = self::ELT_REMOTE;
break;
case 'remote_auth':
$attr = self::ELT_REMOTE_AUTH;
break;
case 'remote_mbox':
$attr = self::ELT_REMOTE_MBOX;
break;
case 'subscribed':
if ($elt->inbox) {
return true;
}
$attr = self::ELT_IS_SUBSCRIBED;
break;
case 'vfolder':
$attr = self::ELT_VFOLDER;
break;
default:
return null;
}
return (bool)($this->_elts[$s_elt] & $attr);
}
/**
* Change an attribute value.
*
* @param string $type The attribute type.
* @param string $elt The element name.
* @param boolean $bool The boolean value.
*/
public function setAttribute($type, $elt, $bool)
{
if (!($elt = $this[$elt])) {
return;
}
$attr = null;
$s_elt = strval($elt);
switch ($type) {
case 'container':
$attr = self::ELT_NOSELECT;
$this->eltdiff->change($elt);
break;
case 'invisible':
$attr = self::ELT_INVISIBLE;
$this->eltdiff->change($elt);
break;
case 'needsort':
$attr = self::ELT_NEED_SORT;
break;
case 'open':
$attr = self::ELT_IS_OPEN;
if ($bool) {
$this->expanded[$elt] = true;
} else {
unset($this->expanded[$elt]);
}
break;
case 'polled':
if ($bool) {
$attr = self::ELT_IS_POLLED;
$remove = self::ELT_NOT_POLLED;
} else {
$attr = self::ELT_NOT_POLLED;
$remove = self::ELT_IS_POLLED;
}
$this->_elts[$s_elt] &= ~$remove;
break;
case 'subscribed':
$attr = self::ELT_IS_SUBSCRIBED;
$this->eltdiff->change($elt);
break;
default:
return;
}
if ($bool) {
$this->_elts[$s_elt] |= $attr;
} else {
$this->_elts[$s_elt] &= ~$attr;
}
$this->_changed = true;
}
/**
* Get the account object for a given element ID.
*
* @param string $id Element ID.
*
* @return IMP_Ftree_Account Account object.
*/
public function getAccount($id)
{
foreach (array_diff(array_keys($this->_accounts), array(self::BASE_ELT)) as $val) {
if (strpos($id, $val) === 0) {
return $this->_accounts[$val];
}
}
return $this->_accounts[self::BASE_ELT];
}
/**
* Return the list of children for a given element ID.
*
* @param string $id Element ID.
*
* @return array Array of tree elements.
*/
public function getChildren($id)
{
if (!($elt = $this[$id]) || !isset($this->_parent[strval($elt)])) {
return array();
}
$this->_sortLevel($elt);
return array_map(
array($this, 'offsetGet'), $this->_parent[strval($elt)]
);
}
/**
* Get the parent element for a given element ID.
*
* @param string $id Element ID.
*
* @return mixed IMP_Ftree_Element object, or null if no parent.
*/
public function getParent($id)
{
$id = strval($id);
if ($id == self::BASE_ELT) {
return null;
}
foreach ($this->_parent as $key => $val) {
if (in_array($id, $val, true)) {
return $this[$key];
}
}
return $this[self::BASE_ELT];
}
/**
* Sorts a list of mailboxes.
*
* @param array &$mbox The list of mailboxes to sort.
* @param IMP_Ftree_Element $base The base element.
*/
public function sortList(&$mbox, $base = false)
{
if (count($mbox) < 2) {
return;
}
if (!$base || (!$base->base_elt && !$base->remote_auth)) {
$list_ob = new Horde_Imap_Client_Mailbox_List($mbox);
$mbox = $list_ob->sort();
return;
}
$prefix = $base->base_elt
? ''
: (strval($this->getAccount($base)) . "\0");
$basesort = $othersort = array();
/* INBOX always appears first. */
$sorted = array($prefix . 'INBOX');
foreach ($mbox as $key => $val) {
$ob = $this[$val];
if ($ob->nonimap) {
$othersort[$key] = $ob->mbox_ob->label;
} elseif ($val !== ($prefix . 'INBOX')) {
$basesort[$key] = $ob->mbox_ob->label;
}
}
natcasesort($basesort);
natcasesort($othersort);
foreach (array_merge(array_keys($basesort), array_keys($othersort)) as $key) {
$sorted[] = $mbox[$key];
}
$mbox = $sorted;
}
/* Internal methods. */
/**
* Normalize an element ID to the correct, internal name.
*
* @param string $id The element ID.
*
* @return string The converted name.
*/
protected function _normalize($id)
{
$id = strval($id);
return (strcasecmp($id, 'INBOX') === 0)
? 'INBOX'
: $id;
}
/**
* Insert an element into the tree.
*
* @param array $elt Element data. Keys:
*
* - a: (integer) Attributes.
* - p: (string) Parent element ID.
* - v: (string) Mailbox ID.
*
*/
protected function _insertElt($elt)
{
$name = $this->_normalize($elt['v']);
$change = false;
if (isset($this->_elts[$name])) {
if ($elt['a'] & self::ELT_NOSELECT) {
return;
}
$change = true;
}
$p_elt = $this[isset($elt['p']) ? $elt['p'] : self::BASE_ELT];
$parent = strval($p_elt);
$this->_changed = true;
if (!isset($this->_parent[$parent])) {
$this->eltdiff->change($p_elt);
}
if (!isset($this->_elts[$name])) {
$this->_parent[$parent][] = $name;
}
$this->_elts[$name] = $elt['a'];
if ($change) {
$this->eltdiff->change($name);
} else {
$this->eltdiff->add($name);
}
/* Check for polled status. */
$this->setAttribute('polled', $name, $this->poll[$name]);
/* Check for expanded status. */
$this->setAttribute('open', $name, $this->expanded[$name]);
if (empty($this->_temp['nohook'])) {
try {
$this->setAttribute(
'invisible',
$name,
!$GLOBALS['injector']->getInstance('Horde_Core_Hooks')->callHook(
'display_folder',
'imp',
array($name)
)
);
} catch (Horde_Exception_HookNotSet $e) {
$this->_temp['nohook'] = true;
}
}
/* Make sure we are sorted correctly. */
$this->setAttribute('needsort', $p_elt, true);
}
/**
* Sort a level in the tree.
*
* @param string $id The parent element whose children need to be sorted.
*/
protected function _sortLevel($id)
{
if (($elt = $this[$id]) && $elt->needsort) {
if (count($this->_parent[strval($elt)]) > 1) {
$this->sortList($this->_parent[strval($elt)], $elt);
}
$this->setAttribute('needsort', $elt, false);
}
}
/* ArrayAccess methods. */
/**
*/
public function offsetExists($offset)
{
/* Optimization: Only normalize in the rare case it is not found on
* the first attempt. */
$offset = strval($offset);
return (isset($this->_elts[$offset]) ||
isset($this->_elts[$this->_normalize($offset)]));
}
/**
* @return IMP_Ftree_Element
*/
public function offsetGet($offset)
{
if ($offset instanceof IMP_Ftree_Element) {
return $offset;
}
/* Optimization: Only normalize in the rare case it is not found on
* the first attempt. */
$offset = strval($offset);
if (isset($this->_elts[$offset])) {
return new IMP_Ftree_Element($offset, $this);
}
$offset = $this->_normalize($offset);
return isset($this->_elts[$offset])
? new IMP_Ftree_Element($offset, $this)
: null;
}
/**
*/
public function offsetSet($offset, $value)
{
$this->insert($offset);
}
/**
*/
public function offsetUnset($offset)
{
$this->delete($offset);
}
/* Countable methods. */
/**
* Return the number of mailboxes on the server.
*/
public function count()
{
$this->loadUnsubscribed();
$iterator = new IMP_Ftree_IteratorFilter($this);
$iterator->add($iterator::NONIMAP);
$iterator->remove($iterator::UNSUB);
return iterator_count($iterator);
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return $GLOBALS['injector']->getInstance('Horde_Pack')->pack(array(
$this->_accounts,
$this->_eltdiff,
$this->_elts,
$this->_parent
), array(
'compress' => false,
'phpob' => true
));
}
/**
* @throws Horde_Pack_Exception
*/
public function unserialize($data)
{
list(
$this->_accounts,
$this->_eltdiff,
$this->_elts,
$this->_parent
) = $GLOBALS['injector']->getInstance('Horde_Pack')->unpack($data);
}
/**
* Creates a Horde_Tree representation of the current tree.
*
* @param string|Horde_Tree $name Either the tree name, or a Horde_Tree
* object to add nodes to.
* @param array $opts Additional options:
*
* - basename: (boolean) Use raw basename instead of abbreviated label?
* DEFAULT: false
* - checkbox: (boolean) Display checkboxes?
* DEFAULT: false
* - editvfolder: (boolean) Display vfolder edit links?
* DEFAULT: false
* - iterator: (Iterator) Tree iterator to use.
* DEFAULT: Base iterator.
* - open: (boolean) Force child mailboxes to this status.
* DEFAULT: null
* - parent: (string) The parent object of the current level.
* DEFAULT: null (add to base level)
* - poll_info: (boolean) Include poll information in output?
* DEFAULT: false
* - render_params: (array) List of params to pass to renderer if
* auto-creating.
* DEFAULT: 'alternate', 'lines', and 'lines_base'
* are passed in with true values.
* - render_type: (string) The renderer name.
* DEFAULT: Javascript
*
*
* @return Horde_Tree The tree object.
*/
public function createTree($name, array $opts = array())
{
global $injector, $registry;
$opts = array_merge(array(
'parent' => null,
'render_params' => array(),
'render_type' => 'Javascript'
), $opts);
$view = $registry->getView();
if ($name instanceof Horde_Tree_Renderer_Base) {
$tree = $name;
$parent = $opts['parent'];
} else {
$tree = $injector->getInstance('Horde_Core_Factory_Tree')->create($name, $opts['render_type'], array_merge(array(
'alternate' => true,
'lines' => true,
'lines_base' => true,
'nosession' => true
), $opts['render_params']));
$parent = null;
}
$iterator = empty($opts['iterator'])
? new IMP_Ftree_IteratorFilter($this)
: $opts['iterator'];
foreach ($iterator as $val) {
$after = '';
$elt_parent = null;
$mbox_ob = $val->mbox_ob;
$params = array();
switch ($opts['render_type']) {
case 'IMP_Tree_Flist':
if ($mbox_ob->vfolder_container) {
continue 2;
}
$is_open = true;
$label = $params['orig_label'] = empty($opts['basename'])
? $mbox_ob->abbrev_label
: $mbox_ob->basename;
break;
case 'IMP_Tree_Jquerymobile':
$is_open = true;
$label = $mbox_ob->display_html;
$icon = $mbox_ob->icon;
$params['icon'] = $icon->icon;
$params['special'] = $mbox_ob->inbox || $mbox_ob->special;
$params['class'] = 'imp-folder';
$params['urlattributes'] = array(
'id' => 'imp-mailbox-' . $mbox_ob->form_to
);
/* Force to flat tree so that non-polled parents don't cause
* polled children to be skipped by renderer (see Bug
* #11238). */
$elt_parent = $this[self::BASE_ELT];
break;
case 'IMP_Tree_Simplehtml':
$is_open = $val->open;
if ($tree->shouldToggle($mbox_ob->form_to)) {
if ($is_open) {
$this->collapse($val);
} else {
$this->expand($val);
}
$is_open = !$is_open;
}
$label = htmlspecialchars(Horde_String::abbreviate($mbox_ob->abbrev_label, 30 - ($val->level * 2)));
break;
case 'Javascript':
$is_open = $val->open;
$label = empty($opts['basename'])
? htmlspecialchars($mbox_ob->abbrev_label)
: htmlspecialchars($mbox_ob->basename);
$icon = $mbox_ob->icon;
$params['icon'] = $icon->icon;
$params['iconopen'] = $icon->iconopen;
break;
}
if (!empty($opts['poll_info']) && $val->polled) {
$poll_info = $mbox_ob->poll_info;
if ($poll_info->unseen) {
switch ($opts['render_type']) {
case 'IMP_Tree_Jquerymobile':
$after = $poll_info->unseen;
break;
default:
$label = '' . $label . ' (' .
$poll_info->unseen . ')';
}
}
}
if ($val->container) {
$params['container'] = true;
} else {
switch ($view) {
case $registry::VIEW_MINIMAL:
$params['url'] = IMP_Minimal_Mailbox::url(array('mailbox' => $mbox_ob));
break;
case $registry::VIEW_SMARTMOBILE:
$url = new Horde_Core_Smartmobile_Url();
$url->add('mbox', $mbox_ob->form_to);
$url->setAnchor('mailbox');
$params['url'] = strval($url);
break;
default:
$params['url'] = $mbox_ob->url('mailbox')->setRaw(true);
break;
}
if (!$val->subscribed) {
$params['class'] = 'mboxunsub';
}
}
$checkbox = empty($opts['checkbox'])
? ''
: 'nonimap) {
$checkbox .= ' disabled="disabled"';
}
if ($val->vfolder &&
!empty($opts['editvfolder']) &&
$val->container) {
$after = ' [' .
$registry->getServiceLink('prefs', 'imp')->add('group', 'searches')->link(array('title' => _("Edit Virtual Folder"))) . _("Edit") . ''.
']';
}
if (is_null($elt_parent)) {
$elt_parent = $val->parent;
}
$tree->addNode(array(
'id' => $mbox_ob->form_to,
'parent' => $elt_parent->base_elt ? $parent : $elt_parent->mbox_ob->form_to,
'label' => $label,
'expanded' => isset($opts['open']) ? $opts['open'] : $is_open,
'params' => $params,
'right' => $after,
'left' => empty($opts['checkbox']) ? null : $checkbox . ' />'
));
}
return $tree;
}
/* IteratorAggregate methods. */
/**
* This returns a RecursiveIterator - a RecursiveIteratorIterator is
* needed to properly iterate through all elements.
*
* @return IMP_Ftree_Iterator Iterator object.
*/
public function getIterator()
{
return new IMP_Ftree_Iterator($this[self::BASE_ELT]);
}
}