1: <?php
2: /**
3: * XOOPS tree class
4: *
5: * You may not change or alter any portion of this comment or credits
6: * of supporting developers from this source code or any supporting source code
7: * which is considered copyrighted (c) material of the original comment or credit authors.
8: * This program is distributed in the hope that it will be useful,
9: * but WITHOUT ANY WARRANTY; without even the implied warranty of
10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11: *
12: * @copyright (c) 2000-2016 XOOPS Project (www.xoops.org)
13: * @license GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14: * @package kernel
15: * @since 2.0.0
16: * @author Kazumi Ono (http://www.myweb.ne.jp/, http://jp.xoops.org/)
17: */
18:
19: defined('XOOPS_ROOT_PATH') || exit('Restricted access');
20:
21: /**
22: * A tree structures with {@link XoopsObject}s as nodes
23: *
24: * @package kernel
25: * @subpackage core
26: * @author Kazumi Ono <onokazu@xoops.org>
27: */
28: class XoopsObjectTree
29: {
30: /**
31: * @access private
32: */
33: protected $parentId;
34: protected $myId;
35: protected $rootId;
36: protected $tree = array();
37: protected $objects;
38:
39: /**
40: * Constructor
41: *
42: * @param array $objectArr Array of {@link XoopsObject}s
43: * @param string $myId field name of object ID
44: * @param string $parentId field name of parent object ID
45: * @param string $rootId field name of root object ID
46: */
47: public function __construct(&$objectArr, $myId, $parentId, $rootId = null)
48: {
49: $this->objects = $objectArr;
50: $this->myId = $myId;
51: $this->parentId = $parentId;
52: if (isset($rootId)) {
53: $this->rootId = $rootId;
54: }
55: $this->initialize();
56: }
57:
58: /**
59: * Initialize the object
60: *
61: * @access private
62: */
63: protected function initialize()
64: {
65: foreach (array_keys($this->objects) as $i) {
66: $key1 = $this->objects[$i]->getVar($this->myId);
67: $this->tree[$key1]['obj'] = $this->objects[$i];
68: $key2 = $this->objects[$i]->getVar($this->parentId);
69: $this->tree[$key1]['parent'] = $key2;
70: $this->tree[$key2]['child'][] = $key1;
71: if (isset($this->rootId)) {
72: $this->tree[$key1]['root'] = $this->objects[$i]->getVar($this->rootId);
73: }
74: }
75: }
76:
77: /**
78: * Get the tree
79: *
80: * @return array Associative array comprising the tree
81: */
82: public function &getTree()
83: {
84: return $this->tree;
85: }
86:
87: /**
88: * returns an object from the tree specified by its id
89: *
90: * @param string $key ID of the object to retrieve
91: * @return object Object within the tree
92: */
93: public function &getByKey($key)
94: {
95: return $this->tree[$key]['obj'];
96: }
97:
98: /**
99: * returns an array of all the first child object of an object specified by its id
100: *
101: * @param string $key ID of the parent object
102: * @return array Array of children of the parent
103: */
104: public function getFirstChild($key)
105: {
106: $ret = array();
107: if (isset($this->tree[$key]['child'])) {
108: foreach ($this->tree[$key]['child'] as $childKey) {
109: $ret[$childKey] = $this->tree[$childKey]['obj'];
110: }
111: }
112:
113: return $ret;
114: }
115:
116: /**
117: * returns an array of all child objects of an object specified by its id
118: *
119: * @param string $key ID of the parent
120: * @param array $ret (Empty when called from client) Array of children from previous recursions.
121: * @return array Array of child nodes.
122: */
123: public function getAllChild($key, $ret = array())
124: {
125: if (isset($this->tree[$key]['child'])) {
126: foreach ($this->tree[$key]['child'] as $childKey) {
127: $ret[$childKey] = $this->tree[$childKey]['obj'];
128: $children = $this->getAllChild($childKey, $ret);
129: foreach (array_keys($children) as $newKey) {
130: $ret[$newKey] = $children[$newKey];
131: }
132: }
133: }
134:
135: return $ret;
136: }
137:
138: /**
139: * returns an array of all parent objects.
140: * the key of returned array represents how many levels up from the specified object
141: *
142: * @param string $key ID of the child object
143: * @param array $ret (empty when called from outside) Result from previous recursions
144: * @param int $upLevel (empty when called from outside) level of recursion
145: * @return array Array of parent nodes.
146: */
147: public function getAllParent($key, $ret = array(), $upLevel = 1)
148: {
149: if (isset($this->tree[$key]['parent']) && isset($this->tree[$this->tree[$key]['parent']]['obj'])) {
150: $ret[$upLevel] = $this->tree[$this->tree[$key]['parent']]['obj'];
151: $parents = $this->getAllParent($this->tree[$key]['parent'], $ret, $upLevel + 1);
152: foreach (array_keys($parents) as $newKey) {
153: $ret[$newKey] = $parents[$newKey];
154: }
155: }
156:
157: return $ret;
158: }
159:
160: /**
161: * Make options for a select box from
162: *
163: * @param string $fieldName Name of the member variable from the
164: * node objects that should be used as the title for the options.
165: * @param string $selected Value to display as selected
166: * @param int $key ID of the object to display as the root of select options
167: * @param string $ret (reference to a string when called from outside) Result from previous recursions
168: * @param string $prefix_orig String to indent items at deeper levels
169: * @param string $prefix_curr String to indent the current item
170: *
171: * @return void
172: * @deprecated since 2.5.9, please use makeSelectElement() functionality
173: */
174: protected function makeSelBoxOptions($fieldName, $selected, $key, &$ret, $prefix_orig, $prefix_curr = '')
175: {
176: if ($key > 0) {
177: $value = $this->tree[$key]['obj']->getVar($this->myId);
178: $ret .= '<option value="' . $value . '"';
179: if ($value == $selected) {
180: $ret .= ' selected';
181: }
182: $ret .= '>' . $prefix_curr . $this->tree[$key]['obj']->getVar($fieldName) . '</option>';
183: $prefix_curr .= $prefix_orig;
184: }
185: if (isset($this->tree[$key]['child']) && !empty($this->tree[$key]['child'])) {
186: foreach ($this->tree[$key]['child'] as $childKey) {
187: $this->makeSelBoxOptions($fieldName, $selected, $childKey, $ret, $prefix_orig, $prefix_curr);
188: }
189: }
190: }
191:
192: /**
193: * Make a select box with options from the tree
194: *
195: * @param string $name Name of the select box
196: * @param string $fieldName Name of the member variable from the
197: * node objects that should be used as the title for the options.
198: * @param string $prefix String to indent deeper levels
199: * @param string $selected Value to display as selected
200: * @param bool $addEmptyOption Set TRUE to add an empty option with value "0" at the top of the hierarchy
201: * @param integer $key ID of the object to display as the root of select options
202: * @param string $extra
203: * @return string HTML select box
204: *
205: * @deprecated since 2.5.9, please use makeSelectElement()
206: */
207: public function makeSelBox(
208: $name,
209: $fieldName,
210: $prefix = '-',
211: $selected = '',
212: $addEmptyOption = false,
213: $key = 0,
214: $extra = ''
215: ) {
216: $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
217: $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . " is deprecated since 2.5.9, please use makeSelectElement(), called from {$trace[0]['file']} line {$trace[0]['line']}");
218:
219: $ret = '<select name="' . $name . '" id="' . $name . '" ' . $extra . '>';
220: if (false !== (bool)$addEmptyOption) {
221: $ret .= '<option value="0"></option>';
222: }
223: $this->makeSelBoxOptions($fieldName, $selected, $key, $ret, $prefix);
224:
225: return $ret . '</select>';
226: }
227:
228: /**
229: * Make a select box with options from the tree
230: *
231: * @param string $name Name of the select box
232: * @param string $fieldName Name of the member variable from the
233: * node objects that should be used as the title for the options.
234: * @param string $prefix String to indent deeper levels
235: * @param string $selected Value to display as selected
236: * @param bool $addEmptyOption Set TRUE to add an empty option with value "0" at the top of the hierarchy
237: * @param integer $key ID of the object to display as the root of select options
238: * @param string $extra extra content to add to the element
239: * @param string $caption optional caption for form element
240: *
241: * @return XoopsFormSelect form element
242: */
243: public function makeSelectElement(
244: $name,
245: $fieldName,
246: $prefix = '-',
247: $selected = '',
248: $addEmptyOption = false,
249: $key = 0,
250: $extra = '',
251: $caption = ''
252: ) {
253: xoops_load('xoopsformselect');
254: $element = new XoopsFormSelect($caption, $name, $selected);
255: $element->setExtra($extra);
256:
257: if (false !== (bool)$addEmptyOption) {
258: $element->addOption('0', ' ');
259: }
260: $this->addSelectOptions($element, $fieldName, $key, $prefix);
261:
262: return $element;
263: }
264:
265: /**
266: * Make options for a select box from
267: *
268: * @param XoopsFormSelect $element form element to receive tree values as options
269: * @param string $fieldName Name of the member variable from the node objects that
270: * should be used as the title for the options.
271: * @param int $key ID of the object to display as the root of select options
272: * @param string $prefix_orig String to indent items at deeper levels
273: * @param string $prefix_curr String to indent the current item
274: *
275: * @return void
276: * @access private
277: */
278: protected function addSelectOptions($element, $fieldName, $key, $prefix_orig, $prefix_curr = '')
279: {
280: if ($key > 0) {
281: $value = $this->tree[$key]['obj']->getVar($this->myId);
282: $name = $prefix_curr . $this->tree[$key]['obj']->getVar($fieldName);
283: $element->addOption($value, $name);
284: $prefix_curr .= $prefix_orig;
285: }
286: if (isset($this->tree[$key]['child']) && !empty($this->tree[$key]['child'])) {
287: foreach ($this->tree[$key]['child'] as $childKey) {
288: $this->addSelectOptions($element, $fieldName, $childKey, $prefix_orig, $prefix_curr);
289: }
290: }
291: }
292:
293: /**
294: * Magic __get method
295: *
296: * Some modules did not respect the leading underscore is private convention and broke
297: * when code was modernized. This will keep them running for now.
298: *
299: * @param string $name unknown variable name requested
300: * currently, only '_tree' is supported
301: *
302: * @return mixed value
303: */
304: public function __get($name)
305: {
306: $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
307: if ($name === '_tree') {
308: $GLOBALS['xoopsLogger']->addDeprecated("XoopsObjectTree::\$_tree is deprecated, accessed from {$trace[0]['file']} line {$trace[0]['line']}");
309: return $this->tree;
310: }
311: $message = 'Undefined property: XoopsObjectTree::$' . $name . " in {$trace[0]['file']} line {$trace[0]['line']}";
312: $GLOBALS['xoopsLogger']->addExtra(get_called_class(), $message);
313:
314: return null;
315: }
316: }
317: