From: Michael M Slusarz Date: Wed, 16 Dec 2009 01:00:17 +0000 (-0700) Subject: Import Horde_Tree from CVS HEAD X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=455873ca21d25dec2c32de81ee3c9b2f04dbe5c9;p=horde.git Import Horde_Tree from CVS HEAD --- diff --git a/framework/Tree/lib/Horde/Tree.php b/framework/Tree/lib/Horde/Tree.php new file mode 100644 index 000000000..c063fd1cf --- /dev/null +++ b/framework/Tree/lib/Horde/Tree.php @@ -0,0 +1,573 @@ + + * @category Horde + * @package Horde_Tree + */ +class Horde_Tree +{ + /** + * Display extra columns to the left of the main tree. + */ + const EXTRA_LEFT = 0; + + /** + * Display extra columns to the right of the main tree. + */ + const EXTRA_RIGHT = 1; + + /** + * The preceding text, before the Horde_Tree instance name, used for + * collapse/expand submissions. + */ + const TOGGLE = 'ht_toggle_'; + + /** + * Singleton instances. + * + * @var array + */ + static protected $_instances = array(); + + /** + * The name of this instance. + * + * @var string + */ + protected $_instance = null; + + /** + * Hash with header information. + * + * @var array + */ + protected $_header = array(); + + /** + * An array containing all the tree nodes. + * + * @var array + */ + protected $_nodes = array(); + + /** + * The top-level nodes in the tree. + * + * @var array + */ + protected $_root_nodes = array(); + + /** + * Keep count of how many extra columns there are on the left side + * of the node. + * + * @var integer + */ + protected $_extra_cols_left = 0; + + /** + * Keep count of how many extra columns there are on the right side + * of the node. + * + * @var integer + */ + protected $_extra_cols_right = 0; + + /** + * Option values. + * + * @var array + */ + protected $_options = array( + 'lines' => true + ); + + /** + * Image directory location. + * + * @var string + */ + protected $_img_dir = ''; + + /** + * Images array. + * + * @var array + */ + protected $_images = array( + 'line' => 'line.png', + 'blank' => 'blank.png', + 'join' => 'join.png', + 'join_bottom' => 'joinbottom.png', + 'plus' => 'plus.png', + 'plus_bottom' => 'plusbottom.png', + 'plus_only' => 'plusonly.png', + 'minus' => 'minus.png', + 'minus_bottom' => 'minusbottom.png', + 'minus_only' => 'minusonly.png', + 'null_only' => 'nullonly.png', + 'folder' => 'folder.png', + 'folderopen' => 'folderopen.png', + 'leaf' => 'leaf.png' + ); + + /** + * Stores the sorting criteria temporarily. + * + * @var string + */ + protected $_sortCriteria; + + /** + * Use session to store cached Tree data? + * + * @var boolean + */ + protected $_usesession = true; + + /** + * Should the tree be rendered statically? + * + * @var boolean + */ + protected $_static = false; + + /** + * Attempts to return a reference to a concrete instance. + * It will only create a new instance if no instance with the same + * parameters currently exists. + * + * This method must be invoked as: + * $var = Horde_Tree::singleton($name[, $renderer[, $params]]); + * + * @param mixed $name @see Horde_Tree::factory. + * @param string $renderer @see Horde_Tree::factory. + * @param array $params @see Horde_Tree::factory. + * + * @return Horde_Tree The concrete instance. + * @throws Horde_Exception + */ + static public function singleton($name, $renderer, $params = array()) + { + ksort($params); + $id = $name . ':' . $renderer . ':' . serialize($params); + + if (!isset(self::$_instances[$id])) { + self::$_instances[$id] = Horde_Tree::factory($name, $renderer, $params); + if (!self::$_instances[$id]->isSupported()) { + $renderer = Horde_Tree::fallback($renderer); + return Horde_Tree::singleton($name, $renderer, $params); + } + } + + return self::$_instances[$id]; + } + + /** + * Attempts to return a concrete instance. + * + * @param string $name The name of this tree instance. + * @param mixed $renderer The type of concrete subclass to return. This + * is based on the rendering driver. The code is + * dynamically included. + * @param array $params Any additional parameters the constructor + * needs. + * + * @return Horde_Tree The newly created concrete instance. + * @throws Horde_Exception + */ + static public function factory($name, $renderer, $params = array()) + { + $class = 'Horde_Tree_' . ucfirst($renderer); + if (class_exists($class)) { + return new $class($name, $params); + } + + throw new Horde_Exception('Horde_Tree renderer not found: ' . $renderer); + } + + /** + * Try to fall back to a simpler renderer. + * + * @paran string $renderer The renderer that we can't handle. + * + * @return string The next best renderer. + * @throws Horde_Exception + */ + public function fallback($renderer) + { + switch ($renderer) { + case 'javascript': + return 'html'; + + case 'html': + throw new Horde_Exception('No fallback renderer found.'); + } + } + + /** + * Constructor. + * + * @param string $name The name of this tree instance. + * @param array $params Additional parameters: + *
+     * 'nosession' - (boolean) If true, do not store tree data in session.
+     * 
+ */ + public function __construct($name, $params = array()) + { + $this->_instance = $name; + $this->_usesession = empty($params['nosession']); + unset($params['nosession']); + $this->setOption($params); + + /* Set up the session for later to save tree states. */ + if ($this->_usesession && + !isset($_SESSION['horde_tree'][$this->_instance])) { + $_SESSION['horde_tree'][$this->_instance] = array(); + } + + $this->_img_dir = $GLOBALS['registry']->getImageDir('horde') . '/tree'; + + if (!empty($GLOBALS['nls']['rtl'][$GLOBALS['language']])) { + $rev_imgs = array( + 'line', 'join', 'join_bottom', 'plus', 'plus_bottom', + 'plus_only', 'minus', 'minus_bottom', 'minus_only', + 'null_only', 'leaf' + ); + foreach ($rev_imgs as $val) { + $this->_images[$val] = 'rev-' . $this->_images[$val]; + } + } + } + + /** + * Renders the tree. + * + * @param boolean $static If true the tree nodes can't be expanded and + * collapsed and the tree gets rendered expanded. + */ + public function renderTree($static = false) + { + echo $this->getTree($static); + } + + /** + * Sets an option. + * + * @param mixed $option The option name -or- an array of option name/value + * pairs. Available options: + *
+     * alternate - (boolean) Alternate shading in the table?
+     * class - (string) The class to use for the table.
+     * hideHeaders - (boolean) Don't render any HTML for the header row, just
+     *               use the widths.
+     * lines - (boolean) Show tree lines?
+     * multiline - (boolean) Do the node labels contain linebreaks?
+     * 
+ * @param mixed $value The option's value. + */ + public function setOption($options, $value = null) + { + if (!is_array($options)) { + $options = array($options => $value); + } + + foreach ($options as $option => $value) { + $this->_options[$option] = $value; + } + } + + /** + * Gets an option's value. + * + * @param string $option The name of the option to fetch. + * @param boolean $html Whether to format the return value in HTML. + * @param string $default A default value to use in case none is set for + * the requested option. + * + * @return mixed The option's value. + */ + public function getOption($option, $html = false, $default = null) + { + $value = null; + + if (!isset($this->_options[$option]) && !is_null($default)) { + /* Requested option has not been but there is a + * default. */ + $value = $default; + } elseif (isset($this->_options[$option])) { + /* Requested option has been set, get its value. */ + $value = $this->_options[$option]; + } + + if ($html && !is_null($value)) { + /* Format value for html output. */ + $value = sprintf(' %s="%s"', $option, $value); + } + + return $value; + } + + /** + * Adds a node to the node tree array. + * + * @param string $id The unique node id. + * @param string $parent The parent's unique node id. + * @param string $label The text label for the node. + * @param string $indent Deprecated, this is calculated automatically + * based on the parent node. + * @param boolean $expanded Is this level expanded or not. + * @param array $params Any other parameters to set (@see + * addNodeParams() for full details). + * @param array $extra_right Any other columns to display to the right of + * the tree. + * @param array $extra_left Any other columns to display to the left of + * the tree. + */ + public function addNode($id, $parent, $label, $indent = null, + $expanded = true, $params = array(), + $extra_right = array(), $extra_left = array()) + { + $this->_nodes[$id]['label'] = $label; + + if ($this->_usesession) { + $session_state = $_SESSION['horde_tree'][$this->_instance]; + $toggle_id = Horde_Util::getFormData(self::TOGGLE . $this->_instance); + if ($id == $toggle_id) { + /* We have a url toggle request for this node. */ + if (isset($session_state['expanded'][$id])) { + /* Use session state if it is set. */ + $expanded = (!$session_state['expanded'][$id]); + } else { + /* Otherwise use what was passed through the + * function. */ + $expanded = (!$expanded); + } + + /* Save this state to session. */ + $_SESSION['horde_tree'][$this->_instance]['expanded'][$id] = $expanded; + } elseif (isset($session_state['expanded'][$id])) { + /* If we have a saved session state use it. */ + $expanded = $session_state['expanded'][$id]; + } + } + + $this->_nodes[$id]['expanded'] = $expanded; + + /* If any params included here add them now. */ + if (!empty($params)) { + $this->addNodeParams($id, $params); + } + + /* If any extra columns included here add them now. */ + if (!empty($extra_right)) { + $this->addNodeExtra($id, self::EXTRA_RIGHT, $extra_right); + } + if (!empty($extra_left)) { + $this->addNodeExtra($id, self::EXTRA_LEFT, $extra_left); + } + + if (is_null($parent)) { + if (!in_array($id, $this->_root_nodes)) { + $this->_root_nodes[] = $id; + } + } else { + if (empty($this->_nodes[$parent]['children'])) { + $this->_nodes[$parent]['children'] = array(); + } + if (!in_array($id, $this->_nodes[$parent]['children'])) { + $this->_nodes[$parent]['children'][] = $id; + } + } + } + + /** + * Adds additional parameters to a node. + * + * @param string $id The unique node id. + * @param array $params Any other parameters to set. + *
+     * class - CSS class to use with this node
+     * icon - Icon to display next node
+     * iconalt - Alt text to use for the icon
+     * icondir - Icon directory
+     * iconopen - Icon to indicate this node as expanded
+     * onclick - Onclick event attached to this node
+     * url - URL to link the node to
+     * urlclass - CSS class for the node's URL
+     * title - Link tooltip title
+     * target - Target for the 'url' link
+     * 
+ */ + public function addNodeParams($id, $params = array()) + { + if (!is_array($params)) { + $params = array($params); + } + + $allowed = array( + 'class', 'icon', 'iconalt', 'icondir', 'iconopen', + 'onclick', 'url', 'urlclass', 'title', 'target', + ); + + foreach ($params as $param_id => $param_val) { + // Set only allowed and non-null params. + if (in_array($param_id, $allowed) && !is_null($param_val)) { + // Cast Horde_Url objects + if ($param_id == 'url') { + $param_val = (string)$param_val; + } + $this->_nodes[$id][$param_id] = $param_val; + } + } + } + + /** + * Adds extra columns to be displayed to the side of the node. + * + * @param mixed $id The unique node id. + * @param integer $side Which side to place the extra columns on. + * @param array $extra Extra columns to display. + */ + public function addNodeExtra($id, $side, $extra) + { + if (!is_array($extra)) { + $extra = array($extra); + } + + $col_count = count($extra); + + switch ($side) { + case self::EXTRA_LEFT: + $this->_nodes[$id]['extra'][self::EXTRA_LEFT] = $extra; + if ($col_count > $this->_extra_cols_left) { + $this->_extra_cols_left = $col_count; + } + break; + + case self::EXTRA_RIGHT: + $this->_nodes[$id]['extra'][self::EXTRA_RIGHT] = $extra; + if ($col_count > $this->_extra_cols_right) { + $this->_extra_cols_right = $col_count; + } + break; + } + } + + /** + * Sorts the tree by the specified node property. + * + * @param string $criteria The node property to sort by. + * @param integer $id Used internally for recursion. + */ + public function sort($criteria, $id = -1) + { + if (!isset($this->_nodes[$id]['children'])) { + return; + } + + if ($criteria == 'key') { + ksort($this->_nodes[$id]['children']); + } else { + $this->_sortCriteria = $criteria; + usort($this->_nodes[$id]['children'], array($this, 'sortHelper')); + } + + foreach ($this->_nodes[$id]['children'] as $child) { + $this->sort($criteria, $child); + } + } + + /** + * Helper method for sort() to compare two tree elements. + */ + public function sortHelper($a, $b) + { + if (!isset($this->_nodes[$a][$this->_sortCriteria])) { + return 1; + } + + if (!isset($this->_nodes[$b][$this->_sortCriteria])) { + return -1; + } + + return strcoll($this->_nodes[$a][$this->_sortCriteria], + $this->_nodes[$b][$this->_sortCriteria]); + } + + /** + * Returns whether the specified node is currently expanded. + * + * @param mixed $id The unique node id. + * + * @return boolean True if the specified node is expanded. + */ + public function isExpanded($id) + { + return isset($this->_nodes[$id]) + ? $this->_nodes[$id]['expanded'] + : false; + } + + /** + * Adds column headers to the tree table. + * + * @param array $header An array containing hashes with header + * information. The following keys are allowed: + *
+     * align - The alignment inside the header cell
+     * class - The CSS class of the header cell
+     * html - The HTML content of the header cell
+     * width - The width of the header cell
+     * 
+ */ + public function setHeader($header) + { + $this->_header = $header; + } + + /** + * Set the indent level for each node in the tree. + * + * @param array $nodes TODO + * @param integer $indent TODO + */ + protected function _buildIndents($nodes, $indent = 0) + { + foreach ($nodes as $id) { + $this->_nodes[$id]['indent'] = $indent; + if (!empty($this->_nodes[$id]['children'])) { + $this->_buildIndents($this->_nodes[$id]['children'], $indent + 1); + } + } + } + + /** + * Check the current environment to see if we can render the tree. + * + * @return boolean Whether or not this backend will function. + */ + public function isSupported() + { + return true; + } + + /** + * Returns just the JS node definitions as a string. + * + * @return string The Javascript node array definitions. + */ + public function renderNodeDefinitions() + { + } + +} diff --git a/framework/Tree/lib/Horde/Tree/Html.php b/framework/Tree/lib/Horde/Tree/Html.php new file mode 100644 index 000000000..f1e5eafba --- /dev/null +++ b/framework/Tree/lib/Horde/Tree/Html.php @@ -0,0 +1,439 @@ + + * @category Horde + * @package Horde_Tree + */ +class Horde_Tree_Html extends Horde_Tree +{ + /** + * TODO + * + * @var array + */ + protected $_nodes = array(); + + /** + * TODO + * + * @var array + */ + protected $_node_pos = array(); + + /** + * TODO + * + * @var array + */ + protected $_dropline = array(); + + /** + * Current value of the alt tag count. + * + * @var integer + */ + protected $_alt_count = 0; + + /** + * Returns the tree. + * + * @param boolean $static If true the tree nodes can't be expanded and + * collapsed and the tree gets rendered expanded. + * + * @return string The HTML code of the rendered tree. + */ + public function getTree($static = false) + { + $this->_static = $static; + $this->_buildIndents($this->_root_nodes); + + $tree = $this->_buildHeader(); + foreach ($this->_root_nodes as $node_id) { + $tree .= $this->_buildTree($node_id); + } + return $tree; + } + + /** + * Returns the HTML code for a header row, if necessary. + * + * @return string The HTML code of the header row or an empty string. + */ + protected function _buildHeader() + { + if (!count($this->_header)) { + return ''; + } + + $html = 'getOption('alternate')) { + $html .= ' class="item' . $this->_alt_count . '"'; + $this->_alt_count = 1 - $this->_alt_count; + } + $html .= '>'; + + foreach ($this->_header as $header) { + $html .= '
'; + } + + /** + * Recursive function to walk through the tree array and build the output. + * + * @param string $node_id The Node ID. + * + * @return string The tree rendering. + */ + protected function _buildTree($node_id) + { + $output = $this->_buildLine($node_id); + + if (isset($this->_nodes[$node_id]['children']) && + $this->_nodes[$node_id]['expanded']) { + $num_subnodes = count($this->_nodes[$node_id]['children']); + for ($c = 0; $c < $num_subnodes; $c++) { + $child_node_id = $this->_nodes[$node_id]['children'][$c]; + $this->_node_pos[$child_node_id] = array(); + $this->_node_pos[$child_node_id]['pos'] = $c + 1; + $this->_node_pos[$child_node_id]['count'] = $num_subnodes; + $output .= $this->_buildTree($child_node_id); + } + } + + return $output; + } + + /** + * Function to create a single line of the tree. + * + * @param string $node_id The Node ID. + * + * @return string The rendered line. + */ + protected function _buildLine($node_id) + { + $className = 'treeRow'; + if (!empty($this->_nodes[$node_id]['class'])) { + $className .= ' ' . $this->_nodes[$node_id]['class']; + } + /* If using alternating row shading, work out correct + * shade. */ + if ($this->getOption('alternate')) { + $className .= ' item' . $this->_alt_count; + $this->_alt_count = 1 - $this->_alt_count; + } + + $line = '
'; + + /* If we have headers, track which logical "column" we're in + * for any given cell of content. */ + $column = 0; + + if (isset($this->_nodes[$node_id]['extra'][self::EXTRA_LEFT])) { + $extra = $this->_nodes[$node_id]['extra'][self::EXTRA_LEFT]; + $cMax = count($extra); + for ($c = 0; $c < $cMax; $c++) { + $style = ''; + if (isset($this->_header[$column]['width'])) { + $style .= 'width:' . $this->_header[$column]['width'] . ';'; + } + + $line .= '
'; + + $column++; + } + } + + $style = ''; + if (isset($this->_header[$column]['width'])) { + $style .= 'width:' . $this->_header[$column]['width'] . ';'; + } + $line .= '
getOption('multiline')) { + $line .= '
'; + } + + for ($i = $this->_static ? 1 : 0; $i < $this->_nodes[$node_id]['indent']; $i++) { + $line .= '_images['blank'] . '" ' + . 'alt="   " '; + } + $line .= 'height="20" width="20" style="vertical-align:middle" />'; + } + $line .= $this->_setNodeToggle($node_id) . $this->_setNodeIcon($node_id); + if ($this->getOption('multiline')) { + $line .= ''; + } + $line .= $this->_setLabel($node_id); + + if ($this->getOption('multiline')) { + $line .= '
'; + } + + $line .= '
'; + $column++; + + if (isset($this->_nodes[$node_id]['extra'][self::EXTRA_RIGHT])) { + $extra = $this->_nodes[$node_id]['extra'][self::EXTRA_RIGHT]; + $cMax = count($extra); + for ($c = 0; $c < $cMax; $c++) { + $style = ''; + if (isset($this->_header[$column]['width'])) { + $style .= 'width:' . $this->_header[$column]['width'] . ';'; + } + + $line .= '
'; + + $column++; + } + } + + return $line . "
\n"; + } + + /** + * Sets the label on the tree line. + * + * @param string $node_id The Node ID. + * + * @return string The label for the tree line. + */ + protected function _setLabel($node_id) + { + $n = $this->_nodes[$node_id]; + + $output = 'getOption('target')) { + $target = ' target="' . $target . '"'; + } + $output .= '' . $label . ''; + } else { + $output .= $label; + } + + return $output . ''; + } + + /** + * Sets the node toggle on the tree line. + * + * @param string $node_id The Node ID. + * + * @return string The node toggle for the tree line. + */ + protected function _setNodeToggle($node_id) + { + $link_start = ''; + + if (($this->_nodes[$node_id]['indent'] == 0) && + isset($this->_nodes[$node_id]['children'])) { + /* Top level node with children. */ + $this->_dropline[0] = false; + if ($this->_static) { + return ''; + } elseif (!$this->getOption('lines', false, true)) { + $img = $this->_images['blank']; + $alt = '   '; + } elseif ($this->_nodes[$node_id]['expanded']) { + $img = $this->_images['minus_only']; + $alt = '-'; + } else { + $img = $this->_images['plus_only']; + $alt = '+'; + } + if (!$this->_static) { + $url = Horde_Util::addParameter(Horde::selfUrl(), self::TOGGLE . $this->_instance, $node_id); + $link_start = Horde::link($url); + } + } elseif (($this->_nodes[$node_id]['indent'] != 0) && + !isset($this->_nodes[$node_id]['children'])) { + /* Node without children. */ + if ($this->_node_pos[$node_id]['pos'] < $this->_node_pos[$node_id]['count']) { + /* Not last node. */ + if ($this->getOption('lines', false, true)) { + $img = $this->_images['join']; + $alt = '|-'; + } else { + $img = $this->_images['blank']; + $alt = '   '; + } + $this->_dropline[$this->_nodes[$node_id]['indent']] = true; + } else { + /* Last node. */ + if ($this->getOption('lines', false, true)) { + $img = $this->_images['join_bottom']; + $alt = '`-'; + } else { + $img = $this->_images['blank']; + $alt = '   '; + } + $this->_dropline[$this->_nodes[$node_id]['indent']] = false; + } + } elseif (isset($this->_nodes[$node_id]['children'])) { + /* Node with children. */ + if ($this->_node_pos[$node_id]['pos'] < $this->_node_pos[$node_id]['count']) { + /* Not last node. */ + if (!$this->getOption('lines', false, true)) { + $img = $this->_images['blank']; + $alt = '   '; + } elseif ($this->_static) { + $img = $this->_images['join']; + $alt = '|-'; + } elseif ($this->_nodes[$node_id]['expanded']) { + $img = $this->_images['minus']; + $alt = '-'; + } else { + $img = $this->_images['plus']; + $alt = '+'; + } + $this->_dropline[$this->_nodes[$node_id]['indent']] = true; + } else { + /* Last node. */ + if (!$this->getOption('lines', false, true)) { + $img = $this->_images['blank']; + $alt = '   '; + } elseif ($this->_static) { + $img = $this->_images['join_bottom']; + $alt = '`-'; + } elseif ($this->_nodes[$node_id]['expanded']) { + $img = $this->_images['minus_bottom']; + $alt = '-'; + } else { + $img = $this->_images['plus_bottom']; + $alt = '+'; + } + $this->_dropline[$this->_nodes[$node_id]['indent']] = false; + } + if (!$this->_static) { + $url = Horde_Util::addParameter(Horde::selfUrl(), self::TOGGLE . $this->_instance, $node_id); + $link_start = Horde::link($url); + } + } else { + /* Top level node with no children. */ + if ($this->_static) { + return ''; + } + if ($this->getOption('lines', false, true)) { + $img = $this->_images['null_only']; + $alt = '  '; + } else { + $img = $this->_images['blank']; + $alt = '   '; + } + $this->_dropline[0] = false; + } + + $link_end = ($link_start) ? '' : ''; + + $img = $link_start . '' . $alt . '' + . $link_end; + + return $img; + } + + /** + * Sets the icon for the node. + * + * @param string $node_id The Node ID. + * + * @return string The node icon for the tree line. + */ + protected function _setNodeIcon($node_id) + { + $img_dir = isset($this->_nodes[$node_id]['icondir']) ? $this->_nodes[$node_id]['icondir'] : $this->_img_dir; + if ($img_dir) { + $img_dir .= '/'; + } + + if (isset($this->_nodes[$node_id]['icon'])) { + if (empty($this->_nodes[$node_id]['icon'])) { + return ''; + } + /* Node has a user defined icon. */ + if (isset($this->_nodes[$node_id]['iconopen']) && + $this->_nodes[$node_id]['expanded']) { + $img = $this->_nodes[$node_id]['iconopen']; + } else { + $img = $this->_nodes[$node_id]['icon']; + } + } else { + /* Use standard icon set. */ + if (isset($this->_nodes[$node_id]['children'])) { + /* Node with children. */ + $img = ($this->_nodes[$node_id]['expanded']) ? $this->_images['folder_open'] : $this->_images['folder']; + } else { + /* Leaf node (no children). */ + $img = $this->_images['leaf']; + } + } + + $imgtxt = '_nodes[$node_id]['iconalt'])) { + $imgtxt .= ' alt="' . htmlspecialchars($this->_nodes[$node_id]['iconalt']) . '"'; + } + + return $imgtxt . ' />'; + } + +} diff --git a/framework/Tree/lib/Horde/Tree/Javascript.php b/framework/Tree/lib/Horde/Tree/Javascript.php new file mode 100644 index 000000000..3fe2a7256 --- /dev/null +++ b/framework/Tree/lib/Horde/Tree/Javascript.php @@ -0,0 +1,125 @@ + + * @category Horde + * @package Horde_Tree + */ +class Horde_Tree_Javascript extends Horde_Tree +{ + /** + * Constructor. + * + * @param string $name @see Horde_Tree::__construct(). + * @param array $params @see Horde_Tree::__construct(). + */ + public function __construct($tree_name, $params = array()) + { + parent::__construct($tree_name, $params); + + /* Check for a javascript session state. */ + if ($this->_usesession && + isset($_COOKIE[$this->_instance . '_expanded'])) { + /* Remove "exp" prefix from cookie value. */ + $nodes = explode(',', substr($_COOKIE[$this->_instance . '_expanded'], 3)); + + /* Make sure there are no previous nodes stored in the + * session. */ + $_SESSION['horde_tree'][$this->_instance]['expanded'] = array(); + + /* Save nodes to the session. */ + foreach ($nodes as $id) { + $_SESSION['horde_tree'][$this->_instance]['expanded'][$id] = true; + } + } + + Horde::addScriptFile('prototype.js', 'horde'); + Horde::addScriptFile('hordetree.js', 'horde'); + if (!empty($this->_options['alternate'])) { + Horde::addScriptFile('stripe.js', 'horde'); + } + } + + /** + * Returns the tree. + * + * @param boolean $static If true the tree nodes can't be expanded and + * collapsed and the tree gets rendered expanded. + * + * @return string The HTML code of the rendered tree. + */ + public function getTree($static = false) + { + $this->_static = $static; + + $opts = array( + 'extraColsLeft' => $this->_extra_cols_left, + 'extraColsRight' => $this->_extra_cols_right, + 'header' => $this->_header, + 'options' => $this->_options, + 'target' => $this->_instance, + + 'cookieDomain' => $GLOBALS['conf']['cookie']['domain'], + 'cookiePath' => $GLOBALS['conf']['cookie']['path'], + + 'scrollbar_in_way' => $GLOBALS['browser']->hasQuirk('scrollbar_in_way'), + + 'imgDir' => $this->_img_dir, + 'imgBlank' => $this->_images['blank'], + 'imgFolder' => $this->_images['folder'], + 'imgFolderOpen' => $this->_images['folderopen'], + 'imgLine' => $this->_images['line'], + 'imgJoin' => $this->_images['join'], + 'imgJoinBottom' => $this->_images['join_bottom'], + 'imgPlus' => $this->_images['plus'], + 'imgPlusBottom' => $this->_images['plus_bottom'], + 'imgPlusOnly' => $this->_images['plus_only'], + 'imgMinus' => $this->_images['minus'], + 'imgMinusBottom' => $this->_images['minus_bottom'], + 'imgMinusOnly' => $this->_images['minus_only'], + 'imgNullOnly' => $this->_images['null_only'], + 'imgLeaf' => $this->_images['leaf'], + + 'floatDir' => empty($GLOBALS['nls']['rtl'][$GLOBALS['language']]) ? 'float:left;' : 'float:right' + ); + + Horde::addInlineScript(array( + $this->_instance . ' = new Horde_Tree(' . Horde_Serialize::serialize($opts, Horde_Serialize::JSON, Horde_Nls::getCharset()) . ')', + $this->renderNodeDefinitions() + ), 'dom'); + + return '
'; + } + + /** + * Check the current environment to see if we can render the HTML tree. + * We check for DOM support in the browser. + * + * @return boolean Whether or not this backend will function. + */ + public function isSupported() + { + $browser = Horde_Browser::singleton(); + return $browser->hasFeature('dom'); + } + + /** + * Returns just the JS node definitions as a string. + * + * @return string The Javascript node array definitions. + */ + public function renderNodeDefinitions() + { + $this->_buildIndents($this->_root_nodes); + + return $this->_instance . '.renderTree(' . Horde_Serialize::serialize($this->_nodes, Horde_Serialize::JSON, Horde_Nls::getCharset()) . ',' . Horde_Serialize::serialize($this->_root_nodes, Horde_Serialize::JSON, Horde_Nls::getCharset()) . ',' . ($this->_static ? 'true' : 'false') . ')'; + } + +} diff --git a/framework/Tree/lib/Horde/Tree/Select.php b/framework/Tree/lib/Horde/Tree/Select.php new file mode 100644 index 000000000..3aea8407b --- /dev/null +++ b/framework/Tree/lib/Horde/Tree/Select.php @@ -0,0 +1,105 @@ + tag rendering. + * + * Copyright 2005-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Ben Chavet + * @category Horde + * @package Horde_Tree + */ +class Horde_Tree_Select extends Horde_Tree +{ + /** + * TODO + * + * @var array + */ + protected $_nodes = array(); + + /** + * Constructor. + * + * @param string $name @see Horde_Tree::__construct(). + * @param array $params @see Horde_Tree::__construct(). + */ + public function __construct($tree_name, $params = array()) + { + parent::__construct($tree_name, $params); + $this->_static = true; + } + + /** + * Returns the tree. + * + * @return string The HTML code of the rendered tree. + */ + public function getTree() + { + $this->_buildIndents($this->_root_nodes); + + $tree = ''; + foreach ($this->_root_nodes as $node_id) { + $tree .= $this->_buildTree($node_id); + } + + return $tree; + } + + /** + * Adds additional parameters to a node. + * + * @param string $id The unique node id. + * @param array $params Any other parameters to set. + *
+     * selected - (boolean) Whether this node is selected.
+     * 
+ */ + public function addNodeParams($id, $params = array()) + { + if (!is_array($params)) { + $params = array($params); + } + + $allowed = array('selected'); + + foreach ($params as $param_id => $param_val) { + /* Set only allowed and non-null params. */ + if (in_array($param_id, $allowed) && !is_null($param_val)) { + $this->_nodes[$id][$param_id] = $param_val; + } + } + } + + /** + * Recursive function to walk through the tree array and build the output. + * + * @param string $node_id The Node ID. + * + * @return string The tree rendering. + */ + protected function _buildTree($node_id) + { + $selected = $this->_nodes[$node_id]['selected'] ? ' selected="selected"' : ''; + + $output = ''; + + if (isset($this->_nodes[$node_id]['children']) && + $this->_nodes[$node_id]['expanded']) { + $num_subnodes = count($this->_nodes[$node_id]['children']); + for ($c = 0; $c < $num_subnodes; $c++) { + $child_node_id = $this->_nodes[$node_id]['children'][$c]; + $output .= $this->_buildTree($child_node_id); + } + } + + return $output; + } + +} diff --git a/framework/Tree/package.xml b/framework/Tree/package.xml new file mode 100644 index 000000000..f50927ec8 --- /dev/null +++ b/framework/Tree/package.xml @@ -0,0 +1,107 @@ + + + Tree + pear.horde.org + Horde Tree API + The Horde_Tree:: class provides a tree view of hierarchical information. It allows for expanding/collapsing of branches and maintains their state. It can work together with javascript code to achieve this in DHTML on supported browsers. + + + Chuck Hagenbuch + chuck + chuck@horde.org + yes + + + Jan Schneider + jan + jan@horde.org + yes + + 2009-12-15 + + 0.1.0 + 0.1.0 + + + beta + beta + + GPL + * Initial Horde 4 package. + + + + + + + + + + + + + + + + + + + 5.2.0 + + + 1.7.0 + + + Util + pear.horde.org + + + + + gettext + + + + + + + + + + + + + + 2006-05-08 + + + 0.0.2 + 0.0.2 + + + alpha + alpha + + GPL + Converted to package.xml 2.0 for pear.horde.org + + + + + 0.0.1 + 0.0.1 + + + alpha + alpha + + 2003-07-05 + GPL + Initial release as a PEAR package + + + +