1: <?php
2: /**
3: * This file contains the tree item storage class.
4: *
5: * @package Core
6: * @subpackage Backend
7: * @version SVN Revision $Rev:$
8: *
9: * @author Timo Hummel
10: * @copyright four for business AG <www.4fb.de>
11: * @license http://www.contenido.org/license/LIZENZ.txt
12: * @link http://www.4fb.de
13: * @link http://www.contenido.org
14: */
15:
16: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
17:
18: /**
19: * Class TreeItem
20: * Class to create tree-based items
21: *
22: * The treeitem class allows you to logically store
23: * tree-based structures.
24: *
25: * Example:
26: *
27: * Let's have a tree with 3 nodes. It's important that
28: * we always have a "root" key.
29: *
30: * $root = new TreeItem("root", 1);
31: * $item1 = new TreeItem("node1",2);
32: * $item2 = new TreeItem("node2",3);
33: * $item3 = new TreeItem("node3",4);
34: *
35: * $root->addItem($item1);
36: * $root->addItem($item2);
37: * $root->addItem($item3);
38: *
39: * This represents the tree we described above.
40: *
41: * If you know the ID of the item you want to add
42: * to, there's no need to have a specific item handy,
43: * but rather you can use the "addItemToID" function.
44: *
45: * @package Core
46: * @subpackage Backend
47: */
48: class TreeItem {
49:
50: /**
51: * Sub Items for this tree item
52: *
53: * @var array
54: */
55: var $subitems;
56:
57: /**
58: * Determinates if this tree item is collapsed
59: *
60: * @var bool
61: */
62: var $collapsed;
63:
64: /**
65: * ID for this item
66: *
67: * @var string
68: */
69: var $id;
70:
71: /**
72: * Name for this item
73: *
74: * @var string
75: */
76: var $name;
77:
78: /**
79: * Icon for the collapsed item
80: *
81: * @var string
82: */
83: var $collapsed_icon;
84:
85: /**
86: * Icon for the expanded item
87: *
88: * @var string
89: */
90: var $expanded_icon;
91:
92: /**
93: * Icon for last node in a branch
94: *
95: * @var string
96: */
97: var $lastnode_icon;
98:
99: /**
100: * Contains the level of this item
101: *
102: * @var int
103: */
104: var $level;
105:
106: /**
107: * Contains custom entries
108: *
109: * @var array
110: */
111: var $custom;
112:
113: /**
114: * Contains the parent of this item
115: *
116: * @var array
117: */
118: var $parent;
119:
120: /**
121: * Constructor Function
122: * Creates a new, independant tree item.
123: *
124: * @param string $name [optional]
125: * The name of that item
126: * @param string $id [optional]
127: * The unique ID of that item
128: * @param bool $collapsed [optional]
129: * Is this item collapsed by default
130: */
131: public function __construct($name = "", $id = "", $collapsed = false) {
132: $this->name = $name;
133: $this->id = $id;
134: $this->collapsed = $collapsed;
135: $this->subitems = array();
136: $this->collapsed_icon = 'images/but_plus.gif';
137: $this->expanded_icon = 'images/but_minus.gif';
138: $this->lastnode_icon = 'images/but_lastnode.gif';
139: $this->parent = -1;
140: }
141:
142: /**
143: * Adds a new subitem to this item.
144: *
145: * @param object $item
146: * the item to add
147: */
148: function addItem(&$item) {
149: $this->subitems[count($this->subitems)] = &$item;
150: $item->parent = $this->id;
151: }
152:
153: /**
154: * Adds a new subitem to a specific item with an ID.
155: * Traverses all subitems to find the correct item.
156: *
157: * @param object $item
158: * the item to add
159: * @param string $id
160: * the ID to add the item to
161: */
162: function addItemToID($item, $id) {
163: if ($this->id == $id) {
164: $this->subitems[count($this->subitems)] = &$item;
165: $item->parent = $this->id;
166: } else {
167: foreach (array_keys($this->subitems) as $key) {
168: $this->subitems[$key]->addItemToID($item, $id);
169: }
170: }
171: }
172:
173: /**
174: * Retrieves a specific item by its ID.
175: * Note that this
176: * function traverses all subitems to find the correct item.
177: *
178: * @param string $id
179: * the ID to find
180: * @return object
181: * The item, or false if nothing was found
182: */
183: function &getItemByID($id) {
184: if ($this->id == $id) {
185: return $this;
186: } else {
187: foreach (array_keys($this->subitems) as $key) {
188: $retObj = &$this->subitems[$key]->getItemByID($id);
189: if ($retObj->id == $id) {
190: return $retObj;
191: }
192: }
193: }
194:
195: return false;
196: }
197:
198: /**
199: * Removes an item with a specific ID.
200: *
201: * @param string $id
202: * the ID to find
203: */
204: function removeItem($id) {
205: foreach (array_keys($this->subitems) as $key) {
206: if ($this->subitems[$key]->id == $id) {
207: unset($this->subitems[$key]);
208: }
209: }
210: }
211:
212: /**
213: * Checks if a specific custom attribute is set
214: *
215: * @param string $item
216: * the attribute name to find
217: * @return bool
218: */
219: function isCustomAttributeSet($item) {
220: if (array_key_exists($item, $this->custom)) {
221: return true;
222: } else {
223: foreach (array_keys($this->subitems) as $key) {
224: if ($this->subitems[$key]->isCustomAttributeSet($item)) {
225: return true;
226: }
227: }
228: }
229:
230: return false;
231: }
232:
233: /**
234: * Marks an item as expanded.
235: * Traverses all subitems to find the ID. Note that only the item with $id
236: * is expanded, but not its childs.
237: *
238: * @param string $id
239: * the ID to expand, or an array with all id's
240: * @return bool
241: */
242: function markExpanded($id) {
243: if (is_array($id)) {
244: if (in_array($this->id, $id)) {
245: $this->collapsed = false;
246: }
247:
248: foreach (array_keys($this->subitems) as $key) {
249: $this->subitems[$key]->markExpanded($id);
250: }
251: } else {
252: if ($this->id == $id) {
253: $this->collapsed = false;
254: return true;
255: } else {
256: foreach (array_keys($this->subitems) as $key) {
257: $this->subitems[$key]->markExpanded($id);
258: }
259: }
260: }
261: }
262:
263: /**
264: * Expands all items, starting from the $start item.
265: *
266: * @param string $start [optional]
267: * the ID to start expanding from
268: */
269: function expandAll($start = -2) {
270: if ($start != $this->id) {
271: $this->collapsed = false;
272: }
273:
274: foreach (array_keys($this->subitems) as $key) {
275: $this->subitems[$key]->expandAll();
276: }
277: }
278:
279: /**
280: * Collapses all items, starting from the $start item.
281: *
282: * @param string $start [optional]
283: * the ID to start collapsing from
284: */
285: function collapseAll($start = -2) {
286: if ($start != $this->id) {
287: $this->collapsed = true;
288: }
289:
290: foreach (array_keys($this->subitems) as $key) {
291: $this->subitems[$key]->collapseAll();
292: }
293: }
294:
295: /**
296: * Marks an item as collpased.
297: * Traverses all subitems
298: * to find the ID. Note that only the item with $id is
299: * collapsed, but not its childs.
300: *
301: * @param string $id
302: * the ID to collapse
303: */
304: function markCollapsed($id) {
305: if ($this->id == $id) {
306: $this->collapsed = true;
307: } else {
308: foreach (array_keys($this->subitems) as $key) {
309: $this->subitems[$key]->markCollapsed($id);
310: }
311: }
312: }
313:
314: /**
315: * Traverses the tree starting from this item, and returning
316: * all objects as $objects.
317: *
318: * @param object $objects
319: * all found objects
320: * @param int $level [optional]
321: * Level to start on
322: */
323: function traverse(&$objects, $level = 0) {
324: $objects[count($objects)] = &$this;
325: $this->level = $level;
326:
327: if ($this->collapsed == false) {
328: foreach (array_keys($this->subitems) as $key) {
329: $this->subitems[$key]->traverse($objects, $level + 1);
330: }
331: }
332: }
333:
334: /**
335: * Starts iterating at root node and flattens the tree into an array
336: *
337: * @param unknown_type $item
338: * @param unknown_type $flat_tree
339: */
340: function getFlatTree($item, &$flat_tree) {
341: foreach ($item->subitems as $curItem) {
342: $curItem->custom['vertline'] = array();
343: $flat_tree[] = $curItem;
344: $this->getFlatTree($curItem, $flat_tree);
345: }
346: }
347:
348: /**
349: *
350: * @param unknown_type $item_id
351: * @return bool
352: */
353: function hasCollapsedNode($item_id) {
354: $parentNodeList = array();
355: $this->getTreeParentNodes($parentNodeList, $item_id);
356: $collapsedList = array();
357: $this->getRealCollapsedList($collapsedList);
358:
359: if (sizeof(array_intersect($parentNodeList, $collapsedList)) > 0) {
360: return TRUE;
361: } else {
362: return FALSE;
363: }
364: }
365:
366: /**
367: * Returns a list of the id of all parent nodes of the given node
368: *
369: * @param unknown_type $parentNodes
370: * @param unknown_type $id
371: */
372: function getTreeParentNodes(&$parentNodes, $id) {
373: $curItem = $this->getItemByID($id);
374: $parentId = $curItem->parent;
375:
376: if ($parentId && $parentId != -1) {
377: $parentNodes[] = $parentId;
378: $this->getTreeParentNodes($parentNodes, $parentId);
379: }
380: }
381:
382: /**
383: * Returns a list of the id of all parent nodes of the given node
384: * Not using the nodes of hierarchical tree, but flat tree !!
385: *
386: * @param unknown_type $parentNodes
387: * @param unknown_type $stop_id
388: */
389: function getParentNodes(&$parentNodes, $stop_id) {
390: $flat_tree = array();
391: $this->getFlatTree($this, $flat_tree);
392:
393: foreach ($flat_tree as $key => $value) {
394: if ($value->id != $stop_id) {
395: $parentNodes[] = $value->id;
396: } else {
397: break;
398: }
399: }
400: }
401:
402: /**
403: * getCollapsedList thinks if a node has no subnodes it is collapsed
404: * I don't think so
405: *
406: * @param unknown_type $list
407: */
408: function getRealCollapsedList(&$list) {
409: $this->getCollapsedList($list);
410: $cleared_list = array();
411:
412: // remove all nodes that have no subnodes
413: foreach ($list as $key) {
414: $item = $this->getItemByID($key);
415: if (sizeof($item->subitems) > 0) {
416: $cleared_list[] = $key;
417: }
418: }
419: }
420:
421: /**
422: * Returns all items (as ID array) which are collapsed.
423: *
424: * @param array $list
425: * Contains the list with all collapsed items
426: */
427: function getCollapsedList(&$list) {
428: if ($this->collapsed == true) {
429: $list[] = $this->id;
430: }
431:
432: foreach (array_keys($this->subitems) as $key) {
433: $this->subitems[$key]->getCollapsedList($list);
434: }
435: }
436:
437: /**
438: * Returns all items (as ID array) which are expanded.
439: *
440: * @param array $list
441: * Contains the list with all expanded items
442: */
443: function getExpandedList(&$list) {
444: if ($this->collapsed == false && !in_array($this->id, $list)) {
445: $list[] = $this->id;
446: }
447:
448: foreach (array_keys($this->subitems) as $key) {
449: $this->subitems[$key]->getExpandedList($list);
450: }
451: }
452: }
453: