4 include_once
'./Services/Tree/interfaces/interface.ilTreeImplementation.php';
26 $this->tree = $a_tree;
46 $query =
'SELECT s.child FROM '.
47 $this->
getTree()->getTreeTable().
' s, '.
48 $this->
getTree()->getTreeTable().
' t '.
49 'WHERE t.child = %s '.
52 'AND s.'.$this->getTree()->getTreePk().
' = %s';
56 array(
'integer',
'integer'),
57 array($a_node_id,$this->
getTree()->getTreeId())
61 $childs[] =
$row->child;
63 return $childs ? $childs : array();
72 public function getSubTreeQuery($a_node, $a_types =
'', $a_force_join_reference =
true, $a_fields = array())
77 if (is_array($a_types))
81 $type_str =
"AND ".$ilDB->in($this->
getTree()->getObjectDataTable().
".type", $a_types,
false,
"text");
84 else if(strlen($a_types))
86 $type_str =
"AND ".$this->getTree()->getObjectDataTable().
".type = ".$ilDB->quote($a_types,
"text");
90 if($type_str or $a_force_join_reference)
92 $join = $this->
getTree()->buildJoin();
98 $fields = implode(
',',$a_fields);
103 "FROM ".$this->getTree()->getTreeTable().
" ".
105 "WHERE ".$this->getTree()->getTreeTable().
'.lft '.
106 'BETWEEN '.$ilDB->quote($a_node[
'lft'],
'integer').
' '.
107 'AND '.$ilDB->quote($a_node[
'rgt'],
'integer').
' '.
108 "AND ".$this->getTree()->getTreeTable().
".".$this->
getTree()->getTreePk().
" = ".$ilDB->quote($this->
getTree()->getTreeId(),
'integer').
' '.
110 "ORDER BY ".$this->getTree()->getTreeTable().
".lft";
112 #$GLOBALS['ilLog']->write(__METHOD__.'-----------------: '. $query);
125 if($a_node_a[
'child'] == $a_node_b[
'child'])
127 $GLOBALS[
'ilLog']->write(__METHOD__.
': EQUALS');
130 if($a_node_a[
'lft'] < $a_node_b[
'lft'] and $a_node_a[
'rgt'] > $a_node_b[
'rgt'])
132 $GLOBALS[
'ilLog']->write(__METHOD__.
': PARENT');
135 if($a_node_b[
'lft'] < $a_node_a[
'lft'] and $a_node_b[
'rgt'] > $a_node_a[
'rgt'])
137 $GLOBALS[
'ilLog']->write(__METHOD__.
': CHILD');
142 if($a_node_a[
'parent'] == $a_node_b[
'parent'])
144 $GLOBALS[
'ilLog']->write(__METHOD__.
': SIBLING');
147 $GLOBALS[
'ilLog']->write(__METHOD__.
': NONE');
172 if($this->
getTree()->__isMainTree())
183 $query = sprintf(
'SELECT * FROM '.$this->
getTree()->getTreeTable().
' '.
185 'AND '.$this->
getTree()->getTreePk().
' = %s ',
186 $ilDB->quote($a_parent_id,
'integer'),
187 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
190 $r = $ilDB->fetchObject($res);
192 if ($r->parent == NULL)
194 if($this->
getTree()->__isMainTree())
196 $ilDB->unlockTables();
207 $query = sprintf(
'UPDATE '.$this->
getTree()->getTreeTable().
' SET '.
208 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, '.
209 'rgt = CASE WHEN rgt > %s THEN rgt + 2 ELSE rgt END '.
210 'WHERE '.$this->
getTree()->getTreePk().
' = %s ',
211 $ilDB->quote($left,
'integer'),
212 $ilDB->quote($left,
'integer'),
213 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
214 $res = $ilDB->manipulate(
$query);
219 if ($this->
getTree()->getGap() > 0)
222 $query = sprintf(
'SELECT rgt,lft,parent FROM '.$this->
getTree()->getTreeTable().
' '.
224 'AND '.$this->
getTree()->getTreePk().
' = %s',
225 $ilDB->quote($a_parent_id,
'integer'),
226 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
227 $res = $ilDB->query(
$query);
228 $r = $ilDB->fetchAssoc($res);
230 if ($r[
'parent'] == null)
232 if($this->
getTree()->__isMainTree())
234 $ilDB->unlockTables();
239 $parentRgt = $r[
'rgt'];
240 $parentLft = $r[
'lft'];
243 $availableSpace = $parentRgt - $parentLft;
244 if ($availableSpace < 2)
255 $query = sprintf(
'SELECT MAX(rgt) max_rgt FROM '.$this->
getTree()->getTreeTable().
' '.
256 'WHERE parent = %s '.
257 'AND '.$this->
getTree()->getTreePk().
' = %s',
258 $ilDB->quote($a_parent_id,
'integer'),
259 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
260 $res = $ilDB->query(
$query);
261 $r = $ilDB->fetchAssoc($res);
263 if (isset($r[
'max_rgt']))
267 $availableSpace = $parentRgt - $r[
'max_rgt'];
268 $lft = $r[
'max_rgt'] + 1;
275 $lft = $parentLft + 1;
282 if ($availableSpace < 2)
285 $query = sprintf(
'UPDATE '.$this->
getTree()->getTreeTable().
' SET '.
286 'lft = CASE WHEN lft > %s THEN lft + %s ELSE lft END, '.
287 'rgt = CASE WHEN rgt >= %s THEN rgt + %s ELSE rgt END '.
288 'WHERE '.$this->
getTree()->getTreePk().
' = %s ',
289 $ilDB->quote($parentRgt,
'integer'),
290 $ilDB->quote((2 + $this->
getTree()->getGap() * 2),
'integer'),
291 $ilDB->quote($parentRgt,
'integer'),
292 $ilDB->quote((2 + $this->
getTree()->getGap() * 2),
'integer'),
293 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
294 $res = $ilDB->manipulate(
$query);
302 $query = sprintf(
'SELECT * FROM '.$this->
getTree()->getTreeTable().
' '.
304 'AND '.$this->
getTree()->getTreePk().
' = %s ',
305 $ilDB->quote($a_parent_id,
'integer'),
306 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
307 $res = $ilDB->query(
$query);
308 $r = $ilDB->fetchObject($res);
310 if ($r->parent == null)
312 if($this->
getTree()->__isMainTree())
314 $ilDB->unlockTables();
325 $query = sprintf(
'UPDATE '.$this->
getTree()->getTreeTable().
' SET '.
326 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, '.
327 'rgt = CASE WHEN rgt >= %s THEN rgt + 2 ELSE rgt END '.
328 'WHERE '.$this->
getTree()->getTreePk().
' = %s',
329 $ilDB->quote($right,
'integer'),
330 $ilDB->quote($right,
'integer'),
331 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
332 $res = $ilDB->manipulate(
$query);
340 $query = sprintf(
'SELECT * FROM '.$this->
getTree()->getTreeTable().
' '.
342 'AND '.$this->
getTree()->getTreePk().
' = %s ',
343 $ilDB->quote($a_pos,
'integer'),
344 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
345 $res = $ilDB->query(
$query);
346 $r = $ilDB->fetchObject($res);
349 if ($r->parent != $a_parent_id)
351 if($this->
getTree()->__isMainTree())
353 $ilDB->unlockTables();
364 $query = sprintf(
'UPDATE '.$this->
getTree()->getTreeTable().
' SET '.
365 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, '.
366 'rgt = CASE WHEN rgt > %s THEN rgt + 2 ELSE rgt END '.
367 'WHERE '.$this->
getTree()->getTreePk().
' = %s',
368 $ilDB->quote($right,
'integer'),
369 $ilDB->quote($right,
'integer'),
370 $ilDB->quote($this->
getTree()->getTreeId(),
'integer'));
371 $res = $ilDB->manipulate(
$query);
377 $depth = $this->
getTree()->getDepth($a_parent_id) + 1;
380 $query = sprintf(
'INSERT INTO '.$this->
getTree()->getTreeTable().
' ('.$this->
getTree()->getTreePk().
',child,parent,lft,rgt,depth) '.
381 'VALUES (%s,%s,%s,%s,%s,%s)',
382 $ilDB->quote($this->getTree()->getTreeId(),
'integer'),
383 $ilDB->quote($a_node_id,
'integer'),
384 $ilDB->quote($a_parent_id,
'integer'),
385 $ilDB->quote($lft,
'integer'),
386 $ilDB->quote($rgt,
'integer'),
387 $ilDB->quote($depth,
'integer'));
391 if($this->
getTree()->__isMainTree())
393 $ilDB->unlockTables();
408 if($this->
getTree()->__isMainTree())
417 $query =
'SELECT * FROM '.$this->getTree()->getTreeTable().
' '.
418 'WHERE child = '.$ilDB->quote($a_node_id,
'integer').
' '.
419 'AND '.$this->getTree()->getTreePk().
' = '.$ilDB->quote($this->
getTree()->getTreeId(),
'integer');
424 $query = sprintf(
'DELETE FROM '.$this->
getTree()->getTreeTable().
' '.
425 'WHERE lft BETWEEN %s AND %s '.
426 'AND rgt BETWEEN %s AND %s '.
427 'AND '.$this->
getTree()->getTreePk().
' = %s',
428 $ilDB->quote($a_node[
'lft'],
'integer'),
429 $ilDB->quote($a_node[
'rgt'],
'integer'),
430 $ilDB->quote($a_node[
'lft'],
'integer'),
431 $ilDB->quote($a_node[
'rgt'],
'integer'),
432 $ilDB->quote($a_node[$this->
getTree()->getTreePk()],
'integer'));
439 $diff = $a_node[
"rgt"] - $a_node[
"lft"] + 1;
441 $a_node[$this->
getTree()->getTreePk()] >= 0 &&
442 $a_node[
'rgt'] - $a_node[
'lft'] >= $this->
getTree()->getGap() * 2
446 $query = sprintf(
'UPDATE '.$this->
getTree()->getTreeTable().
' SET '.
447 'lft = CASE WHEN lft > %s THEN lft - %s ELSE lft END, '.
448 'rgt = CASE WHEN rgt > %s THEN rgt - %s ELSE rgt END '.
449 'WHERE '.$this->
getTree()->getTreePk().
' = %s ',
450 $ilDB->quote($a_node[
'lft'],
'integer'),
451 $ilDB->quote($diff,
'integer'),
452 $ilDB->quote($a_node[
'lft'],
'integer'),
453 $ilDB->quote($diff,
'integer'),
454 $ilDB->quote($a_node[$this->
getTree()->getTreePk()],
'integer'));
456 $res = $ilDB->manipulate(
$query);
459 if($this->
getTree()->__isMainTree())
461 $ilDB->unlockTables();
477 $node = $this->
getTree()->getNodeTreeData($a_node_id);
479 $query =
'UPDATE '.$this->getTree()->getTreeTable().
' '.
480 'SET tree = '.$ilDB->quote(-1 * $node[
'child'],
'integer').
' '.
481 'WHERE '.$this->getTree()->getTreePk().
' = '.$ilDB->quote($this->
getTree()->getTreeId(),
'integer').
' '.
482 'AND lft BETWEEN '.$ilDB->quote($node[
'lft'],
'integer').
' AND '.$ilDB->quote($node[
'rgt'],
'integer').
' ';
484 $ilDB->manipulate(
$query);
506 $takeId = $a_startnode_id == 0;
508 $depth_cache = $this->
getTree()->getDepthCache();
509 $parent_cache = $this->
getTree()->getParentCache();
512 $this->
getTree()->__isMainTree() &&
513 isset($depth_cache[$a_endnode_id]) &&
514 isset($parent_cache[$a_endnode_id]))
516 $nodeDepth = $depth_cache[$a_endnode_id];
517 $parentId = $parent_cache[$a_endnode_id];
521 $nodeDepth = $this->
getTree()->getDepth($a_endnode_id);
522 $parentId = $this->
getTree()->getParentId($a_endnode_id);
533 else if ($nodeDepth == 1)
535 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
536 if ($takeId) $pathIds[] = $a_endnode_id;
538 else if ($nodeDepth == 2)
540 $takeId = $takeId || $parentId == $a_startnode_id;
541 if ($takeId) $pathIds[] = $parentId;
542 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
543 if ($takeId) $pathIds[] = $a_endnode_id;
545 else if ($nodeDepth == 3)
547 $takeId = $takeId || $this->
getTree()->getRootId() == $a_startnode_id;
548 if ($takeId) $pathIds[] = $this->
getTree()->getRootId();
549 $takeId = $takeId || $parentId == $a_startnode_id;
550 if ($takeId) $pathIds[] = $parentId;
551 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
552 if ($takeId) $pathIds[] = $a_endnode_id;
554 else if ($nodeDepth < 32)
565 $qSelect =
't1.child c0';
567 for ($i = 1; $i < $nodeDepth - 2; $i++)
569 $qSelect .=
', t'.$i.
'.parent c'.$i;
570 $qJoin .=
' JOIN '.$this->getTree()->getTreeTable().
' t'.$i.
' ON '.
571 't'.$i.
'.child=t'.($i - 1).
'.parent AND '.
572 't'.$i.
'.'.$this->getTree()->getTreePk().
' = '.(int) $this->
getTree()->getTreeId();
575 $types = array(
'integer',
'integer');
576 $data = array($this->
getTree()->getTreeId(),$parentId);
577 $query =
'SELECT '.$qSelect.
' '.
578 'FROM '.$this->getTree()->getTreeTable().
' t0 '.$qJoin.
' '.
579 'WHERE t0.'.$this->getTree()->getTreePk().
' = %s '.
580 'AND t0.child = %s ';
585 if (
$res->numRows() == 0)
592 $takeId = $takeId || $this->
getTree()->getRootId() == $a_startnode_id;
593 if ($takeId) $pathIds[] = $this->
getTree()->getRootId();
594 for ($i = $nodeDepth - 4; $i >=0; $i--)
596 $takeId = $takeId ||
$row[
'c'.$i] == $a_startnode_id;
597 if ($takeId) $pathIds[] =
$row[
'c'.$i];
599 $takeId = $takeId || $parentId == $a_startnode_id;
600 if ($takeId) $pathIds[] = $parentId;
601 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
602 if ($takeId) $pathIds[] = $a_endnode_id;
630 $fields = array(
'integer',
'integer',
'integer');
631 $data = array($a_endnode_id,$this->
getTree()->getTreeId(),$this->
getTree()->getTreeId());
633 $query =
"SELECT T2.child ".
634 "FROM ".$this->getTree()->getTreeTable().
" T1, ".$this->
getTree()->getTreeTable().
" T2 ".
635 "WHERE T1.child = %s ".
636 "AND T1.lft BETWEEN T2.lft AND T2.rgt ".
637 "AND T1.".$this->getTree()->getTreePk().
" = %s ".
638 "AND T2.".$this->getTree()->getTreePk().
" = %s ".
643 $takeId = $a_startnode_id == 0;
644 while(
$row = $ilDB->fetchAssoc(
$res))
646 if ($takeId ||
$row[
'child'] == $a_startnode_id)
649 $pathIds[] =
$row[
'child'];
652 return $pathIds ? $pathIds : array();
662 public function moveTree($a_source_id, $a_target_id, $a_position)
666 if ($this->
getTree()->__isMainTree())
673 $query =
'SELECT * FROM ' . $this->
getTree()->getTreeTable() .
' ' .
674 'WHERE ( child = %s OR child = %s ) ' .
675 'AND ' . $this->
getTree()->getTreePk() .
' = %s ';
676 $res = $ilDB->queryF(
$query, array(
'integer',
'integer',
'integer'), array(
679 $this->
getTree()->getTreeId()));
682 if (
$res->numRows() != 2)
684 if ($this->
getTree()->__isMainTree())
686 $ilDB->unlockTables();
689 $GLOBALS[
'ilLog']->write(__METHOD__.
': Objects not found in tree');
690 throw new InvalidArgumentException(
'Error moving subtree');
692 while (
$row = $ilDB->fetchObject(
$res))
694 if (
$row->child == $a_source_id)
696 $source_lft =
$row->lft;
697 $source_rgt =
$row->rgt;
698 $source_depth =
$row->depth;
699 $source_parent =
$row->parent;
703 $target_lft =
$row->lft;
704 $target_rgt =
$row->rgt;
705 $target_depth =
$row->depth;
710 if ($target_lft >= $source_lft and $target_rgt <= $source_rgt)
712 if ($this->
getTree()->__isMainTree())
714 $ilDB->unlockTables();
717 $GLOBALS[
'ilLog']->write(__METHOD__.
': Target is child of source');
718 throw new ilInvalidArgumentException(
'Error moving subtree: target is child of source');
723 $spread_diff = $source_rgt - $source_lft + 1;
724 #var_dump("<pre>","SPREAD_DIFF: ",$spread_diff,"<pre>");
726 $query =
'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
727 'lft = CASE WHEN lft > %s THEN lft + %s ELSE lft END, ' .
728 'rgt = CASE WHEN rgt >= %s THEN rgt + %s ELSE rgt END ' .
729 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s ';
730 $res = $ilDB->manipulateF(
$query, array(
'integer',
'integer',
'integer',
'integer',
'integer'), array(
735 $this->
getTree()->getTreeId()));
739 if ($source_lft > $target_rgt)
741 $where_offset = $spread_diff;
742 $move_diff = $target_rgt - $source_lft - $spread_diff;
747 $move_diff = $target_rgt - $source_lft;
749 $depth_diff = $target_depth - $source_depth + 1;
752 $query =
'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
753 'parent = CASE WHEN parent = %s THEN %s ELSE parent END, ' .
756 'depth = depth + %s ' .
759 'AND ' . $this->
getTree()->getTreePk() .
' = %s ';
760 $res = $ilDB->manipulateF(
$query, array(
'integer',
'integer',
'integer',
'integer',
'integer',
'integer',
'integer',
'integer'), array(
766 $source_lft + $where_offset,
767 $source_rgt + $where_offset,
768 $this->
getTree()->getTreeId()));
771 $query =
'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
772 'lft = CASE WHEN lft >= %s THEN lft - %s ELSE lft END, ' .
773 'rgt = CASE WHEN rgt >= %s THEN rgt - %s ELSE rgt END ' .
774 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s ';
776 $res = $ilDB->manipulateF(
$query, array(
'integer',
'integer',
'integer',
'integer',
'integer'), array(
777 $source_lft + $where_offset,
779 $source_rgt + $where_offset,
781 $this->
getTree()->getTreeId()));
783 if ($this->
getTree()->__isMainTree())
785 $ilDB->unlockTables();
799 $query =
"SELECT t2.lft lft, t2.rgt rgt, t2.child child, type ".
800 "FROM ".$this->getTree()->getTreeTable().
" t1 ".
801 "JOIN ".$this->getTree()->getTreeTable().
" t2 ON (t2.lft BETWEEN t1.lft AND t1.rgt) ".
802 "JOIN ".$this->getTree()->getTableReference().
" obr ON t2.child = obr.ref_id ".
803 "JOIN ".$this->getTree()->getObjectDataTable().
" obd ON obr.obj_id = obd.obj_id ".
804 "WHERE t1.child = ".$ilDB->quote($a_endnode_id,
'integer').
" ".
805 "AND t1.".$this->getTree()->getTreePk().
" = ".$ilDB->quote($this->
getTree()->getTreeId(),
'integer').
" ".
806 "AND t2.".$this->getTree()->getTreePk().
" = ".$ilDB->quote($this->
getTree()->getTreeId(),
'integer').
" ".
816 $nodes[
$row->child][
'lft'] =
$row->lft;
817 $nodes[
$row->child][
'rgt'] =
$row->rgt;
818 $nodes[
$row->child][
'child']=
$row->child;
819 $nodes[
$row->child][
'type'] =
$row->type;
822 return (array) $nodes;