00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 define("IL_LAST_NODE", -2);
00025 define("IL_FIRST_NODE", -1);
00026
00041 class ilTree
00042 {
00048 var $ilias;
00049
00050
00056 var $log;
00057
00063 var $root_id;
00064
00070 var $tree_id;
00071
00077 var $table_tree;
00078
00084 var $table_obj_data;
00085
00091 var $table_obj_reference;
00092
00098 var $ref_pk;
00099
00105 var $obj_pk;
00106
00112 var $tree_pk;
00113
00138 var $gap;
00139
00146 function ilTree($a_tree_id, $a_root_id = 0)
00147 {
00148 global $ilDB,$ilErr,$ilias,$ilLog;
00149
00150
00151 (isset($ilDB)) ? $this->ilDB =& $ilDB : $this->ilDB =& $ilias->db;
00152
00153 if (!isset($ilErr))
00154 {
00155 $ilErr = new ilErrorHandling();
00156 $ilErr->setErrorHandling(PEAR_ERROR_CALLBACK,array($ilErr,'errorHandler'));
00157 }
00158 else
00159 {
00160 $this->ilErr =& $ilErr;
00161 }
00162
00163 $this->lang_code = "en";
00164
00165 if (!isset($a_tree_id) or (func_num_args() == 0) )
00166 {
00167 $this->ilErr->raiseError(get_class($this)."::Constructor(): No tree_id given!",$this->ilErr->WARNING);
00168 }
00169
00170 if (func_num_args() > 2)
00171 {
00172 $this->ilErr->raiseError(get_class($this)."::Constructor(): Wrong parameter count!",$this->ilErr->WARNING);
00173 }
00174
00175
00176 $this->log =& $ilLog;
00177
00178
00179 if (empty($a_root_id))
00180 {
00181 $a_root_id = ROOT_FOLDER_ID;
00182 }
00183
00184 $this->tree_id = $a_tree_id;
00185 $this->root_id = $a_root_id;
00186 $this->table_tree = 'tree';
00187 $this->table_obj_data = 'object_data';
00188 $this->table_obj_reference = 'object_reference';
00189 $this->ref_pk = 'ref_id';
00190 $this->obj_pk = 'obj_id';
00191 $this->tree_pk = 'tree';
00192 $this->use_cache = false;
00193
00194
00195 $this->gap = 50;
00196 }
00197
00201 function useCache($a_use = true)
00202 {
00203 $this->use_cache = $a_use;
00204 }
00205
00210 function initLangCode()
00211 {
00212 global $ilUser;
00213
00214
00215 if (!is_object($ilUser))
00216 {
00217 $this->lang_code = "en";
00218 }
00219 else
00220 {
00221 $this->lang_code = $ilUser->getCurrentLanguage();
00222 }
00223 }
00224
00225
00240 function setTableNames($a_table_tree,$a_table_obj_data,$a_table_obj_reference = "")
00241 {
00242 if (!isset($a_table_tree) or !isset($a_table_obj_data))
00243 {
00244 $this->ilErr->raiseError(get_class($this)."::setTableNames(): Missing parameter! ".
00245 "tree table: ".$a_table_tree." object data table: ".$a_table_obj_data,$this->ilErr->WARNING);
00246 }
00247
00248 $this->table_tree = $a_table_tree;
00249 $this->table_obj_data = $a_table_obj_data;
00250 $this->table_obj_reference = $a_table_obj_reference;
00251
00252 return true;
00253 }
00254
00261 function setReferenceTablePK($a_column_name)
00262 {
00263 if (!isset($a_column_name))
00264 {
00265 $this->ilErr->raiseError(get_class($this)."::setReferenceTablePK(): No column name given!",$this->ilErr->WARNING);
00266 }
00267
00268 $this->ref_pk = $a_column_name;
00269 return true;
00270 }
00271
00278 function setObjectTablePK($a_column_name)
00279 {
00280 if (!isset($a_column_name))
00281 {
00282 $this->ilErr->raiseError(get_class($this)."::setObjectTablePK(): No column name given!",$this->ilErr->WARNING);
00283 }
00284
00285 $this->obj_pk = $a_column_name;
00286 return true;
00287 }
00288
00295 function setTreeTablePK($a_column_name)
00296 {
00297 if (!isset($a_column_name))
00298 {
00299 $this->ilErr->raiseError(get_class($this)."::setTreeTablePK(): No column name given!",$this->ilErr->WARNING);
00300 }
00301
00302 $this->tree_pk = $a_column_name;
00303 return true;
00304 }
00305
00311 function buildJoin()
00312 {
00313 if ($this->table_obj_reference)
00314 {
00315 return "LEFT JOIN ".$this->table_obj_reference." ON ".$this->table_tree.".child=".$this->table_obj_reference.".".$this->ref_pk." ".
00316 "LEFT JOIN ".$this->table_obj_data." ON ".$this->table_obj_reference.".".$this->obj_pk."=".$this->table_obj_data.".".$this->obj_pk." ";
00317 }
00318 else
00319 {
00320 return "LEFT JOIN ".$this->table_obj_data." ON ".$this->table_tree.".child=".$this->table_obj_data.".".$this->obj_pk." ";
00321 }
00322 }
00323
00332 function getChilds($a_node_id, $a_order = "", $a_direction = "ASC")
00333 {
00334 global $ilBench;
00335
00336 if (!isset($a_node_id))
00337 {
00338 $message = get_class($this)."::getChilds(): No node_id given!";
00339 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
00340 }
00341
00342
00343 $childs = array();
00344
00345
00346 $count = 0;
00347
00348
00349 $order_clause = "";
00350
00351
00352 if (!empty($a_order))
00353 {
00354 $order_clause = "ORDER BY ".$a_order." ".$a_direction;
00355 }
00356 else
00357 {
00358 $order_clause = "ORDER BY ".$this->table_tree.".lft";
00359 }
00360
00361
00362 $q = "SELECT * FROM ".$this->table_tree." ".
00363 $this->buildJoin().
00364 "WHERE parent = ".$this->ilDB->quote($a_node_id)." ".
00365 "AND ".$this->table_tree.".".$this->tree_pk." = ".$this->ilDB->quote($this->tree_id)." ".
00366 $order_clause;
00367
00368
00369 $r = $this->ilDB->query($q);
00370
00371
00372 $count = $r->numRows();
00373
00374
00375 if ($count > 0)
00376 {
00377
00378 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
00379 {
00380 $childs[] = $this->fetchNodeData($row);
00381 }
00382
00383
00384
00385 $childs[$count - 1]["last"] = true;
00386 return $childs;
00387 }
00388 else
00389 {
00390 return $childs;
00391 }
00392 }
00393
00403 function getFilteredChilds($a_filter,$a_node,$a_order = "",$a_direction = "ASC")
00404 {
00405 $childs = $this->getChilds($a_node,$a_order,$a_direction);
00406
00407 foreach($childs as $child)
00408 {
00409 if(!in_array($child["type"],$a_filter))
00410 {
00411 $filtered[] = $child;
00412 }
00413 }
00414 return $filtered ? $filtered : array();
00415 }
00416
00417
00425 function getChildsByType($a_node_id,$a_type)
00426 {
00427 if (!isset($a_node_id) or !isset($a_type))
00428 {
00429 $message = get_class($this)."::getChildsByType(): Missing parameter! node_id:".$a_node_id." type:".$a_type;
00430 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
00431 }
00432
00433
00434 $childs = array();
00435
00436 $q = "SELECT * FROM ".$this->table_tree." ".
00437 $this->buildJoin().
00438 "WHERE parent = '".$a_node_id."' ".
00439 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."' ".
00440 "AND ".$this->table_obj_data.".type='".$a_type."' ".
00441 "ORDER BY ".$this->table_tree.".lft";
00442 $r = $this->ilDB->query($q);
00443
00444 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
00445 {
00446 $childs[] = $this->fetchNodeData($row);
00447 }
00448
00449
00450 return $childs;
00451 }
00452
00453
00461 function insertNode($a_node_id, $a_parent_id, $a_pos = IL_LAST_NODE, $a_reset_deletion_date = false)
00462 {
00463
00464
00465 if($this->__isMainTree())
00466 {
00467 if($a_node_id <= 1 or $a_parent_id <= 0)
00468 {
00469 $message = sprintf('%s::insertNode(): Invalid parameters! $a_node_id: %s $a_parent_id: %s',
00470 get_class($this),
00471 $a_node_id,
00472 $a_parent_id);
00473 $this->log->write($message,$this->log->FATAL);
00474 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
00475 }
00476 }
00477
00478
00479 if (!isset($a_node_id) or !isset($a_parent_id))
00480 {
00481 $this->ilErr->raiseError(get_class($this)."::insertNode(): Missing parameter! ".
00482 "node_id: ".$a_node_id." parent_id: ".$a_parent_id,$this->ilErr->WARNING);
00483 }
00484 if ($this->isInTree($a_node_id))
00485 {
00486 $this->ilErr->raiseError(get_class($this)."::insertNode(): Node ".$a_node_id." already in tree ".
00487 $this->table_tree."!",$this->ilErr->WARNING);
00488 }
00489
00490
00491 switch ($a_pos)
00492 {
00493 case IL_FIRST_NODE:
00494
00495 if($this->__isMainTree())
00496 {
00497 ilDBx::_lockTables(array('tree' => 'WRITE'));
00498 }
00499
00500
00501 $q = "SELECT * FROM ".$this->table_tree." ".
00502 "WHERE child = '".$a_parent_id."' ".
00503 "AND ".$this->tree_pk." = '".$this->tree_id."'";
00504 $res = $this->ilDB->query($q);
00505 $r = $res->fetchRow(DB_FETCHMODE_OBJECT);
00506
00507 if ($r->parent == NULL)
00508 {
00509 if($this->__isMainTree())
00510 {
00511 ilDBx::_unlockTables();
00512 }
00513 $this->ilErr->raiseError(get_class($this)."::insertNode(): Parent with ID ".$a_parent_id." not found in ".
00514 $this->table_tree."!",$this->ilErr->WARNING);
00515 }
00516
00517 $left = $r->lft;
00518 $lft = $left + 1;
00519 $rgt = $left + 2;
00520
00521
00522 $q = "UPDATE ".$this->table_tree." SET ".
00523 "lft = CASE ".
00524 "WHEN lft > ".$left." ".
00525 "THEN lft + 2 ".
00526 "ELSE lft ".
00527 "END, ".
00528 "rgt = CASE ".
00529 "WHEN rgt > ".$left." ".
00530 "THEN rgt + 2 ".
00531 "ELSE rgt ".
00532 "END ".
00533 "WHERE ".$this->tree_pk." = '".$this->tree_id."'";
00534 $this->ilDB->query($q);
00535 break;
00536
00537 case IL_LAST_NODE:
00538
00539 if ($this->gap > 0)
00540 {
00541 if($this->__isMainTree())
00542 {
00543 ilDBx::_lockTables(array('tree' => 'WRITE'));
00544 }
00545
00546
00547 $q = 'SELECT rgt,lft,parent FROM '.$this->table_tree.' '.
00548 'WHERE child = '.$a_parent_id.' '.
00549 'AND '.$this->tree_pk.' = '.$this->tree_id;
00550 $res = $this->ilDB->query($q);
00551 $r = $res->fetchRow(DB_FETCHMODE_ASSOC);
00552
00553
00554 if ($r['parent'] == NULL)
00555 {
00556 if($this->__isMainTree())
00557 {
00558 ilDBx::_unlockTables();
00559 }
00560 $this->ilErr->raiseError(get_class($this)."::insertNode(): Parent with ID ".
00561 $a_parent_id." not found in ".$this->table_tree."!",$this->ilErr->WARNING);
00562 }
00563 $parentRgt = $r['rgt'];
00564 $parentLft = $r['lft'];
00565
00566
00567 $availableSpace = $parentRgt - $parentLft;
00568 if ($availableSpace < 2)
00569 {
00570
00571
00572 $lft = $parentRgt;
00573 }
00574 else
00575 {
00576
00577
00578
00579 $q = 'SELECT MAX(rgt) AS max_rgt FROM '.$this->table_tree.' '.
00580 'WHERE parent = '.$a_parent_id.' '.
00581 'AND '.$this->tree_pk.' = '.$this->tree_id;
00582 $res = $this->ilDB->query($q);
00583 $r = $res->fetchRow(DB_FETCHMODE_ASSOC);
00584 if (isset($r['max_rgt']))
00585 {
00586
00587
00588 $availableSpace = $parentRgt - $r['max_rgt'];
00589 $lft = $r['max_rgt'] + 1;
00590 }
00591 else
00592 {
00593
00594
00595
00596 $lft = $parentLft + 1;
00597 }
00598 }
00599 $rgt = $lft + 1;
00600
00601
00602
00603 if ($availableSpace < 2)
00604 {
00605 $this->log->write('ilTree.insertNode('.$a_node_id.','.$a_parent_id.') creating gap at '.$a_parent_id.' '.$parentLft.'..'.$parentRgt.'+'.(2 + $this->gap * 2));
00606 $q = "UPDATE ".$this->table_tree." SET ".
00607 "lft = CASE ".
00608 "WHEN lft > ".$parentRgt." ".
00609 "THEN lft + ".(2 + $this->gap * 2).' '.
00610 "ELSE lft ".
00611 "END, ".
00612 "rgt = CASE ".
00613 "WHEN rgt >= ".$parentRgt." ".
00614 "THEN rgt + ".(2 + $this->gap * 2).' '.
00615 "ELSE rgt ".
00616 "END ".
00617 "WHERE ".$this->tree_pk." = '".$this->tree_id."'";
00618 $this->ilDB->query($q);
00619 }
00620 else
00621 {
00622 $this->log->write('ilTree.insertNode('.$a_node_id.','.$a_parent_id.') reusing gap at '.$a_parent_id.' '.$parentLft.'..'.$parentRgt.' for node '.$a_node_id.' '.$lft.'..'.$rgt);
00623 }
00624 }
00625
00626 else
00627 {
00628 if($this->__isMainTree())
00629 {
00630 ilDBx::_lockTables(array('tree' => 'WRITE'));
00631 }
00632
00633
00634 $q = "SELECT * FROM ".$this->table_tree." ".
00635 "WHERE child = '".$a_parent_id."' ".
00636 "AND ".$this->tree_pk." = '".$this->tree_id."'";
00637 $res = $this->ilDB->query($q);
00638 $r = $res->fetchRow(DB_FETCHMODE_OBJECT);
00639
00640 if ($r->parent == NULL)
00641 {
00642 if($this->__isMainTree())
00643 {
00644 ilDBx::_unlockTables();
00645 }
00646 $this->ilErr->raiseError(get_class($this)."::insertNode(): Parent with ID ".
00647 $a_parent_id." not found in ".$this->table_tree."!",$this->ilErr->WARNING);
00648 }
00649
00650 $right = $r->rgt;
00651 $lft = $right;
00652 $rgt = $right + 1;
00653
00654
00655 $q = "UPDATE ".$this->table_tree." SET ".
00656 "lft = CASE ".
00657 "WHEN lft > ".$right." ".
00658 "THEN lft + 2 ".
00659 "ELSE lft ".
00660 "END, ".
00661 "rgt = CASE ".
00662 "WHEN rgt >= ".$right." ".
00663 "THEN rgt + 2 ".
00664 "ELSE rgt ".
00665 "END ".
00666 "WHERE ".$this->tree_pk." = '".$this->tree_id."'";
00667 $this->ilDB->query($q);
00668 }
00669
00670 break;
00671
00672 default:
00673
00674
00675 if($this->__isMainTree())
00676 {
00677 ilDBx::_lockTables(array('tree' => 'WRITE'));
00678 }
00679
00680
00681 $q = "SELECT * FROM ".$this->table_tree." ".
00682 "WHERE child = '".$a_pos."' ".
00683 "AND ".$this->tree_pk." = '".$this->tree_id."'";
00684 $res = $this->ilDB->query($q);
00685 $r = $res->fetchRow(DB_FETCHMODE_OBJECT);
00686
00687
00688 if ($r->parent != $a_parent_id)
00689 {
00690 if($this->__isMainTree())
00691 {
00692 ilDBx::_unlockTables();
00693 }
00694 $this->ilErr->raiseError(get_class($this)."::insertNode(): Parents mismatch! ".
00695 "new node parent: ".$a_parent_id." sibling parent: ".$r->parent,$this->ilErr->WARNING);
00696 }
00697
00698 $right = $r->rgt;
00699 $lft = $right + 1;
00700 $rgt = $right + 2;
00701
00702
00703 $q = "UPDATE ".$this->table_tree." SET ".
00704 "lft = CASE ".
00705 "WHEN lft > ".$right." ".
00706 "THEN lft + 2 ".
00707 "ELSE lft ".
00708 "END, ".
00709 "rgt = CASE ".
00710 "WHEN rgt > ".$right." ".
00711 "THEN rgt + 2 ".
00712 "ELSE rgt ".
00713 "END ".
00714 "WHERE ".$this->tree_pk." = '".$this->tree_id."'";
00715 $this->ilDB->query($q);
00716 break;
00717
00718 }
00719
00720
00721 $depth = $this->getDepth($a_parent_id) + 1;
00722
00723
00724 $this->log->write('ilTree.insertNode('.$a_node_id.','.$a_parent_id.') inserting node:'.$a_node_id.' parent:'.$a_parent_id." ".$lft."..".$rgt." depth:".$depth);
00725 $q = "INSERT INTO ".$this->table_tree." (".$this->tree_pk.",child,parent,lft,rgt,depth) ".
00726 "VALUES ".
00727 "('".$this->tree_id."','".$a_node_id."','".$a_parent_id."','".$lft."','".$rgt."','".$depth."')";
00728
00729 $this->ilDB->query($q);
00730
00731
00732 if($this->__isMainTree())
00733 {
00734 ilDBx::_unlockTables();
00735 }
00736
00737
00738 if ($a_reset_deletion_date)
00739 {
00740 ilObject::_resetDeletedDate($a_node_id);
00741 }
00742 }
00743
00752 function getSubTree($a_node,$a_with_data = true, $a_type = "")
00753 {
00754 if (!is_array($a_node))
00755 {
00756 $this->ilErr->raiseError(get_class($this)."::getSubTree(): Wrong datatype for node_data! ",$this->ilErr->WARNING);
00757 }
00758
00759 if($a_node['lft'] < 1 or $a_node['rgt'] < 2)
00760 {
00761 $message = sprintf('%s::getSubTree(): Invalid node given! $a_node["lft"]: %s $a_node["rgt"]: %s',
00762 get_class($this),
00763 $a_node['lft'],
00764 $a_node['rgt']);
00765
00766 $this->log->write($message,$this->log->FATAL);
00767
00768 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
00769 }
00770
00771 $subtree = array();
00772
00773 $type_str = "";
00774 if ($a_type != "")
00775 {
00776 $type_str = "AND ".$this->table_obj_data.".type='".$a_type."' ";
00777 }
00778
00779 $q = "SELECT * FROM ".$this->table_tree." ".
00780 $this->buildJoin().
00781 "WHERE ".$this->table_tree.".lft BETWEEN '".$a_node["lft"]."' AND '".$a_node["rgt"]."' ".
00782 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."' ".
00783 $type_str.
00784 "ORDER BY ".$this->table_tree.".lft";
00785
00786 $r = $this->ilDB->query($q);
00787
00788 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
00789 {
00790 if($a_with_data)
00791 {
00792 $subtree[] = $this->fetchNodeData($row);
00793 }
00794 else
00795 {
00796 $subtree[] = $row['child'];
00797 }
00798 $this->in_tree_cache[$row['child']] = true;
00799 }
00800
00801 return $subtree ? $subtree : array();
00802 }
00803
00812 function getSubTreeTypes($a_node,$a_filter = 0)
00813 {
00814 $a_filter = $a_filter ? $a_filter : array();
00815
00816 foreach($this->getSubtree($this->getNodeData($a_node)) as $node)
00817 {
00818 if(in_array($node["type"],$a_filter))
00819 {
00820 continue;
00821 }
00822 $types["$node[type]"] = $node["type"];
00823 }
00824 return $types ? $types : array();
00825 }
00826
00832 function deleteTree($a_node)
00833 {
00834 if (!is_array($a_node))
00835 {
00836 $this->ilErr->raiseError(get_class($this)."::deleteTree(): Wrong datatype for node_data! ",$this->ilErr->WARNING);
00837 }
00838 if($this->__isMainTree() and $a_node[$this->tree_pk] === 1)
00839 {
00840 if($a_node['lft'] <= 1 or $a_node['rgt'] <= 2)
00841 {
00842 $message = sprintf('%s::deleteTree(): Invalid parameters given: $a_node["lft"]: %s, $a_node["rgt"] %s',
00843 get_class($this),
00844 $a_node['lft'],
00845 $a_node['rgt']);
00846
00847 $this->log->write($message,$this->log->FATAL);
00848 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
00849 }
00850 else if(!$this->__checkDelete($a_node))
00851 {
00852 $message = sprintf('%s::deleteTree(): Check delete failed: $a_node["lft"]: %s, $a_node["rgt"] %s',
00853 get_class($this),
00854 $a_node['lft'],
00855 $a_node['rgt']);
00856 $this->log->write($message,$this->log->FATAL);
00857 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
00858 }
00859
00860 }
00861 $diff = $a_node["rgt"] - $a_node["lft"] + 1;
00862
00863
00864
00865
00866 if($this->__isMainTree())
00867 {
00868 ilDBx::_lockTables(array('tree' => 'WRITE'));
00869 }
00870
00871 $query = "SELECT * FROM ".$this->table_tree." ".
00872 "WHERE child = '".$a_node['child']."' ".
00873 "AND ".$this->tree_pk." = '".$a_node[$this->tree_pk]."'";
00874
00875 $res = $this->ilDB->query($query);
00876 while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
00877 {
00878 $a_node['lft'] = $row->lft;
00879 $a_node['rgt'] = $row->rgt;
00880 $diff = $a_node["rgt"] - $a_node["lft"] + 1;
00881 }
00882
00883
00884 $q = "DELETE FROM ".$this->table_tree." ".
00885 "WHERE lft BETWEEN '".$a_node["lft"]."' AND '".$a_node["rgt"]."' ".
00886 "AND rgt BETWEEN '".$a_node["lft"]."' AND '".$a_node["rgt"]."' ".
00887 "AND ".$this->tree_pk." = '".$a_node[$this->tree_pk]."'";
00888 $this->ilDB->query($q);
00889
00890
00891 if ($a_node['rgt'] - $a_node['lft'] >= $this->gap * 2)
00892 {
00893 $this->log->write('ilTree.deleteTree('.$a_node['child'].') closing gap at '.$a_node['lft'].'...'.$a_node['rgt']);
00894
00895 $q = "UPDATE ".$this->table_tree." SET ".
00896 "lft = CASE ".
00897 "WHEN lft > '".$a_node["lft"]." '".
00898 "THEN lft - '".$diff." '".
00899 "ELSE lft ".
00900 "END, ".
00901 "rgt = CASE ".
00902 "WHEN rgt > '".$a_node["lft"]." '".
00903 "THEN rgt - '".$diff." '".
00904 "ELSE rgt ".
00905 "END ".
00906 "WHERE ".$this->tree_pk." = '".$a_node[$this->tree_pk]."'";
00907 $this->ilDB->query($q);
00908 }
00909 else
00910 {
00911 $this->log->write('ilTree.deleteTree('.$a_node['child'].') leaving gap open '.$a_node['lft'].'...'.$a_node['rgt']);
00912 }
00913
00914 if($this->__isMainTree())
00915 {
00916 ilDBx::_unlockTables();
00917 }
00918
00919 }
00920
00931 function getPathFull($a_endnode_id, $a_startnode_id = 0)
00932 {
00933 $pathIds =& $this->getPathId($a_endnode_id, $a_startnode_id);
00934 $dataPath = array();
00935 foreach ($pathIds as $id) {
00936 $dataPath[] = $this->getNodeData($id);
00937 }
00938
00939 return $dataPath;
00940 }
00949 function getPathIdsUsingNestedSets($a_endnode_id, $a_startnode_id = 0)
00950 {
00951
00952
00953
00954
00955
00956
00957
00958 if (!isset($a_endnode_id))
00959 {
00960 $this->ilErr->raiseError(get_class($this)."::getPathId(): No endnode_id given! ",$this->ilErr->WARNING);
00961 }
00962
00963 $q = "SELECT T2.child ".
00964 "FROM ".$this->table_tree." AS T1, ".$this->table_tree." AS T2 ".
00965 "WHERE T1.child = '".$a_endnode_id."' ".
00966 "AND T1.lft BETWEEN T2.lft AND T2.rgt ".
00967 "AND T1.".$this->tree_pk." = '".$this->tree_id." '".
00968 "AND T2.".$this->tree_pk." = '".$this->tree_id." '".
00969 "ORDER BY T2.depth";
00970
00971 $r = $this->ilDB->query($q);
00972 $takeId = $a_startnode_id == 0;
00973 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
00974 {
00975 if ($takeId || $row['child'] == $a_startnode_id)
00976 {
00977 $takeId = true;
00978 $pathIds[] = $row['child'];
00979 }
00980 }
00981 return $pathIds;
00982
00983 }
00992 function getPathIdsUsingAdjacencyMap($a_endnode_id, $a_startnode_id = 0)
00993 {
00994
00995
00996
00997
00998
00999 $takeId = $a_startnode_id == 0;
01000
01001 if (!isset($a_endnode_id))
01002 {
01003 $this->ilErr->raiseError(get_class($this)."::getPathId(): No endnode_id given! ",$this->ilErr->WARNING);
01004 }
01005
01006 global $log, $ilDB;
01007
01008
01009 $q = 'SELECT t.depth,t.parent '.
01010 'FROM '.$this->table_tree.' AS t '.
01011 'WHERE child='.$this->ilDB->quote($a_endnode_id).' '.
01012 'AND '.$this->tree_pk.' = '.$this->tree_id.' '.
01013 'LIMIT 1';
01014
01015 $r = $this->ilDB->query($q);
01016
01017 if ($r->numRows() == 0)
01018 {
01019 return array();
01020 }
01021 $row = $r->fetchRow(DB_FETCHMODE_ASSOC);
01022 $nodeDepth = $row['depth'];
01023 $parentId = $row['parent'];
01024
01025
01026
01027 $pathIds = array();
01028 if ($nodeDepth == 1)
01029 {
01030 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
01031 if ($takeId) $pathIds[] = $a_endnode_id;
01032 }
01033 else if ($nodeDepth == 2)
01034 {
01035 $takeId = $takeId || $parentId == $a_startnode_id;
01036 if ($takeId) $pathIds[] = $parentId;
01037 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
01038 if ($takeId) $pathIds[] = $a_endnode_id;
01039 }
01040 else if ($nodeDepth == 3)
01041 {
01042 $takeId = $takeId || $this->root_id == $a_startnode_id;
01043 if ($takeId) $pathIds[] = $this->root_id;
01044 $takeId = $takeId || $parentId == $a_startnode_id;
01045 if ($takeId) $pathIds[] = $parentId;
01046 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
01047 if ($takeId) $pathIds[] = $a_endnode_id;
01048 }
01049 else if ($nodeDepth < 32)
01050 {
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060 $qSelect = 't1.child as c0';
01061 $qJoin = '';
01062 for ($i = 1; $i < $nodeDepth - 2; $i++)
01063 {
01064 $qSelect .= ', t'.$i.'.parent as c'.$i;
01065 $qJoin .= ' JOIN '.$this->table_tree.' AS t'.$i.' ON '.
01066 't'.$i.'.child=t'.($i - 1).'.parent AND '.
01067 't'.$i.'.'.$this->tree_pk.' = '.$this->tree_id;
01068 }
01069 $q = 'SELECT '.$qSelect.' '.
01070 'FROM '.$this->table_tree.' AS t0 '.$qJoin.' '.
01071 'WHERE t0.'.$this->tree_pk.' = '.$this->tree_id.' '.
01072 'AND t0.child='.$parentId.' '.
01073 'LIMIT 1';
01074 $r = $this->ilDB->query($q);
01075 if ($r->numRows() == 0)
01076 {
01077 return array();
01078 }
01079 $row = $r->fetchRow(DB_FETCHMODE_ASSOC);
01080
01081 $takeId = $takeId || $this->root_id == $a_startnode_id;
01082 if ($takeId) $pathIds[] = $this->root_id;
01083 for ($i = $nodeDepth - 4; $i >=0; $i--)
01084 {
01085 $takeId = $takeId || $row['c'.$i] == $a_startnode_id;
01086 if ($takeId) $pathIds[] = $row['c'.$i];
01087 }
01088 $takeId = $takeId || $parentId == $a_startnode_id;
01089 if ($takeId) $pathIds[] = $parentId;
01090 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
01091 if ($takeId) $pathIds[] = $a_endnode_id;
01092 }
01093 else
01094 {
01095
01096 return $this->getPathIdsUsingNestedSets($a_endnode_id, $a_startnode_id);
01097 }
01098
01099 return $pathIds;
01100 }
01101
01110 function getPathId($a_endnode_id, $a_startnode_id = 0)
01111 {
01112
01113 if ($this->use_cache && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id]))
01114 {
01115
01116 return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
01117 }
01118
01119
01120 $pathIds =& $this->getPathIdsUsingAdjacencyMap($a_endnode_id, $a_startnode_id);
01121
01122 $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
01123 return $pathIds;
01124 }
01125
01132 function checkTree()
01133 {
01134 $q = "SELECT lft,rgt FROM ".$this->table_tree." ".
01135 "WHERE ".$this->tree_pk." = '".$this->tree_id."'";
01136
01137 $r = $this->ilDB->query($q);
01138
01139 while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
01140 {
01141 $lft[] = $row->lft;
01142 $rgt[] = $row->rgt;
01143 }
01144
01145 $all = array_merge($lft,$rgt);
01146 $uni = array_unique($all);
01147
01148 if (count($all) != count($uni))
01149 {
01150 $message = sprintf('%s::checkTree(): Tree is corrupted!',
01151 get_class($this));
01152
01153 $this->log->write($message,$this->log->FATAL);
01154 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
01155 }
01156
01157 return true;
01158 }
01159
01163 function checkTreeChilds($a_no_zero_child = true)
01164 {
01165 $q = "SELECT * FROM ".$this->table_tree." ".
01166 "WHERE ".$this->tree_pk." = '".$this->tree_id."' ".
01167 "ORDER BY lft";
01168 $r1 = $this->ilDB->query($q);
01169 while ($row = $r1->fetchRow(DB_FETCHMODE_ASSOC))
01170 {
01171
01172 if (($row["child"] == 0) && $a_no_zero_child)
01173 {
01174 $this->ilErr->raiseError(get_class($this)."::checkTreeChilds(): Tree contains child with ID 0!",$this->ilErr->WARNING);
01175 }
01176
01177 if ($this->table_obj_reference)
01178 {
01179
01180 $q = "SELECT * FROM ".$this->table_obj_reference." WHERE ".$this->ref_pk."='".$row["child"]."'";
01181 $r2 = $this->ilDB->query($q);
01182
01183 if ($r2->numRows() == 0)
01184 {
01185 $this->ilErr->raiseError(get_class($this)."::checkTree(): No Object-to-Reference entry found for ID ".
01186 $row["child"]."!",$this->ilErr->WARNING);
01187 }
01188 if ($r2->numRows() > 1)
01189 {
01190 $this->ilErr->raiseError(get_class($this)."::checkTree(): More Object-to-Reference entries found for ID ".
01191 $row["child"]."!",$this->ilErr->WARNING);
01192 }
01193
01194
01195 $obj_ref = $r2->fetchRow(DB_FETCHMODE_ASSOC);
01196
01197 $q = "SELECT * FROM ".$this->table_obj_data." WHERE ".$this->obj_pk."='".$obj_ref[$this->obj_pk]."'";
01198 $r3 = $this->ilDB->query($q);
01199 if ($r3->numRows() == 0)
01200 {
01201 $this->ilErr->raiseError(get_class($this)."::checkTree(): No child found for ID ".
01202 $obj_ref[$this->obj_pk]."!",$this->ilErr->WARNING);
01203 }
01204 if ($r3->numRows() > 1)
01205 {
01206 $this->ilErr->raiseError(get_class($this)."::checkTree(): More childs found for ID ".
01207 $obj_ref[$this->obj_pk]."!",$this->ilErr->WARNING);
01208 }
01209
01210 }
01211 else
01212 {
01213
01214 $q = "SELECT * FROM ".$this->table_obj_data." WHERE ".$this->obj_pk."='".$row["child"]."'";
01215 $r2 = $this->ilDB->query($q);
01216
01217 if ($r2->numRows() == 0)
01218 {
01219 $this->ilErr->raiseError(get_class($this)."::checkTree(): No child found for ID ".
01220 $row["child"]."!",$this->ilErr->WARNING);
01221 }
01222 if ($r2->numRows() > 1)
01223 {
01224 $this->ilErr->raiseError(get_class($this)."::checkTree(): More childs found for ID ".
01225 $row["child"]."!",$this->ilErr->WARNING);
01226 }
01227 }
01228 }
01229
01230 return true;
01231 }
01232
01238 function getMaximumDepth()
01239 {
01240 $q = "SELECT MAX(depth) FROM ".$this->table_tree;
01241 $r = $this->ilDB->query($q);
01242
01243 $row = $r->fetchRow();
01244
01245 return $row[0];
01246 }
01247
01254 function getDepth($a_node_id)
01255 {
01256 if ($a_node_id)
01257 {
01258 $q = "SELECT depth FROM ".$this->table_tree." ".
01259 "WHERE child = '".$a_node_id."' ".
01260 "AND ".$this->tree_pk." = '".$this->tree_id."'";
01261
01262 $res = $this->ilDB->query($q);
01263 $row = $res->fetchRow(DB_FETCHMODE_OBJECT);
01264
01265 return $row->depth;
01266 }
01267 else
01268 {
01269 return 1;
01270 }
01271 }
01272
01273
01281 function getNodeData($a_node_id)
01282 {
01283 if (!isset($a_node_id))
01284 {
01285 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
01286 }
01287 if($this->__isMainTree())
01288 {
01289 if($a_node_id < 1)
01290 {
01291 $message = sprintf('%s::getNodeData(): No valid parameter given! $a_node_id: %s',
01292 get_class($this),
01293 $a_node_id);
01294
01295 $this->log->write($message,$this->log->FATAL);
01296 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
01297 }
01298 }
01299
01300 $q = "SELECT * FROM ".$this->table_tree." ".
01301 $this->buildJoin().
01302 "WHERE ".$this->table_tree.".child = ".$this->ilDB->quote($a_node_id)." ".
01303 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."'";
01304 $r = $this->ilDB->query($q);
01305 $row = $r->fetchRow(DB_FETCHMODE_ASSOC);
01306 $row[$this->tree_pk] = $this->tree_id;
01307
01308 return $this->fetchNodeData($row);
01309 }
01310
01318 function fetchNodeData($a_row)
01319 {
01320 global $objDefinition, $lng, $ilBench;
01321
01322
01323 $data = $a_row;
01324 $data["desc"] = $a_row["description"];
01325
01326
01327
01328
01329 if (is_object($objDefinition))
01330 {
01331 $translation_type = $objDefinition->getTranslationType($data["type"]);
01332 }
01333
01334
01335 if ($translation_type == "sys")
01336 {
01337
01338 if ($data["type"] == "rolf" and $data["obj_id"] != ROLE_FOLDER_ID)
01339 {
01340 $data["description"] = $lng->txt("obj_".$data["type"]."_local_desc").$data["title"].$data["desc"];
01341 $data["desc"] = $lng->txt("obj_".$data["type"]."_local_desc").$data["title"].$data["desc"];
01342 $data["title"] = $lng->txt("obj_".$data["type"]."_local");
01343 }
01344 else
01345 {
01346 $data["title"] = $lng->txt("obj_".$data["type"]);
01347 $data["description"] = $lng->txt("obj_".$data["type"]."_desc");
01348 $data["desc"] = $lng->txt("obj_".$data["type"]."_desc");
01349 }
01350
01351 }
01352 elseif ($translation_type == "db")
01353 {
01354
01355 $q = "SELECT title,description FROM object_translation ".
01356 "WHERE obj_id = ".$data["obj_id"]." ".
01357 "AND lang_code = '".$this->lang_code."' ".
01358 "AND NOT lang_default = 1";
01359 $r = $this->ilDB->query($q);
01360
01361 $row = $r->fetchRow(DB_FETCHMODE_OBJECT);
01362
01363 if ($row)
01364 {
01365 $data["title"] = $row->title;
01366 $data["description"] = ilUtil::shortenText($row->description,MAXLENGTH_OBJ_DESC,true);
01367 $data["desc"] = $row->description;
01368 }
01369
01370 }
01371
01372 return $data ? $data : array();
01373 }
01374
01375
01383 function isInTree($a_node_id)
01384 {
01385 if (!isset($a_node_id))
01386 {
01387 return false;
01388 #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
01389 }
01390
01391
01392 if ($this->use_cache && isset($this->in_tree_cache[$a_node_id]))
01393 {
01394
01395 return $this->in_tree_cache[$a_node_id];
01396 }
01397
01398 $q = "SELECT * FROM ".$this->table_tree." ".
01399 "WHERE ".$this->table_tree.".child = ".$this->ilDB->quote($a_node_id)." ".
01400 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."'";
01401 $r = $this->ilDB->query($q);
01402
01403 if ($r->numRows() > 0)
01404 {
01405 $this->in_tree_cache[$a_node_id] = true;
01406 return true;
01407 }
01408 else
01409 {
01410 $this->in_tree_cache[$a_node_id] = false;
01411 return false;
01412 }
01413
01414 }
01415
01422 function getParentNodeData($a_node_id)
01423 {
01424 if (!isset($a_node_id))
01425 {
01426 $this->ilErr->raiseError(get_class($this)."::getParentNodeData(): No node_id given! ",$this->ilErr->WARNING);
01427 }
01428
01429 if ($this->table_obj_reference)
01430 {
01431 $leftjoin = "LEFT JOIN ".$this->table_obj_reference." ON v.child=".$this->table_obj_reference.".".$this->ref_pk." ".
01432 "LEFT JOIN ".$this->table_obj_data." ON ".$this->table_obj_reference.".".$this->obj_pk."=".$this->table_obj_data.".".$this->obj_pk." ";
01433 }
01434 else
01435 {
01436 $leftjoin = "LEFT JOIN ".$this->table_obj_data." ON v.child=".$this->table_obj_data.".".$this->obj_pk." ";
01437 }
01438
01439 $q = "SELECT * FROM ".$this->table_tree." s,".$this->table_tree." v ".
01440 $leftjoin.
01441 "WHERE s.child = '".$a_node_id."' ".
01442 "AND s.parent = v.child ".
01443 "AND s.lft > v.lft ".
01444 "AND s.rgt < v.rgt ".
01445 "AND s.".$this->tree_pk." = '".$this->tree_id."' ".
01446 "AND v.".$this->tree_pk." = '".$this->tree_id."'";
01447 $r = $this->ilDB->query($q);
01448
01449 $row = $r->fetchRow(DB_FETCHMODE_ASSOC);
01450
01451 return $this->fetchNodeData($row);
01452 }
01453
01461 function isGrandChild($a_startnode_id,$a_querynode_id)
01462 {
01463 if (!isset($a_startnode_id) or !isset($a_querynode_id))
01464 {
01465 return false;
01466
01467 #$this->ilErr->raiseError(get_class($this)."::isGrandChild(): Missing parameter! startnode: ".$a_startnode_id." querynode: ".
01468 # $a_querynode_id,$this->ilErr->WARNING);
01469 }
01470
01471 $q = "SELECT * FROM ".$this->table_tree." s,".$this->table_tree." v ".
01472 "WHERE s.child = '".$a_startnode_id."' ".
01473 "AND v.child = '".$a_querynode_id."' ".
01474 "AND s.".$this->tree_pk." = '".$this->tree_id."' ".
01475 "AND v.".$this->tree_pk." = '".$this->tree_id."' ".
01476 "AND v.lft BETWEEN s.lft AND s.rgt ".
01477 "AND v.rgt BETWEEN s.lft AND s.rgt";
01478 $r = $this->ilDB->query($q);
01479
01480 return $r->numRows();
01481 }
01482
01491 function addTree($a_tree_id,$a_node_id = -1)
01492 {
01493
01494
01495 if($this->__isMainTree())
01496 {
01497 $message = sprintf('%s::addTree(): Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
01498 get_class($this),
01499 $a_tree_id,
01500 $a_node_id);
01501 $this->log->write($message,$this->log->FATAL);
01502 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
01503 }
01504
01505 if (!isset($a_tree_id))
01506 {
01507 $this->ilErr->raiseError(get_class($this)."::addTree(): No tree_id given! ",$this->ilErr->WARNING);
01508 }
01509
01510 if ($a_node_id <= 0)
01511 {
01512 $a_node_id = $a_tree_id;
01513 }
01514
01515 $q = "INSERT INTO ".$this->table_tree." (".$this->tree_pk.", child, parent, lft, rgt, depth) ".
01516 "VALUES ".
01517 "('".$a_tree_id."','".$a_node_id."', 0, 1, 2, 1)";
01518
01519 $this->ilDB->query($q);
01520
01521 return true;
01522 }
01523
01531 function getNodeDataByType($a_type)
01532 {
01533 if (!isset($a_type) or (!is_string($a_type)))
01534 {
01535 $this->ilErr->raiseError(get_class($this)."::getNodeDataByType(): Type not given or wrong datatype!",$this->ilErr->WARNING);
01536 }
01537
01538 $data = array();
01539 $row = "";
01540 $left = "";
01541 $right = "";
01542
01543 $q = "SELECT * FROM ".$this->table_tree." ".
01544 "WHERE ".$this->tree_pk." = '".$this->tree_id."'".
01545 "AND parent = '0'";
01546 $r = $this->ilDB->query($q);
01547
01548 while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
01549 {
01550 $left = $row->lft;
01551 $right = $row->rgt;
01552 }
01553
01554 $q = "SELECT * FROM ".$this->table_tree." ".
01555 $this->buildJoin().
01556 "WHERE ".$this->table_obj_data.".type = '".$a_type."' ".
01557 "AND ".$this->table_tree.".lft BETWEEN '".$left."' AND '".$right."' ".
01558 "AND ".$this->table_tree.".rgt BETWEEN '".$left."' AND '".$right."' ".
01559 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."'";
01560 $r = $this->ilDB->query($q);
01561
01562 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
01563 {
01564 $data[] = $this->fetchNodeData($row);
01565 }
01566
01567 return $data;
01568 }
01569
01577 function removeTree($a_tree_id)
01578 {
01579
01580 if($this->__isMainTree())
01581 {
01582 $message = sprintf('%s::removeTree(): Operation not allowed on main tree! $a_tree_if: %s',
01583 get_class($this),
01584 $a_tree_id);
01585 $this->log->write($message,$this->log->FATAL);
01586 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
01587 }
01588 if (!$a_tree_id)
01589 {
01590 $this->ilErr->raiseError(get_class($this)."::removeTree(): No tree_id given! Action aborted",$this->ilErr->MESSAGE);
01591 }
01592
01593 $q = "DELETE FROM ".$this->table_tree." WHERE ".$this->tree_pk." = '".$a_tree_id."'";
01594 $this->ilDB->query($q);
01595
01596 return true;
01597 }
01598
01606 function saveSubTree($a_node_id, $a_set_deleted = false)
01607 {
01608 global $ilDB;
01609
01610 if (!$a_node_id)
01611 {
01612 $message = sprintf('%s::saveSubTree(): No valid parameter given! $a_node_id: %s',
01613 get_class($this),
01614 $a_node_id);
01615 $this->log->write($message,$this->log->FATAL);
01616 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
01617 }
01618
01619
01620 if($this->__isMainTree())
01621 {
01622 ilDBx::_lockTables(array('tree' => 'WRITE',
01623 'object_reference' => 'WRITE'));
01624 }
01625
01626
01627 $q = "SELECT * FROM ".$this->table_tree." ".
01628 "WHERE ".$this->tree_pk." = '".$this->tree_id."' ".
01629 "AND child = '".$a_node_id."' ";
01630 $r = $this->ilDB->query($q);
01631
01632 while($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
01633 {
01634 $lft = $row->lft;
01635 $rgt = $row->rgt;
01636 }
01637
01638
01639 $q = "SELECT child FROM ".$this->table_tree." ".
01640 "WHERE ".$this->tree_pk." = '".$this->tree_id."' ".
01641 "AND lft BETWEEN '".$lft."' AND '".$rgt."'";
01642 $r = $this->ilDB->query($q);
01643
01644 $subnodes = array();
01645 while($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
01646 {
01647 $subnodes[] = $row['child'];
01648 }
01649
01650 if(!count($subnodes))
01651 {
01652
01653
01654
01655 if($this->__isMainTree())
01656 {
01657 ilDBX::_unlockTables();
01658 }
01659
01660 return false;
01661 }
01662
01663
01664 foreach($subnodes as $child)
01665 {
01666 #$q = "INSERT INTO ".$this->table_tree." ".
01667 # "VALUES ('".-$a_node_id."','".$node["child"]."','".$node["parent"]."','".
01668 # $node["lft"]."','".$node["rgt"]."','".$node["depth"]."')";
01669 #$r = $this->ilDB->query($q);
01670
01671
01672 if ($a_set_deleted)
01673 {
01674 ilObject::_setDeletedDate($child);
01675 }
01676 }
01677
01678
01679 $query = "UPDATE ".$this->table_tree." ".
01680 "SET tree = ".$ilDB->quote(-$a_node_id)." ".
01681 "WHERE ".$this->tree_pk." = ".$ilDB->quote($this->tree_id)." ".
01682 "AND lft BETWEEN ".$ilDB->quote($lft)." AND ".$ilDB->quote($rgt)." ";
01683 $res = $ilDB->query($query);
01684
01685
01686 if($this->__isMainTree())
01687 {
01688 ilDBX::_unlockTables();
01689 }
01690
01691
01692 return true;
01693 }
01694
01698 function isDeleted($a_node_id)
01699 {
01700 return $this->isSaved($a_node_id);
01701 }
01702
01706 function isSaved($a_node_id)
01707 {
01708
01709 if ($this->use_cache && isset($this->is_saved_cache[$a_node_id]))
01710 {
01711
01712 return $this->is_saved_cache[$a_node_id];
01713 }
01714
01715 $q = "SELECT * FROM ".$this->table_tree." ".
01716 "WHERE child = '".$a_node_id."'";
01717 $s = $this->ilDB->query($q);
01718 $r = $s->fetchRow(DB_FETCHMODE_ASSOC);
01719
01720 if ($r[$this->tree_pk] < 0)
01721 {
01722 $this->is_saved_cache[$a_node_id] = true;
01723 return true;
01724 }
01725 else
01726 {
01727 $this->is_saved_cache[$a_node_id] = false;
01728 return false;
01729 }
01730 }
01731
01732
01733
01740 function getSavedNodeData($a_parent_id)
01741 {
01742 if (!isset($a_parent_id))
01743 {
01744 $this->ilErr->raiseError(get_class($this)."::getSavedNodeData(): No node_id given!",$this->ilErr->WARNING);
01745 }
01746
01747 $q = "SELECT * FROM ".$this->table_tree." ".
01748 $this->buildJoin().
01749 "WHERE ".$this->table_tree.".".$this->tree_pk." < 0 ".
01750 "AND ".$this->table_tree.".parent = '".$a_parent_id."' ";
01751 $r = $this->ilDB->query($q);
01752
01753 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
01754 {
01755 $saved[] = $this->fetchNodeData($row);
01756 }
01757
01758 return $saved ? $saved : array();
01759 }
01760
01767 function getParentId($a_node_id)
01768 {
01769 if (!isset($a_node_id))
01770 {
01771 $this->ilErr->raiseError(get_class($this)."::getParentId(): No node_id given! ",$this->ilErr->WARNING);
01772 }
01773
01774 $q = "SELECT parent FROM ".$this->table_tree." ".
01775 "WHERE child='".$a_node_id."' ".
01776 "AND ".$this->tree_pk."='".$this->tree_id."'";
01777 $r = $this->ilDB->query($q);
01778
01779 $row = $r->fetchRow(DB_FETCHMODE_OBJECT);
01780
01781 return $row->parent;
01782 }
01783
01790 function getLeftValue($a_node_id)
01791 {
01792 if (!isset($a_node_id))
01793 {
01794 $this->ilErr->raiseError(get_class($this)."::getLeftValued(): No node_id given! ",$this->ilErr->WARNING);
01795 }
01796
01797 $q = "SELECT lft FROM ".$this->table_tree." ".
01798 "WHERE child='".$a_node_id."' ".
01799 "AND ".$this->tree_pk."='".$this->tree_id."'";
01800 $r = $this->ilDB->query($q);
01801
01802 $row = $r->fetchRow(DB_FETCHMODE_OBJECT);
01803
01804 return $row->lft;
01805 }
01806
01813 function getChildSequenceNumber($a_node, $type = "")
01814 {
01815 if (!isset($a_node))
01816 {
01817 $this->ilErr->raiseError(get_class($this)."::getChildSequenceNumber(): No node_id given! ",$this->ilErr->WARNING);
01818 }
01819
01820 $type_str = ($type != "")
01821 ? "AND type='$type'"
01822 : "";
01823
01824 $q = "SELECT count(*) AS cnt FROM ".$this->table_tree." ".
01825 $this->buildJoin().
01826 "WHERE lft <=".$this->ilDB->quote($a_node["lft"])." ".
01827 $type_str.
01828 "AND parent=".$this->ilDB->quote($a_node["parent"])." ".
01829 "AND ".$this->table_tree.".".$this->tree_pk."=".$this->ilDB->quote($this->tree_id);
01830 $r = $this->ilDB->query($q);
01831
01832 $row = $r->fetchRow(DB_FETCHMODE_ASSOC);
01833
01834 return $row["cnt"];
01835 }
01836
01843 function readRootId()
01844 {
01845 $query = "SELECT child FROM $this->table_tree ".
01846 "WHERE parent = '0'".
01847 "AND ".$this->tree_pk." = '".$this->tree_id."'";
01848 $row = $this->ilDB->getRow($query,DB_FETCHMODE_OBJECT);
01849
01850 $this->root_id = $row->child;
01851 return $this->root_id;
01852 }
01853
01859 function getRootId()
01860 {
01861 return $this->root_id;
01862 }
01863 function setRootId($a_root_id)
01864 {
01865 $this->root_id = $a_root_id;
01866 }
01867
01873 function getTreeId()
01874 {
01875 return $this->tree_id;
01876 }
01877
01883 function setTreeId($a_tree_id)
01884 {
01885 $this->tree_id = $a_tree_id;
01886 }
01887
01895 function fetchSuccessorNode($a_node_id, $a_type = "")
01896 {
01897 if (!isset($a_node_id))
01898 {
01899 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
01900 }
01901
01902
01903 $q = "SELECT lft FROM ".$this->table_tree." ".
01904 "WHERE ".$this->table_tree.".child = '".$a_node_id."' ".
01905 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."'";
01906 $r = $this->ilDB->query($q);
01907 $curr_node = $r->fetchRow(DB_FETCHMODE_ASSOC);
01908
01909
01910 $type_where = ($a_type != "")
01911 ? "AND ".$this->table_obj_data.".type = '$a_type' "
01912 : "";
01913 $q = "SELECT * FROM ".$this->table_tree." ".
01914 $this->buildJoin().
01915 "WHERE lft > '".$curr_node["lft"]."' ".
01916 $type_where.
01917 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."'".
01918 "ORDER BY lft LIMIT 1";
01919 $r = $this->ilDB->query($q);
01920
01921 if ($r->numRows() < 1)
01922 {
01923 return false;
01924 }
01925 else
01926 {
01927 $row = $r->fetchRow(DB_FETCHMODE_ASSOC);
01928 return $this->fetchNodeData($row);
01929 }
01930 }
01931
01939 function fetchPredecessorNode($a_node_id, $a_type = "")
01940 {
01941 if (!isset($a_node_id))
01942 {
01943 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
01944 }
01945
01946
01947 $q = "SELECT lft FROM ".$this->table_tree." ".
01948 "WHERE ".$this->table_tree.".child = '".$a_node_id."' ".
01949 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."'";
01950 $r = $this->ilDB->query($q);
01951 $curr_node = $r->fetchRow(DB_FETCHMODE_ASSOC);
01952
01953
01954 $type_where = ($a_type != "")
01955 ? "AND ".$this->table_obj_data.".type = '$a_type' "
01956 : "";
01957 $q = "SELECT * FROM ".$this->table_tree." ".
01958 $this->buildJoin().
01959 "WHERE lft < '".$curr_node["lft"]."' ".
01960 $type_where.
01961 "AND ".$this->table_tree.".".$this->tree_pk." = '".$this->tree_id."'".
01962 "ORDER BY lft DESC LIMIT 1";
01963 $r = $this->ilDB->query($q);
01964
01965 if ($r->numRows() < 1)
01966 {
01967 return false;
01968 }
01969 else
01970 {
01971 $row = $r->fetchRow(DB_FETCHMODE_ASSOC);
01972 return $this->fetchNodeData($row);
01973 }
01974 }
01975
01984 function renumber($node_id = 1, $i = 1)
01985 {
01986
01987 if($this->__isMainTree())
01988 {
01989 ilDBx::_lockTables(array($this->table_tree => 'WRITE',
01990 $this->table_obj_data => 'WRITE',
01991 $this->table_obj_reference => 'WRITE',
01992 'object_translation' => 'WRITE'));
01993 }
01994 $return = $this->__renumber($node_id,$i);
01995 if($this->__isMainTree())
01996 {
01997 ilDBx::_unlockTables();
01998 }
01999
02000 return $return;
02001 }
02002
02003
02013 function __renumber($node_id = 1, $i = 1)
02014 {
02015 $q = "UPDATE ".$this->table_tree." SET lft='".$i."' WHERE child='".$node_id."'";
02016 $this->ilDB->query($q);
02017
02018 $childs = $this->getChilds($node_id);
02019
02020 foreach ($childs as $child)
02021 {
02022 $i = $this->__renumber($child["child"],$i+1);
02023 }
02024
02025 $i++;
02026
02027
02028 if (count($childs) > 0)
02029 {
02030 $i += $this->gap * 2;
02031 }
02032
02033 $q = "UPDATE ".$this->table_tree." SET rgt='".$i."' WHERE child='".$node_id."'";
02034 $this->ilDB->query($q);
02035
02036 return $i;
02037 }
02038
02039
02049 function checkForParentType($a_ref_id,$a_type)
02050 {
02051 if(!$this->isInTree($a_ref_id))
02052 {
02053 return false;
02054 }
02055 $path = array_reverse($this->getPathFull($a_ref_id));
02056
02057 foreach($path as $node)
02058 {
02059 if($node["type"] == $a_type)
02060 {
02061 return $node["child"];
02062 }
02063 }
02064 return 0;
02065 }
02066
02076 function _removeEntry($a_tree,$a_child,$a_db_table = "tree")
02077 {
02078 global $ilDB,$ilLog,$ilErr;
02079
02080 if($a_db_table === 'tree')
02081 {
02082 if($a_tree == 1 and $a_child == ROOT_FOLDER_ID)
02083 {
02084 $message = sprintf('%s::_removeEntry(): Tried to delete root node! $a_tree: %s $a_child: %s',
02085 get_class($this),
02086 $a_tree,
02087 $a_child);
02088 $ilLog->write($message,$ilLog->FATAL);
02089 $ilErr->raiseError($message,$ilErr->WARNING);
02090 }
02091 }
02092
02093 $q = "DELETE from ".$a_db_table." WHERE tree='".$a_tree."' AND child='".$a_child."'";
02094 $ilDB->query($q);
02095 }
02096
02103 function moveSubTreeAlex($a_source_id, $a_target_id)
02104 {
02105
02106 if($a_source_id <= 0 or $a_target_id <= 0)
02107 {
02108 $message = sprintf('%s::insertNode(): Invalid parameters! $a_source_id: %s $a_target_id: %s',
02109 get_class($this),
02110 $a_source_id,
02111 $a_target_id);
02112 $this->log->write($message,$this->log->FATAL);
02113 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
02114 }
02115 }
02116
02117
02118
02125 function __isMainTree()
02126 {
02127 return $this->table_tree === 'tree';
02128 }
02129
02138 function __checkDelete($a_node)
02139 {
02140
02141 $query = "SELECT * FROM ".$this->table_tree." ".
02142 "WHERE lft >= ".$a_node['lft']." ".
02143 "AND rgt <= ".$a_node['rgt']." ".
02144 "AND ".$this->tree_pk." = '".$a_node[$this->tree_pk]."'";
02145
02146
02147 $res = $this->ilDB->query($query);
02148
02149 $counter = (int) $lft_childs = array();
02150 while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
02151 {
02152 $lft_childs[$row->child] = $row->parent;
02153 ++$counter;
02154 }
02155
02156
02157 if($counter != count($lft_childs))
02158 {
02159 $message = sprintf('%s::__checkTree(): Duplicate entries for "child" in maintree! $a_node_id: %s',
02160 get_class($this),
02161 $a_node['child']);
02162 $this->log->write($message,$this->log->FATAL);
02163 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
02164 }
02165
02166
02167 $parent_childs = array();
02168 $this->__getSubTreeByParentRelation($a_node['child'],$parent_childs);
02169 $this->__validateSubtrees($lft_childs,$parent_childs);
02170
02171 return true;
02172 }
02173
02174 function __getSubTreeByParentRelation($a_node_id,&$parent_childs)
02175 {
02176
02177 $query = "SELECT * FROM ".$this->table_tree." ".
02178 "WHERE child = '".$a_node_id."' ".
02179 "AND tree = '".$this->tree_id."'";
02180
02181 $res = $this->ilDB->query($query);
02182 $counter = 0;
02183 while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
02184 {
02185 $parent_childs[$a_node_id] = $row->parent;
02186 ++$counter;
02187 }
02188
02189 if($counter > 1)
02190 {
02191 $message = sprintf('%s::__getSubTreeByParentRelation(): Multiple entries in maintree! $a_node_id: %s',
02192 get_class($this),
02193 $a_node_id);
02194 $this->log->write($message,$this->log->FATAL);
02195 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
02196 }
02197
02198
02199 $query = "SELECT * FROM ".$this->table_tree." ".
02200 "WHERE parent = '".$a_node_id."'";
02201
02202 $res = $this->ilDB->query($query);
02203 while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
02204 {
02205
02206 $this->__getSubTreeByParentRelation($row->child,$parent_childs);
02207 }
02208 return true;
02209 }
02210
02211 function __validateSubtrees(&$lft_childs,$parent_childs)
02212 {
02213
02214 ksort($lft_childs);
02215 ksort($parent_childs);
02216
02217 if(count($lft_childs) != count($parent_childs))
02218 {
02219 $message = sprintf('%s::__validateSubtrees(): (COUNT) Tree is corrupted! Left/Right subtree does not comply .'.
02220 'with parent relation',
02221 get_class($this));
02222 $this->log->write($message,$this->log->FATAL);
02223 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
02224 }
02225
02226 foreach($lft_childs as $key => $value)
02227 {
02228 if($parent_childs[$key] != $value)
02229 {
02230 $message = sprintf('%s::__validateSubtrees(): (COMPARE) Tree is corrupted! Left/Right subtree does not comply '.
02231 'with parent relation',
02232 get_class($this));
02233 $this->log->write($message,$this->log->FATAL);
02234 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
02235 }
02236 if($key == ROOT_FOLDER_ID)
02237 {
02238 $message = sprintf('%s::__validateSubtrees(): (ROOT_FOLDER) Tree is corrupted! Tried to delete root folder',
02239 get_class($this));
02240 $this->log->write($message,$this->log->FATAL);
02241 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
02242 }
02243 }
02244 return true;
02245 }
02246
02257 public function moveTree($a_source_id,$a_target_id,$a_location = IL_LAST_NODE)
02258 {
02259 if($this->__isMainTree())
02260 {
02261 ilDBx::_lockTables(array('tree' => 'WRITE'));
02262 }
02263
02264 $query = "SELECT * FROM ".$this->table_tree." ".
02265 "WHERE (child = ".$this->ilDB->quote($a_source_id)." ".
02266 "OR child = ".$this->ilDB->quote($a_target_id).") ".
02267 "AND tree = ".$this->ilDB->quote($this->tree_id);
02268 $res = $this->ilDB->query($query);
02269 #var_dump("<pre>",$query,"<pre>");
02270
02271
02272 if($res->numRows() != 2)
02273 {
02274 if($this->__isMainTree())
02275 {
02276 ilDBx::_unlockTables();
02277 }
02278 $this->log->write(__METHOD__.' Objects not found in tree!',$this->log->FATAL);
02279 $this->ilErr->raiseError('Error moving node',$this->ilErr->WARNING);
02280 }
02281 while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
02282 {
02283 if($row->child == $a_source_id)
02284 {
02285 $source_lft = $row->lft;
02286 $source_rgt = $row->rgt;
02287 $source_depth = $row->depth;
02288 $source_parent = $row->parent;
02289 }
02290 else
02291 {
02292 $target_lft = $row->lft;
02293 $target_rgt = $row->rgt;
02294 $target_depth = $row->depth;
02295 }
02296 }
02297
02298 #var_dump("<pre>",$source_lft,$source_rgt,$source_depth,$target_lft,$target_rgt,$target_depth,"<pre>");
02299
02300 if($target_lft >= $source_lft and $target_rgt <= $source_rgt)
02301 {
02302 if($this->__isMainTree())
02303 {
02304 ilDBx::_unlockTables();
02305 }
02306 $this->log->write(__METHOD__.' Target is child of source',$this->log->FATAL);
02307 $this->ilErr->raiseError('Error moving node',$this->ilErr->WARNING);
02308 }
02309
02310
02311
02312 $spread_diff = $source_rgt - $source_lft + 1;
02313 #var_dump("<pre>","SPREAD_DIFF: ",$spread_diff,"<pre>");
02314
02315 $query = "UPDATE ".$this->table_tree ." SET ".
02316 "lft = CASE ".
02317 "WHEN lft > ".$this->ilDB->quote($target_rgt)." ".
02318 "THEN lft + ".$this->ilDB->quote($spread_diff)." ".
02319 "ELSE lft ".
02320 "END, ".
02321 "rgt = CASE ".
02322 "WHEN rgt >= ".$this->ilDB->quote($target_rgt)." ".
02323 "THEN rgt + ".$this->ilDB->quote($spread_diff)." ".
02324 "ELSE rgt ".
02325 "END ".
02326 "WHERE tree = ".$this->ilDB->quote($this->tree_id);
02327 #var_dump("<pre>",$query,"<pre>");
02328 $res = $this->ilDB->query($query);
02329
02330
02331
02332 if($source_lft > $target_rgt)
02333 {
02334 $where_offset = $spread_diff;
02335 $move_diff = $target_rgt - $source_lft - $spread_diff;
02336 }
02337 else
02338 {
02339 $where_offset = 0;
02340 $move_diff = $target_rgt - $source_lft;
02341 }
02342 $depth_diff = $target_depth - $source_depth + 1;
02343
02344
02345 $query = "UPDATE ".$this->table_tree ." SET ".
02346 "parent = CASE ".
02347 "WHEN parent = ".$this->ilDB->quote($source_parent)." ".
02348 "THEN ".$this->ilDB->quote($a_target_id)." ".
02349 "ELSE parent ".
02350 "END, ".
02351 "rgt = rgt + ".$this->ilDB->quote($move_diff).", ".
02352 "lft = lft + ".$this->ilDB->quote($move_diff).", ".
02353 "depth = depth + ".$this->ilDB->quote($depth_diff)." ".
02354 "WHERE lft >= ".$this->ilDB->quote(($source_lft + $where_offset))." ".
02355 "AND rgt <= ".$this->ilDB->quote(($source_rgt + $where_offset))." ".
02356 "AND tree = ".$this->ilDB->quote($this->tree_id);
02357 #var_dump("<pre>",$query,"<pre>");
02358 $res = $this->ilDB->query($query);
02359
02360
02361 $query = "UPDATE ".$this->table_tree ." SET ".
02362 "lft = CASE ".
02363 "WHEN lft >= ".$this->ilDB->quote(($source_lft + $where_offset))." ".
02364 "THEN lft - ".$this->ilDB->quote($spread_diff)." ".
02365 "ELSE lft ".
02366 "END, ".
02367 "rgt = CASE ".
02368 "WHEN rgt >= ".$this->ilDB->quote(($source_rgt + $where_offset))." ".
02369 "THEN rgt - ".$this->ilDB->quote($spread_diff)." ".
02370 "ELSE rgt ".
02371 "END ".
02372 "WHERE tree = ".$this->tree_id;
02373 #var_dump("<pre>",$query,"</pre>");
02374 $res = $this->ilDB->query($query);
02375
02376 if($this->__isMainTree())
02377 {
02378 ilDBx::_unlockTables();
02379 }
02380 return true;
02381 }
02382
02383
02384 }
02385 ?>