[ Index ] |
|
Code source de Horde 3.1.3 |
1 <?php 2 3 /** 4 * List every object in an array, similar to PEAR/html/menu.php. 5 */ 6 define('DATATREE_FORMAT_TREE', 1); 7 8 /** 9 * Get a full list - an array of keys. 10 */ 11 define('DATATREE_FORMAT_FLAT', 2); 12 13 /** 14 * The root element (top-level parent) of each DataTree group. 15 */ 16 define('DATATREE_ROOT', -1); 17 18 /** 19 * Build a normal select query. 20 */ 21 define('DATATREE_BUILD_SELECT', 0); 22 23 /** 24 * Build a count only query. 25 */ 26 define('DATATREE_BUILD_COUNT', 1); 27 28 /** 29 * The DataTree:: class provides a common abstracted interface into the 30 * various backends for the Horde DataTree system. 31 * 32 * A piece of data is just a title that is saved in the page for the null 33 * driver or can be saved in a database to be accessed from everywhere. Every 34 * stored object must have a different name (inside each groupid). 35 * 36 * Required values for $params:<pre> 37 * 'group' -- Define each group of objects we want to build.</pre> 38 * 39 * $Horde: framework/DataTree/DataTree.php,v 1.151.2.18 2006/07/13 08:58:44 jan Exp $ 40 * 41 * Copyright 1999-2003 Stephane Huther <shuther@bigfoot.com> 42 * Copyright 2001-2006 Chuck Hagenbuch <chuck@horde.org> 43 * 44 * See the enclosed file COPYING for license information (LGPL). If you did 45 * not receive this file, see http://www.fsf.org/copyleft/lgpl.html. 46 * 47 * @author Stephane Huther <shuther@bigfoot.com> 48 * @author Chuck Hagenbuch <chuck@horde.org> 49 * @since Horde 2.1 50 * @package Horde_DataTree 51 */ 52 class DataTree { 53 54 /** 55 * Array of all data: indexed by id. The format is: array(id => 56 * 'name' => name, 'parent' => parent). 57 * 58 * @var array 59 */ 60 var $_data = array(); 61 62 /** 63 * A hash that can be used to map a full object name 64 * (parent:child:object) to that object's unique ID. 65 * 66 * @var array 67 */ 68 var $_nameMap = array(); 69 70 /** 71 * Actual attribute sorting hash. 72 * 73 * @var array 74 */ 75 var $_sortHash = null; 76 77 /** 78 * Hash containing connection parameters. 79 * 80 * @var array 81 */ 82 var $_params = array(); 83 84 /** 85 * Constructor. 86 * 87 * @param array $params A hash containing any additional configuration or 88 * connection parameters a subclass might need. 89 * We always need 'group', a string that defines the 90 * prefix for each set of hierarchical data. 91 */ 92 function DataTree($params = null) 93 { 94 $this->_params = $params; 95 } 96 97 /** 98 * Returns a parameter of this DataTree instance. 99 * 100 * @param string $param The parameter to return. 101 * 102 * @return mixed The parameter's value or null if it doesn't exist. 103 */ 104 function getParam($param) 105 { 106 return isset($this->_params[$param]) ? $this->_params[$param] : null; 107 } 108 109 /** 110 * Removes an object. 111 * 112 * @param string $object The object to remove. 113 * @param $force Force removal of every child object? 114 */ 115 function remove($object, $force = false) 116 { 117 if (is_a($object, 'DataTreeObject')) { 118 $object = $object->getName(); 119 } 120 121 if (!$this->exists($object)) { 122 return PEAR::raiseError($object . ' does not exist'); 123 } 124 125 $children = $this->getNumberOfChildren($object); 126 if ($children) { 127 /* TODO: remove children if $force == true */ 128 return PEAR::raiseError(sprintf(_("Cannot remove, %d children exist."), count($children))); 129 } 130 131 $id = $this->getId($object); 132 $pid = $this->getParent($object); 133 $order = $this->_data[$id]['order']; 134 unset($this->_data[$id]); 135 unset($this->_nameMap[$id]); 136 137 // Shift down the order positions. 138 $this->_reorder($pid, $order); 139 140 return $id; 141 } 142 143 /** 144 * Removes all DataTree objects owned by a certain user. 145 * 146 * @param string $user A user name. 147 */ 148 function removeUserData($user) 149 { 150 return PEAR::raiseError('not supported'); 151 } 152 153 /** 154 * Move an object to a new parent. 155 * 156 * @param mixed $object The object to move. 157 * @param string $newparent The new parent object. Defaults to the root. 158 */ 159 function move($object, $newparent = null) 160 { 161 $cid = $this->getId($object); 162 if (is_a($cid, 'PEAR_Error')) { 163 return PEAR::raiseError(sprintf('Object to move does not exist: %s', $cid->getMessage())); 164 } 165 166 if (!is_null($newparent)) { 167 $pid = $this->getId($newparent); 168 if (is_a($pid, 'PEAR_Error')) { 169 return PEAR::raiseError(sprintf('New parent does not exist: %s', $pid->getMessage())); 170 } 171 } else { 172 $pid = DATATREE_ROOT; 173 } 174 175 $this->_data[$cid]['parent'] = $pid; 176 177 return true; 178 } 179 180 /** 181 * Change an object's name. 182 * 183 * @param mixed $old_object The old object. 184 * @param string $new_object_name The new object name. 185 */ 186 function rename($old_object, $new_object_name) 187 { 188 /* Check whether the object exists at all */ 189 if (!$this->exists($old_object)) { 190 return PEAR::raiseError($old_object . ' does not exist'); 191 } 192 193 /* Check for duplicates - get parent and create new object 194 * name */ 195 $parent = $this->getName($this->getParent($old_object)); 196 if ($this->exists($parent . ':' . $new_object_name)) { 197 return PEAR::raiseError('Duplicate name ' . $new_object_name); 198 } 199 200 /* Replace the old name with the new one in the cache */ 201 $old_object_id = $this->getId($old_object); 202 $this->_data[$old_object_id]['name'] = $new_object_name; 203 204 return true; 205 } 206 207 /** 208 * Changes the order of the children of an object. 209 * 210 * @param string $parent The full id path of the parent object. 211 * @param mixed $order If an array it specifies the new positions for 212 * all child objects. 213 * If an integer and $cid is specified, the position 214 * where the child specified by $cid is inserted. If 215 * $cid is not specified, the position gets deleted, 216 * causing the following positions to shift up. 217 * @param integer $cid See $order. 218 */ 219 function reorder($parents, $order = null, $cid = null) 220 { 221 return PEAR::raiseError('not supported'); 222 } 223 224 /** 225 * Change order of children of an object. 226 * 227 * @param string $pid The parent object id string path. 228 * @param mixed $order Specific new order position or an array containing 229 * the new positions for the given parent. 230 * @param integer $cid If provided indicates insertion of a new child to 231 * the parent to avoid incrementing it when 232 * shifting up all other children's order. If not 233 * provided indicates deletion, so shift all other 234 * positions down one. 235 */ 236 function _reorder($pid, $order = null, $cid = null) 237 { 238 if (!is_array($order) && !is_null($order)) { 239 // Single update (add/del). 240 if (is_null($cid)) { 241 // No id given so shuffle down. 242 foreach ($this->_data as $c_key => $c_val) { 243 if ($this->_data[$c_key]['parent'] == $pid 244 && $this->_data[$c_key]['order'] > $order) { 245 $this->_data[$c_key]['order']--; 246 } 247 } 248 } else { 249 // We have an id so shuffle up. 250 foreach ($this->_data as $c_key => $c_val) { 251 if ($c_key != $cid && $this->_data[$c_key]['parent'] == $pid 252 && $this->_data[$c_key]['order'] >= $order) { 253 $this->_data[$c_key]['order']++; 254 } 255 } 256 } 257 } elseif (is_array($order) && count($order)) { 258 // Multi update. 259 foreach ($order as $order_position => $cid) { 260 $this->_data[$cid]['order'] = $order_position; 261 } 262 } 263 } 264 265 /** 266 * Explicitly set the order for a datatree object. 267 * 268 * @abstract 269 * 270 * @param integer $id The datatree object id to change. 271 * @param integer $order The new order. 272 */ 273 function setOrder($id, $order) 274 { 275 return PEAR::raiseError('not supported'); 276 } 277 278 /** 279 * Dynamically determines the object class. 280 * 281 * @param array $attributes The set of attributes that contain the class 282 * information. Defaults to DataTreeObject. 283 */ 284 function _defineObjectClass($attributes) 285 { 286 global $registry; 287 288 $class = 'DataTreeObject'; 289 if (!is_array($attributes)) { 290 return $class; 291 } 292 293 foreach ($attributes as $attr) { 294 if ($attr['name'] == 'DataTree') { 295 if ($attr['key'] == 'objectClass') { 296 $class = $attr['value']; 297 break; 298 } elseif ($attr['key'] == 'objectType') { 299 $result = explode('/', $attr['value']); 300 $class = $registry->callByPackage($result[0], 'defineClass', array('type' => $result[1])); 301 break; 302 } 303 } 304 } 305 306 return $class; 307 } 308 309 /** 310 * Returns a DataTreeObject (or subclass) object of the data in the 311 * object defined by $object. 312 * 313 * @param string $object The object to fetch: 'parent:sub-parent:name'. 314 * @param string $class Subclass of DataTreeObject to use. Defaults to 315 * DataTreeObject. Null forces the driver to look 316 * into the attributes table to determine the 317 * subclass to use. If none is found it uses 318 * DataTreeObject. 319 */ 320 function &getObject($object, $class = 'DataTreeObject') 321 { 322 if (empty($object)) { 323 $error = PEAR::raiseError('No object requested.'); 324 return $error; 325 } 326 327 $this->_load($object); 328 if (!$this->exists($object)) { 329 $error = PEAR::raiseError($object . ' not found.'); 330 return $error; 331 } 332 333 $use_attributes = is_null($class) || is_callable(array($class, '_fromAttributes')); 334 if ($use_attributes) { 335 $attributes = $this->getAttributes($this->getId($object)); 336 if (is_a($attributes, 'PEAR_Error')) { 337 return $attributes; 338 } 339 340 if (is_null($class)) { 341 $class = $this->_defineObjectClass($attributes); 342 } 343 } 344 345 if (!class_exists($class)) { 346 $error = PEAR::raiseError($class . ' not found.'); 347 return $error; 348 } 349 350 $dataOb = &new $class($object); 351 $dataOb->setDataTree($this); 352 /* If the class has a _fromAttributes method, load data from 353 * the attributes backend. */ 354 if ($use_attributes) { 355 $dataOb->_fromAttributes($attributes); 356 } else { 357 /* Otherwise load it from the old data storage field. */ 358 $dataOb->setData($this->getData($this->getId($object))); 359 } 360 361 $dataOb->setOrder($this->getOrder($object)); 362 return $dataOb; 363 } 364 365 /** 366 * Returns a DataTreeObject (or subclass) object of the data in the 367 * object with the ID $id. 368 * 369 * @param integer $id An object id. 370 * @param string $class Subclass of DataTreeObject to use. Defaults to 371 * DataTreeObject. Null forces the driver to look 372 * into the attributes table to determine the 373 * subclass to use. If none is found it uses 374 * DataTreeObject. 375 */ 376 function &getObjectById($id, $class = 'DataTreeObject') 377 { 378 if (empty($id)) { 379 $object = PEAR::raiseError('No id requested.'); 380 return $object; 381 } 382 383 $result = $this->_loadById($id); 384 if (is_a($result, 'PEAR_Error')) { 385 return $result; 386 } 387 388 $use_attributes = is_null($class) || is_callable(array($class, '_fromAttributes')); 389 if ($use_attributes) { 390 $attributes = $this->getAttributes($id); 391 if (is_a($attributes, 'PEAR_Error')) { 392 return $attributes; 393 } 394 395 if (is_null($class)) { 396 $class = $this->_defineObjectClass($attributes); 397 } 398 } 399 400 if (!class_exists($class)) { 401 return PEAR::raiseError($class . ' not found.'); 402 } 403 404 $name = $this->getName($id); 405 $dataOb = &new $class($name); 406 $dataOb->setDataTree($this); 407 /* If the class has a _fromAttributes method, load data from 408 * the attributes backend. */ 409 if ($use_attributes) { 410 $dataOb->_fromAttributes($attributes); 411 } else { 412 /* Otherwise load it from the old data storage field. */ 413 $dataOb->setData($this->getData($id)); 414 } 415 416 $dataOb->setOrder($this->getOrder($name)); 417 return $dataOb; 418 } 419 420 /** 421 * Returns an array of DataTreeObject (or subclass) objects 422 * corresponding to the objects in $ids, with the object 423 * names as the keys of the array. 424 * 425 * @param array $ids An array of object ids. 426 * @param string $class Subclass of DataTreeObject to use. Defaults to 427 * DataTreeObject. Null forces the driver to look 428 * into the attributes table to determine the 429 * subclass to use. If none is found it uses 430 * DataTreeObject. 431 */ 432 function &getObjects($ids, $class = 'DataTreeObject') 433 { 434 $result = $this->_loadById($ids); 435 if (is_a($result, 'PEAR_Error')) { 436 return $result; 437 } 438 439 $defineClass = is_null($class); 440 $attributes = is_null($class) || is_callable(array($class, '_fromAttributes')); 441 442 if ($attributes) { 443 $data = $this->getAttributes($ids); 444 } else { 445 $data = $this->getData($ids); 446 } 447 448 $objects = array(); 449 foreach ($ids as $id) { 450 $name = $this->getName($id); 451 if (!empty($name) && !empty($data[$id])) { 452 if ($defineClass) { 453 $class = $this->_defineObjectClass($data[$id]); 454 } 455 456 if (!class_exists($class)) { 457 return PEAR::raiseError($class . ' not found.'); 458 } 459 460 $objects[$name] = &new $class($name); 461 $objects[$name]->setDataTree($this); 462 if ($attributes) { 463 $objects[$name]->_fromAttributes($data[$id]); 464 } else { 465 $objects[$name]->setData($data[$id]); 466 } 467 $objects[$name]->setOrder($this->getOrder($name)); 468 } 469 } 470 471 return $objects; 472 } 473 474 /** 475 * Export a list of objects. 476 * 477 * @param constant $format Format of the export 478 * @param string $startleaf The name of the leaf from which we start 479 * the export tree. 480 * @param boolean $reload Re-load the requested chunk? Defaults to 481 * false (only what is currently loaded). 482 * @param string $rootname The label to use for the root element. 483 * Defaults to DATATREE_ROOT. 484 * @param integer $maxdepth The maximum number of levels to return. 485 * Defaults to DATATREE_ROOT, which is no 486 * limit. 487 * @param boolean $loadTree Load a tree starting at $root, or just the 488 * requested level and direct parents? 489 * Defaults to single level. 490 * @param string $sortby_name Attribute name to use for sorting. 491 * @param string $sortby_key Attribute key to use for sorting. 492 * @param integer $direction Sort direction: 493 * 0 - ascending 494 * 1 - descending 495 * 496 * @return mixed The tree representation of the objects, or a PEAR_Error 497 * on failure. 498 */ 499 function get($format, $startleaf = DATATREE_ROOT, $reload = false, 500 $rootname = DATATREE_ROOT, $maxdepth = -1, $loadTree = false, 501 $sortby_name = null, $sortby_key = null, $direction = 0) 502 { 503 /* Set sorting hash */ 504 if (!is_null($sortby_name) || !is_null($sortby_name)) { 505 $this->_sortHash = DataTree::sortHash($startleaf, $sortby_name, $sortby_key, $direction); 506 } 507 508 $this->_load($startleaf, $loadTree, $reload, $sortby_name, $sortby_key, $direction); 509 $out = array(); 510 511 switch ($format) { 512 case DATATREE_FORMAT_TREE: 513 $startid = $this->getId($startleaf, $maxdepth); 514 if (is_a($startid, 'PEAR_Error')) { 515 return $startid; 516 } 517 $this->_extractAllLevelTree($out, $startid, $maxdepth); 518 break; 519 520 case DATATREE_FORMAT_FLAT: 521 $startid = $this->getId($startleaf); 522 if (is_a($startid, 'PEAR_Error')) { 523 return $startid; 524 } 525 $this->_extractAllLevelList($out, $startid, $maxdepth); 526 if (!empty($out[DATATREE_ROOT])) { 527 $out[DATATREE_ROOT] = $rootname; 528 } 529 break; 530 531 default: 532 return PEAR::raiseError('Not supported'); 533 } 534 535 if (!is_null($this->_sortHash)) { 536 /* Reset sorting hash. */ 537 $this->_sortHash = null; 538 /* Reverse since the attribute sorting combined with tree up-ward 539 * sorting produces a reversed object order. */ 540 $out = array_reverse($out, true); 541 } 542 543 return $out; 544 } 545 546 /** 547 * Counts objects. 548 * 549 * @param string $startleaf The name of the leaf from which we start 550 * counting. 551 * 552 * @return integer The number of the objects below $startleaf. 553 */ 554 function count($startleaf = DATATREE_ROOT) 555 { 556 return $this->_count($startleaf); 557 } 558 559 /** 560 * Create attribute sort hash 561 * 562 * @since Horde 3.1 563 * 564 * @param string $root The name of the leaf from which we start 565 * the export tree. 566 * @param string $sortby_name Attribute name to use for sorting. 567 * @param string $sortby_key Attribute key to use for sorting. 568 * @param integer $direction Sort direction: 569 * 0 - ascending 570 * 1 - descending 571 * 572 * @return string The sort hash 573 */ 574 function sortHash($root, $sortby_name = null, $sortby_key = null, 575 $direction = 0) 576 { 577 return sprintf('%s-%s-%s-%s', $root, $sortby_name, $sortby_key, $direction); 578 } 579 580 /** 581 * Export a list of objects just like get() above, but uses an 582 * object id to fetch the list of objects. 583 * 584 * @param constant $format Format of the export. 585 * @param string $startleaf The id of the leaf from which we start the 586 * export tree. 587 * @param boolean $reload Reload the requested chunk? Defaults to 588 * false (only what is currently loaded). 589 * @param string $rootname The label to use for the root element. 590 * Defaults to DATATREE_ROOT. 591 * @param integer $maxdepth The maximum number of levels to return 592 * Defaults to -1, which is no limit. 593 * 594 * @return mixed The tree representation of the objects, or a PEAR_Error 595 * on failure. 596 */ 597 function getById($format, $startleaf = DATATREE_ROOT, $reload = false, 598 $rootname = DATATREE_ROOT, $maxdepth = -1) 599 { 600 $this->_loadById($startleaf); 601 $out = array(); 602 603 switch ($format) { 604 case DATATREE_FORMAT_TREE: 605 $this->_extractAllLevelTree($out, $startleaf, $maxdepth); 606 break; 607 608 case DATATREE_FORMAT_FLAT: 609 $this->_extractAllLevelList($out, $startleaf, $maxdepth); 610 if (!empty($out[DATATREE_ROOT])) { 611 $out[DATATREE_ROOT] = $rootname; 612 } 613 break; 614 615 default: 616 return PEAR::raiseError('Not supported'); 617 } 618 619 return $out; 620 } 621 622 /** 623 * Returns a list of all groups (root nodes) of the data tree. 624 * 625 * @return array The the group IDs 626 */ 627 function getGroups() 628 { 629 return PEAR::raiseError('not supported'); 630 } 631 632 /** 633 * Retrieve data for an object from the datatree_data field. 634 * 635 * @abstract 636 * 637 * @param integer $cid The object id to fetch, or an array of object ids. 638 */ 639 function getData($cid) 640 { 641 return PEAR::raiseError('not supported'); 642 } 643 644 /** 645 * Import a list of objects. Used by drivers to populate the internal 646 * $_data array. 647 * 648 * @param array $data The data to import. 649 * @param string $charset The charset to convert the object name from. 650 */ 651 function set($data, $charset = null) 652 { 653 $cids = array(); 654 foreach ($data as $id => $cat) { 655 if (!is_null($charset)) { 656 $cat[1] = String::convertCharset($cat[1], $charset); 657 } 658 $cids[$cat[0]] = $cat[1]; 659 $cparents[$cat[0]] = $cat[2]; 660 $corders[$cat[0]] = $cat[3]; 661 $sorders[$cat[0]] = $id; 662 } 663 664 foreach ($cids as $id => $name) { 665 $this->_data[$id]['name'] = $name; 666 $this->_data[$id]['order'] = $corders[$id]; 667 if (!is_null($this->_sortHash)) { 668 $this->_data[$id]['sorter'][$this->_sortHash] = $sorders[$id]; 669 } 670 if (!empty($cparents[$id])) { 671 $parents = explode(':', substr($cparents[$id], 1)); 672 $par = $parents[count($parents) - 1]; 673 $this->_data[$id]['parent'] = $par; 674 675 if (!empty($this->_nameMap[$par])) { 676 // If we've already loaded the direct parent of 677 // this object, use that to find the full name. 678 $this->_nameMap[$id] = $this->_nameMap[$par] . ':' . $name; 679 } else { 680 // Otherwise, run through parents one by one to 681 // build it up. 682 $this->_nameMap[$id] = ''; 683 foreach ($parents as $parID) { 684 if (!empty($cids[$parID])) { 685 $this->_nameMap[$id] .= ':' . $cids[$parID]; 686 } 687 } 688 $this->_nameMap[$id] = substr($this->_nameMap[$id], 1) . ':' . $name; 689 } 690 } else { 691 $this->_data[$id]['parent'] = DATATREE_ROOT; 692 $this->_nameMap[$id] = $name; 693 } 694 } 695 696 return true; 697 } 698 699 /** 700 * Extract one level of data for a parent leaf, sorted first by 701 * their order and then by name. This function is a way to get a 702 * collection of $leaf's children. 703 * 704 * @param string $leaf Name of the parent from which to start. 705 * 706 * @return array 707 */ 708 function _extractOneLevel($leaf = DATATREE_ROOT) 709 { 710 $out = array(); 711 foreach ($this->_data as $id => $vals) { 712 if ($vals['parent'] == $leaf) { 713 $out[$id] = $vals; 714 } 715 } 716 717 if (is_null($this->_sortHash)) { 718 uasort($out, array($this, '_cmp')); 719 } else { 720 uasort($out, array($this, '_cmpSorted')); 721 } 722 723 return $out; 724 } 725 726 /** 727 * Extract all levels of data, starting from a given parent 728 * leaf in the datatree. 729 * 730 * @param array $out This is an iterating function, so $out is 731 * passed by reference to contain the result. 732 * @param string $parent The name of the parent from which to begin. 733 * @param integer $maxdepth Max of levels of depth to check. 734 * 735 * @note If nothing is returned that means there is no child, but 736 * don't forget to add the parent if any subsequent operations are 737 * required! 738 */ 739 function _extractAllLevelTree(&$out, $parent = DATATREE_ROOT, $maxdepth = -1) 740 { 741 if ($maxdepth == 0) { 742 return false; 743 } 744 745 $out[$parent] = true; 746 747 $k = $this->_extractOneLevel($parent); 748 foreach ($k as $object => $v) { 749 if (!is_array($out[$parent])) { 750 $out[$parent] = array(); 751 } 752 $out[$parent][$object] = true; 753 $this->_extractAllLevelTree($out[$parent], $object, $maxdepth - 1); 754 } 755 } 756 757 /** 758 * Extract all levels of data, starting from any parent in 759 * the tree. 760 * 761 * Returned array format: array(parent => array(child => true)) 762 * 763 * @param array $out This is an iterating function, so $out is 764 * passed by reference to contain the result. 765 * @param string $parent The name of the parent from which to begin. 766 * @param integer $maxdepth Max number of levels of depth to check. 767 */ 768 function _extractAllLevelList(&$out, $parent = DATATREE_ROOT, $maxdepth = -1) 769 { 770 if ($maxdepth == 0) { 771 return false; 772 } 773 774 // This is redundant most of the time, so make sure we need to 775 // do it. 776 if (empty($out[$parent])) { 777 $out[$parent] = $this->getName($parent); 778 } 779 780 $k = array_keys($this->_extractOneLevel($parent)); 781 foreach ($k as $object) { 782 $out[$object] = $this->getName($object); 783 $this->_extractAllLevelList($out, $object, $maxdepth - 1); 784 } 785 } 786 787 /** 788 * Returns a child's direct parent ID. 789 * 790 * @param mixed $child Either the object, an array containing the 791 * path elements, or the object name for which 792 * to look up the parent's ID. 793 * 794 * @return integer The unique ID of the parent. 795 */ 796 function getParent($child) 797 { 798 if (is_a($child, 'DataTreeObject')) { 799 $child = $child->getName(); 800 } 801 $id = $this->getId($child); 802 if (is_a($id, 'PEAR_Error')) { 803 return $id; 804 } 805 806 return $this->_data[$id]['parent']; 807 } 808 809 /** 810 * Get a $child's direct parent ID. 811 * 812 * @param integer $childId Get the parent of this object. 813 * 814 * @return integer The unique ID of the parent. 815 */ 816 function getParentById($childId) 817 { 818 $this->_loadById($childId); 819 return isset($this->_data[$childId]) ? 820 $this->_data[$childId]['parent'] : 821 PEAR::raiseError($childId . ' not found'); 822 } 823 824 /** 825 * Get a list of parents all the way up to the root object for 826 * $child. 827 * 828 * @param mixed $child The name of the child 829 * @param boolean $getids If true, return parent IDs; otherwise, return 830 * names. 831 * 832 * @return array [child] [parent] in a tree format. 833 */ 834 function getParents($child, $getids = false) 835 { 836 $pid = $this->getParent($child); 837 if (is_a($pid, 'PEAR_Error')) { 838 return PEAR::raiseError('Parents not found: ' . $pid->getMessage()); 839 } 840 $pname = $this->getName($pid); 841 if ($getids) { 842 $parents = array($pid => true); 843 } else { 844 $parents = array($pname => true); 845 } 846 847 if ($pid != DATATREE_ROOT) { 848 if ($getids) { 849 $parents[$pid] = $this->getParents($pname, $getids); 850 } else { 851 $parents[$pname] = $this->getParents($pname, $getids); 852 } 853 } 854 855 return $parents; 856 } 857 858 /** 859 * Get a list of parents all the way up to the root object for 860 * $child. 861 * 862 * @param integer $childId The id of the child. 863 * @param array $parents The array, as we build it up. 864 * 865 * @return array A flat list of all of the parents of $child, 866 * hashed in $id => $name format. 867 */ 868 function getParentList($childId, $parents = array()) 869 { 870 $pid = $this->getParentById($childId); 871 if (is_a($pid, 'PEAR_Error')) { 872 return PEAR::raiseError('Parents not found: ' . $pid->getMessage()); 873 } 874 875 if ($pid != DATATREE_ROOT) { 876 $parents[$pid] = $this->getName($pid); 877 $parents = $this->getParentList($pid, $parents); 878 } 879 880 return $parents; 881 } 882 883 /** 884 * Get a parent ID string (id:cid format) for the specified object. 885 * 886 * @param mixed $object The object to return a parent string for. 887 * 888 * @return string|PEAR_Error The ID "path" to the parent object or 889 * PEAR_Error on failure. 890 */ 891 function getParentIdString($object) 892 { 893 $ptree = $this->getParents($object, true); 894 if (is_a($ptree, 'PEAR_Error')) { 895 return $ptree; 896 } 897 898 $pids = ''; 899 while ((list($id, $parent) = each($ptree)) && is_array($parent)) { 900 $pids = ':' . $id . $pids; 901 $ptree = $parent; 902 } 903 904 return $pids; 905 } 906 907 /** 908 * Get the number of children an object has, only counting immediate 909 * children, not grandchildren, etc. 910 * 911 * @param mixed $parent Either the object or the name for which to count 912 * the children, defaults to the root 913 * (DATATREE_ROOT). 914 * 915 * @return integer 916 */ 917 function getNumberOfChildren($parent = DATATREE_ROOT) 918 { 919 if (is_a($parent, 'DataTreeObject')) { 920 $parent = $parent->getName(); 921 } 922 $this->_load($parent); 923 $out = $this->_extractOneLevel($this->getId($parent)); 924 return is_array($out) ? count($out) : 0; 925 } 926 927 /** 928 * Check if an object exists or not. The root element DATATREE_ROOT always exists. 929 * 930 * @param mixed $object The name of the object. 931 * 932 * @return boolean True if the object exists, false otherwise. 933 */ 934 function exists($object) 935 { 936 if (empty($object)) { 937 return false; 938 } 939 if (is_a($object, 'DataTreeObject')) { 940 $object = $object->getName(); 941 } elseif (is_array($object)) { 942 $object = implode(':', $object); 943 } 944 945 if ($object == DATATREE_ROOT) { 946 return true; 947 } 948 949 $idMap = array_flip($this->_nameMap); 950 if (isset($idMap[$object])) { 951 return true; 952 } 953 954 $this->_load($object); 955 $idMap = array_flip($this->_nameMap); 956 return isset($idMap[$object]); 957 } 958 959 /** 960 * Get the name of an object from its id. 961 * 962 * @param integer $id The id for which to look up the name. 963 * 964 * @return string 965 */ 966 function getName($id) 967 { 968 /* If no id or if id is a PEAR error, return null. */ 969 if (empty($id) || is_a($id, 'PEAR_Error')) { 970 return null; 971 } 972 973 /* If checking name of root, return DATATREE_ROOT. */ 974 if ($id == DATATREE_ROOT) { 975 return DATATREE_ROOT; 976 } 977 978 /* If found in the name map, return the name. */ 979 if (isset($this->_nameMap[$id])) { 980 return $this->_nameMap[$id]; 981 } 982 983 /* Not found in name map, try loading this id into the name 984 * map. */ 985 $this->_loadById($id); 986 987 /* If id loaded return the name, otherwise return null. */ 988 return isset($this->_nameMap[$id]) ? 989 $this->_nameMap[$id] : 990 null; 991 } 992 993 /** 994 * Get the id of an object from its name. 995 * 996 * @param mixed $name Either the object, an array containing the 997 * path elements, or the object name for which 998 * to look up the id. 999 * 1000 * @return string 1001 */ 1002 function getId($name) 1003 { 1004 /* Check if $name is not a string. */ 1005 if (is_a($name, 'DataTreeObject')) { 1006 /* DataTreeObject, get the string name. */ 1007 $name = $name->getName(); 1008 } elseif (is_array($name)) { 1009 /* Path array, implode to get the string name. */ 1010 $name = implode(':', $name); 1011 } 1012 1013 /* If checking id of root, return DATATREE_ROOT. */ 1014 if ($name == DATATREE_ROOT) { 1015 return DATATREE_ROOT; 1016 } 1017 1018 /* Check if the name actually exists, if not return a PEAR 1019 * error. */ 1020 if (!$this->exists($name)) { 1021 return PEAR::raiseError($name . ' does not exist'); 1022 } 1023 1024 /* Flip the name map to look up the id using the name as key. */ 1025 $idMap = array_flip($this->_nameMap); 1026 return $idMap[$name]; 1027 } 1028 1029 /** 1030 * Get the order position of an object. 1031 * 1032 * @param mixed $child Either the object or the name. 1033 * 1034 * @return mixed The object's order position or a PEAR error on failure. 1035 */ 1036 function getOrder($child) 1037 { 1038 if (is_a($child, 'DataTreeObject')) { 1039 $child = $child->getName(); 1040 } 1041 $id = $this->getId($child); 1042 if (is_a($id, 'PEAR_Error')) { 1043 return $id; 1044 } 1045 1046 return isset($this->_data[$id]['order']) ? 1047 $this->_data[$id]['order'] : 1048 null; 1049 } 1050 1051 /** 1052 * Replace all occurences of ':' in an object name with '.'. 1053 * 1054 * @param string $name The name of the object. 1055 * 1056 * @return string The encoded name. 1057 */ 1058 function encodeName($name) 1059 { 1060 return str_replace(':', '.', $name); 1061 } 1062 1063 /** 1064 * Get the short name of an object, returns only the last portion of the 1065 * full name. For display purposes only. 1066 * 1067 * @static 1068 * 1069 * @param string $name The name of the object. 1070 * 1071 * @return string The object's short name. 1072 */ 1073 function getShortName($name) 1074 { 1075 /* If there are several components to the name, explode and get the 1076 * last one, otherwise just return the name. */ 1077 if (strpos($name, ':') !== false) { 1078 $name = explode(':', $name); 1079 $name = array_pop($name); 1080 } 1081 return $name; 1082 } 1083 1084 /** 1085 * Returns a tree sorted by the specified attribute name and/or key. 1086 * 1087 * @abstract 1088 * 1089 * @since Horde 3.1 1090 * 1091 * @param string $root Which portion of the tree to sort. 1092 * Defaults to all of it. 1093 * @param boolean $loadTree Sort the tree starting at $root, or just the 1094 * requested level and direct parents? 1095 * Defaults to single level. 1096 * @param string $sortby_name Attribute name to use for sorting. 1097 * @param string $sortby_key Attribute key to use for sorting. 1098 * @param integer $direction Sort direction: 1099 * 0 - ascending 1100 * 1 - descending 1101 */ 1102 function getSortedTree($root, $loadTree = false, $sortby_name = null, 1103 $sortby_key = null, $direction = 0) 1104 { 1105 return PEAR::raiseError('not supported'); 1106 } 1107 1108 /** 1109 * Adds an object. 1110 * 1111 * @abstract 1112 * 1113 * @param mixed $object The object to add (string or 1114 * DataTreeObject). 1115 * @param boolean $id_as_name True or false to indicate if object ID is to 1116 * be used as object name. Used in situations 1117 * where there is no available unique input for 1118 * object name. 1119 */ 1120 function add($object, $id_as_name = false) 1121 { 1122 return PEAR::raiseError('not supported'); 1123 } 1124 1125 /** 1126 * Add an object. 1127 * 1128 * @param string $name The short object name. 1129 * @param integer $id The new object's unique ID. 1130 * @param integer $pid The unique ID of the object's parent. 1131 * @param integer $order The ordering data for the object. 1132 * 1133 * @access protected 1134 */ 1135 function _add($name, $id, $pid, $order = '') 1136 { 1137 $this->_data[$id] = array('name' => $name, 1138 'parent' => $pid, 1139 'order' => $order); 1140 $this->_nameMap[$id] = $name; 1141 1142 /* Shift along the order positions. */ 1143 $this->_reorder($pid, $order, $id); 1144 1145 return true; 1146 } 1147 1148 /** 1149 * Retrieve data for an object from the horde_datatree_attributes 1150 * table. 1151 * 1152 * @abstract 1153 * 1154 * @param integer | array $cid The object id to fetch, 1155 * or an array of object ids. 1156 * 1157 * @return array A hash of attributes, or a multi-level hash 1158 * of object ids => their attributes. 1159 */ 1160 function getAttributes($cid) 1161 { 1162 return PEAR::raiseError('not supported'); 1163 } 1164 1165 /** 1166 * Returns the number of objects matching a set of attribute criteria. 1167 * 1168 * @abstract 1169 * 1170 * @see buildAttributeQuery() 1171 * 1172 * @param array $criteria The array of criteria. 1173 * @param string $parent The parent node to start searching from. 1174 * @param boolean $allLevels Return all levels, or just the direct 1175 * children of $parent? Defaults to all levels. 1176 * @param string $restrict Only return attributes with the same 1177 * attribute_name or attribute_id. 1178 */ 1179 function countByAttributes($criteria, $parent = DATATREE_ROOT, 1180 $allLevels = true, $restrict = 'name') 1181 { 1182 return PEAR::raiseError('not supported'); 1183 } 1184 1185 /** 1186 * Returns a set of object ids based on a set of attribute criteria. 1187 * 1188 * @abstract 1189 * 1190 * @see buildAttributeQuery() 1191 * 1192 * @param array $criteria The array of criteria. 1193 * @param string $parent The parent node to start searching from. 1194 * @param boolean $allLevels Return all levels, or just the direct 1195 * children of $parent? Defaults to all levels. 1196 * @param string $restrict Only return attributes with the same 1197 * attribute_name or attribute_id. 1198 * @param integer $from The object to start to fetching 1199 * @param integer $count The number of objects to fetch 1200 * @param string $sortby_name Attribute name to use for sorting. 1201 * @param string $sortby_key Attribute key to use for sorting. 1202 * @param integer $direction Sort direction: 1203 * 0 - ascending 1204 * 1 - descending 1205 */ 1206 function getByAttributes($criteria, $parent = DATATREE_ROOT, 1207 $allLevels = true, $restrict = 'name', $from = 0, 1208 $count = 0, $sortby_name = null, 1209 $sortby_key = null, $direction = 0) 1210 { 1211 return PEAR::raiseError('not supported'); 1212 } 1213 1214 /** 1215 * Sorts IDs by attribute values. IDs without attributes will be added to 1216 * the end of the sorted list. 1217 * 1218 * @abstract 1219 * 1220 * @param array $unordered_ids Array of ids to sort. 1221 * @param array $sortby_name Attribute name to use for sorting. 1222 * @param array $sortby_key Attribute key to use for sorting. 1223 * @param array $direction Sort direction: 1224 * 0 - ascending 1225 * 1 - descending 1226 * 1227 * @return array Sorted ids. 1228 */ 1229 function sortByAttributes($unordered_ids, $sortby_name = null, 1230 $sortby_key = null, $direction = 0) 1231 { 1232 return PEAR::raiseError('not supported'); 1233 } 1234 1235 /** 1236 * Update the data in an object. Does not change the object's 1237 * parent or name, just serialized data or attributes. 1238 * 1239 * @abstract 1240 * 1241 * @param DataTree $object A DataTree object. 1242 */ 1243 function updateData($object) 1244 { 1245 return PEAR::raiseError('not supported'); 1246 } 1247 1248 /** 1249 * Sort two objects by their order field, and if that is the same, 1250 * alphabetically (case insensitive) by name. 1251 * 1252 * You never call this function; it's used in uasort() calls. Do 1253 * NOT use usort(); you'll lose key => value associations. 1254 * 1255 * @param array $a The first object 1256 * @param array $b The second object 1257 * 1258 * @return integer 1 if $a should be first, 1259 * -1 if $b should be first, 1260 * 0 if they are entirely equal. 1261 */ 1262 function _cmp($a, $b) 1263 { 1264 if ($a['order'] > $b['order']) { 1265 return 1; 1266 } elseif ($a['order'] < $b['order']) { 1267 return -1; 1268 } else { 1269 return strcasecmp($a['name'], $b['name']); 1270 } 1271 } 1272 1273 /** 1274 * Sorts two objects by their sorter hash field. 1275 * 1276 * You never call this function; it's used in uasort() calls. Do NOT use 1277 * usort(); you'll lose key => value associations. 1278 * 1279 * @since Horde 3.1 1280 * 1281 * @param array $a The first object 1282 * @param array $b The second object 1283 * 1284 * @return integer 1 if $a should be first, 1285 * -1 if $b should be first, 1286 * 0 if they are entirely equal. 1287 */ 1288 function _cmpSorted($a, $b) 1289 { 1290 if ($a['sorter'][$this->_sortHash] < $b['sorter'][$this->_sortHash]) { 1291 return 1; 1292 } else { 1293 return 0; 1294 } 1295 } 1296 1297 /** 1298 * Attempts to return a concrete DataTree instance based on $driver. 1299 * 1300 * @param mixed $driver The type of concrete DataTree subclass to return. 1301 * This is based on the storage driver ($driver). The 1302 * code is dynamically included. If $driver is an array, 1303 * then we will look in $driver[0]/lib/DataTree/ for 1304 * the subclass implementation named $driver[1].php. 1305 * @param array $params A hash containing any additional configuration or 1306 * connection parameters a subclass might need. 1307 * Here, we need 'group' = a string that defines 1308 * top-level groups of objects. 1309 * 1310 * @return DataTree The newly created concrete DataTree instance, or false 1311 * on an error. 1312 */ 1313 function &factory($driver, $params = null) 1314 { 1315 $driver = basename($driver); 1316 1317 if (is_null($params)) { 1318 $params = Horde::getDriverConfig('datatree', $driver); 1319 } 1320 1321 if (empty($driver)) { 1322 $driver = 'null'; 1323 } 1324 1325 include_once 'Horde/DataTree/' . $driver . '.php'; 1326 $class = 'DataTree_' . $driver; 1327 if (class_exists($class)) { 1328 $dt = &new $class($params); 1329 } else { 1330 $dt = PEAR::raiseError('Class definition of ' . $class . ' not found.'); 1331 } 1332 1333 return $dt; 1334 } 1335 1336 /** 1337 * Attempts to return a reference to a concrete DataTree instance based on 1338 * $driver. 1339 * 1340 * It will only create a new instance if no DataTree instance with the same 1341 * parameters currently exists. 1342 * 1343 * This should be used if multiple DataTree sources (and, thus, multiple 1344 * DataTree instances) are required. 1345 * 1346 * This method must be invoked as: $var = &DataTree::singleton(); 1347 * 1348 * @param mixed $driver Type of concrete DataTree subclass to return, 1349 * based on storage driver ($driver). The code is 1350 * dynamically included. If $driver is an array, then 1351 * look in $driver[0]/lib/DataTree/ for subclass 1352 * implementation named $driver[1].php. 1353 * @param array $params A hash containing any additional configuration or 1354 * connection parameters a subclass might need. 1355 * 1356 * @return DataTree The concrete DataTree reference, or false on an error. 1357 */ 1358 function &singleton($driver, $params = null) 1359 { 1360 static $instances; 1361 if (!isset($instances)) { 1362 $instances = array(); 1363 } 1364 1365 if (is_null($params)) { 1366 $params = Horde::getDriverConfig('datatree', $driver); 1367 } 1368 1369 $signature = serialize(array($driver, $params)); 1370 if (!isset($instances[$signature])) { 1371 $instances[$signature] = &DataTree::factory($driver, $params); 1372 } 1373 1374 return $instances[$signature]; 1375 } 1376 1377 } 1378 1379 /** 1380 * Class that can be extended to save arbitrary information as part of a stored 1381 * object. 1382 * 1383 * @author Stephane Huther <shuther@bigfoot.com> 1384 * @author Chuck Hagenbuch <chuck@horde.org> 1385 * @since Horde 2.1 1386 * @package Horde_DataTree 1387 */ 1388 class DataTreeObject { 1389 1390 /** 1391 * This object's DataTree instance. 1392 * 1393 * @var DataTree 1394 */ 1395 var $datatree; 1396 1397 /** 1398 * Key-value hash that will be serialized. 1399 * 1400 * @see getData() 1401 * @var array 1402 */ 1403 var $data = array(); 1404 1405 /** 1406 * The unique name of this object. 1407 * These names have the same requirements as other object names - they must 1408 * be unique, etc. 1409 * 1410 * @var string 1411 */ 1412 var $name; 1413 1414 /** 1415 * If this object has ordering data, store it here. 1416 * 1417 * @var integer 1418 */ 1419 var $order = null; 1420 1421 /** 1422 * DataTreeObject constructor. 1423 * Just sets the $name parameter. 1424 * 1425 * @param string $name The object name. 1426 */ 1427 function DataTreeObject($name) 1428 { 1429 $this->setName($name); 1430 } 1431 1432 /** 1433 * Sets the {@link DataTree} instance used to retrieve this object. 1434 * 1435 * @param DataTree $datatree A {@link DataTree} instance. 1436 */ 1437 function setDataTree(&$datatree) 1438 { 1439 $this->datatree = &$datatree; 1440 } 1441 1442 /** 1443 * Gets the name of this object. 1444 * 1445 * @return string The object name. 1446 */ 1447 function getName() 1448 { 1449 return $this->name; 1450 } 1451 1452 /** 1453 * Sets the name of this object. 1454 * 1455 * NOTE: Use with caution. This may throw out of sync the cached datatree 1456 * tables if not used properly. 1457 * 1458 * @param string $name The name to set this object's name to. 1459 */ 1460 function setName($name) 1461 { 1462 $this->name = $name; 1463 } 1464 1465 /** 1466 * Gets the short name of this object. 1467 * For display purposes only. 1468 * 1469 * @return string The object's short name. 1470 */ 1471 function getShortName() 1472 { 1473 return DataTree::getShortName($this->name); 1474 } 1475 1476 /** 1477 * Gets the ID of this object. 1478 * 1479 * @return string The object's ID. 1480 */ 1481 function getId() 1482 { 1483 return $this->datatree->getId($this); 1484 } 1485 1486 /** 1487 * Gets the data array. 1488 * 1489 * @return array The internal data array. 1490 */ 1491 function getData() 1492 { 1493 return $this->data; 1494 } 1495 1496 /** 1497 * Sets the data array. 1498 * 1499 * @param array The data array to store internally. 1500 */ 1501 function setData($data) 1502 { 1503 $this->data = $data; 1504 } 1505 1506 /** 1507 * Sets the order of this object in its object collection. 1508 * 1509 * @param integer $order 1510 */ 1511 function setOrder($order) 1512 { 1513 $this->order = $order; 1514 } 1515 1516 /** 1517 * Returns this object's parent. 1518 * 1519 * @param string $class Subclass of DataTreeObject to use. Defaults to 1520 * DataTreeObject. Null forces the driver to look 1521 * into the attributes table to determine the 1522 * subclass to use. If none is found it uses 1523 * DataTreeObject. 1524 * 1525 * @return DataTreeObject This object's parent 1526 */ 1527 function &getParent($class = 'DataTreeObject') 1528 { 1529 $id = $this->datatree->getParent($this); 1530 if (is_a($id, 'PEAR_Error')) { 1531 return $id; 1532 } 1533 return $this->datatree->getObjectById($id, $class); 1534 } 1535 1536 /** 1537 * Returns a child of this object. 1538 * 1539 * @param string $name The child's name. 1540 * @param boolean $autocreate If true and no child with the given name 1541 * exists, one gets created. 1542 */ 1543 function &getChild($name, $autocreate = true) 1544 { 1545 $name = $this->getShortName() . ':' . $name; 1546 1547 /* If the child shouldn't get created, we don't check for its 1548 * existance to return the "not found" error of 1549 * getObject(). */ 1550 if (!$autocreate || $this->datatree->exists($name)) { 1551 $child = &$this->datatree->getObject($name); 1552 } else { 1553 $child = &new DataTreeObject($name); 1554 $child->setDataTree($this->datatree); 1555 $this->datatree->add($child); 1556 } 1557 1558 return $child; 1559 } 1560 1561 /** 1562 * Saves any changes to this object to the backend permanently. New objects 1563 * are added instead. 1564 * 1565 * @return boolean|PEAR_Error PEAR_Error on failure. 1566 */ 1567 function save() 1568 { 1569 if ($this->datatree->exists($this)) { 1570 return $this->datatree->updateData($this); 1571 } else { 1572 return $this->datatree->add($this); 1573 } 1574 } 1575 1576 /** 1577 * Delete this object from the backend permanently. 1578 * 1579 * @return boolean|PEAR_Error PEAR_Error on failure. 1580 */ 1581 function delete() 1582 { 1583 return $this->datatree->remove($this); 1584 } 1585 1586 /** 1587 * Gets one of the attributes of the object, or null if it isn't defined. 1588 * 1589 * @param string $attribute The attribute to get. 1590 * 1591 * @return mixed The value of the attribute, or null. 1592 */ 1593 function get($attribute) 1594 { 1595 return isset($this->data[$attribute]) 1596 ? $this->data[$attribute] 1597 : null; 1598 } 1599 1600 /** 1601 * Sets one of the attributes of the object. 1602 * 1603 * @param string $attribute The attribute to set. 1604 * @param mixed $value The value for $attribute. 1605 */ 1606 function set($attribute, $value) 1607 { 1608 $this->data[$attribute] = $value; 1609 } 1610 1611 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |