Overview

Packages

  • IMP

Classes

  • IMP
  • IMP_Ajax_Addresses
  • IMP_Ajax_Application
  • IMP_Ajax_Application_Compose
  • IMP_Ajax_Application_Handler_Common
  • IMP_Ajax_Application_Handler_ComposeAttach
  • IMP_Ajax_Application_Handler_Draft
  • IMP_Ajax_Application_Handler_Dynamic
  • IMP_Ajax_Application_Handler_ImageUnblock
  • IMP_Ajax_Application_Handler_Mboxtoggle
  • IMP_Ajax_Application_Handler_Passphrase
  • IMP_Ajax_Application_Handler_Remote
  • IMP_Ajax_Application_Handler_RemotePrefs
  • IMP_Ajax_Application_Handler_Search
  • IMP_Ajax_Application_Handler_Smartmobile
  • IMP_Ajax_Application_ListMessages
  • IMP_Ajax_Application_ShowMessage
  • IMP_Ajax_Application_Viewport
  • IMP_Ajax_Application_Viewport_Error
  • IMP_Ajax_Imple_ImportEncryptKey
  • IMP_Ajax_Imple_ItipRequest
  • IMP_Ajax_Imple_PassphraseDialog
  • IMP_Ajax_Imple_VcardImport
  • IMP_Ajax_Queue
  • IMP_Api
  • IMP_Application
  • IMP_Auth
  • IMP_Basic_Base
  • IMP_Basic_Compose
  • IMP_Basic_Contacts
  • IMP_Basic_Error
  • IMP_Basic_Folders
  • IMP_Basic_Listinfo
  • IMP_Basic_Mailbox
  • IMP_Basic_Message
  • IMP_Basic_Pgp
  • IMP_Basic_Saveimage
  • IMP_Basic_Search
  • IMP_Basic_Searchbasic
  • IMP_Basic_Smime
  • IMP_Basic_Thread
  • IMP_Block_Newmail
  • IMP_Block_Summary
  • IMP_Compose
  • IMP_Compose_Attachment
  • IMP_Compose_Attachment_Linked_Metadata
  • IMP_Compose_Attachment_Metadata
  • IMP_Compose_Attachment_Storage
  • IMP_Compose_Attachment_Storage_AutoDetermine
  • IMP_Compose_Attachment_Storage_Temp
  • IMP_Compose_Attachment_Storage_VfsLinked
  • IMP_Compose_Exception
  • IMP_Compose_Exception_Address
  • IMP_Compose_HtmlSignature
  • IMP_Compose_Link
  • IMP_Compose_LinkedAttachment
  • IMP_Compose_Ui
  • IMP_Compose_View
  • IMP_Contacts
  • IMP_Contacts_Avatar_Addressbook
  • IMP_Contacts_Avatar_Gravatar
  • IMP_Contacts_Avatar_Unknown
  • IMP_Contacts_Flag_Host
  • IMP_Contacts_Image
  • IMP_Contents
  • IMP_Contents_InlineOutput
  • IMP_Contents_View
  • IMP_Crypt_Pgp
  • IMP_Crypt_Smime
  • IMP_Dynamic_AddressList
  • IMP_Dynamic_Base
  • IMP_Dynamic_Compose
  • IMP_Dynamic_Compose_Common
  • IMP_Dynamic_Helper_Base
  • IMP_Dynamic_Mailbox
  • IMP_Dynamic_Message
  • IMP_Exception
  • IMP_Factory_AuthImap
  • IMP_Factory_Compose
  • IMP_Factory_ComposeAtc
  • IMP_Factory_Contacts
  • IMP_Factory_Contents
  • IMP_Factory_Flags
  • IMP_Factory_Ftree
  • IMP_Factory_Identity
  • IMP_Factory_Imap
  • IMP_Factory_Mail
  • IMP_Factory_MailAutoconfig
  • IMP_Factory_Mailbox
  • IMP_Factory_MailboxCache
  • IMP_Factory_MailboxList
  • IMP_Factory_Maillog
  • IMP_Factory_MimeViewer
  • IMP_Factory_Pgp
  • IMP_Factory_PrefsSort
  • IMP_Factory_Quota
  • IMP_Factory_Search
  • IMP_Factory_Sentmail
  • IMP_Factory_Smime
  • IMP_Factory_Spam
  • IMP_Filter
  • IMP_Flag_Base
  • IMP_Flag_Imap
  • IMP_Flag_Imap_Answered
  • IMP_Flag_Imap_Deleted
  • IMP_Flag_Imap_Draft
  • IMP_Flag_Imap_Flagged
  • IMP_Flag_Imap_Forwarded
  • IMP_Flag_Imap_Junk
  • IMP_Flag_Imap_NotJunk
  • IMP_Flag_Imap_Seen
  • IMP_Flag_System_Attachment
  • IMP_Flag_System_Encrypted
  • IMP_Flag_System_HighPriority
  • IMP_Flag_System_List
  • IMP_Flag_System_LowPriority
  • IMP_Flag_System_Match_Address
  • IMP_Flag_System_Match_Flag
  • IMP_Flag_System_Match_Header
  • IMP_Flag_System_Personal
  • IMP_Flag_System_Signed
  • IMP_Flag_System_Unseen
  • IMP_Flag_User
  • IMP_Flags
  • IMP_Ftree
  • IMP_Ftree_Account
  • IMP_Ftree_Account_Imap
  • IMP_Ftree_Account_Inboxonly
  • IMP_Ftree_Account_Remote
  • IMP_Ftree_Account_Vfolder
  • IMP_Ftree_Element
  • IMP_Ftree_Eltdiff
  • IMP_Ftree_Iterator
  • IMP_Ftree_Iterator_Ancestors
  • IMP_Ftree_IteratorFilter
  • IMP_Ftree_IteratorFilter_Children
  • IMP_Ftree_IteratorFilter_Containers
  • IMP_Ftree_IteratorFilter_Expanded
  • IMP_Ftree_IteratorFilter_Invisible
  • IMP_Ftree_IteratorFilter_Mailboxes
  • IMP_Ftree_IteratorFilter_Nonimap
  • IMP_Ftree_IteratorFilter_Polled
  • IMP_Ftree_IteratorFilter_Remote
  • IMP_Ftree_IteratorFilter_Special
  • IMP_Ftree_IteratorFilter_Subscribed
  • IMP_Ftree_IteratorFilter_Vfolder
  • IMP_Ftree_Prefs
  • IMP_Ftree_Prefs_Expanded
  • IMP_Ftree_Prefs_Poll
  • IMP_Ftree_Select
  • IMP_Images
  • IMP_Imap
  • IMP_Imap_Acl
  • IMP_Imap_Cache_Wrapper
  • IMP_Imap_Config
  • IMP_Imap_Exception
  • IMP_Imap_Password
  • IMP_Imap_PermanentFlags
  • IMP_Imap_Remote
  • IMP_Indices
  • IMP_Indices_Mailbox
  • IMP_LoginTasks_SystemTask_GarbageCollection
  • IMP_LoginTasks_SystemTask_Upgrade
  • IMP_LoginTasks_SystemTask_UpgradeAuth
  • IMP_LoginTasks_Task_Autocreate
  • IMP_LoginTasks_Task_DeleteAttachmentsMonthly
  • IMP_LoginTasks_Task_DeleteSentmailMonthly
  • IMP_LoginTasks_Task_FilterOnLogin
  • IMP_LoginTasks_Task_PurgeSentmail
  • IMP_LoginTasks_Task_PurgeSpam
  • IMP_LoginTasks_Task_PurgeTrash
  • IMP_LoginTasks_Task_RecoverDraft
  • IMP_LoginTasks_Task_RenameSentmailMonthly
  • IMP_Mailbox
  • IMP_Mailbox_List
  • IMP_Mailbox_List_Pop3
  • IMP_Mailbox_List_Thread
  • IMP_Mailbox_List_Virtual
  • IMP_Mailbox_SessionCache
  • IMP_Mailbox_Ui
  • IMP_Maillog
  • IMP_Maillog_Log_Base
  • IMP_Maillog_Log_Forward
  • IMP_Maillog_Log_Mdn
  • IMP_Maillog_Log_Redirect
  • IMP_Maillog_Log_Reply
  • IMP_Maillog_Log_Replyall
  • IMP_Maillog_Log_Replylist
  • IMP_Maillog_Message
  • IMP_Maillog_Storage_Base
  • IMP_Maillog_Storage_Composite
  • IMP_Maillog_Storage_History
  • IMP_Maillog_Storage_Mdnsent
  • IMP_Maillog_Storage_Null
  • IMP_Mbox_Generate
  • IMP_Mbox_Import
  • IMP_Mbox_Size
  • IMP_Message
  • IMP_Message_Date
  • IMP_Message_Ui
  • IMP_Mime_Headers
  • IMP_Mime_Status
  • IMP_Mime_Status_RenderIssue
  • IMP_Mime_Status_RenderIssue_Display
  • IMP_Mime_Viewer_Alternative
  • IMP_Mime_Viewer_Appledouble
  • IMP_Mime_Viewer_Audio
  • IMP_Mime_Viewer_Enriched
  • IMP_Mime_Viewer_Externalbody
  • IMP_Mime_Viewer_Html
  • IMP_Mime_Viewer_Images
  • IMP_Mime_Viewer_Itip
  • IMP_Mime_Viewer_Mdn
  • IMP_Mime_Viewer_Partial
  • IMP_Mime_Viewer_Pdf
  • IMP_Mime_Viewer_Pgp
  • IMP_Mime_Viewer_Plain
  • IMP_Mime_Viewer_Related
  • IMP_Mime_Viewer_Rfc822
  • IMP_Mime_Viewer_Smil
  • IMP_Mime_Viewer_Smime
  • IMP_Mime_Viewer_Status
  • IMP_Mime_Viewer_Vcard
  • IMP_Mime_Viewer_Video
  • IMP_Mime_Viewer_Zip
  • IMP_Minimal_Base
  • IMP_Minimal_Compose
  • IMP_Minimal_Error
  • IMP_Minimal_Folders
  • IMP_Minimal_Mailbox
  • IMP_Minimal_Message
  • IMP_Minimal_Messagepart
  • IMP_Minimal_Search
  • IMP_Notification_Event_Status
  • IMP_Notification_Handler_Decorator_ImapAlerts
  • IMP_Notification_Handler_Decorator_NewmailNotify
  • IMP_Perms
  • IMP_Prefs_AttribText
  • IMP_Prefs_Identity
  • IMP_Prefs_Sort
  • IMP_Prefs_Sort_FixedDate
  • IMP_Prefs_Sort_None
  • IMP_Prefs_Sort_Sortpref
  • IMP_Prefs_Sort_Sortpref_Locked
  • IMP_Prefs_Special_Acl
  • IMP_Prefs_Special_ComposeTemplates
  • IMP_Prefs_Special_Drafts
  • IMP_Prefs_Special_Encrypt
  • IMP_Prefs_Special_Flag
  • IMP_Prefs_Special_HtmlSignature
  • IMP_Prefs_Special_ImageReplacement
  • IMP_Prefs_Special_InitialPage
  • IMP_Prefs_Special_Mailto
  • IMP_Prefs_Special_NewmailSound
  • IMP_Prefs_Special_PgpPrivateKey
  • IMP_Prefs_Special_PgpPublicKey
  • IMP_Prefs_Special_Remote
  • IMP_Prefs_Special_Searches
  • IMP_Prefs_Special_Sentmail
  • IMP_Prefs_Special_SmimePrivateKey
  • IMP_Prefs_Special_SmimePublicKey
  • IMP_Prefs_Special_Sourceselect
  • IMP_Prefs_Special_Spam
  • IMP_Prefs_Special_SpecialMboxes
  • IMP_Prefs_Special_Trash
  • IMP_Quota
  • IMP_Quota_Hook
  • IMP_Quota_Imap
  • IMP_Quota_Null
  • IMP_Quota_Ui
  • IMP_Remote
  • IMP_Remote_Account
  • IMP_Script_Package_Autocomplete
  • IMP_Script_Package_ComposeBase
  • IMP_Script_Package_DynamicBase
  • IMP_Script_Package_Editor
  • IMP_Script_Package_Imp
  • IMP_Search
  • IMP_Search_Element
  • IMP_Search_Element_Attachment
  • IMP_Search_Element_Autogenerated
  • IMP_Search_Element_Bulk
  • IMP_Search_Element_Contacts
  • IMP_Search_Element_Daterange
  • IMP_Search_Element_Flag
  • IMP_Search_Element_Header
  • IMP_Search_Element_Mailinglist
  • IMP_Search_Element_Or
  • IMP_Search_Element_Personal
  • IMP_Search_Element_Recipient
  • IMP_Search_Element_Size
  • IMP_Search_Element_Text
  • IMP_Search_Element_Within
  • IMP_Search_Filter
  • IMP_Search_Filter_Attachment
  • IMP_Search_Filter_Autogenerated
  • IMP_Search_Filter_Builtin
  • IMP_Search_Filter_Bulk
  • IMP_Search_Filter_Contacts
  • IMP_Search_Filter_Mailinglist
  • IMP_Search_Filter_Personal
  • IMP_Search_IteratorFilter
  • IMP_Search_Query
  • IMP_Search_Ui
  • IMP_Search_Vfolder
  • IMP_Search_Vfolder_Builtin
  • IMP_Search_Vfolder_Vinbox
  • IMP_Search_Vfolder_Vtrash
  • IMP_Sentmail
  • IMP_Sentmail_Mongo
  • IMP_Sentmail_Null
  • IMP_Sentmail_Sql
  • IMP_Smartmobile
  • IMP_Spam
  • IMP_Spam_Email
  • IMP_Spam_Null
  • IMP_Spam_Program
  • IMP_Test
  • IMP_Tree_Flist
  • IMP_Tree_Jquerymobile
  • IMP_Tree_Simplehtml
  • IMP_View_Subinfo

Interfaces

  • IMP_Compose_Attachment_Linked
  • IMP_Contacts_Avatar_Backend
  • IMP_Contacts_Flag_Backend
  • IMP_Spam_Base
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Copyright 2000-2014 Horde LLC (http://www.horde.org/)
   4:  *
   5:  * See the enclosed file COPYING for license information (GPL). If you
   6:  * did not receive this file, see http://www.horde.org/licenses/gpl.
   7:  *
   8:  * @category  Horde
   9:  * @copyright 2000-2014 Horde LLC
  10:  * @license   http://www.horde.org/licenses/gpl GPL
  11:  * @package   IMP
  12:  */
  13: 
  14: /**
  15:  * IMP_Ftree (folder tree) provides a tree view of the mailboxes on a backend
  16:  * (a/k/a a folder list; in IMP, folders = collection of mailboxes), along
  17:  * with other display elements (Remote Accounts; Virtual Folders).
  18:  *
  19:  * @author    Chuck Hagenbuch <chuck@horde.org>
  20:  * @author    Anil Madhavapeddy <avsm@horde.org>
  21:  * @author    Jon Parise <jon@horde.org>
  22:  * @author    Michael Slusarz <slusarz@horde.org>
  23:  * @category  Horde
  24:  * @copyright 2000-2014 Horde LLC
  25:  * @license   http://www.horde.org/licenses/gpl GPL
  26:  * @package   IMP
  27:  *
  28:  * @property-read boolean $changed  Has the tree changed?
  29:  * @property-read IMP_Ftree_Eltdiff $eltdiff  Element diff tracker.
  30:  * @property-read IMP_FTree_Prefs_Expanded $expanded  The expanded folders
  31:  *                                                    list.
  32:  * @property-read IMP_Ftree_Prefs_Poll $poll  The poll list.
  33:  * @property-read boolean $subscriptions  Whether IMAP subscriptions are
  34:  *                                        enabled.
  35:  * @property-read boolean $unsubscribed_loaded  True if unsubscribed mailboxes
  36:  *                                              have been loaded.
  37:  */
  38: class IMP_Ftree
  39: implements ArrayAccess, Countable, IteratorAggregate, Serializable
  40: {
  41:     /* Constants for mailboxElt attributes. */
  42:     const ELT_NOSELECT = 1;
  43:     const ELT_NAMESPACE_OTHER = 2;
  44:     const ELT_NAMESPACE_SHARED = 4;
  45:     const ELT_IS_OPEN = 8;
  46:     const ELT_IS_SUBSCRIBED = 16;
  47:     const ELT_NOINFERIORS = 32;
  48:     const ELT_IS_POLLED = 64;
  49:     const ELT_NOT_POLLED = 128;
  50:     const ELT_VFOLDER = 256;
  51:     const ELT_NONIMAP = 512;
  52:     const ELT_INVISIBLE = 1024;
  53:     const ELT_NEED_SORT = 2048;
  54:     const ELT_REMOTE = 4096;
  55:     const ELT_REMOTE_AUTH = 8192;
  56:     const ELT_REMOTE_MBOX = 16384;
  57: 
  58:     /* The string used to indicate the base of the tree. This must include
  59:      * null since this is the only 7-bit character not allowed in IMAP
  60:      * mailboxes (nulls allow us to sort by name but never conflict with an
  61:      * IMAP mailbox). */
  62:     const BASE_ELT = "base\0";
  63: 
  64:     /**
  65:      * Account sources.
  66:      *
  67:      * @var array
  68:      */
  69:     protected $_accounts;
  70: 
  71:     /**
  72:      * Tree changed flag.  Set when something in the tree has been altered.
  73:      *
  74:      * @var boolean
  75:      */
  76:     protected $_changed = false;
  77: 
  78:     /**
  79:      * Element diff tracking.
  80:      *
  81:      * @var IMP_Ftree_Eltdiff
  82:      */
  83:     protected $_eltdiff;
  84: 
  85:     /**
  86:      * Array containing the mailbox elements.
  87:      *
  88:      * @var array
  89:      */
  90:     protected $_elts;
  91: 
  92:     /**
  93:      * Parent/child list.
  94:      *
  95:      * @var array
  96:      */
  97:     protected $_parent;
  98: 
  99:     /**
 100:      * Temporary data that is not saved across serialization.
 101:      *
 102:      * @var array
 103:      */
 104:     protected $_temp = array();
 105: 
 106:     /**
 107:      * Constructor.
 108:      */
 109:     public function __construct()
 110:     {
 111:         $this->init();
 112:     }
 113: 
 114:     /**
 115:      */
 116:     public function __get($name)
 117:     {
 118:         global $prefs;
 119: 
 120:         switch ($name) {
 121:         case 'changed':
 122:             return ($this->_changed || $this->eltdiff->changed);
 123: 
 124:         case 'expanded':
 125:             if (!isset($this->_temp['expanded'])) {
 126:                 $this->_temp['expanded'] = new IMP_Ftree_Prefs_Expanded();
 127:             }
 128:             return $this->_temp['expanded'];
 129: 
 130:         case 'eltdiff':
 131:             return $this->_eltdiff;
 132: 
 133:         case 'poll':
 134:             if (!isset($this->_temp['poll'])) {
 135:                 $this->_temp['poll'] = new IMP_Ftree_Prefs_Poll($this);
 136:             }
 137:             return $this->_temp['poll'];
 138: 
 139:         case 'subscriptions':
 140:             return $prefs->getValue('subscribe');
 141: 
 142:         case 'unsubscribed_loaded':
 143:             return $this[self::BASE_ELT]->subscribed;
 144:         }
 145:     }
 146: 
 147:     /**
 148:      * Initialize the tree.
 149:      */
 150:     public function init()
 151:     {
 152:         global $injector, $session;
 153: 
 154:         $access_folders = $injector->getInstance('IMP_Factory_Imap')->create()->access(IMP_Imap::ACCESS_FOLDERS);
 155: 
 156:         /* Reset class variables to the defaults. */
 157:         $this->_accounts = $this->_elts = $this->_parent = array();
 158:         $this->_changed = true;
 159: 
 160:         $old_track = (isset($this->_eltdiff) && $this->_eltdiff->track);
 161:         $this->_eltdiff = new IMP_Ftree_Eltdiff();
 162: 
 163:         /* Create a placeholder element to the base of the tree so we can
 164:          * keep track of whether the base level needs to be sorted. */
 165:         $this->_elts[self::BASE_ELT] = self::ELT_NEED_SORT | self::ELT_NONIMAP;
 166:         $this->_parent[self::BASE_ELT] = array();
 167: 
 168:         $mask = IMP_Ftree_Account::INIT;
 169:         if (!$access_folders || !$this->subscriptions || $session->get('imp', 'showunsub')) {
 170:             $mask |= IMP_Ftree_Account::UNSUB;
 171:             $this->setAttribute('subscribed', self::BASE_ELT, true);
 172:         }
 173: 
 174:         /* Add base account. */
 175:         $ob = $this->_accounts[self::BASE_ELT] = $access_folders
 176:             ? new IMP_Ftree_Account_Imap()
 177:             : new IMP_Ftree_Account_Inboxonly();
 178:         array_map(array($this, '_insertElt'), $ob->getList(null, $mask));
 179: 
 180:         if ($access_folders) {
 181:             /* Add remote servers. */
 182:             $this->insert(iterator_to_array(
 183:                 $injector->getInstance('IMP_Remote')
 184:             ));
 185: 
 186:             /* Add virtual folders to the tree. */
 187:             $this->insert(iterator_to_array(
 188:                 IMP_Search_IteratorFilter::create(
 189:                     IMP_Search_IteratorFilter::VFOLDER
 190:                 )
 191:             ));
 192:         }
 193: 
 194:         if ($old_track) {
 195:             $this->eltdiff->track = true;
 196:         }
 197:     }
 198: 
 199:     /**
 200:      * Insert an element into the tree.
 201:      *
 202:      * @param mixed $id  The name of the mailbox (or a list of mailboxes),
 203:      *                   an IMP_Search_Vfolder object, an IMP_Remote_Account
 204:      *                   object, or an array containing any mixture of these.
 205:      */
 206:     public function insert($id)
 207:     {
 208:         foreach ((is_array($id) ? $id : array($id)) as $val) {
 209:             if (($val instanceof IMP_Search_Vfolder) &&
 210:                 !isset($this->_accounts[strval($val)])) {
 211:                 /* Virtual Folders. */
 212:                 $account = $this->_accounts[strval($val)] = new IMP_Ftree_Account_Vfolder($val);
 213:             } elseif (($val instanceof IMP_Remote_Account) &&
 214:                       !isset($this->_accounts[strval($val)])) {
 215:                 /* Remote accounts. */
 216:                 $account = $this->_accounts[strval($val)] = new IMP_Ftree_Account_Remote($val);
 217:             } else {
 218:                 $account = $this->getAccount($val);
 219:                 $val = $this->_normalize($val);
 220:             }
 221: 
 222:             array_map(array($this, '_insertElt'), $account->getList(array($val)));
 223:         }
 224:     }
 225: 
 226:     /**
 227:      * Expand an element.
 228:      *
 229:      * @param mixed $elts         The element (or an array of elements) to
 230:      *                            expand.
 231:      * @param boolean $expandall  Expand all subelements?
 232:      */
 233:     public function expand($elts, $expandall = false)
 234:     {
 235:         foreach ((is_array($elts) ? $elts : array($elts)) as $val) {
 236:             if (($elt = $this[$val]) && $elt->children) {
 237:                 if (!$elt->open) {
 238:                     $elt->open = true;
 239:                 }
 240: 
 241:                 /* Expand all children beneath this one. */
 242:                 if ($expandall) {
 243:                     $this->expand($this->_parent[strval($elt)]);
 244:                 }
 245:             }
 246:         }
 247:     }
 248: 
 249:     /**
 250:      * Expand all elements.
 251:      */
 252:     public function expandAll()
 253:     {
 254:         $this->expand($this->_parent[self::BASE_ELT], true);
 255:     }
 256: 
 257:     /**
 258:      * Collapse an element.
 259:      *
 260:      * @param mixed $elts  The element (or an array of elements) to expand.
 261:      */
 262:     public function collapse($elts)
 263:     {
 264:         foreach ((is_array($elts) ? $elts : array($elts)) as $val) {
 265:             if ($elt = $this[$val]) {
 266:                 $elt->open = false;
 267:             }
 268:         }
 269:     }
 270: 
 271:     /**
 272:      * Collapse all elements.
 273:      */
 274:     public function collapseAll()
 275:     {
 276:         $this->collapse(
 277:             array_diff_key(array_keys($this->_elts), array(self::BASE_ELT))
 278:         );
 279:     }
 280: 
 281:     /**
 282:      * Delete an element from the tree.
 283:      *
 284:      * @param mixed $elts  The element (or an array of elements) to delete.
 285:      */
 286:     public function delete($id)
 287:     {
 288:         if (is_array($id)) {
 289:             /* We want to delete from the TOP of the tree down to ensure that
 290:              * parents have an accurate view of what children are left. */
 291:             $this->sortList($id);
 292:             $id = array_reverse($id);
 293:         } else {
 294:             $id = array($id);
 295:         }
 296: 
 297:         foreach (array_filter(array_map(array($this, 'offsetGet'), $id)) as $elt) {
 298:             $account = $this->getAccount($elt);
 299:             if (!($mask = $account->delete($elt))) {
 300:                 continue;
 301:             }
 302: 
 303:             $this->_changed = true;
 304: 
 305:             if ($mask & IMP_Ftree_Account::DELETE_RECURSIVE) {
 306:                 foreach (array_map('strval', iterator_to_array(new IMP_Ftree_Iterator($elt), false)) as $val) {
 307:                     unset(
 308:                         $this->_elts[$val],
 309:                         $this->_parent[$val]
 310:                     );
 311:                     $this->eltdiff->delete($val);
 312:                 }
 313:                 unset($this->_parent[strval($elt)]);
 314:             }
 315: 
 316:             if (strval($account) == strval($elt)) {
 317:                 unset($this->_accounts[strval($elt)]);
 318:             }
 319: 
 320:             if ($mask & IMP_Ftree_Account::DELETE_ELEMENT) {
 321:                 /* Do not delete from tree if there are child elements -
 322:                  * instead, convert to a container element. */
 323:                 if ($elt->children) {
 324:                     $elt->container = true;
 325:                     continue;
 326:                 }
 327: 
 328:                 /* Remove the mailbox from the expanded folders list. */
 329:                 unset($this->expanded[$elt]);
 330: 
 331:                 /* Remove the mailbox from the polled list. */
 332:                 $this->poll->removePollList($elt);
 333:             }
 334: 
 335:             $parent = strval($elt->parent);
 336:             $this->eltdiff->delete($elt);
 337: 
 338:             /* Delete the entry from the parent tree. */
 339:             unset(
 340:                 $this->_elts[strval($elt)],
 341:                 $this->_parent[$parent][array_search(strval($elt), $this->_parent[$parent], true)]
 342:             );
 343: 
 344:             if (empty($this->_parent[$parent])) {
 345:                 /* This mailbox is now completely empty (no children). */
 346:                 unset($this->_parent[$parent]);
 347:                 if ($p_elt = $this[$parent]) {
 348:                     if ($p_elt->container && !$p_elt->namespace) {
 349:                         $this->delete($p_elt);
 350:                     } else {
 351:                         $p_elt->open = false;
 352:                         $this->eltdiff->change($p_elt);
 353:                     }
 354:                 }
 355:             }
 356: 
 357:             if (!empty($this->_parent[$parent])) {
 358:                 $this->_parent[$parent] = array_values($this->_parent[$parent]);
 359:             }
 360:         }
 361:     }
 362: 
 363:     /**
 364:      * Rename a mailbox.
 365:      *
 366:      * @param string $old  The old mailbox name.
 367:      * @param string $new  The new mailbox name.
 368:      */
 369:     public function rename($old, $new)
 370:     {
 371:         if (!($old_elt = $this[$old])) {
 372:             return;
 373:         }
 374: 
 375:         $new_list = $polled = array();
 376:         $old_list = array_merge(
 377:             array($old),
 378:             iterator_to_array(new IMP_Ftree_IteratorFilter(new IMP_Ftree_Iterator($old_elt)), false)
 379:         );
 380: 
 381:         foreach ($old_list as $val) {
 382:             $new_list[] = $new_name = substr_replace($val, $new, 0, strlen($old));
 383:             if ($val->polled) {
 384:                 $polled[] = $new_name;
 385:             }
 386:         }
 387: 
 388:         $this->insert($new_list);
 389:         $this->poll->addPollList($polled);
 390:         $this->delete($old_list);
 391:     }
 392: 
 393:     /**
 394:      * Subscribe an element to the tree.
 395:      *
 396:      * @param mixed $id  The element name or an array of element names.
 397:      */
 398:     public function subscribe($id)
 399:     {
 400:         foreach ((is_array($id) ? $id : array($id)) as $val) {
 401:             $this->setAttribute('subscribed', $val, true);
 402:             $this->setAttribute('container', $val, false);
 403:         }
 404:     }
 405: 
 406:     /**
 407:      * Unsubscribe an element from the tree.
 408:      *
 409:      * @param mixed $id  The element name or an array of element names.
 410:      */
 411:     public function unsubscribe($id)
 412:     {
 413:         if (is_array($id)) {
 414:             /* We want to delete from the TOP of the tree down to ensure that
 415:              * parents have an accurate view of what children are left. */
 416:             $this->sortList($id);
 417:             $id = array_reverse($id);
 418:         } else {
 419:             $id = array($id);
 420:         }
 421: 
 422:         foreach ($id as $val) {
 423:             /* INBOX can never be unsubscribed to. */
 424:             if (($elt = $this[$val]) && !$elt->inbox) {
 425:                 $this->_changed = true;
 426: 
 427:                 /* Do not delete from tree if there are child elements -
 428:                  * instead, convert to a container element. */
 429:                 if ($elt->children) {
 430:                     $this->setAttribute('container', $elt, true);
 431:                 }
 432: 
 433:                 /* Set as unsubscribed, add to unsubscribed list, and remove
 434:                  * from subscribed list. */
 435:                 $this->setAttribute('subscribed', $elt, false);
 436:             }
 437:         }
 438:     }
 439: 
 440:     /**
 441:      * Load unsubscribed mailboxes.
 442:      */
 443:     public function loadUnsubscribed()
 444:     {
 445:         /* If we are switching from unsubscribed to subscribed, no need
 446:          * to do anything (we just ignore unsubscribed stuff). */
 447:         if ($this->unsubscribed_loaded) {
 448:             return;
 449:         }
 450: 
 451:         $this->_changed = true;
 452: 
 453:         /* The BASE_ELT having the SUBSCRIBED mask indicates the unsubscribed
 454:          * mailboxes have been loaded into the object. */
 455:         $this->setAttribute('subscribed', self::BASE_ELT, true);
 456: 
 457:         /* If we are switching from subscribed to unsubscribed, we need
 458:          * to add all unsubscribed elements that live in currently
 459:          * discovered items. */
 460:         $old_track = $this->eltdiff->track;
 461:         $this->eltdiff->track = false;
 462:         foreach ($this->_accounts as $val) {
 463:             array_map(array($this, '_insertElt'), $val->getList(array(), $val::UNSUB));
 464:         }
 465:         $this->eltdiff->track = $old_track;
 466:     }
 467: 
 468:     /**
 469:      * Get an attribute value.
 470:      *
 471:      * @param string $type  The attribute type.
 472:      * @param string $name  The element name.
 473:      *
 474:      * @return mixed  Boolean attribute result, or null if element or
 475:      *                attribute doesn't exist
 476:      */
 477:     public function getAttribute($type, $name)
 478:     {
 479:         if (!($elt = $this[$name])) {
 480:             return null;
 481:         }
 482:         $s_elt = strval($elt);
 483: 
 484:         switch ($type) {
 485:         case 'children':
 486:             return isset($this->_parent[$s_elt]);
 487: 
 488:         case 'container':
 489:             $attr = self::ELT_NOSELECT;
 490:             break;
 491: 
 492:         case 'invisible':
 493:             $attr = self::ELT_INVISIBLE;
 494:             break;
 495: 
 496:         case 'namespace_other':
 497:             $attr = self::ELT_NAMESPACE_OTHER;
 498:             break;
 499: 
 500:         case 'namespace_shared':
 501:             $attr = self::ELT_NAMESPACE_SHARED;
 502:             break;
 503: 
 504:         case 'needsort':
 505:             $attr = self::ELT_NEED_SORT;
 506:             break;
 507: 
 508:         case 'nochildren':
 509:             $attr = self::ELT_NOINFERIORS;
 510:             break;
 511: 
 512:         case 'nonimap':
 513:             $attr = self::ELT_NONIMAP;
 514:             break;
 515: 
 516:         case 'open':
 517:             if (!$elt->children) {
 518:                 return false;
 519:             }
 520:             $attr = self::ELT_IS_OPEN;
 521:             break;
 522: 
 523:         case 'polled':
 524:             if ($this->_elts[$s_elt] & self::ELT_IS_POLLED) {
 525:                 return true;
 526:             } elseif ($this->_elts[$s_elt] & self::ELT_NOT_POLLED) {
 527:                 return false;
 528:             }
 529: 
 530:             $polled = $this->poll[$elt];
 531:             $this->setAttribute('polled', $elt, $polled);
 532:             return $polled;
 533: 
 534:         case 'remote':
 535:             $attr = self::ELT_REMOTE;
 536:             break;
 537: 
 538:         case 'remote_auth':
 539:             $attr = self::ELT_REMOTE_AUTH;
 540:             break;
 541: 
 542:         case 'remote_mbox':
 543:             $attr = self::ELT_REMOTE_MBOX;
 544:             break;
 545: 
 546:         case 'subscribed':
 547:             if ($elt->inbox) {
 548:                 return true;
 549:             }
 550:             $attr = self::ELT_IS_SUBSCRIBED;
 551:             break;
 552: 
 553:         case 'vfolder':
 554:             $attr = self::ELT_VFOLDER;
 555:             break;
 556: 
 557:         default:
 558:             return null;
 559:         }
 560: 
 561:         return (bool)($this->_elts[$s_elt] & $attr);
 562:     }
 563: 
 564:     /**
 565:      * Change an attribute value.
 566:      *
 567:      * @param string $type   The attribute type.
 568:      * @param string $elt    The element name.
 569:      * @param boolean $bool  The boolean value.
 570:      */
 571:     public function setAttribute($type, $elt, $bool)
 572:     {
 573:         if (!($elt = $this[$elt])) {
 574:             return;
 575:         }
 576: 
 577:         $attr = null;
 578:         $s_elt = strval($elt);
 579: 
 580:         switch ($type) {
 581:         case 'container':
 582:             $attr = self::ELT_NOSELECT;
 583:             $this->eltdiff->change($elt);
 584:             break;
 585: 
 586:         case 'invisible':
 587:             $attr = self::ELT_INVISIBLE;
 588:             $this->eltdiff->change($elt);
 589:             break;
 590: 
 591:         case 'needsort':
 592:             $attr = self::ELT_NEED_SORT;
 593:             break;
 594: 
 595:         case 'open':
 596:             $attr = self::ELT_IS_OPEN;
 597:             if ($bool) {
 598:                 $this->expanded[$elt] = true;
 599:             } else {
 600:                 unset($this->expanded[$elt]);
 601:             }
 602:             break;
 603: 
 604:         case 'polled':
 605:             if ($bool) {
 606:                 $attr = self::ELT_IS_POLLED;
 607:                 $remove = self::ELT_NOT_POLLED;
 608:             } else {
 609:                 $attr = self::ELT_NOT_POLLED;
 610:                 $remove = self::ELT_IS_POLLED;
 611:             }
 612:             $this->_elts[$s_elt] &= ~$remove;
 613:             break;
 614: 
 615:         case 'subscribed':
 616:             $attr = self::ELT_IS_SUBSCRIBED;
 617:             $this->eltdiff->change($elt);
 618:             break;
 619: 
 620:         default:
 621:             return;
 622:         }
 623: 
 624:         if ($bool) {
 625:             $this->_elts[$s_elt] |= $attr;
 626:         } else {
 627:             $this->_elts[$s_elt] &= ~$attr;
 628:         }
 629: 
 630:         $this->_changed = true;
 631:     }
 632: 
 633:     /**
 634:      * Get the account object for a given element ID.
 635:      *
 636:      * @param string $id  Element ID.
 637:      *
 638:      * @return IMP_Ftree_Account  Account object.
 639:      */
 640:     public function getAccount($id)
 641:     {
 642:         foreach (array_diff(array_keys($this->_accounts), array(self::BASE_ELT)) as $val) {
 643:             if (strpos($id, $val) === 0) {
 644:                 return $this->_accounts[$val];
 645:             }
 646:         }
 647: 
 648:         return $this->_accounts[self::BASE_ELT];
 649:     }
 650: 
 651:     /**
 652:      * Return the list of children for a given element ID.
 653:      *
 654:      * @param string $id  Element ID.
 655:      *
 656:      * @return array  Array of tree elements.
 657:      */
 658:     public function getChildren($id)
 659:     {
 660:         if (!($elt = $this[$id]) || !isset($this->_parent[strval($elt)])) {
 661:             return array();
 662:         }
 663: 
 664:         $this->_sortLevel($elt);
 665:         return array_map(
 666:             array($this, 'offsetGet'), $this->_parent[strval($elt)]
 667:         );
 668:     }
 669: 
 670:     /**
 671:      * Get the parent element for a given element ID.
 672:      *
 673:      * @param string $id  Element ID.
 674:      *
 675:      * @return mixed  IMP_Ftree_Element object, or null if no parent.
 676:      */
 677:     public function getParent($id)
 678:     {
 679:         $id = strval($id);
 680: 
 681:         if ($id == self::BASE_ELT) {
 682:             return null;
 683:         }
 684: 
 685:         foreach ($this->_parent as $key => $val) {
 686:             if (in_array($id, $val, true)) {
 687:                 return $this[$key];
 688:             }
 689:         }
 690: 
 691:         return $this[self::BASE_ELT];
 692:     }
 693: 
 694:     /**
 695:      * Sorts a list of mailboxes.
 696:      *
 697:      * @param array &$mbox             The list of mailboxes to sort.
 698:      * @param IMP_Ftree_Element $base  The base element.
 699:      */
 700:     public function sortList(&$mbox, $base = false)
 701:     {
 702:         if (count($mbox) < 2) {
 703:             return;
 704:         }
 705: 
 706:         if (!$base || (!$base->base_elt && !$base->remote_auth)) {
 707:             $list_ob = new Horde_Imap_Client_Mailbox_List($mbox);
 708:             $mbox = $list_ob->sort();
 709:             return;
 710:         }
 711: 
 712:         $prefix = $base->base_elt
 713:             ? ''
 714:             : (strval($this->getAccount($base)) . "\0");
 715: 
 716:         $basesort = $othersort = array();
 717:         /* INBOX always appears first. */
 718:         $sorted = array($prefix . 'INBOX');
 719: 
 720:         foreach ($mbox as $key => $val) {
 721:             $ob = $this[$val];
 722:             if ($ob->nonimap) {
 723:                 $othersort[$key] = $ob->mbox_ob->label;
 724:             } elseif ($val !== ($prefix . 'INBOX')) {
 725:                 $basesort[$key] = $ob->mbox_ob->label;
 726:             }
 727:         }
 728: 
 729:         natcasesort($basesort);
 730:         natcasesort($othersort);
 731:         foreach (array_merge(array_keys($basesort), array_keys($othersort)) as $key) {
 732:             $sorted[] = $mbox[$key];
 733:         }
 734: 
 735:         $mbox = $sorted;
 736:     }
 737: 
 738: 
 739:     /* Internal methods. */
 740: 
 741:     /**
 742:      * Normalize an element ID to the correct, internal name.
 743:      *
 744:      * @param string $id  The element ID.
 745:      *
 746:      * @return string  The converted name.
 747:      */
 748:     protected function _normalize($id)
 749:     {
 750:         $id = strval($id);
 751: 
 752:         return (strcasecmp($id, 'INBOX') === 0)
 753:             ? 'INBOX'
 754:             : $id;
 755:     }
 756: 
 757:     /**
 758:      * Insert an element into the tree.
 759:      *
 760:      * @param array $elt  Element data. Keys:
 761:      *   - a: (integer) Attributes.
 762:      *   - p: (string) Parent element ID.
 763:      *   - v: (string) Mailbox ID.
 764:      */
 765:     protected function _insertElt($elt)
 766:     {
 767:         $name = $this->_normalize($elt['v']);
 768: 
 769:         $change = false;
 770:         if (isset($this->_elts[$name])) {
 771:             if ($elt['a'] && self::ELT_NOSELECT) {
 772:                 return;
 773:             }
 774:             $change = true;
 775:         }
 776: 
 777:         $p_elt = $this[isset($elt['p']) ? $elt['p'] : self::BASE_ELT];
 778:         $parent = strval($p_elt);
 779: 
 780:         $this->_changed = true;
 781: 
 782:         if (!isset($this->_parent[$parent])) {
 783:             $this->eltdiff->change($p_elt);
 784:         }
 785:         $this->_parent[$parent][] = $name;
 786:         $this->_elts[$name] = $elt['a'];
 787: 
 788:         if ($change) {
 789:             $this->eltdiff->change($name);
 790:         } else {
 791:             $this->eltdiff->add($name);
 792:         }
 793: 
 794:         /* Check for polled status. */
 795:         $this->setAttribute('polled', $name, $this->poll[$name]);
 796: 
 797:         /* Check for expanded status. */
 798:         $this->setAttribute('open', $name, $this->expanded[$name]);
 799: 
 800:         if (empty($this->_temp['nohook'])) {
 801:             try {
 802:                 $this->setAttribute(
 803:                     'invisible',
 804:                     $name,
 805:                     !$GLOBALS['injector']->getInstance('Horde_Core_Hooks')->callHook(
 806:                         'display_folder',
 807:                         'imp',
 808:                         array($name)
 809:                     )
 810:                 );
 811:             } catch (Horde_Exception_HookNotSet $e) {
 812:                 $this->_temp['nohook'] = true;
 813:             }
 814:         }
 815: 
 816:         /* Make sure we are sorted correctly. */
 817:         $this->setAttribute('needsort', $p_elt, true);
 818:     }
 819: 
 820:     /**
 821:      * Sort a level in the tree.
 822:      *
 823:      * @param string $id  The parent element whose children need to be sorted.
 824:      */
 825:     protected function _sortLevel($id)
 826:     {
 827:         if (($elt = $this[$id]) && $elt->needsort) {
 828:             if (count($this->_parent[strval($elt)]) > 1) {
 829:                 $this->sortList($this->_parent[strval($elt)], $elt);
 830:             }
 831:             $this->setAttribute('needsort', $elt, false);
 832:         }
 833:     }
 834: 
 835:     /* ArrayAccess methods. */
 836: 
 837:     /**
 838:      */
 839:     public function offsetExists($offset)
 840:     {
 841:         /* Optimization: Only normalize in the rare case it is not found on
 842:          * the first attempt. */
 843:         $offset = strval($offset);
 844:         return (isset($this->_elts[$offset]) ||
 845:                 isset($this->_elts[$this->_normalize($offset)]));
 846:     }
 847: 
 848:     /**
 849:      * @return IMP_Ftree_Element
 850:      */
 851:     public function offsetGet($offset)
 852:     {
 853:         if ($offset instanceof IMP_Ftree_Element) {
 854:             return $offset;
 855:         }
 856: 
 857:         /* Optimization: Only normalize in the rare case it is not found on
 858:          * the first attempt. */
 859:         $offset = strval($offset);
 860:         if (isset($this->_elts[$offset])) {
 861:             return new IMP_Ftree_Element($offset, $this);
 862:         }
 863: 
 864:         $offset = $this->_normalize($offset);
 865:         return isset($this->_elts[$offset])
 866:             ? new IMP_Ftree_Element($offset, $this)
 867:             : null;
 868:     }
 869: 
 870:     /**
 871:      */
 872:     public function offsetSet($offset, $value)
 873:     {
 874:         $this->insert($offset);
 875:     }
 876: 
 877:     /**
 878:      */
 879:     public function offsetUnset($offset)
 880:     {
 881:         $this->delete($offset);
 882:     }
 883: 
 884:     /* Countable methods. */
 885: 
 886:     /**
 887:      * Return the number of mailboxes on the server.
 888:      */
 889:     public function count()
 890:     {
 891:         $this->loadUnsubscribed();
 892: 
 893:         $iterator = new IMP_Ftree_IteratorFilter($this);
 894:         $iterator->add($iterator::NONIMAP);
 895:         $iterator->remove($iterator::UNSUB);
 896: 
 897:         return iterator_count($iterator);
 898:     }
 899: 
 900:     /* Serializable methods. */
 901: 
 902:     /**
 903:      */
 904:     public function serialize()
 905:     {
 906:         return $GLOBALS['injector']->getInstance('Horde_Pack')->pack(array(
 907:             $this->_accounts,
 908:             $this->_eltdiff,
 909:             $this->_elts,
 910:             $this->_parent
 911:         ), array(
 912:             'compress' => false,
 913:             'phpob' => true
 914:         ));
 915:     }
 916: 
 917:     /**
 918:      * @throws Horde_Pack_Exception
 919:      */
 920:     public function unserialize($data)
 921:     {
 922:         list(
 923:             $this->_accounts,
 924:             $this->_eltdiff,
 925:             $this->_elts,
 926:             $this->_parent
 927:         ) = $GLOBALS['injector']->getInstance('Horde_Pack')->unpack($data);
 928:     }
 929: 
 930:     /**
 931:      * Creates a Horde_Tree representation of the current tree.
 932:      *
 933:      * @param string|Horde_Tree $name  Either the tree name, or a Horde_Tree
 934:      *                                 object to add nodes to.
 935:      * @param array $opts              Additional options:
 936:      *   - basename: (boolean) Use raw basename instead of abbreviated label?
 937:      *               DEFAULT: false
 938:      *   - checkbox: (boolean) Display checkboxes?
 939:      *               DEFAULT: false
 940:      *   - editvfolder: (boolean) Display vfolder edit links?
 941:      *                  DEFAULT: false
 942:      *   - iterator: (Iterator) Tree iterator to use.
 943:      *               DEFAULT: Base iterator.
 944:      *   - open: (boolean) Force child mailboxes to this status.
 945:      *           DEFAULT: null
 946:      *   - parent: (string) The parent object of the current level.
 947:      *             DEFAULT: null (add to base level)
 948:      *   - poll_info: (boolean) Include poll information in output?
 949:      *                DEFAULT: false
 950:      *   - render_params: (array) List of params to pass to renderer if
 951:      *                    auto-creating.
 952:      *                    DEFAULT: 'alternate', 'lines', and 'lines_base'
 953:      *                             are passed in with true values.
 954:      *   - render_type: (string) The renderer name.
 955:      *                  DEFAULT: Javascript
 956:      *
 957:      * @return Horde_Tree  The tree object.
 958:      */
 959:     public function createTree($name, array $opts = array())
 960:     {
 961:         global $injector, $registry;
 962: 
 963:         $opts = array_merge(array(
 964:             'parent' => null,
 965:             'render_params' => array(),
 966:             'render_type' => 'Javascript'
 967:         ), $opts);
 968: 
 969:         $view = $registry->getView();
 970: 
 971:         if ($name instanceof Horde_Tree_Renderer_Base) {
 972:             $tree = $name;
 973:             $parent = $opts['parent'];
 974:         } else {
 975:             $tree = $injector->getInstance('Horde_Core_Factory_Tree')->create($name, $opts['render_type'], array_merge(array(
 976:                 'alternate' => true,
 977:                 'lines' => true,
 978:                 'lines_base' => true,
 979:                 'nosession' => true
 980:             ), $opts['render_params']));
 981:             $parent = null;
 982:         }
 983: 
 984:         $iterator = empty($opts['iterator'])
 985:             ? new IMP_Ftree_IteratorFilter($this)
 986:             : $opts['iterator'];
 987: 
 988:         foreach ($iterator as $val) {
 989:             $after = '';
 990:             $elt_parent = null;
 991:             $mbox_ob = $val->mbox_ob;
 992:             $params = array();
 993: 
 994:             switch ($opts['render_type']) {
 995:             case 'IMP_Tree_Flist':
 996:                 if ($mbox_ob->vfolder_container) {
 997:                     continue 2;
 998:                 }
 999: 
1000:                 $is_open = true;
1001:                 $label = $params['orig_label'] = empty($opts['basename'])
1002:                     ? $mbox_ob->abbrev_label
1003:                     : $mbox_ob->basename;
1004:                 break;
1005: 
1006:             case 'IMP_Tree_Jquerymobile':
1007:                 $is_open = true;
1008:                 $label = $mbox_ob->display_html;
1009:                 $icon = $mbox_ob->icon;
1010:                 $params['icon'] = $icon->icon;
1011:                 $params['special'] = $mbox_ob->inbox || $mbox_ob->special;
1012:                 $params['class'] = 'imp-folder';
1013:                 $params['urlattributes'] = array(
1014:                     'id' => 'imp-mailbox-' . $mbox_ob->form_to
1015:                 );
1016: 
1017:                 /* Force to flat tree so that non-polled parents don't cause
1018:                  * polled children to be skipped by renderer (see Bug
1019:                  * #11238). */
1020:                 $elt_parent = $this[self::BASE_ELT];
1021:                 break;
1022: 
1023:             case 'IMP_Tree_Simplehtml':
1024:                 $is_open = $val->open;
1025:                 if ($tree->shouldToggle($mbox_ob->form_to)) {
1026:                     if ($is_open) {
1027:                         $this->collapse($val);
1028:                     } else {
1029:                         $this->expand($val);
1030:                     }
1031:                     $is_open = !$is_open;
1032:                 }
1033:                 $label = htmlspecialchars(Horde_String::abbreviate($mbox_ob->abbrev_label, 30 - ($val->level * 2)));
1034:                 break;
1035: 
1036:             case 'Javascript':
1037:                 $is_open = $val->open;
1038:                 $label = empty($opts['basename'])
1039:                     ? htmlspecialchars($mbox_ob->abbrev_label)
1040:                     : htmlspecialchars($mbox_ob->basename);
1041:                 $icon = $mbox_ob->icon;
1042:                 $params['icon'] = $icon->icon;
1043:                 $params['iconopen'] = $icon->iconopen;
1044:                 break;
1045:             }
1046: 
1047:             if (!empty($opts['poll_info']) && $val->polled) {
1048:                 $poll_info = $mbox_ob->poll_info;
1049: 
1050:                 if ($poll_info->unseen) {
1051:                     switch ($opts['render_type']) {
1052:                     case 'IMP_Tree_Jquerymobile':
1053:                         $after = $poll_info->unseen;
1054:                         break;
1055: 
1056:                     default:
1057:                         $label = '<strong>' . $label . '</strong>&nbsp;(' .
1058:                             $poll_info->unseen . ')';
1059:                     }
1060:                 }
1061:             }
1062: 
1063:             if ($val->container) {
1064:                 $params['container'] = true;
1065:             } else {
1066:                 switch ($view) {
1067:                 case $registry::VIEW_MINIMAL:
1068:                     $params['url'] = IMP_Minimal_Mailbox::url(array('mailbox' => $mbox_ob));
1069:                     break;
1070: 
1071:                 case $registry::VIEW_SMARTMOBILE:
1072:                     $url = new Horde_Core_Smartmobile_Url();
1073:                     $url->add('mbox', $mbox_ob->form_to);
1074:                     $url->setAnchor('mailbox');
1075:                     $params['url'] = strval($url);
1076:                     break;
1077: 
1078:                 default:
1079:                     $params['url'] = $mbox_ob->url('mailbox')->setRaw(true);
1080:                     break;
1081:                 }
1082: 
1083:                 if (!$val->subscribed) {
1084:                     $params['class'] = 'mboxunsub';
1085:                 }
1086:             }
1087: 
1088:             $checkbox = empty($opts['checkbox'])
1089:                 ? ''
1090:                 : '<input type="checkbox" class="checkbox" name="mbox_list[]" value="' . $mbox_ob->form_to . '"';
1091: 
1092:             if ($val->nonimap) {
1093:                 $checkbox .= ' disabled="disabled"';
1094:             }
1095: 
1096:             if ($val->vfolder &&
1097:                 !empty($opts['editvfolder']) &&
1098:                 $val->container) {
1099:                 $after = '&nbsp[' .
1100:                     $registry->getServiceLink('prefs', 'imp')->add('group', 'searches')->link(array('title' => _("Edit Virtual Folder"))) . _("Edit") . '</a>'.
1101:                     ']';
1102:             }
1103: 
1104:             if (is_null($elt_parent)) {
1105:                 $elt_parent = $val->parent;
1106:             }
1107: 
1108:             $tree->addNode(array(
1109:                 'id' => $mbox_ob->form_to,
1110:                 'parent' => $elt_parent->base_elt ? $parent : $elt_parent->mbox_ob->form_to,
1111:                 'label' => $label,
1112:                 'expanded' => isset($opts['open']) ? $opts['open'] : $is_open,
1113:                 'params' => $params,
1114:                 'right' => $after,
1115:                 'left' => empty($opts['checkbox']) ? null : $checkbox . ' />'
1116:             ));
1117:         }
1118: 
1119:         return $tree;
1120:     }
1121: 
1122:     /* IteratorAggregate methods. */
1123: 
1124:     /**
1125:      * This returns a RecursiveIterator - a RecursiveIteratorIterator is
1126:      * needed to properly iterate through all elements.
1127:      *
1128:      * @return IMP_Ftree_Iterator  Iterator object.
1129:      */
1130:     public function getIterator()
1131:     {
1132:         return new IMP_Ftree_Iterator($this[self::BASE_ELT]);
1133:     }
1134: 
1135: }
1136: 
API documentation generated by ApiGen