File: Tree.php

package info (click to toggle)
horde3 3.1.3-4etch7
  • links: PTS
  • area: main
  • in suites: etch
  • size: 22,876 kB
  • ctags: 18,071
  • sloc: php: 75,151; xml: 2,979; sql: 1,069; makefile: 79; sh: 64
file content (513 lines) | stat: -rw-r--r-- 15,803 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
<?php

/**
 * Display extra columns to the left of the main tree.
 */
define('HORDE_TREE_EXTRA_LEFT', 0);

/**
 * Display extra columns to the right of the main tree.
 */
define('HORDE_TREE_EXTRA_RIGHT', 1);

/**
 * The preceding text, before the Horde_Tree instance name, used for
 * collapse/expand submissions.
 */
define('HORDE_TREE_TOGGLE', 'ht_toggle_');

/**
 * 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 the Horde_Tree javascript class to achieve this in
 * DHTML on supported browsers.
 *
 * Copyright 2003-2006 Marko Djukic <marko@oblo.com>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * $Horde: framework/Tree/Tree.php,v 1.46.6.11 2006/01/01 21:28:40 jan Exp $
 *
 * @author  Marko Djukic <marko@oblo.com>
 * @package Horde_Tree
 * @since   Horde 3.0
 */
class Horde_Tree {

    /**
     * The name of this instance.
     *
     * @var string
     */
    var $_instance = null;

    /**
     * Hash with header information.
     *
     * @var array
     */
    var $_header = array();

    /**
     * An array containing all the tree nodes.
     *
     * @var array
     */
    var $_nodes = array();

    /**
     * The top-level nodes in the tree.
     *
     * @var array
     */
    var $_root_nodes = array();

    /**
     * Keep count of how many extra columns there are on the left side
     * of the node.
     *
     * @var integer
     */
    var $_extra_cols_left = 0;

    /**
     * Keep count of how many extra columns there are on the right side
     * of the node.
     *
     * @var integer
     */
    var $_extra_cols_right = 0;

    /**
     * Option values.
     *
     * @var array
     */
    var $_options = array('lines' => true);

    /**
     * Stores the sorting criteria temporarily.
     *
     * @var string
     */
    var $_sortCriteria;

    /**
     * Use session to store cached Tree data?
     *
     * @var boolean
     */
    var $_usesession = true;

    /**
     * Should the tree be rendered statically?
     *
     * @var boolean
     */
    var $_static = false;

    /**
     * Attempts to return a reference to a concrete Horde_Tree
     * instance based on $name and $renderer. It will only create a
     * new instance if no Horde_Tree instance with the same parameters
     * currently exists.
     *
     * This method must be invoked as:
     *   $var = &Horde_Tree::singleton($name[, $renderer]);
     *
     * @static
     *
     * @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 Horde_Tree reference, or PEAR_Error on
     *                     error.
     */
    function &singleton($name, $renderer, $params = array())
    {
        static $instances = array();

        $id = $name . ':' . $renderer . ':' . serialize($params);
        if (!isset($instances[$id])) {
            $instances[$id] = &Horde_Tree::factory($name, $renderer, $params);
            if (!$instances[$id]->isSupported()) {
                $renderer = Horde_Tree::fallback($renderer);
                if (is_a($renderer, 'PEAR_Error')) {
                    return $renderer;
                }
                return Horde_Tree::singleton($name, $renderer, $params);
            }
        }

        return $instances[$id];
    }

    /**
     * Attempts to return a concrete Horde_Tree instance.
     *
     * @static
     *
     * @param string $name      The name of this tree instance.
     * @param mixed  $renderer  The type of concrete Horde_Tree subclass to
     *                          return. This is based on the rendering driver
     *                          ($renderer). The code is dynamically included.
     * @param array  $params    Any additional parameters the constructor
     *                          needs.
     *
     * @return Horde_Tree  The newly created concrete Horde_Tree instance, or
     *                     PEAR_Error on error.
     */
    function &factory($name, $renderer, $params = array())
    {
        /* Require the renderer lib. */
        include_once dirname(__FILE__) . '/Tree/' . $renderer . '.php';

        $class = 'Horde_Tree_' . $renderer;
        if (class_exists($class)) {
            $tree = &new $class($name, $params);
        } else {
            $tree = PEAR::raiseError(sprintf(_("\"%s\" tree renderer not found."), $renderer));
        }

        return $tree;
    }

    /**
     * Try to fall back to a simpler renderer.
     *
     * @paran string $renderer  The renderer that we can't handle.
     *
     * @return string | PEAR_Error  The next best renderer, or an error
     *                              if we've run out of renderers.
     */
    function fallback($renderer)
    {
        switch ($renderer) {
        case 'javascript':
            return 'html';

        case 'html':
            return PEAR::raiseError('out of renderers');
        }
    }

    /**
     * Constructor.
     *
     * @param string $name    The name of this tree instance.
     * @param array  $params  Additional parameters.
     * <pre>
     * 'nosession'  --  (boolean) If true, do not store tree data in session.
     * </pre>
     */
    function Horde_Tree($name, $params = array())
    {
        $this->_instance = $name;
        $this->_usesession = empty($params['nosession']);

        /* 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();
        }
    }

    /**
     * Renders the tree.
     *
     * @param boolean $static  If true the tree nodes can't be expanded and
     *                         collapsed and the tree gets rendered expanded.
     */
    function renderTree($static = false)
    {
        echo $this->getTree($static);
    }

    /**
     * Sets an option.
     * Available options:
     * <pre>
     *   alternate    --  Alternate shading in the table? (boolean)
     *   hideHeaders  --  Don't render any HTML for the header row, just use the widths.
     *   class        --  The class to use for the table. (string)
     *   lines        --  Show tree lines? (boolean)
     *   multiline    --  Do the node labels contain linebreaks? (boolean)
     * </pre>
     *
     * @param mixed $option  The option name -or- an array of option name/value
     *                       pairs.
     * @param mixed $val     The option's value.
     */
    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    True or false whether to format the return value
     *                         in html. Defaults to false.
     * @param string $default  A default value to use in case none is set for
     *                         the requested option.
     *
     * @return mixed  The option's value.
     */
    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.
     */
    function addNode($id, $parent, $label, $indent, $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 = Util::getFormData(HORDE_TREE_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, HORDE_TREE_EXTRA_RIGHT, $extra_right);
        }
        if (!empty($extra_left)) {
            $this->addNodeExtra($id, HORDE_TREE_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.
     * <pre>
     * 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
     * target    --  Target for the 'url' link
     * </pre>
     */
    function addNodeParams($id, $params = array())
    {
        if (!is_array($params)) {
            $params = array($params);
        }

        $allowed = array(
            'class', 'icon', 'iconalt', 'icondir', 'iconopen',
            'onclick', 'url', 'urlclass', '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)) {
                $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.
     */
    function addNodeExtra($id, $side, $extra)
    {
        if (!is_array($extra)) {
            $extra = array($extra);
        }

        $col_count = count($extra);

        switch ($side) {
        case HORDE_TREE_EXTRA_LEFT:
            $this->_nodes[$id]['extra'][HORDE_TREE_EXTRA_LEFT] = $extra;
            if ($col_count > $this->_extra_cols_left) {
                $this->_extra_cols_left = $col_count;
            }
            break;

        case HORDE_TREE_EXTRA_RIGHT:
            $this->_nodes[$id]['extra'][HORDE_TREE_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.
     */
    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, '_sort'));
        }

        foreach ($this->_nodes[$id]['children'] as $child) {
            $this->sort($criteria, $child);
        }
    }

    /**
     * Helper method for sort() to compare two tree elements.
     *
     * @access private
     */
    function _sort($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.
     */
    function isExpanded($id)
    {
        if (isset($this->_nodes[$id])) {
            return $this->_nodes[$id]['expanded'];
        }
        return false;
    }

    /**
     * Adds column headers to the tree table.
     *
     * @param array $header  An array containing hashes with header
     *                       information. The following keys are allowed:
     * <pre>
     * html  -- The HTML content of the header cell
     * width -- The width of the header cell
     * align -- The alignment inside the header cell
     * class -- The CSS class of the header cell
     * </pre>
     */
    function setHeader($header)
    {
        $this->_header = $header;
    }

    /**
     * Set the indent level for each node in the tree.
     */
    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);
            }
        }
    }

}