4define(
"IL_LAST_NODE", -2);
 
    5define(
"IL_FIRST_NODE", -1);
 
    7include_once 
'./Services/Tree/exceptions/class.ilInvalidTreeStructureException.php';
 
  155        $this->lang_code = 
"en";
 
  160        if (!isset($a_tree_id) or (func_num_args() == 0)) {
 
  161            $this->log->error(
"No tree_id given!");
 
  163            throw new InvalidArgumentException(
"No tree_id given!");
 
  166        if (func_num_args() > 2) {
 
  167            $this->log->error(
"Wrong parameter count!");
 
  168            throw new InvalidArgumentException(
"Wrong parameter count!");
 
  172        if (empty($a_root_id)) {
 
  173            $a_root_id = ROOT_FOLDER_ID;
 
  176        $this->tree_id            = $a_tree_id;
 
  177        $this->root_id            = $a_root_id;
 
  178        $this->table_tree     = 
'tree';
 
  179        $this->table_obj_data = 
'object_data';
 
  180        $this->table_obj_reference = 
'object_reference';
 
  181        $this->ref_pk = 
'ref_id';
 
  182        $this->obj_pk = 
'obj_id';
 
  183        $this->tree_pk = 
'tree';
 
  185        $this->use_cache = 
true;
 
  188        $this->translation_cache = array();
 
  189        $this->parent_type_cache = array();
 
  207        if (!is_object(
$GLOBALS[
'ilSetting']) or 
$GLOBALS[
'ilSetting']->getModule() != 
'common') {
 
  208            include_once 
'./Services/Administration/classes/class.ilSetting.php';
 
  215            if ($setting->get(
'main_tree_impl', 
'ns') == 
'ns') {
 
  216                #$GLOBALS['ilLog']->write(__METHOD__.': Using nested set.'); 
  217                include_once 
'./Services/Tree/classes/class.ilNestedSetTree.php';
 
  220                #$GLOBALS['ilLog']->write(__METHOD__.': Using materialized path.'); 
  221                include_once 
'./Services/Tree/classes/class.ilMaterializedPathTree.php';
 
  225            #$GLOBALS['ilLog']->write(__METHOD__.': Using netsted set for non main tree.'); 
  226            include_once 
'./Services/Tree/classes/class.ilNestedSetTree.php';
 
  245        $this->use_cache = $a_use;
 
  285            $this->lang_code = 
"en";
 
  287            $this->lang_code = 
$ilUser->getCurrentLanguage();
 
  339        $this->in_tree_cache = array();
 
  359    public function setTableNames($a_table_tree, $a_table_obj_data, $a_table_obj_reference = 
"")
 
  361        if (!isset($a_table_tree) or !isset($a_table_obj_data)) {
 
  363                                "tree table: " . $a_table_tree . 
" object data table: " . $a_table_obj_data;
 
  365            throw new InvalidArgumentException(
$message);
 
  368        $this->table_tree = $a_table_tree;
 
  369        $this->table_obj_data = $a_table_obj_data;
 
  370        $this->table_obj_reference = $a_table_obj_reference;
 
  386        if (!isset($a_column_name)) {
 
  389            throw new InvalidArgumentException(
$message);
 
  392        $this->ref_pk = $a_column_name;
 
  405        if (!isset($a_column_name)) {
 
  408            throw new InvalidArgumentException(
$message);
 
  411        $this->obj_pk = $a_column_name;
 
  424        if (!isset($a_column_name)) {
 
  427            throw new InvalidArgumentException(
$message);
 
  430        $this->tree_pk = $a_column_name;
 
  441        if ($this->table_obj_reference) {
 
  443            return "JOIN " . $this->table_obj_reference . 
" ON " . $this->table_tree . 
".child=" . $this->table_obj_reference . 
"." . $this->ref_pk . 
" " .
 
  444                   "JOIN " . $this->table_obj_data . 
" ON " . $this->table_obj_reference . 
"." . $this->obj_pk . 
"=" . $this->table_obj_data . 
"." . $this->obj_pk . 
" ";
 
  447            return "JOIN " . $this->table_obj_data . 
" ON " . $this->table_tree . 
".child=" . $this->table_obj_data . 
"." . $this->obj_pk . 
" ";
 
  485        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
  486                'WHERE parent = ' . 
$ilDB->quote($a_node, 
'integer') . 
' ' .
 
  487                'AND tree = ' . 
$ilDB->quote($this->tree_id, 
'integer' . 
' ' .
 
  493            $childs[] = 
$row->child;
 
  507    public function getChilds($a_node_id, $a_order = 
"", $a_direction = 
"ASC")
 
  511        if (!isset($a_node_id)) {
 
  514            throw new InvalidArgumentException(
$message);
 
  527        if (!empty($a_order)) {
 
  528            $order_clause = 
"ORDER BY " . $a_order . 
" " . $a_direction;
 
  530            $order_clause = 
"ORDER BY " . $this->table_tree . 
".lft";
 
  535            'SELECT * FROM ' . $this->table_tree . 
' ' .
 
  537                "WHERE parent = %s " .
 
  538                "AND " . $this->table_tree . 
"." . $this->tree_pk . 
" = %s " .
 
  540            $ilDB->quote($a_node_id, 
'integer'),
 
  541            $ilDB->quote($this->tree_id, 
'integer')
 
  546        if (!$count = 
$res->numRows()) {
 
  554            $obj_ids[] = 
$r[
"obj_id"];
 
  559            is_object(
$ilUser) && $this->lang_code == 
$ilUser->getLanguage() && !$this->oc_preloaded[$a_node_id]) {
 
  561            $ilObjDataCache->preloadObjectCache($obj_ids, $this->lang_code);
 
  563            $this->oc_preloaded[$a_node_id] = 
true;
 
  571                #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child'].' = true'); 
  572                $this->in_tree_cache[
$row[
'child']] = 
$row[
'tree'] == 1;
 
  575        $childs[$count - 1][
"last"] = 
true;
 
  590        $childs = $this->
getChilds($a_node, $a_order, $a_direction);
 
  592        foreach ($childs as $child) {
 
  593            if (!in_array($child[
"type"], $a_filter)) {
 
  594                $filtered[] = $child;
 
  597        return $filtered ? $filtered : array();
 
  613        if (!isset($a_node_id) or !isset(
$a_type)) {
 
  614            $message = 
"Missing parameter! node_id:" . $a_node_id . 
" type:" . 
$a_type;
 
  616            throw new InvalidArgumentException(
$message);
 
  619        if (
$a_type==
'rolf' && $this->table_obj_reference) {
 
  623            $ilDB->setLimit(1, 0);
 
  625                "SELECT * FROM " . $this->table_tree . 
" " .
 
  627                "WHERE parent = %s " .
 
  628                "AND " . $this->table_tree . 
"." . $this->tree_pk . 
" = %s " .
 
  629                "AND " . $this->table_obj_data . 
".type = %s ",
 
  630                $ilDB->quote($a_node_id, 
'integer'),
 
  631                $ilDB->quote($this->tree_id, 
'integer'),
 
  636                "SELECT * FROM " . $this->table_tree . 
" " .
 
  638                "WHERE parent = %s " .
 
  639                "AND " . $this->table_tree . 
"." . $this->tree_pk . 
" = %s " .
 
  640                "AND " . $this->table_obj_data . 
".type = %s " .
 
  641                "ORDER BY " . $this->table_tree . 
".lft",
 
  642                $ilDB->quote($a_node_id, 
'integer'),
 
  643                $ilDB->quote($this->tree_id, 
'integer'),
 
  655        return $childs ? $childs : array();
 
  671        if (!isset($a_node_id) or !$a_types) {
 
  672            $message = 
"Missing parameter! node_id:" . $a_node_id . 
" type:" . $a_types;
 
  674            throw new InvalidArgumentException(
$message);
 
  679            $filter = 
'AND ' . $this->table_obj_data . 
'.type IN(' . implode(
',', 
ilUtil::quoteArray($a_types)) . 
') ';
 
  683        if (!empty($a_order)) {
 
  684            $order_clause = 
"ORDER BY " . $a_order . 
" " . $a_direction;
 
  686            $order_clause = 
"ORDER BY " . $this->table_tree . 
".lft";
 
  689        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
  691            'WHERE parent = ' . $ilDB->quote($a_node_id, 
'integer') . 
' ' .
 
  692            'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = ' . 
$ilDB->quote($this->tree_id, 
'integer') . 
' ' .
 
  701        return $childs ? $childs : array();
 
  720            if ($a_source_id <= 1 or $a_target_id <= 0) {
 
  722                throw new InvalidArgumentException(
'Invalid parameter given for ilTree::insertNodeFromTrash');
 
  725        if (!isset($a_source_id) or !isset($a_target_id)) {
 
  727            throw new InvalidArgumentException(
'Missing parameter for ilTree::insertNodeFromTrash');
 
  729        if ($this->
isInTree($a_source_id)) {
 
  732            throw new InvalidArgumentException(
'Node already in tree.');
 
  735        $query = 
'DELETE from tree ' .
 
  736                'WHERE tree = ' . 
$ilDB->quote($a_tree_id, 
'integer') . 
' ' .
 
  737                'AND child = ' . 
$ilDB->quote($a_source_id, 
'integer');
 
  759            if ($a_node_id <= 1 or $a_parent_id <= 0) {
 
  761                    'Invalid parameters! $a_node_id: %s $a_parent_id: %s',
 
  766                throw new InvalidArgumentException(
$message);
 
  771        if (!isset($a_node_id) or !isset($a_parent_id)) {
 
  773            throw new InvalidArgumentException(
"Missing parameter! " .
 
  774                "node_id: " . $a_node_id . 
" parent_id: " . $a_parent_id);
 
  777            throw new InvalidArgumentException(
"Node " . $a_node_id . 
" already in tree " .
 
  778                                     $this->table_tree . 
"!");
 
  783        $this->in_tree_cache[$a_node_id] = 
true;
 
  786        if ($a_reset_deletion_date) {
 
  791            $GLOBALS[
'ilAppEventHandler']->raise(
 
  795                        'tree'          => $this->table_tree,
 
  796                        'node_id'       => $a_node_id,
 
  797                        'parent_id'     => $a_parent_id)
 
  820        foreach ($this->
getSubTree($node) as $subnode) {
 
  821            if ($depth and $subnode[
'depth'] > $depth) {
 
  824            if (!$first and in_array($subnode[
'type'], $a_filter)) {
 
  825                $depth = $subnode[
'depth'];
 
  831            $filtered[] = $subnode;
 
  833        return $filtered ? $filtered : array();
 
  860        if (!is_array($a_node)) {
 
  862            throw new InvalidArgumentException(__METHOD__ . 
': wrong datatype for node data given');
 
  884                $subtree[] = 
$row[
'child'];
 
  887            if ($this->
__isMainTree() || $this->table_tree == 
"lm_tree") {
 
  888                $this->in_tree_cache[
$row[
'child']] = 
true;
 
  891        return $subtree ? $subtree : array();
 
  904        $a_filter = $a_filter ? $a_filter : array();
 
  906        foreach ($this->getSubtree($this->
getNodeData($a_node)) as $node) {
 
  907            if (in_array($node[
"type"], $a_filter)) {
 
  910            $types[
"$node[type]"] = $node[
"type"];
 
  912        return $types ? $types : array();
 
  926        $this->log->debug(
'Delete tree with node ' . $a_node);
 
  928        if (!is_array($a_node)) {
 
  930            throw new InvalidArgumentException(__METHOD__ . 
': Wrong datatype for node data!');
 
  933        $this->log->debug($this->tree_pk);
 
  970        $pathIds = $this->
getPathId($a_endnode_id, $a_startnode_id);
 
  976        if (count($pathIds) == 0) {
 
  980        $inClause = 
'child IN (';
 
  981        for (
$i=0; 
$i < count($pathIds); 
$i++) {
 
  985            $inClause .= 
$ilDB->quote($pathIds[
$i], 
'integer');
 
  990            'FROM ' . $this->table_tree . 
' ' .
 
  992            'WHERE ' . $inClause . 
' ' .
 
  993            'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = ' . $this->
ilDB->
quote($this->tree_id, 
'integer') . 
' ' .
 
 1003                #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child']); 
 1004                $this->in_tree_cache[
$row[
'child']] = 
$row[
'tree'] == 1;
 
 1025        $res = $ilDB->query(
'SELECT t.depth, t.parent, t.child ' .
 
 1026            'FROM ' . $this->table_tree . 
' t ' .
 
 1027            'WHERE ' . 
$ilDB->in(
"child", $a_node_ids, 
false, 
"integer") .
 
 1028            'AND ' . $this->tree_pk . 
' = ' . 
$ilDB->quote($this->tree_id, 
"integer"));
 
 1030            $this->depth_cache[
$row[
"child"]] = 
$row[
"depth"];
 
 1031            $this->parent_cache[
$row[
"child"]] = 
$row[
"parent"];
 
 1044    public function getPathId($a_endnode_id, $a_startnode_id = 0)
 
 1046        if (!$a_endnode_id) {
 
 1048            throw new InvalidArgumentException(__METHOD__ . 
': No endnode given!');
 
 1052        if ($this->
isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id])) {
 
 1054            return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
 
 1061            $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
 
 1090        if ($titlePath == 
null || count($titlePath) == 0) {
 
 1091            if ($a_startnode_id == 0) {
 
 1099        if ($a_startnode_id != 
null && $a_startnode_id != 0) {
 
 1102            $parent = $a_startnode_id;
 
 1105            $nodePath = array();
 
 1113        require_once(
'include/Unicode/UtfNormal.php');
 
 1114        include_once 
'./Services/Utilities/classes/class.ilStr.php';
 
 1115        $inClause = 
'd.title IN (';
 
 1116        for (
$i=0; 
$i < count($titlePath); 
$i++) {
 
 1121            $inClause .= 
$ilDB->quote($titlePath[
$i], 
'text');
 
 1126        if ($this->table_obj_reference) {
 
 1127            $joinClause = 
'JOIN ' . $this->table_obj_reference . 
'  r ON t.child = r.' . $this->ref_pk . 
' ' .
 
 1128                'JOIN ' . $this->table_obj_data . 
' d ON r.' . $this->obj_pk . 
' = d.' . 
$this->obj_pk;
 
 1130            $joinClause = 
'JOIN ' . $this->table_obj_data . 
'  d ON t.child = d.' . 
$this->obj_pk;
 
 1137        $q = 
'SELECT t.depth, t.parent, t.child, d.' . $this->obj_pk . 
' obj_id, d.type, d.title ' .
 
 1138            'FROM ' . $this->table_tree . 
'  t ' .
 
 1140            'WHERE ' . $inClause . 
' ' .
 
 1141            'AND t.depth <= ' . (count($titlePath)+count($nodePath)) . 
' ' .
 
 1143            'ORDER BY t.depth, t.child ASC';
 
 1154        for (
$i = 0; 
$i < count($titlePath); 
$i++) {
 
 1155            $pathElementFound = 
false;
 
 1157                if (
$row[
'parent'] == $parent &&
 
 1162                    $parent = 
$row[
'child'];
 
 1163                    $pathElementFound = 
true;
 
 1168            if (!$pathElementFound) {
 
 1199        $pathIds = $this->
getPathId($a_endnode_id, $a_startnode_id);
 
 1202        if (count($pathIds) == 0) {
 
 1209        for (
$i = 0; 
$i < count($pathIds); 
$i++) {
 
 1210            $types[] = 
'integer';
 
 1214        $query = 
'SELECT t.depth,t.parent,t.child,d.obj_id,d.type,d.title ' .
 
 1215            'FROM ' . $this->table_tree . 
' t ' .
 
 1216            'JOIN ' . $this->table_obj_reference . 
' r ON r.ref_id = t.child ' .
 
 1217            'JOIN ' . $this->table_obj_data . 
' d ON d.obj_id = r.obj_id ' .
 
 1218            'WHERE ' . 
$ilDB->in(
't.child', 
$data, 
false, 
'integer') . 
' ' .
 
 1219            'ORDER BY t.depth ';
 
 1223        $titlePath = array();
 
 1225            $titlePath[] = 
$row;
 
 1242        $types = array(
'integer');
 
 1243        $query = 
'SELECT lft,rgt FROM ' . $this->table_tree . 
' ' .
 
 1244            'WHERE ' . $this->tree_pk . 
' = %s ';
 
 1252        $all = array_merge($lft, $rgt);
 
 1253        $uni = array_unique($all);
 
 1255        if (count($all) != count($uni)) {
 
 1276        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 1277                'WHERE ' . $this->tree_pk . 
' = %s ' .
 
 1279        $r1 = 
$ilDB->queryF(
$query, array(
'integer'), array($this->tree_id));
 
 1283            if ((
$row[
"child"] == 0) && $a_no_zero_child) {
 
 1284                $message = 
"Tree contains child with ID 0!";
 
 1289            if ($this->table_obj_reference) {
 
 1291                $query = 
'SELECT * FROM ' . $this->table_obj_reference . 
' WHERE ' . $this->ref_pk . 
' = %s ';
 
 1295                if ($r2->numRows() == 0) {
 
 1296                    $message = 
"No Object-to-Reference entry found for ID " . 
$row[
"child"] . 
"!";
 
 1300                if ($r2->numRows() > 1) {
 
 1301                    $message = 
"More Object-to-Reference entries found for ID " . 
$row[
"child"] . 
"!";
 
 1307                $obj_ref = 
$ilDB->fetchAssoc($r2);
 
 1309                $query = 
'SELECT * FROM ' . $this->table_obj_data . 
' WHERE ' . $this->obj_pk . 
' = %s';
 
 1310                $r3 = 
$ilDB->queryF(
$query, array(
'integer'), array($obj_ref[$this->obj_pk]));
 
 1311                if ($r3->numRows() == 0) {
 
 1316                if ($r3->numRows() > 1) {
 
 1323                $query = 
'SELECT * FROM ' . $this->table_obj_data . 
' WHERE ' . $this->obj_pk . 
' = %s';
 
 1326                if ($r2->numRows() == 0) {
 
 1327                    $message = 
"No child found for ID " . 
$row[
"child"] . 
"!";
 
 1331                if ($r2->numRows() > 1) {
 
 1332                    $message = 
"More childs found for ID " . 
$row[
"child"] . 
"!";
 
 1355        return $row[
'depth'];
 
 1369            $query = 
'SELECT depth FROM ' . $this->table_tree . 
' ' .
 
 1370                'WHERE child = %s ' .
 
 1371                'AND ' . $this->tree_pk . 
' = %s ';
 
 1372            $res = 
$ilDB->queryF(
$query, array(
'integer',
'integer'), array($a_node_id,$this->tree_id));
 
 1394            throw new InvalidArgumentException(
'Missing or empty parameter $a_node_id: ' . $a_node_id);
 
 1397        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 1398                'WHERE child = ' . 
$ilDB->quote($a_node_id, 
'integer');
 
 1422        if (!isset($a_node_id)) {
 
 1424            throw new InvalidArgumentException(
"No node_id given!");
 
 1427            if ($a_node_id < 1) {
 
 1428                $message = 
'No valid parameter given! $a_node_id: %s' . $a_node_id;
 
 1431                throw new InvalidArgumentException(
$message);
 
 1436        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 1438            'WHERE ' . $this->table_tree . 
'.child = %s ' .
 
 1439            'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ';
 
 1442            $a_tree_pk === 
null ? $this->tree_id : $a_tree_pk));
 
 1463        $data[
"desc"] = $a_row[
"description"];  
 
 1468        if (is_object($objDefinition)) {
 
 1469            $translation_type = $objDefinition->getTranslationType(
$data[
"type"]);
 
 1473        if ($translation_type == 
"sys") {
 
 1475            if (
$data[
"type"] == 
"rolf" and 
$data[
"obj_id"] != ROLE_FOLDER_ID) {
 
 1481                $data[
"description"] = 
$lng->txt(
"obj_" . 
$data[
"type"] . 
"_desc");
 
 1485        } elseif ($translation_type == 
"db") {
 
 1489                array_key_exists(
$data[
"obj_id"] . 
'.' . $lang_code, $this->translation_cache)) {
 
 1490                $key = 
$data[
"obj_id"] . 
'.' . $lang_code;
 
 1491                $data[
"title"] = $this->translation_cache[
$key][
'title'];
 
 1492                $data[
"description"] = $this->translation_cache[
$key][
'description'];
 
 1493                $data[
"desc"] = $this->translation_cache[
$key][
'desc'];
 
 1497                $query = 
'SELECT title,description FROM object_translation ' .
 
 1498                    'WHERE obj_id = %s ' .
 
 1499                    'AND lang_code = %s ' .
 
 1500                    'AND NOT lang_default = %s';
 
 1502                $res = 
$ilDB->queryF(
$query, array(
'integer',
'text',
'integer'), array(
 
 1516                if ($this->
isCacheUsed() && count($this->translation_cache) < 1000) {
 
 1517                    $key = 
$data[
"obj_id"] . 
'.' . $lang_code;
 
 1518                    $this->translation_cache[
$key] = array();
 
 1519                    $this->translation_cache[
$key][
'title'] = 
$data[
"title"] ;
 
 1520                    $this->translation_cache[
$key][
'description'] = 
$data[
"description"];
 
 1521                    $this->translation_cache[
$key][
'desc'] = 
$data[
"desc"];
 
 1527        if (
$data[
'type'] == 
'crsr' or 
$data[
'type'] == 
'catr' or 
$data[
'type'] == 
'grpr') {
 
 1528            include_once(
'./Services/ContainerReference/classes/class.ilContainerReference.php');
 
 1542        global $ilObjDataCache;
 
 1544        if ($this->
isCacheUsed() && is_array($a_obj_ids) && is_object($ilObjDataCache)) {
 
 1545            foreach ($a_obj_ids as 
$id) {
 
 1546                $this->translation_cache[
$id . 
'.'][
'title'] = $ilObjDataCache->lookupTitle(
$id);
 
 1547                $this->translation_cache[
$id . 
'.'][
'description'] = $ilObjDataCache->lookupDescription(
$id);
 
 1549                $this->translation_cache[
$id . 
'.'][
'desc'] =
 
 1550                    $this->translation_cache[
$id . 
'.'][
'description'];
 
 1567        if (!isset($a_node_id)) {
 
 1569            #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
 
 1572        if ($this->
isCacheUsed() && isset($this->in_tree_cache[$a_node_id])) {
 
 1573            #$GLOBALS['ilLog']->write(__METHOD__.': Using in tree cache '.$a_node_id); 
 1575            return $this->in_tree_cache[$a_node_id];
 
 1578        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 1579            'WHERE ' . $this->table_tree . 
'.child = %s ' .
 
 1580            'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s';
 
 1586        if (
$res->numRows() > 0) {
 
 1588                #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = true'); 
 1589                $this->in_tree_cache[$a_node_id] = 
true;
 
 1594                #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = false'); 
 1595                $this->in_tree_cache[$a_node_id] = 
false;
 
 1613        if (!isset($a_node_id)) {
 
 1615            throw new InvalidArgumentException(__METHOD__ . 
': No node_id given!');
 
 1618        if ($this->table_obj_reference) {
 
 1620            $innerjoin = 
"JOIN " . $this->table_obj_reference . 
" ON v.child=" . $this->table_obj_reference . 
"." . $this->ref_pk . 
" " .
 
 1621                        "JOIN " . $this->table_obj_data . 
" ON " . $this->table_obj_reference . 
"." . $this->obj_pk . 
"=" . $this->table_obj_data . 
"." . $this->obj_pk . 
" ";
 
 1624            $innerjoin = 
"JOIN " . $this->table_obj_data . 
" ON v.child=" . $this->table_obj_data . 
"." . $this->obj_pk . 
" ";
 
 1627        $query = 
'SELECT * FROM ' . $this->table_tree . 
' s, ' . $this->table_tree . 
' v ' .
 
 1629            'WHERE s.child = %s ' .
 
 1630            'AND s.parent = v.child ' .
 
 1631            'AND s.' . $this->tree_pk . 
' = %s ' .
 
 1632            'AND v.' . $this->tree_pk . 
' = %s';
 
 1633        $res = 
$ilDB->queryF(
$query, array(
'integer',
'integer',
'integer'), array(
 
 1662    public function addTree($a_tree_id, $a_node_id = -1)
 
 1669                'Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
 
 1674            throw new InvalidArgumentException(
$message);
 
 1677        if (!isset($a_tree_id)) {
 
 1680            throw new InvalidArgumentException(
$message);
 
 1683        if ($a_node_id <= 0) {
 
 1684            $a_node_id = $a_tree_id;
 
 1687        $query = 
'INSERT INTO ' . $this->table_tree . 
' (' .
 
 1688            $this->tree_pk . 
', child,parent,lft,rgt,depth) ' .
 
 1690            '(%s,%s,%s,%s,%s,%s)';
 
 1691        $res = 
$ilDB->manipulateF(
$query, array(
'integer',
'integer',
'integer',
'integer',
'integer',
'integer'), array(
 
 1717            throw new InvalidArgumentException(
'Type not given or wrong datatype');
 
 1720        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 1722            'WHERE ' . $this->table_obj_data . 
'.type = ' . $this->
ilDB->
quote(
$a_type, 
'text') .
 
 1723            'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = ' . $this->
ilDB->
quote($this->tree_id, 
'integer');
 
 1749            throw new InvalidArgumentException(
'Operation not allowed on main tree');
 
 1753            throw new InvalidArgumentException(
'Missing parameter tree id');
 
 1756        $query = 
'DELETE FROM ' . $this->table_tree .
 
 1757            ' WHERE ' . $this->tree_pk . 
' = %s ';
 
 1758        $ilDB->manipulateF(
$query, array(
'integer'), array($a_tree_id));
 
 1775            throw new InvalidArgumentException(
'No valid parameter given! $a_node_id: ' . $a_node_id);
 
 1782        $subnodes = array();
 
 1784            $subnodes[] = 
$row[
'child'];
 
 1787        if (!count($subnodes)) {
 
 1792        if ($a_set_deleted) {
 
 1793            include_once 
'./Services/Object/classes/class.ilObject.php';
 
 1815        return $this->
moveToTrash($a_node_id, $a_set_deleted);
 
 1824        return $this->
isSaved($a_node_id);
 
 1837        if ($this->
isCacheUsed() && isset($this->is_saved_cache[$a_node_id])) {
 
 1839            return $this->is_saved_cache[$a_node_id];
 
 1842        $query = 
'SELECT ' . $this->tree_pk . 
' FROM ' . $this->table_tree . 
' ' .
 
 1843            'WHERE child = %s ';
 
 1847        if (
$row[$this->tree_pk] < 0) {
 
 1849                $this->is_saved_cache[$a_node_id] = 
true;
 
 1854                $this->is_saved_cache[$a_node_id] = 
false;
 
 1870        if (!is_array($a_node_ids) || !$this->
isCacheUsed()) {
 
 1874        $query = 
'SELECT ' . $this->tree_pk . 
', child FROM ' . $this->table_tree . 
' ' .
 
 1875            'WHERE ' . 
$ilDB->in(
"child", $a_node_ids, 
false, 
"integer");
 
 1879            if (
$row[$this->tree_pk] < 0) {
 
 1881                    $this->is_saved_cache[
$row[
"child"]] = 
true;
 
 1885                    $this->is_saved_cache[
$row[
"child"]] = 
false;
 
 1903        if (!isset($a_parent_id)) {
 
 1906            throw new InvalidArgumentException(
$message);
 
 1909        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 1911            'WHERE ' . $this->table_tree . 
'.' . $this->tree_pk . 
' < %s ' .
 
 1912            'AND ' . $this->table_tree . 
'.parent = %s';
 
 1921        return $saved ? $saved : array();
 
 1934        $query = 
'SELECT ' . $this->table_obj_data . 
'.obj_id FROM ' . $this->table_tree . 
' ' .
 
 1936            'WHERE ' . $this->table_tree . 
'.' . $this->tree_pk . 
' < ' . 
$ilDB->quote(0, 
'integer') . 
' ' .
 
 1937            'AND ' . 
$ilDB->in($this->table_obj_data . 
'.obj_id', $a_obj_ids, 
'', 
'integer');
 
 1940            $saved[] = 
$row[
'obj_id'];
 
 1943        return $saved ? $saved : array();
 
 1957        if (!isset($a_node_id)) {
 
 1960            throw new InvalidArgumentException(
$message);
 
 1963        $query = 
'SELECT parent FROM ' . $this->table_tree . 
' ' .
 
 1964            'WHERE child = %s ' .
 
 1965            'AND ' . $this->tree_pk . 
' = %s ';
 
 1971        return $row->parent;
 
 1985        if (!isset($a_node_id)) {
 
 1988            throw new InvalidArgumentException(
$message);
 
 1991        $query = 
'SELECT lft FROM ' . $this->table_tree . 
' ' .
 
 1992            'WHERE child = %s ' .
 
 1993            'AND ' . $this->tree_pk . 
' = %s ';
 
 2012        if (!isset($a_node)) {
 
 2015            throw new InvalidArgumentException(
$message);
 
 2019            $query = 
'SELECT count(*) cnt FROM ' . $this->table_tree . 
' ' .
 
 2021                'WHERE lft <= %s ' .
 
 2023                'AND parent = %s ' .
 
 2024                'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ';
 
 2026            $res = 
$ilDB->queryF(
$query, array(
'integer',
'text',
'integer',
'integer'), array(
 
 2032            $query = 
'SELECT count(*) cnt FROM ' . $this->table_tree . 
' ' .
 
 2034                'WHERE lft <= %s ' .
 
 2035                'AND parent = %s ' .
 
 2036                'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ';
 
 2038            $res = 
$ilDB->queryF(
$query, array(
'integer',
'integer',
'integer'), array(
 
 2057        $query = 
'SELECT child FROM ' . $this->table_tree . 
' ' .
 
 2058            'WHERE parent = %s ' .
 
 2059            'AND ' . $this->tree_pk . 
' = %s ';
 
 2064        $this->root_id = 
$row->child;
 
 2079        $this->root_id = $a_root_id;
 
 2099        $this->tree_id = $a_tree_id;
 
 2114        if (!isset($a_node_id)) {
 
 2117            throw new InvalidArgumentException(
$message);
 
 2121        $query = 
'SELECT lft FROM ' . $this->table_tree . 
' ' .
 
 2122            'WHERE ' . $this->table_tree . 
'.child = %s ' .
 
 2123            'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ';
 
 2130            $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 2133                'AND ' . $this->table_obj_data . 
'.type = %s ' .
 
 2134                'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ' .
 
 2137            $res = 
$ilDB->queryF(
$query, array(
'integer',
'text',
'integer'), array(
 
 2142            $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 2145                'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ' .
 
 2153        if (
$res->numRows() < 1) {
 
 2173        if (!isset($a_node_id)) {
 
 2176            throw new InvalidArgumentException(
$message);
 
 2180        $query = 
'SELECT lft FROM ' . $this->table_tree . 
' ' .
 
 2181            'WHERE ' . $this->table_tree . 
'.child = %s ' .
 
 2182            'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ';
 
 2190            $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 2193                'AND ' . $this->table_obj_data . 
'.type = %s ' .
 
 2194                'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ' .
 
 2195                'ORDER BY lft DESC';
 
 2197            $res = 
$ilDB->queryF(
$query, array(
'integer',
'text',
'integer'), array(
 
 2202            $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 2205                'AND ' . $this->table_tree . 
'.' . $this->tree_pk . 
' = %s ' .
 
 2206                'ORDER BY lft DESC';
 
 2213        if (
$res->numRows() < 1) {
 
 2239            $ilAtomQuery = 
$ilDB->buildAtomQuery();
 
 2240            $ilAtomQuery->addTableLock($this->table_tree);
 
 2242            $ilAtomQuery->addQueryCallable($renumber_callable);
 
 2243            $ilAtomQuery->run();
 
 2245            $renumber_callable(
$ilDB);
 
 2264        $query = 
'UPDATE ' . $this->table_tree . 
' SET lft = %s WHERE child = %s AND tree = %s';
 
 2265        $res = 
$ilDB->manipulateF(
$query, array(
'integer',
'integer',
'integer'), array(
 
 2274        foreach ($childs as $child) {
 
 2280        if (count($childs) > 0) {
 
 2281            $i += $this->gap * 2;
 
 2285        $query = 
'UPDATE ' . $this->table_tree . 
' SET rgt = %s WHERE child = %s AND tree = %s';
 
 2286        $res = 
$ilDB->manipulateF(
$query, array(
'integer',
'integer', 
'integer'), array(
 
 2307        $cache_key = $a_ref_id . 
'.' . 
$a_type . 
'.' . ((int) $a_exclude_source_check);
 
 2311            array_key_exists($cache_key, $this->parent_type_cache)) {
 
 2312            return $this->parent_type_cache[$cache_key];
 
 2316        $do_cache = ($this->
__isMainTree() && count($this->parent_type_cache) < 1000);
 
 2321                $this->parent_type_cache[$cache_key] = 
false;
 
 2329        if ($a_exclude_source_check) {
 
 2333        foreach (
$path as $node) {
 
 2335            if ($node[
"type"] == 
$a_type) {
 
 2337                    $this->parent_type_cache[$cache_key] = $node[
"child"];
 
 2339                return $node[
"child"];
 
 2344            $this->parent_type_cache[$cache_key] = 
false;
 
 2359    public static function _removeEntry($a_tree, $a_child, $a_db_table = 
"tree")
 
 2363        if ($a_db_table === 
'tree') {
 
 2364            if ($a_tree == 1 and $a_child == ROOT_FOLDER_ID) {
 
 2366                    'Tried to delete root node! $a_tree: %s $a_child: %s',
 
 2371                throw new InvalidArgumentException(
$message);
 
 2375        $query = 
'DELETE FROM ' . $a_db_table . 
' ' .
 
 2376            'WHERE tree = %s ' .
 
 2391        return $this->table_tree === 
'tree';
 
 2411        $this->log->debug(
$query);
 
 2414        $counter = (int) $lft_childs = array();
 
 2416            $lft_childs[
$row->child] = 
$row->parent;
 
 2421        if (
$counter != count($lft_childs)) {
 
 2422            $message = 
'Duplicate entries for "child" in maintree! $a_node_id: ' . $a_node[
'child'];
 
 2429        $parent_childs = array();
 
 2450        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 2451            'WHERE child = %s ' .
 
 2459            $parent_childs[$a_node_id] = 
$row->parent;
 
 2464            $message = 
'Multiple entries in maintree! $a_node_id: ' . $a_node_id;
 
 2471        $query = 
'SELECT * FROM ' . $this->table_tree . 
' ' .
 
 2472            'WHERE parent = %s ';
 
 2493        ksort($parent_childs);
 
 2495        $this->log->debug(
'left childs ' . print_r($lft_childs, 
true));
 
 2496        $this->log->debug(
'parent childs ' . print_r($parent_childs, 
true));
 
 2498        if (count($lft_childs) != count($parent_childs)) {
 
 2499            $message = 
'(COUNT) Tree is corrupted! Left/Right subtree does not comply with parent relation';
 
 2505        foreach ($lft_childs as 
$key => $value) {
 
 2506            if ($parent_childs[
$key] != $value) {
 
 2507                $message = 
'(COMPARE) Tree is corrupted! Left/Right subtree does not comply with parent relation';
 
 2511            if (
$key == ROOT_FOLDER_ID) {
 
 2512                $message = 
'(ROOT_FOLDER) Tree is corrupted! Tried to delete root folder';
 
 2529    public function moveTree($a_source_id, $a_target_id, $a_location = self::POS_LAST_NODE)
 
 2531        $old_parent_id = $this->
getParentId($a_source_id);
 
 2534            $GLOBALS[
'ilAppEventHandler']->raise(
 
 2538                        'tree'                  => $this->table_tree,
 
 2539                        'source_id'     => $a_source_id,
 
 2540                        'target_id'     => $a_target_id,
 
 2541                        'old_parent_id' => $old_parent_id
 
 2571    public function getSubTreeQuery($a_node_id, $a_fields = array(), $a_types = 
'', $a_force_join_reference = 
false)
 
 2576            $a_force_join_reference,
 
 2595        if (!
sizeof($node)) {
 
 2604        if (count($a_fields)) {
 
 2605            $fields = implode(
',', $a_fields);
 
 2608        $query = 
"SELECT " . $fields .
 
 2614        while (
$row = 
$ilDB->fetchAssoc($set)) {
 
 2623        global 
$ilDB, $ilAppEventHandler;
 
 2625        $query = 
'DELETE FROM tree where ' .
 
 2626                'child = ' . 
$ilDB->quote($a_node_id, 
'integer') . 
' ' .
 
 2627                'AND tree = ' . 
$ilDB->quote($a_tree_id, 
'integer');
 
 2630        $ilAppEventHandler->raise(
 
 2633            array(
'tree' => $this->table_tree,
 
 2634                          'node_id' => $a_node_id,
 
 2635                          'tree_id' => $a_tree_id
 
 2649        $query = 
'SELECT DISTINCT(o.type) ' . 
$ilDB->quoteIdentifier(
'type') . 
' FROM tree t JOIN object_reference r ON child = r.ref_id ' .
 
 2650                'JOIN object_data o on r.obj_id = o.obj_id ' .
 
 2651                'WHERE tree < ' . 
$ilDB->quote(0, 
'integer') . 
' ' .
 
 2652                'AND child = -tree ' .
 
 2656        $types_deleted = array();
 
 2658            $types_deleted[] = 
$row->type;
 
 2660        return $types_deleted;
 
sprintf('%.4f', $callTime)
An exception for terminatinating execution or to throw for unit testing.
static toNFC($string)
Convert a UTF-8 string to normal form C, canonical composition.
static _lookupTitle($a_obj_id)
Overwitten from base class.
quote($a_query, $a_type=null)
Wrapper for quote method.
Thrown if invalid tree strucutes are found.
static getLogger($a_component_id)
Get component logger.
Base class for materialize path based trees Based on implementation of Werner Randelshofer.
Base class for nested set path based trees.
static setDeletedDates($a_ref_ids)
Set deleted date @global type $ilDB.
static _resetDeletedDate($a_ref_id)
only called in ilObjectGUI::insertSavedNodes
static strToLower($a_string)
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
isCacheUsed()
Check if cache is active.
fetchPredecessorNode($a_node_id, $a_type="")
get node data of predecessor node
lookupTrashedObjectTypes()
Lookup object types in trash @global type $ilDB.
getRelation($a_node_a, $a_node_b)
Get relation of two nodes.
moveToTrash($a_node_id, $a_set_deleted=false)
Wrapper for saveSubTree.
getSubTree($a_node, $a_with_data=true, $a_type="")
get all nodes in the subtree under specified node
getFilteredChilds($a_filter, $a_node, $a_order="", $a_direction="ASC")
get child nodes of given node (exclude filtered obj_types) @access public
isGrandChild($a_startnode_id, $a_querynode_id)
checks if a node is in the path of an other node @access public
getSubTreeTypes($a_node, $a_filter=0)
get types of nodes in the subtree under specified node
getRelationOfNodes($a_node_a_arr, $a_node_b_arr)
get relation of two nodes by node data
setObjectTablePK($a_column_name)
set column containing primary key in object table @access public
getTreePk()
Get tree primary key.
getParentId($a_node_id)
get parent id of given node @access public
getRbacSubtreeInfo($a_endnode_id)
This method is used for change existing objects and returns all necessary information for this action...
getParentCache()
Get parent cache.
setReferenceTablePK($a_column_name)
set column containing primary key in reference table @access public
saveSubTree($a_node_id, $a_set_deleted=false)
Use the wrapper moveToTrash save subtree: delete a subtree (defined by node_id) to a new tree with $t...
checkTreeChilds($a_no_zero_child=true)
check, if all childs of tree nodes exist in object table
getDepth($a_node_id)
return depth of a node in tree @access private
getSavedNodeData($a_parent_id)
get data saved/deleted nodes
getChildSequenceNumber($a_node, $type="")
get sequence number of node in sibling sequence @access public
insertNodeFromTrash($a_source_id, $a_target_id, $a_tree_id, $a_pos=IL_LAST_NODE, $a_reset_deleted_date=false)
Insert node from trash deletes trash entry.
useCache($a_use=true)
Use Cache (usually activated)
deleteTree($a_node)
delete node and the whole subtree under this node @access public
getLeftValue($a_node_id)
get left value of given node @access public
__checkDelete($a_node)
Check for deleteTree() compares a subtree of a given node by checking lft, rgt against parent relatio...
fetchSuccessorNode($a_node_id, $a_type="")
get node data of successor node
getDepthCache()
Get depth cache.
getTreeId()
get tree id @access public
getChildIds($a_node)
Get node child ids @global type $ilDB.
getNodeDataByType($a_type)
get nodes by type
getTreeTable()
Get tree table name.
static _removeEntry($a_tree, $a_child, $a_db_table="tree")
STATIC METHOD Removes a single entry from a tree.
getSubTreeQuery($a_node_id, $a_fields=array(), $a_types='', $a_force_join_reference=false)
Get tree subtree query.
__renumber($node_id=1, $i=1)
This method is private.
setTreeTablePK($a_column_name)
set column containing primary key in tree table @access public
isDeleted($a_node_id)
This is a wrapper for isSaved() with a more useful name.
getNodeTreeData($a_node_id)
return all columns of tabel tree
setTableNames($a_table_tree, $a_table_obj_data, $a_table_obj_reference="")
set table names The primary key of the table containing your object_data must be 'obj_id' You may use...
getTableReference()
Get reference table if available.
fetchTranslationFromObjectDataCache($a_obj_ids)
Get translation data from object cache (trigger in object cache on preload)
readRootId()
read root id from database
removeTree($a_tree_id)
remove an existing tree
fetchNodeData($a_row)
get data of parent node from tree and object_data @access private
checkTree()
check consistence of tree all left & right values are checked if they are exists only once @access pu...
getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields=array())
get all node ids in the subtree under specified node id, filter by object ids
renumber($node_id=1, $i=1)
Wrapper for renumber.
getNodePath($a_endnode_id, $a_startnode_id=0)
Returns the node path for the specified object reference.
getChildsByType($a_node_id, $a_type)
get child nodes of given node by object type @access public
getNodeData($a_node_id, $a_tree_pk=null)
get all information of a node.
setTreeId($a_tree_id)
set tree id @access public
getSavedNodeObjIds(array $a_obj_ids)
get object id of saved/deleted nodes
getChildsByTypeFilter($a_node_id, $a_types, $a_order="", $a_direction="ASC")
get child nodes of given node by object type @access public
deleteNode($a_tree_id, $a_node_id)
getMaximumDepth()
Return the current maximum depth in the tree @access public.
moveTree($a_source_id, $a_target_id, $a_location=self::POS_LAST_NODE)
Move Tree Implementation.
preloadDepthParent($a_node_ids)
Preload depth/parent.
isSaved($a_node_id)
Use method isDeleted check if node is saved.
initLangCode()
Store user language.
buildJoin()
build join depending on table settings @access private
getTreeImplementation()
Get tree implementation.
getNodePathForTitlePath($titlePath, $a_startnode_id=null)
Converts a path consisting of object titles into a path consisting of tree nodes.
initTreeImplementation()
Init tree implementation.
preloadDeleted($a_node_ids)
Preload deleted information.
__construct($a_tree_id, $a_root_id=0)
Constructor @access public.
getFilteredSubTree($a_node_id, $a_filter=array())
get filtered subtree
getObjectDataTable()
Get object data table.
getRootId()
get the root id of tree @access public
insertNode($a_node_id, $a_parent_id, $a_pos=IL_LAST_NODE, $a_reset_deletion_date=false)
insert new node with node_id under parent node with parent_id @access public
checkForParentType($a_ref_id, $a_type, $a_exclude_source_check=false)
Check for parent type e.g check if a folder (ref_id 3) is in a parent course obj => checkForParentTyp...
__isMainTree()
Check if operations are done on main tree.
__getSubTreeByParentRelation($a_node_id, &$parent_childs)
@global type $ilDB
isInTree($a_node_id)
get all information of a node.
getGap()
Get default gap *.
__validateSubtrees(&$lft_childs, $parent_childs)
getPathId($a_endnode_id, $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
addTree($a_tree_id, $a_node_id=-1)
create a new tree to do: ???
getPathFull($a_endnode_id, $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
getParentNodeData($a_node_id)
get data of parent node from tree and object_data @access public
getChilds($a_node_id, $a_order="", $a_direction="ASC")
get child nodes of given node @access public
validateParentRelations()
Validate parent relations of tree.
getSubTreeIds($a_ref_id)
Get all ids of subnodes.
static quoteArray($a_array)
Quotes all members of an array for usage in DB query statement.
static shortenText( $a_str, $a_len, $a_dots=false, $a_next_blank=false, $a_keep_extension=false)
shorten a string to given length.
if(!array_key_exists('StateId', $_REQUEST)) $id
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
catch(Exception $e) $message
foreach($_POST as $key=> $value) $res