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 boolean
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 integer
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 The name of that item
125: * @param string $id The unique ID of that item
126: * @param bool $collapsed Is this item collapsed by default
127: */
128: public function __construct($name = "", $id = "", $collapsed = false) {
129: $this->name = $name;
130: $this->id = $id;
131: $this->collapsed = $collapsed;
132: $this->subitems = array();
133: $this->collapsed_icon = 'images/but_plus.gif';
134: $this->expanded_icon = 'images/but_minus.gif';
135: $this->lastnode_icon = 'images/but_lastnode.gif';
136: $this->parent = -1;
137: }
138:
139: /**
140: * Adds a new subitem to this item.
141: *
142: * @param object $item the item to add
143: */
144: function addItem(&$item) {
145: $this->subitems[count($this->subitems)] = &$item;
146: $item->parent = $this->id;
147: }
148:
149: /**
150: * Adds a new subitem to a specific item with an ID.
151: * Traverses all subitems to find the correct item.
152: *
153: * @param object $item the item to add
154: * @param string $id the ID to add the item to
155: */
156: function addItemToID($item, $id) {
157: if ($this->id == $id) {
158: $this->subitems[count($this->subitems)] = &$item;
159: $item->parent = $this->id;
160: } else {
161: foreach (array_keys($this->subitems) as $key) {
162: $this->subitems[$key]->addItemToID($item, $id);
163: }
164: }
165: }
166:
167: /**
168: * Retrieves a specific item by its ID.
169: * Note that this
170: * function traverses all subitems to find the correct item.
171: *
172: * @param string $id the ID to find
173: * @return object The item, or false if nothing was found
174: */
175: function &getItemByID($id) {
176: if ($this->id == $id) {
177: return ($this);
178: } else {
179: foreach (array_keys($this->subitems) as $key) {
180: $retObj = &$this->subitems[$key]->getItemByID($id);
181: if ($retObj->id == $id) {
182: return ($retObj);
183: }
184: }
185: }
186:
187: return false;
188: }
189:
190: /**
191: * Removes an item with a specific ID.
192: *
193: * @param string $id the ID to find
194: */
195: function removeItem($id) {
196: foreach (array_keys($this->subitems) as $key) {
197: if ($this->subitems[$key]->id == $id) {
198: unset($this->subitems[$key]);
199: }
200: }
201: }
202:
203: /**
204: * Checks if a specific custom attribute is set
205: *
206: * @param string $item the attribute name to find
207: * @return boolean
208: */
209: function isCustomAttributeSet($item) {
210: if (array_key_exists($item, $this->custom)) {
211: return true;
212: } else {
213: foreach (array_keys($this->subitems) as $key) {
214: if ($this->subitems[$key]->isCustomAttributeSet($item)) {
215: return true;
216: }
217: }
218: }
219:
220: return false;
221: }
222:
223: /**
224: * Marks an item as expanded.
225: * Traverses all subitems to find the ID. Note that only the item with $id
226: * is expanded, but not its childs.
227: *
228: * @param string $id the ID to expand, or an array with all id's
229: * @return boolean
230: */
231: function markExpanded($id) {
232: if (is_array($id)) {
233: if (in_array($this->id, $id)) {
234: $this->collapsed = false;
235: }
236:
237: foreach (array_keys($this->subitems) as $key) {
238: $this->subitems[$key]->markExpanded($id);
239: }
240: } else {
241: if ($this->id == $id) {
242: $this->collapsed = false;
243: return true;
244: } else {
245: foreach (array_keys($this->subitems) as $key) {
246: $this->subitems[$key]->markExpanded($id);
247: }
248: }
249: }
250: }
251:
252: /**
253: * Expands all items, starting from the $start item.
254: *
255: * @param string $start the ID to start expanding from
256: */
257: function expandAll($start = -2) {
258: if ($start != $this->id) {
259: $this->collapsed = false;
260: }
261:
262: foreach (array_keys($this->subitems) as $key) {
263: $this->subitems[$key]->expandAll();
264: }
265: }
266:
267: /**
268: * Collapses all items, starting from the $start item.
269: *
270: * @param string $start the ID to start collapsing from
271: */
272: function collapseAll($start = -2) {
273: if ($start != $this->id) {
274: $this->collapsed = true;
275: }
276:
277: foreach (array_keys($this->subitems) as $key) {
278: $this->subitems[$key]->collapseAll();
279: }
280: }
281:
282: /**
283: * Marks an item as collpased.
284: * Traverses all subitems
285: * to find the ID. Note that only the item with $id is
286: * collapsed, but not its childs.
287: *
288: * @param string $id the ID to collapse
289: */
290: function markCollapsed($id) {
291: if ($this->id == $id) {
292: $this->collapsed = true;
293: } else {
294: foreach (array_keys($this->subitems) as $key) {
295: $this->subitems[$key]->markCollapsed($id);
296: }
297: }
298: }
299:
300: /**
301: * Traverses the tree starting from this item, and returning
302: * all objects as $objects.
303: *
304: * @param object $objects all found objects
305: * @param int $level Level to start on
306: */
307: function traverse(&$objects, $level = 0) {
308: $objects[count($objects)] = &$this;
309: $this->level = $level;
310:
311: if ($this->collapsed == false) {
312: foreach (array_keys($this->subitems) as $key) {
313: $this->subitems[$key]->traverse($objects, $level + 1);
314: }
315: }
316: }
317:
318: /**
319: * Starts iterating at root node and flattens the tree into an array
320: *
321: * @param unknown_type $item
322: * @param unknown_type $flat_tree
323: */
324: function getFlatTree($item, &$flat_tree) {
325: foreach ($item->subitems as $curItem) {
326: $curItem->custom['vertline'] = array();
327: $flat_tree[] = $curItem;
328: $this->getFlatTree($curItem, $flat_tree);
329: }
330: }
331:
332: /**
333: *
334: * @param unknown_type $item_id
335: * @return boolean
336: */
337: function hasCollapsedNode($item_id) {
338: $parentNodeList = array();
339: $this->getTreeParentNodes($parentNodeList, $item_id);
340: $collapsedList = array();
341: $this->getRealCollapsedList($collapsedList);
342:
343: if (sizeof(array_intersect($parentNodeList, $collapsedList)) > 0) {
344: return TRUE;
345: } else {
346: return FALSE;
347: }
348: }
349:
350: /**
351: * Returns a list of the id of all parent nodes of the given node
352: *
353: * @param unknown_type $parentNodes
354: * @param unknown_type $id
355: */
356: function getTreeParentNodes(&$parentNodes, $id) {
357: $curItem = $this->getItemByID($id);
358: $parentId = $curItem->parent;
359:
360: if ($parentId && $parentId != -1) {
361: $parentNodes[] = $parentId;
362: $this->getTreeParentNodes($parentNodes, $parentId);
363: }
364: }
365:
366: /**
367: * Returns a list of the id of all parent nodes of the given node
368: * Not using the nodes of hierarchical tree, but flat tree !!
369: *
370: * @param unknown_type $parentNodes
371: * @param unknown_type $stop_id
372: */
373: function getParentNodes(&$parentNodes, $stop_id) {
374: $flat_tree = array();
375: $this->getFlatTree($this, $flat_tree);
376:
377: foreach ($flat_tree as $key => $value) {
378: if ($value->id != $stop_id) {
379: $parentNodes[] = $value->id;
380: } else {
381: break;
382: }
383: }
384: }
385:
386: /**
387: * getCollapsedList thinks if a node has no subnodes it is collapsed
388: * I don't think so
389: *
390: * @param unknown_type $list
391: */
392: function getRealCollapsedList(&$list) {
393: $this->getCollapsedList($list);
394: $cleared_list = array();
395:
396: // remove all nodes that have no subnodes
397: foreach ($list as $key) {
398: $item = $this->getItemByID($key);
399: if (sizeof($item->subitems) > 0) {
400: $cleared_list[] = $key;
401: }
402: }
403: }
404:
405: /**
406: * Returns all items (as ID array) which are collapsed.
407: *
408: * @param array $list Contains the list with all collapsed items
409: */
410: function getCollapsedList(&$list) {
411: if ($this->collapsed == true) {
412: $list[] = $this->id;
413: }
414:
415: foreach (array_keys($this->subitems) as $key) {
416: $this->subitems[$key]->getCollapsedList($list);
417: }
418: }
419:
420: /**
421: * Returns all items (as ID array) which are expanded.
422: *
423: * @param array $list Contains the list with all expanded items
424: */
425: function getExpandedList(&$list) {
426: if ($this->collapsed == false && !in_array($this->id, $list)) {
427: $list[] = $this->id;
428: }
429:
430: foreach (array_keys($this->subitems) as $key) {
431: $this->subitems[$key]->getExpandedList($list);
432: }
433: }
434: }
435:
436: ?>