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: * @var array
53: */
54: var $subitems;
55:
56: /**
57: * Determinates if this tree item is collapsed
58: * @var boolean
59: */
60: var $collapsed;
61:
62: /**
63: * ID for this item
64: * @var string
65: */
66: var $id;
67:
68: /**
69: * Name for this item
70: * @var string
71: */
72: var $name;
73:
74: /**
75: * Icon for the collapsed item
76: * @var string
77: */
78: var $collapsed_icon;
79:
80: /**
81: * Icon for the expanded item
82: * @var string
83: */
84: var $expanded_icon;
85:
86: /**
87: * Icon for last node in a branch
88: * @var string
89: */
90: var $lastnode_icon;
91:
92: /**
93: * Contains the level of this item
94: * @var integer
95: */
96: var $level;
97:
98: /**
99: * Contains custom entries
100: * @var array
101: */
102: var $custom;
103:
104: /**
105: * Contains the parent of this item
106: * @var array
107: */
108: var $parent;
109:
110: /**
111: * Constructor Function
112: * Creates a new, independant tree item.
113: * @param string $name The name of that item
114: * @param string $id The unique ID of that item
115: * @param boolean $collapsed Is this item collapsed by default
116: */
117: public function __construct($name = "", $id = "", $collapsed = false) {
118: $this->name = $name;
119: $this->id = $id;
120: $this->collapsed = $collapsed;
121: $this->subitems = array();
122: $this->collapsed_icon = 'images/but_plus.gif';
123: $this->expanded_icon = 'images/but_minus.gif';
124: $this->lastnode_icon = 'images/but_lastnode.gif';
125: $this->parent = -1;
126: }
127:
128: /**
129: * Adds a new subitem to this item.
130: * @param object $item the item to add
131: */
132: function addItem(&$item) {
133: $this->subitems[count($this->subitems)] = &$item;
134: $item->parent = $this->id;
135: }
136:
137: /**
138: * Adds a new subitem to a specific item with an ID.
139: * Traverses all subitems to find the correct item.
140: * @param object $item the item to add
141: * @param string $id the ID to add the item to
142: */
143: function addItemToID($item, $id) {
144: if ($this->id == $id) {
145: $this->subitems[count($this->subitems)] = &$item;
146: $item->parent = $this->id;
147: } else {
148: foreach (array_keys($this->subitems) as $key) {
149: $this->subitems[$key]->addItemToID($item, $id);
150: }
151: }
152: }
153:
154: /**
155: * Retrieves a specific item by its ID. Note that this
156: * function traverses all subitems to find the correct item.
157: * @param string $id the ID to find
158: * @return object The item, or false if nothing was found
159: */
160: function &getItemByID($id) {
161: if ($this->id == $id) {
162: return ($this);
163: } else {
164: foreach (array_keys($this->subitems) as $key) {
165: $retObj = &$this->subitems[$key]->getItemByID($id);
166: if ($retObj->id == $id) {
167: return ($retObj);
168: }
169: }
170: }
171:
172: return false;
173: }
174:
175: /**
176: * Removes an item with a specific ID.
177: * @param string $id the ID to find
178: */
179: function removeItem($id) {
180: foreach (array_keys($this->subitems) as $key) {
181: if ($this->subitems[$key]->id == $id) {
182: unset($this->subitems[$key]);
183: }
184: }
185: }
186:
187: /**
188: * Checks if a specific custom attribute is set
189: * @param string $item the attribute name to find
190: */
191: function isCustomAttributeSet($item) {
192: if (array_key_exists($item, $this->custom)) {
193: return true;
194: } else {
195: foreach (array_keys($this->subitems) as $key) {
196: if ($this->subitems[$key]->isCustomAttributeSet($item)) {
197: return true;
198: }
199: }
200: }
201:
202: return false;
203: }
204:
205: /**
206: * Marks an item as expanded. Traverses all subitems
207: * to find the ID. Note that only the item with $id is
208: * expanded, but not its childs.
209: * @param string $id the ID to expand, or an array with all id's
210: */
211: function markExpanded($id) {
212: if (is_array($id)) {
213: if (in_array($this->id, $id)) {
214: $this->collapsed = false;
215: }
216:
217: foreach (array_keys($this->subitems) as $key) {
218: $this->subitems[$key]->markExpanded($id);
219: }
220: } else {
221: if ($this->id == $id) {
222: $this->collapsed = false;
223: return true;
224: } else {
225: foreach (array_keys($this->subitems) as $key) {
226: $this->subitems[$key]->markExpanded($id);
227: }
228: }
229: }
230: }
231:
232: /**
233: * Expands all items, starting from the $start item.
234: * @param string $start the ID to start expanding from
235: */
236: function expandAll($start = -2) {
237: if ($start != $this->id) {
238: $this->collapsed = false;
239: }
240:
241: foreach (array_keys($this->subitems) as $key) {
242: $this->subitems[$key]->expandAll();
243: }
244: }
245:
246: /**
247: * Collapses all items, starting from the $start item.
248: * @param string $start the ID to start collapsing from
249: */
250: function collapseAll($start = -2) {
251: if ($start != $this->id) {
252: $this->collapsed = true;
253: }
254:
255: foreach (array_keys($this->subitems) as $key) {
256: $this->subitems[$key]->collapseAll();
257: }
258: }
259:
260: /**
261: * Marks an item as collpased. Traverses all subitems
262: * to find the ID. Note that only the item with $id is
263: * collapsed, but not its childs.
264: * @param string $id the ID to collapse
265: */
266: function markCollapsed($id) {
267: if ($this->id == $id) {
268: $this->collapsed = true;
269: } else {
270: foreach (array_keys($this->subitems) as $key) {
271: $this->subitems[$key]->markCollapsed($id);
272: }
273: }
274: }
275:
276: /**
277: * Traverses the tree starting from this item, and returning
278: * all objects as $objects.
279: * @param object $objects all found objects
280: * @param integer $level Level to start on
281: */
282: function traverse(&$objects, $level = 0) {
283: $objects[count($objects)] = &$this;
284: $this->level = $level;
285:
286: if ($this->collapsed == false) {
287: foreach (array_keys($this->subitems) as $key) {
288: $this->subitems[$key]->traverse($objects, $level + 1);
289: }
290: }
291: }
292:
293: /**
294: * Starts iterating at root node and flattens the tree into an array
295: */
296: function getFlatTree($item, &$flat_tree) {
297: foreach ($item->subitems as $curItem) {
298: $curItem->custom['vertline'] = array();
299: $flat_tree[] = $curItem;
300: $this->getFlatTree($curItem, $flat_tree);
301: }
302: }
303:
304: function hasCollapsedNode($item_id) {
305: $parentNodeList = array();
306: $this->getTreeParentNodes($parentNodeList, $item_id);
307: $collapsedList = array();
308: $this->getRealCollapsedList($collapsedList);
309:
310: if (sizeof(array_intersect($parentNodeList, $collapsedList)) > 0) {
311: return TRUE;
312: } else {
313: return FALSE;
314: }
315: }
316:
317: /**
318: * Returns a list of the id of all parent nodes of the given node
319: */
320: function getTreeParentNodes(&$parentNodes, $id) {
321: $curItem = $this->getItemByID($id);
322: $parentId = $curItem->parent;
323:
324: if ($parentId && $parentId != -1) {
325: $parentNodes[] = $parentId;
326: $this->getTreeParentNodes($parentNodes, $parentId);
327: }
328: }
329:
330: /**
331: * Returns a list of the id of all parent nodes of the given node
332: * Not using the nodes of hierarchical tree, but flat tree !!
333: */
334: function getParentNodes(&$parentNodes, $stop_id) {
335: $flat_tree = array();
336: $this->getFlatTree($this, $flat_tree);
337:
338: foreach ($flat_tree as $key => $value) {
339: if ($value->id != $stop_id) {
340: $parentNodes[] = $value->id;
341: } else {
342: break;
343: }
344: }
345: }
346:
347: /**
348: * getCollapsedList thinks if a node has no subnodes it is collapsed
349: * I don't think so
350: */
351: function getRealCollapsedList(&$list) {
352: $this->getCollapsedList($list);
353: $cleared_list = array();
354:
355: // remove all nodes that have no subnodes
356: foreach ($list as $key) {
357: $item = $this->getItemByID($key);
358: if (sizeof($item->subitems) > 0) {
359: $cleared_list[] = $key;
360: }
361: }
362: }
363:
364: /**
365: * Returns all items (as ID array) which are collapsed.
366: * @param array $list Contains the list with all collapsed items
367: */
368: function getCollapsedList(&$list) {
369: if ($this->collapsed == true) {
370: $list[] = $this->id;
371: }
372:
373: foreach (array_keys($this->subitems) as $key) {
374: $this->subitems[$key]->getCollapsedList($list);
375: }
376: }
377:
378: /**
379: * Returns all items (as ID array) which are expanded.
380: * @param array $list Contains the list with all expanded items
381: */
382: function getExpandedList(&$list) {
383: if ($this->collapsed == false && !in_array($this->id, $list)) {
384: $list[] = $this->id;
385: }
386:
387: foreach (array_keys($this->subitems) as $key) {
388: $this->subitems[$key]->getExpandedList($list);
389: }
390: }
391:
392: }
393:
394: ?>