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 (http://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 19defined('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 */ 28class 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 trigger_error("makeSelBox() is deprecated since 2.5.9, please use makeSelectElement(), accessed from {$trace[0]['file']} line {$trace[0]['line']},"); 218 $ret = '<select name="' . $name . '" id="' . $name . '" ' . $extra . '>'; 219 if (false !== (bool)$addEmptyOption) { 220 $ret .= '<option value="0"></option>'; 221 } 222 $this->makeSelBoxOptions($fieldName, $selected, $key, $ret, $prefix); 223 224 return $ret . '</select>'; 225 } 226 227 /** 228 * Make a select box with options from the tree 229 * 230 * @param string $name Name of the select box 231 * @param string $fieldName Name of the member variable from the 232 * node objects that should be used as the title for the options. 233 * @param string $prefix String to indent deeper levels 234 * @param string $selected Value to display as selected 235 * @param bool $addEmptyOption Set TRUE to add an empty option with value "0" at the top of the hierarchy 236 * @param integer $key ID of the object to display as the root of select options 237 * @param string $extra extra content to add to the element 238 * @param string $caption optional caption for form element 239 * 240 * @return XoopsFormSelect form element 241 */ 242 public function makeSelectElement( 243 $name, 244 $fieldName, 245 $prefix = '-', 246 $selected = '', 247 $addEmptyOption = false, 248 $key = 0, 249 $extra = '', 250 $caption = '' 251 ) { 252 xoops_load('xoopsformselect'); 253 $element = new XoopsFormSelect($caption, $name, $selected); 254 $element->setExtra($extra); 255 256 if (false !== (bool)$addEmptyOption) { 257 $element->addOption('0', ' '); 258 } 259 $this->addSelectOptions($element, $fieldName, $key, $prefix); 260 261 return $element; 262 } 263 264 /** 265 * Make options for a select box from 266 * 267 * @param XoopsFormSelect $element form element to receive tree values as options 268 * @param string $fieldName Name of the member variable from the node objects that 269 * should be used as the title for the options. 270 * @param int $key ID of the object to display as the root of select options 271 * @param string $prefix_orig String to indent items at deeper levels 272 * @param string $prefix_curr String to indent the current item 273 * 274 * @return void 275 * @access private 276 */ 277 protected function addSelectOptions($element, $fieldName, $key, $prefix_orig, $prefix_curr = '') 278 { 279 if ($key > 0) { 280 $value = $this->tree[$key]['obj']->getVar($this->myId); 281 $name = $prefix_curr . $this->tree[$key]['obj']->getVar($fieldName); 282 $element->addOption($value, $name); 283 $prefix_curr .= $prefix_orig; 284 } 285 if (isset($this->tree[$key]['child']) && !empty($this->tree[$key]['child'])) { 286 foreach ($this->tree[$key]['child'] as $childKey) { 287 $this->addSelectOptions($element, $fieldName, $childKey, $prefix_orig, $prefix_curr); 288 } 289 } 290 } 291 292 /** 293 * Magic __get method 294 * 295 * Some modules did not respect the leading underscore is private convention and broke 296 * when code was modernized. This will keep them running for now. 297 * 298 * @param string $name unknown variable name requested 299 * currently only '_tree' is supported 300 * 301 * @return mixed value 302 */ 303 public function __get($name) 304 { 305 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); 306 if ($name === '_tree') { 307 trigger_error("XoopsObjectTree::\$_tree is deprecated, accessed from {$trace[0]['file']} line {$trace[0]['line']},"); 308 return $this->tree; 309 } 310 trigger_error( 311 'Undefined property: XoopsObjectTree::$' . $name . 312 " in {$trace[0]['file']} line {$trace[0]['line']}, ", 313 E_USER_NOTICE); 314 return null; 315 } 316} 317