• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

Services/Tree/classes/class.ilTree.php

Go to the documentation of this file.
00001 <?php
00002 /*
00003         +-----------------------------------------------------------------------------+
00004         | ILIAS open source                                                           |
00005         +-----------------------------------------------------------------------------+
00006         | Copyright (c) 1998-2006 ILIAS open source, University of Cologne            |
00007         |                                                                             |
00008         | This program is free software; you can redistribute it and/or               |
00009         | modify it under the terms of the GNU General Public License                 |
00010         | as published by the Free Software Foundation; either version 2              |
00011         | of the License, or (at your option) any later version.                      |
00012         |                                                                             |
00013         | This program is distributed in the hope that it will be useful,             |
00014         | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
00015         | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
00016         | GNU General Public License for more details.                                |
00017         |                                                                             |
00018         | You should have received a copy of the GNU General Public License           |
00019         | along with this program; if not, write to the Free Software                 |
00020         | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. |
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                 // set db & error handler
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                 // CREATE LOGGER INSTANCE
00176                 $this->log =& $ilLog;
00177 
00178                 //init variables
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                 // By default, we create gaps in the tree sequence numbering for 50 nodes 
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                 // lang_code is only required in $this->fetchnodedata
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                 // init childs
00343                 $childs = array();
00344 
00345                 // number of childs
00346                 $count = 0;
00347 
00348                 // init order_clause
00349                 $order_clause = "";
00350 
00351                 // set order_clause if sort order parameter is given
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         //666
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                 //$ilBench->start("Tree", "getChilds_Query");
00369                 $r = $this->ilDB->query($q);
00370                 //$ilBench->stop("Tree", "getChilds_Query");
00371 
00372                 $count = $r->numRows();
00373 
00374 
00375                 if ($count > 0)
00376                 {
00377                         //$ilBench->start("Tree", "getChilds_fetchNodeData");
00378                         while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
00379                         {
00380                                 $childs[] = $this->fetchNodeData($row);
00381                         }
00382                         //$ilBench->stop("Tree", "getChilds_fetchNodeData");
00383 
00384                         // mark the last child node (important for display)
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                 // init childs
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 //echo "+$a_node_id+$a_parent_id+";
00464                 // CHECK node_id and parent_id > 0 if in main tree
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                                 // get left value of parent
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                                 // spread tree
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                                 // Special treatment for trees with gaps
00539                                 if ($this->gap > 0)
00540                                 {
00541                                         if($this->__isMainTree())
00542                                         {
00543                                                 ilDBx::_lockTables(array('tree' => 'WRITE'));
00544                                         }
00545 
00546                                         // get lft and rgt value of parent
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                                         // Get the available space, without taking children into account yet
00567                                         $availableSpace = $parentRgt - $parentLft;
00568                                         if ($availableSpace < 2)
00569                                         {
00570                                                 // If there is not enough space between parent lft and rgt, we don't need
00571                                                 // to look any further, because we must spread the tree.
00572                                                 $lft = $parentRgt;
00573                                         }
00574                                         else
00575                                         {
00576                                                 // If there is space between parent lft and rgt, we need to check
00577                                                 // whether there is space left between the rightmost child of the
00578                                                 // parent and parent rgt.
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                                                         // If the parent has children, we compute the available space
00587                                                         // between rgt of the rightmost child and parent rgt.
00588                                                         $availableSpace = $parentRgt - $r['max_rgt'];
00589                                                         $lft = $r['max_rgt'] + 1;
00590                                                 }
00591                                                 else
00592                                                 {
00593                                                         // If the parent has no children, we know now, that we can
00594                                                         // add the new node at parent lft + 1 without having to spread
00595                                                         // the tree.
00596                                                         $lft = $parentLft + 1;
00597                                                 }
00598                                         }
00599                                         $rgt = $lft + 1;
00600                                         
00601 
00602                                         // spread tree if there is not enough space to insert the new node
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                                 // Treatment for trees without gaps
00626                                 else 
00627                                 {
00628                                         if($this->__isMainTree())
00629                                         {
00630                                                 ilDBx::_lockTables(array('tree' => 'WRITE'));
00631                                         }
00632 
00633                                         // get right value of parent
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                                         // spread tree
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                                 // this code shouldn't be executed
00675                                 if($this->__isMainTree())
00676                                 {
00677                                         ilDBx::_lockTables(array('tree' => 'WRITE'));
00678                                 }
00679 
00680                                 // get right value of preceeding child
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                                 // crosscheck parents of sibling and new node (must be identical)
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                                 // update lft/rgt values
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                 // get depth
00721                 $depth = $this->getDepth($a_parent_id) + 1;
00722 
00723                 // insert node
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                 // Finally unlock tables
00732                 if($this->__isMainTree())
00733                 {
00734                         ilDBx::_unlockTables();
00735                 }
00736                 
00737                 // reset deletion date
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                 // LOCKED ###########################################################
00865                 // get lft and rgt values. Don't trust parameter lft/rgt values of $a_node
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                 // delete subtree
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                 // We only close the gap, if the resulting gap will be larger then the gap value 
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                         // close gaps
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                 // LOCKED ###########################################################
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                 // The nested sets algorithm is very easy to implement.
00952                 // Unfortunately it always does a full table space scan to retrieve the path
00953                 // regardless whether indices on lft and rgt are set or not.
00954                 // (At least, this is what happens on MySQL 4.1).
00955                 // This algorithms performs well for small trees which are deeply nested.
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                 // The adjacency map algorithm is harder to implement than the nested sets algorithm.
00995                 // This algorithms performs an index search for each of the path element.
00996                 // This algorithms performs well for large trees which are not deeply nested.
00997 
00998                 // The $takeId variable is used, to determine if a given id shall be included in the path
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                 // Determine the depth of the endnode, and fetch its parent field also.
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                         //$this->writelog('getIdsUsingAdjacencyMap q='.$q);
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                         //$this->writelog('getIdsUsingAdjacencyMap depth='.$nodeDepth);
01025 
01026                 // Fetch the node ids. For shallow depths we can fill in the id's directly.     
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                         // Adjacency Map Tree performs better than
01052                         // Nested Sets Tree even for very deep trees.
01053                         // The following code construct nested self-joins
01054                         // Since we already know the root-id of the tree and
01055                         // we also know the id and parent id of the current node,
01056                         // we only need to perform $nodeDepth - 3 self-joins. 
01057                         // We can further reduce the number of self-joins by 1
01058                         // by taking into account, that each row in table tree
01059                         // contains the id of itself and of its parent.
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                         // Fall back to nested sets tree for extremely deep tree structures
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                 // path id cache
01113                 if ($this->use_cache && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id]))
01114                 {
01115 //echo "<br>getPathIdhit";
01116                         return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
01117                 }
01118 //echo "<br>miss";
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 //echo "tree:".$row[$this->tree_pk].":lft:".$row["lft"].":rgt:".$row["rgt"].":child:".$row["child"].":<br>";
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                                 // get object reference data
01180                                 $q = "SELECT * FROM ".$this->table_obj_reference." WHERE ".$this->ref_pk."='".$row["child"]."'";
01181                                 $r2 = $this->ilDB->query($q);
01182 //echo "num_childs:".$r2->numRows().":<br>";
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                                 // get object data
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                                 // get only object data
01214                                 $q = "SELECT * FROM ".$this->table_obj_data." WHERE ".$this->obj_pk."='".$row["child"]."'";
01215                                 $r2 = $this->ilDB->query($q);
01216 //echo "num_childs:".$r2->numRows().":<br>";
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                 //$ilBench->start("Tree", "fetchNodeData_getRow");
01323                 $data = $a_row;
01324                 $data["desc"] = $a_row["description"];  // for compability
01325                 //$ilBench->stop("Tree", "fetchNodeData_getRow");
01326 
01327                 // multilingual support systemobjects (sys) & categories (db)
01328                 //$ilBench->start("Tree", "fetchNodeData_readDefinition");
01329                 if (is_object($objDefinition))
01330                 {
01331                         $translation_type = $objDefinition->getTranslationType($data["type"]);
01332                 }
01333                 //$ilBench->stop("Tree", "fetchNodeData_readDefinition");
01334 
01335                 if ($translation_type == "sys")
01336                 {
01337                         //$ilBench->start("Tree", "fetchNodeData_getLangData");
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                         //$ilBench->stop("Tree", "fetchNodeData_getLangData");
01351                 }
01352                 elseif ($translation_type == "db")
01353                 {
01354                         //$ilBench->start("Tree", "fetchNodeData_getTranslation");
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                         //$ilBench->stop("Tree", "fetchNodeData_getTranslation");
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                 // is in tree cache
01392                 if ($this->use_cache && isset($this->in_tree_cache[$a_node_id]))
01393                 {
01394 //echo "<br>in_tree_hit";
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                         // No raise error, since it is a already a check function
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                 // FOR SECURITY addTree() IS NOT ALLOWED ON MAIN TREE
01494                 // IF SOMEONE WILL NEED FEATURES LIKE $tree->addTree(2) ON THE MAIN TREE PLEASE CONTACT ME (smeyer@databay.de)
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();        // node_data
01539                 $row = "";                      // fetched row
01540                 $left = "";                     // tree_left
01541                 $right = "";            // tree_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                 // OPERATION NOT ALLOWED ON MAIN TREE
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                 // LOCKED ###############################################
01620                 if($this->__isMainTree())
01621                 {
01622                         ilDBx::_lockTables(array('tree' => 'WRITE',
01623                                 'object_reference' => 'WRITE'));
01624                 }
01625 
01626                 // GET LEFT AND RIGHT VALUE
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                 // GET ALL SUBNODES
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                         // possibly already deleted
01653 
01654                         // Unlock locked tables before returning
01655                         if($this->__isMainTree())
01656                         {
01657                                 ilDBX::_unlockTables();
01658                         }
01659 
01660                         return false;
01661                 }
01662 
01663                 // SAVE SUBTREE
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                         // set node as deleted
01672                         if ($a_set_deleted)
01673                         {
01674                                 ilObject::_setDeletedDate($child);
01675                         }
01676                 }
01677                 
01678                 // Set the nodes deleted (negative tree id)
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                 // LOCKED ###############################################
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                 // is saved cache
01709                 if ($this->use_cache && isset($this->is_saved_cache[$a_node_id]))
01710                 {
01711 //echo "<br>issavedhit";
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                 // get lft value for current node
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                 // get data of successor node
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                 // get lft value for current node
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                 // get data of predecessor node
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                 // LOCKED ###################################
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                 // LOCKED ###################################
02000                 return $return;
02001         }
02002 
02003         // PRIVATE
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                 // Insert a gap at the end of node, if the node has children
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                 // check if both IDs are > 0
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         // PRIVATE METHODS
02125         function __isMainTree()
02126         {
02127                 return $this->table_tree === 'tree';
02128         }
02129 
02138         function __checkDelete($a_node)
02139         {
02140                 // get subtree by lft,rgt
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                 // CHECK FOR DUPLICATE CHILD IDS
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                 // GET SUBTREE BY PARENT RELATION
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                 // GET PARENT ID
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                 // MULTIPLE ENTRIES
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                 // GET ALL CHILDS
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                         // RECURSION
02206                         $this->__getSubTreeByParentRelation($row->child,$parent_childs);
02207                 }
02208                 return true;
02209         }
02210 
02211         function __validateSubtrees(&$lft_childs,$parent_childs)
02212         {
02213                 // SORT BY KEY
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             // Receive node infos for source and target
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             // Check in tree
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             // Check target not child of source
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             // Now spread the tree at the target location. After this update the table should be still in a consistent state.
02311             // implementation for IL_LAST_NODE
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             // Ok, maybe the source node has been updated, too.
02331             // Check this:
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             // Update source subtree:
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                         // done: close old gap
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 } // END class.tree
02385 ?>

Generated on Fri Dec 13 2013 17:57:01 for ILIAS Release_3_9_x_branch .rev 46835 by  doxygen 1.7.1