<?php

require_once 'Horde/IMAP/Tree.php';

/** @const IMAPTREE_ELT_VFOLDER  Identify an element as a virtual folder. */
define('IMPTREE_ELT_VFOLDER', 8192);

/**
 * The IMP_tree class provides a tree view of the folders in an
 * IMAP/POP3 repository.  It provides access functions to iterate
 * through this tree and query information about individual
 * mailboxes.
 *
 * $Horde: imp/lib/IMAP/Tree.php,v 1.25.2.1 2005/01/03 12:25:34 jan Exp $
 *
 * Copyright 2000-2005 Chuck Hagenbuch <chuck@horde.org>
 * Copyright 2000-2005 Jon Parise <jon@horde.org>
 * Copyright 2000-2005 Anil Madhavapeddy <avsm@horde.org>
 * Copyright 2003-2005 Michael Slusarz <slusarz@bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @author  Jon Parise <jon@horde.org>
 * @author  Anil Madhavapeddy <avsm@horde.org>
 * @author  Michael Slusarz <slusarz@bigworm.colorado.edu>
 * @version $Revision: 1.25.2.1 $
 * @since   IMP 2.3
 * @package IMP
 */
class IMP_Tree extends IMAP_Tree {

    /**
     * Mapping for virtual folders to their label.
     *
     * @var array $_vfolders
     */
    var $_vfolders = array();

    /**
     * The default IMP initialization mask to use.
     *
     * @var integer $_initmask
     */
    var $_initmask;

    /**
     * Singleton method.
     */
    function &singleton()
    {
        return IMAP_Tree::singleton('imp', 'IMP_Tree', true);
    }

    /**
     * Constructor.
     *
     * @see IMAP_Tree::IMAP_Tree()
     */
    function IMP_Tree($init = IMAPTREE_INIT_SUB, $cachename = null)
    {
        global $imp;

        $this->_app = 'imp';
        $this->_delimiter = $imp['delimiter'];
        $this->_dotfiles = $imp['dotfiles'];
        $this->_mode = IMAPTREE_MODE_MAIL;
        $this->_prefix = $imp['folders'];
        $this->_namespace = $imp['namespace'];
        $this->_server = IMP::serverString();

        /* We just override the init parameter with the following. */
        $this->_initmask = (!$GLOBALS['prefs']->getValue('subscribe') || $imp['showunsub']) ? IMAPTREE_INIT_UNSUB : IMAPTREE_INIT_SUB;
        if ($GLOBALS['prefs']->getValue('show_sidebar')) {
            $this->_initmask |= IMAPTREE_INIT_FETCHALL;
        }

        parent::IMAP_Tree($this->_initmask);
    }

    /**
     * Initalize the list at the top level of the hierarchy.
     *
     * @see IMAP_Tree::_init()
     */
    function init($init = null)
    {
        if (is_null($init)) {
            $init = $this->_initmask;
        }
        parent::init($init);

        /* Add virtual folders to the tree. */
        if (!empty($_SESSION['imp']['vfolder'])) {
            $this->_vfolders = array();
            $this->insertVFolders(IMP_VFolder::listVFolders());
        }
    }

    /**
     * Insert virtual folders into the tree.
     *
     * @access public
     *
     * @param array $id_list  An array with the folder IDs to add as the key
     *                        and the labels as the value.
     */
    function insertVFolders($id_list)
    {
        $id = array();
        foreach ($id_list as $key => $val) {
            $id[IMP_VFOLDER_PREFIX . $key] = $val;
        }

        if (empty($this->_vfolders)) {
            $this->_vfolders = $id;
            $id = array_merge(array(_("Virtual Folders")), array_keys($id));
        } else {
            $this->_vfolders = array_merge($this->_vfolders, $id);
            $id = array_keys($id);
        }

        $this->_nonimapelt = true;
        $this->_initmode = IMAPTREE_INIT_SUB;
        $this->insert($id);
        $this->_initmode = 0;
        $this->_nonimapelt = false;
    }

    /**
     * Subclass specific initialization tasks.
     *
     * @see IMAP_Tree::_init()
     */
    function _init()
    {
        $boxes = array();

        if (empty($this->_prefix)) {
            $boxes = $this->_getList('%');
        }

        if (!isset($boxes['INBOX'])) {
            $boxes['INBOX'] = $this->_getMailbox('INBOX');
        }

        /* Do a sort to make sure that 'INBOX' always appears as the first
         * element. */
        $this->_sortList($boxes, true);

        /* If we have any additional hierarchies, such as UW's '#shared/',
         * add them in. */
        foreach ($GLOBALS['imp']['hierarchies'] as $val) {
            $val = $this->noTrailingDelimiter($val);
            $tmp = $this->_getMailbox($val);
            if ($tmp) {
                $tmp->name = $this->noTrailingDelimiter($tmp->name);
                $boxes[$tmp->name] = $tmp;
            }
        }

        return $boxes;
    }

    /**
     * Returns a reference to a currently open IMAP stream.
     *
     * @see IMAP_Tree::_getStream()
     */
    function &_getStream()
    {
        return $GLOBALS['imp']['stream'];
    }

    /**
     * Initialize the expanded folder list.
     *
     * @see IMAP_Tree::_initExpandedList()
     */
    function _initExpandedList()
    {
        if (is_null($this->_expanded)) {
            $serialized = $GLOBALS['prefs']->getValue('expanded_folders');
            $this->_expanded = ($serialized) ? unserialize($serialized) : array();
        }
    }

    /**
     * Add an element to the expanded list.
     *
     * @see IMAP_Tree::_addExpandedList()
     */
    function _addExpandedList($id)
    {
        $this->_initExpandedList();
        $this->_expanded[$id] = true;
        $GLOBALS['prefs']->setValue('expanded_folders', serialize($this->_expanded));
    }

    /**
     * Remove an element from the expanded list.
     *
     * @see IMAP_Tree::_removeExpandedList()
     */
    function _removeExpandedList($id)
    {
        $this->_initExpandedList();
        unset($this->_expanded[$id]);
        $GLOBALS['prefs']->setValue('expanded_folders', serialize($this->_expanded));
    }

    /**
     * Initialize/get the list of mailboxes to poll.
     * This list is returned sorted via IMAP_Sort::.
     *
     * @see IMAP_Tree::getPollList()
     */
    function getPollList()
    {
        if (is_null($this->_poll)) {
            /* We ALWAYS poll the INBOX. */
            $this->_poll = array('INBOX' => 1);

            /* Add the user's list of polled mailboxes, if not locked. */
            if (!$GLOBALS['prefs']->isLocked('nav_poll')) {
                $navPollList = @unserialize($GLOBALS['prefs']->getValue('nav_poll'));
                if ($navPollList) {
                    $this->_poll += $navPollList;
                }
            }
        }

        return $this->_poll;
    }

    /**
     * Add element to the poll list.
     *
     * @see IMAP_Tree::addPollList()
     */
    function addPollList($id)
    {
        if (!is_array($id)) {
            $id = array($id);
        }

        if (!empty($id) && !$GLOBALS['prefs']->isLocked('nav_poll')) {
            $this->getPollList();
            foreach ($id as $val) {
                $this->_poll[$val] = true;
                $this->_setPolled($this->_tree[$val], true);
            }
            $this->_sortList($this->_poll, true);
            $GLOBALS['prefs']->setValue('nav_poll', serialize($this->_poll));
        }
    }

    /**
     * Remove element from the poll list.
     *
     * @see IMAP_Tree::removePollList()
     */
    function removePollList($id)
    {
        if (!is_array($id)) {
            $id = array($id);
        }

        $removed = false;

        if (!$GLOBALS['prefs']->isLocked('nav_poll')) {
            $this->getPollList();
            foreach ($id as $val) {
                if ($val != 'INBOX') {
                    unset($this->_poll[$val]);
                    if (isset($this->_tree[$val])) {
                        $this->_setPolled($this->_tree[$val], false);
                    }
                    $removed = true;
                }
            }
            if ($removed) {
                $GLOBALS['prefs']->setValue('nav_poll', serialize($this->_poll));
            }
        }
    }

    /**
     * Returns the currently selected initialization expanded mode.
     *
     * @see IMAP_Tree::_getInitExpandedMode()
     */
    function _getInitExpandedMode()
    {
        return $GLOBALS['prefs']->getValue('nav_expanded');
    }

    /**
     * Create the virtual folder container
     *
     * @access private
     *
     * @return array  A mailbox element.
     */
    function _createVFolderContainer()
    {
        $base = _("Virtual Folders");

        $ob = &new stdClass;
        $ob->delimiter = $this->_delimiter;
        $ob->attributes = LATT_NOSELECT | LATT_HASCHILDREN | IMAPTREE_ELT_HAS_CHILDREN | IMAPTREE_ELT_IS_DISCOVERED | IMAPTREE_ELT_IS_SUBSCRIBED | IMPTREE_ELT_VFOLDER;
        $ob->fullServerPath = $ob->name = $base;

        $elt = $this->_makeMailboxTreeElt($ob);
        $elt['l'] = $elt['v'] = $base;

        return $elt;
    }

    /**
     * Create a virtual folder element.
     *
     * @access private
     *
     * @access string $vfolder  Virtual folder ID.
     *
     * @return array  A mailbox element.
     */
    function _createVFolderElt($vfolder)
    {
        $base = _("Virtual Folders");

        $ob = &new stdClass;
        $ob->delimiter = $this->_delimiter;
        $ob->attributes = LATT_HASNOCHILDREN | IMAPTREE_ELT_IS_DISCOVERED | IMAPTREE_ELT_IS_SUBSCRIBED | IMPTREE_ELT_VFOLDER;
        $ob->name = $base . $this->_delimiter . $vfolder;
        $ob->fullServerPath = $ob->name;

        $elt = $this->_makeMailboxTreeElt($ob);
        $elt['l'] = $this->_vfolders[$vfolder];
        $elt['v'] = $vfolder;

        return $elt;
    }

    /**
     * Is this element a virtual folder?
     *
     * @access public
     *
     * @param array $elt  A tree element.
     *
     * @return integer  True if the element is a virtual folder.
     */
    function isVFolder($elt)
    {
        return $elt['a'] & IMPTREE_ELT_VFOLDER;
    }

    /**
     * Return a Non-IMAP mailbox element given an element identifier.
     *
     * @abstract
     *
     * @access private
     *
     * @param string $id  The element identifier.
     *
     * @return array  A mailbox element.
     */
    function _getNonIMAPElt($id)
    {
        if ($id == _("Virtual Folders")) {
            return $this->_createVfolderContainer();
        } else {
            return $this->_createVfolderElt($id);
        }
    }

    /**
     * Delete an element from the tree.
     *
     * @see IMAP_Tree::delete()
     */
    function delete($id)
    {
        if (!is_array($id) && (strpos($id, IMP_VFOLDER_PREFIX) === 0)) {
            unset($this->_vfolders[$id]);
        }
        return parent::delete($id);
    }

}
