3 declare(strict_types=1);
140 $this->db = $db ?? $DIC->database();
143 if (isset($DIC[
'ilAppEventHandler'])) {
144 $this->eventHandler = $DIC[
'ilAppEventHandler'];
147 $this->lang_code = self::DEFAULT_LANGUAGE;
149 if (func_num_args() > 3) {
150 $this->
logger->error(
"Wrong parameter count!");
155 if ($a_root_id > 0) {
156 $this->root_id = $a_root_id;
161 $this->tree_id = $a_tree_id;
162 $this->table_tree =
'tree';
163 $this->table_obj_data =
'object_data';
164 $this->table_obj_reference =
'object_reference';
165 $this->ref_pk =
'ref_id';
166 $this->obj_pk =
'obj_id';
167 $this->tree_pk =
'tree';
169 $this->use_cache =
true;
172 $this->gap = self::DEFAULT_GAP;
186 $db = $DIC->database();
188 $query =
'select tree from tree ' .
194 $trees[] = (
int) $row->tree;
206 if (!$DIC->isDependencyAvailable(
'settings') || $DIC->settings()->getModule() !=
'common') {
209 $setting = $DIC->settings();
213 if ($setting->get(
'main_tree_impl',
'ns') ==
'ns') {
237 $this->use_cache = $a_use;
279 if ($DIC->offsetExists(
'ilUser')) {
280 $this->lang_code = $DIC->user()->getCurrentLanguage() ?
281 $DIC->user()->getCurrentLanguage() : self::DEFAULT_LANGUAGE;
283 $this->lang_code = self::DEFAULT_LANGUAGE;
332 $this->in_tree_cache = array();
344 string $a_table_tree,
345 string $a_table_obj_data,
346 string $a_table_obj_reference =
"" 348 $this->table_tree = $a_table_tree;
349 $this->table_obj_data = $a_table_obj_data;
350 $this->table_obj_reference = $a_table_obj_reference;
361 $this->ref_pk = $a_column_name;
369 $this->obj_pk = $a_column_name;
377 $this->tree_pk = $a_column_name;
387 if ($this->table_obj_reference) {
389 return "JOIN " . $this->table_obj_reference .
" ON " . $this->table_tree .
".child=" . $this->table_obj_reference .
"." . $this->ref_pk .
" " .
390 "JOIN " . $this->table_obj_data .
" ON " . $this->table_obj_reference .
"." . $this->obj_pk .
"=" . $this->table_obj_data .
"." . $this->obj_pk .
" ";
393 return "JOIN " . $this->table_obj_data .
" ON " . $this->table_tree .
".child=" . $this->table_obj_data .
"." . $this->obj_pk .
" ";
423 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
424 'WHERE parent = ' . $this->db->quote($a_node,
'integer') .
' ' .
425 'AND tree = ' . $this->db->quote($this->tree_id,
'integer' .
' ' .
427 $res = $this->db->query($query);
431 $childs[] = (
int) $row->child;
440 public function getChilds(
int $a_node_id,
string $a_order =
"",
string $a_direction =
"ASC"): array
444 $ilObjDataCache = $DIC[
'ilObjDataCache'];
445 $ilUser = $DIC[
'ilUser'];
457 if (!empty($a_order)) {
458 $order_clause =
"ORDER BY " . $a_order .
" " . $a_direction;
460 $order_clause =
"ORDER BY " . $this->table_tree .
".lft";
464 'SELECT * FROM ' . $this->table_tree .
' ' .
466 "WHERE parent = %s " .
467 "AND " . $this->table_tree .
"." . $this->tree_pk .
" = %s " .
469 $this->db->quote($a_node_id,
'integer'),
470 $this->db->quote($this->tree_id,
'integer')
473 $res = $this->db->query($query);
475 if (!$count = $res->numRows()) {
482 while (
$r = $this->db->fetchAssoc($res)) {
484 $obj_ids[] = (
int)
$r[
"obj_id"];
489 is_object($ilUser) && $this->lang_code == $ilUser->getLanguage() && !isset($this->oc_preloaded[$a_node_id])) {
491 $ilObjDataCache->preloadObjectCache($obj_ids, $this->lang_code);
493 $this->oc_preloaded[$a_node_id] =
true;
496 foreach ($rows as $row) {
501 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child'].' = true'); 502 $this->in_tree_cache[$row[
'child']] = $row[
'tree'] == 1;
505 $childs[$count - 1][
"last"] =
true;
520 string $a_order =
"",
521 string $a_direction =
"ASC" 523 $childs = $this->
getChilds($a_node, $a_order, $a_direction);
526 foreach ($childs as $child) {
527 if (!in_array($child[
"type"], $a_filter)) {
528 $filtered[] = $child;
540 if ($a_type ==
'rolf' && $this->table_obj_reference) {
544 $this->db->setLimit(1, 0);
546 "SELECT * FROM " . $this->table_tree .
" " .
548 "WHERE parent = %s " .
549 "AND " . $this->table_tree .
"." . $this->tree_pk .
" = %s " .
550 "AND " . $this->table_obj_data .
".type = %s ",
551 $this->db->quote($a_node_id,
'integer'),
552 $this->db->quote($this->tree_id,
'integer'),
553 $this->db->quote($a_type,
'text')
557 "SELECT * FROM " . $this->table_tree .
" " .
559 "WHERE parent = %s " .
560 "AND " . $this->table_tree .
"." . $this->tree_pk .
" = %s " .
561 "AND " . $this->table_obj_data .
".type = %s " .
562 "ORDER BY " . $this->table_tree .
".lft",
563 $this->db->quote($a_node_id,
'integer'),
564 $this->db->quote($this->tree_id,
'integer'),
565 $this->db->quote($a_type,
'text')
568 $res = $this->db->query($query);
572 while ($row = $this->db->fetchAssoc($res)) {
584 string $a_order =
"",
585 string $a_direction =
"ASC" 589 $filter =
'AND ' . $this->table_obj_data .
'.type IN(' . implode(
',',
ilArrayUtil::quoteArray($a_types)) .
') ';
593 if (!empty($a_order)) {
594 $order_clause =
"ORDER BY " . $a_order .
" " . $a_direction;
596 $order_clause =
"ORDER BY " . $this->table_tree .
".lft";
599 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
601 'WHERE parent = ' . $this->db->quote($a_node_id,
'integer') .
' ' .
602 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = ' . $this->db->quote(
609 $res = $this->db->query($query);
612 while ($row = $this->db->fetchAssoc(
$res)) {
629 int $a_pos = self::POS_LAST_NODE,
630 bool $a_reset_deleted_date =
false 633 if ($a_source_id <= 1 || $a_target_id <= 0) {
638 if ($this->
isInTree($a_source_id)) {
644 $query =
'DELETE from tree ' .
645 'WHERE tree = ' . $this->db->quote($a_tree_id,
'integer') .
' ' .
646 'AND child = ' . $this->db->quote($a_source_id,
'integer');
647 $this->db->manipulate($query);
649 $this->
insertNode($a_source_id, $a_target_id, self::POS_LAST_NODE, $a_reset_deleted_date);
659 int $a_pos = self::POS_LAST_NODE,
660 bool $a_reset_deletion_date =
false 664 if ($a_node_id <= 1 || $a_parent_id <= 0) {
666 'Invalid parameters! $a_node_id: %s $a_parent_id: %s',
676 $this->table_tree .
"!");
681 $this->in_tree_cache[$a_node_id] =
true;
684 if ($a_reset_deletion_date) {
688 $this->eventHandler->raise(
692 'tree' => $this->table_tree,
693 'node_id' => $a_node_id,
694 'parent_id' => $a_parent_id
713 foreach ($this->
getSubTree($node) as $subnode) {
714 if ($depth && $subnode[
'depth'] > $depth) {
717 if (!$first && in_array($subnode[
'type'], $a_filter)) {
718 $depth = $subnode[
'depth'];
724 $filtered[] = $subnode;
744 public function getSubTree(array $a_node,
bool $a_with_data =
true, array $a_type = []): array
748 $res = $this->db->query($query);
750 while ($row = $this->db->fetchAssoc(
$res)) {
754 $subtree[] = (
int) $row[
'child'];
757 if ($this->
__isMainTree() || $this->table_tree ==
"lm_tree") {
758 $this->in_tree_cache[$row[
'child']] =
true;
797 public function getPathFull(
int $a_endnode_id,
int $a_startnode_id = 0): array
799 $pathIds = $this->
getPathId($a_endnode_id, $a_startnode_id);
803 if (count($pathIds) == 0) {
807 $inClause =
'child IN (';
808 for ($i = 0; $i < count($pathIds); $i++) {
812 $inClause .= $this->db->quote($pathIds[$i],
'integer');
817 'FROM ' . $this->table_tree .
' ' .
819 'WHERE ' . $inClause .
' ' .
820 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = ' . $this->db->quote(
825 $r = $this->db->query(
$q);
833 $this->in_tree_cache[$row[
'child']] = $row[
'tree'] == 1;
851 $res = $this->db->query(
'SELECT t.depth, t.parent, t.child ' .
852 'FROM ' . $this->table_tree .
' t ' .
853 'WHERE ' . $this->db->in(
"child", $a_node_ids,
false,
"integer") .
854 'AND ' . $this->tree_pk .
' = ' . $this->db->quote($this->tree_id,
"integer"));
855 while ($row = $this->db->fetchAssoc(
$res)) {
856 $this->depth_cache[$row[
"child"]] = (
int) $row[
"depth"];
857 $this->parent_cache[$row[
"child"]] = (
int) $row[
"parent"];
866 public function getPathId(
int $a_endnode_id,
int $a_startnode_id = 0): array
868 if (!$a_endnode_id) {
874 if ($this->
isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id])) {
875 return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
881 $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
896 public function getNodePath(
int $a_endnode_id,
int $a_startnode_id = 0): array
898 $pathIds = $this->
getPathId($a_endnode_id, $a_startnode_id);
901 if (count($pathIds) == 0) {
907 for ($i = 0; $i < count($pathIds); $i++) {
908 $types[] =
'integer';
909 $data[] = $pathIds[$i];
912 $query =
'SELECT t.depth,t.parent,t.child,d.obj_id,d.type,d.title ' .
913 'FROM ' . $this->table_tree .
' t ' .
914 'JOIN ' . $this->table_obj_reference .
' r ON r.ref_id = t.child ' .
915 'JOIN ' . $this->table_obj_data .
' d ON d.obj_id = r.obj_id ' .
916 'WHERE ' . $this->db->in(
't.child',
$data,
false,
'integer') .
' ' .
919 $res = $this->db->queryF($query, $types,
$data);
922 while ($row = $this->db->fetchAssoc(
$res)) {
935 $types = array(
'integer');
936 $query =
'SELECT lft,rgt FROM ' . $this->table_tree .
' ' .
937 'WHERE ' . $this->tree_pk .
' = %s ';
939 $res = $this->db->queryF($query, $types, array($this->tree_id));
941 while ($row = $this->db->fetchObject(
$res)) {
946 $all = array_merge($lft, $rgt);
947 $uni = array_unique($all);
949 if (count($all) != count($uni)) {
963 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
964 'WHERE ' . $this->tree_pk .
' = %s ' .
966 $r1 = $this->db->queryF($query, array(
'integer'), array($this->tree_id));
968 while ($row = $this->db->fetchAssoc($r1)) {
970 if (($row[
"child"] == 0) && $a_no_zero_child) {
971 $message =
"Tree contains child with ID 0!";
976 if ($this->table_obj_reference) {
978 $query =
'SELECT * FROM ' . $this->table_obj_reference .
' WHERE ' . $this->ref_pk .
' = %s ';
979 $r2 = $this->db->queryF($query, array(
'integer'), array($row[
'child']));
982 if ($r2->numRows() == 0) {
983 $message =
"No Object-to-Reference entry found for ID " . $row[
"child"] .
"!";
987 if ($r2->numRows() > 1) {
988 $message =
"More Object-to-Reference entries found for ID " . $row[
"child"] .
"!";
994 $obj_ref = $this->db->fetchAssoc($r2);
996 $query =
'SELECT * FROM ' . $this->table_obj_data .
' WHERE ' . $this->obj_pk .
' = %s';
997 $r3 = $this->db->queryF($query, array(
'integer'), array($obj_ref[$this->obj_pk]));
998 if ($r3->numRows() == 0) {
1003 if ($r3->numRows() > 1) {
1010 $query =
'SELECT * FROM ' . $this->table_obj_data .
' WHERE ' . $this->obj_pk .
' = %s';
1011 $r2 = $this->db->queryF($query, array(
'integer'), array($row[
'child']));
1013 if ($r2->numRows() == 0) {
1014 $message =
"No child found for ID " . $row[
"child"] .
"!";
1018 if ($r2->numRows() > 1) {
1019 $message =
"More childs found for ID " . $row[
"child"] .
"!";
1036 $res = $this->db->query($query);
1038 $row = $this->db->fetchAssoc(
$res);
1039 return (
int) $row[
'depth'];
1051 $query =
'SELECT depth FROM ' . $this->table_tree .
' ' .
1052 'WHERE child = %s ';
1053 $res = $this->db->queryF($query, array(
'integer'), array($a_node_id));
1054 $row = $this->db->fetchObject(
$res);
1056 $query =
'SELECT depth FROM ' . $this->table_tree .
' ' .
1057 'WHERE child = %s ' .
1058 'AND ' . $this->tree_pk .
' = %s ';
1059 $res = $this->db->queryF($query, array(
'integer',
'integer'), array($a_node_id, $this->tree_id));
1060 $row = $this->db->fetchObject(
$res);
1062 return (
int) ($row->depth ?? 0);
1080 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1081 'WHERE child = ' . $this->db->quote($a_node_id,
'integer');
1082 $res = $this->db->query($query);
1094 public function getNodeData(
int $a_node_id, ?
int $a_tree_pk = null): array
1097 if ($a_node_id < 1) {
1098 $message =
'No valid parameter given! $a_node_id: %s' . $a_node_id;
1104 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1106 'WHERE ' . $this->table_tree .
'.child = %s ' .
1107 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ';
1108 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1110 $a_tree_pk === null ? $this->tree_id : $a_tree_pk
1112 $row = $this->db->fetchAssoc(
$res);
1125 $objDefinition = $DIC[
'objDefinition'];
1129 $data[
"desc"] = (string) ($a_row[
"description"] ??
'');
1132 $translation_type =
'';
1133 if (is_object($objDefinition)) {
1134 $translation_type = $objDefinition->getTranslationType(
$data[
"type"] ??
'');
1137 if ($translation_type ==
"sys") {
1139 $data[
"description"] = (string)
$lng->txt(
"obj_" .
$data[
"type"] .
"_local_desc") .
$data[
"title"] .
$data[
"desc"];
1144 $data[
"description"] =
$lng->txt(
"obj_" .
$data[
"type"] .
"_desc");
1147 } elseif ($translation_type ==
"db") {
1151 array_key_exists(
$data[
"obj_id"] .
'.' . $lang_code, $this->translation_cache)) {
1153 $data[
"title"] = $this->translation_cache[
$key][
'title'];
1154 $data[
"description"] = $this->translation_cache[
$key][
'description'];
1155 $data[
"desc"] = $this->translation_cache[
$key][
'desc'];
1158 $query =
'SELECT title,description FROM object_translation ' .
1159 'WHERE obj_id = %s ' .
1160 'AND lang_code = %s ';
1162 $res = $this->db->queryF($query, array(
'integer',
'text'), array(
1166 $row = $this->db->fetchObject(
$res);
1169 $data[
"title"] = (string) $row->title;
1171 $data[
"desc"] = (string) $row->description;
1175 if ($this->
isCacheUsed() && count($this->translation_cache) < 1000) {
1177 $this->translation_cache[
$key] = [];
1178 $this->translation_cache[
$key][
'title'] =
$data[
"title"];
1179 $this->translation_cache[
$key][
'description'] =
$data[
"description"];
1180 $this->translation_cache[
$key][
'desc'] =
$data[
"desc"];
1186 if (isset(
$data[
'type']) && (
$data[
'type'] ==
'crsr' ||
$data[
'type'] ==
'catr' ||
$data[
'type'] ==
'grpr' ||
$data[
'type'] ===
'prgr')) {
1201 $ilObjDataCache = $DIC[
'ilObjDataCache'];
1203 if ($this->
isCacheUsed() && is_array($a_obj_ids) && is_object($ilObjDataCache)) {
1204 foreach ($a_obj_ids as
$id) {
1205 $this->translation_cache[$id .
'.'][
'title'] = $ilObjDataCache->lookupTitle((
int) $id);
1206 $this->translation_cache[$id .
'.'][
'description'] = $ilObjDataCache->lookupDescription((
int) $id);
1207 $this->translation_cache[$id .
'.'][
'desc'] =
1208 $this->translation_cache[$id .
'.'][
'description'];
1219 if (is_null($a_node_id) || !$a_node_id) {
1223 if ($this->
isCacheUsed() && isset($this->in_tree_cache[$a_node_id])) {
1224 return $this->in_tree_cache[$a_node_id];
1227 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1228 'WHERE ' . $this->table_tree .
'.child = %s ' .
1229 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s';
1231 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1236 if (
$res->numRows() > 0) {
1238 $this->in_tree_cache[$a_node_id] =
true;
1243 $this->in_tree_cache[$a_node_id] =
false;
1256 $ilLog = $DIC[
'ilLog'];
1258 if ($this->table_obj_reference) {
1260 $innerjoin =
"JOIN " . $this->table_obj_reference .
" ON v.child=" . $this->table_obj_reference .
"." . $this->ref_pk .
" " .
1261 "JOIN " . $this->table_obj_data .
" ON " . $this->table_obj_reference .
"." . $this->obj_pk .
"=" . $this->table_obj_data .
"." . $this->obj_pk .
" ";
1264 $innerjoin =
"JOIN " . $this->table_obj_data .
" ON v.child=" . $this->table_obj_data .
"." . $this->obj_pk .
" ";
1267 $query =
'SELECT * FROM ' . $this->table_tree .
' s, ' . $this->table_tree .
' v ' .
1269 'WHERE s.child = %s ' .
1270 'AND s.parent = v.child ' .
1271 'AND s.' . $this->tree_pk .
' = %s ' .
1272 'AND v.' . $this->tree_pk .
' = %s';
1273 $res = $this->db->queryF($query, array(
'integer',
'integer',
'integer'), array(
1278 $row = $this->db->fetchAssoc(
$res);
1279 if (is_array($row)) {
1288 public function isGrandChild(
int $a_startnode_id,
int $a_querynode_id): bool
1290 return $this->
getRelation($a_startnode_id, $a_querynode_id) == self::RELATION_PARENT;
1297 public function addTree(
int $a_tree_id,
int $a_node_id = -1): bool
1304 'Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
1312 if ($a_node_id <= 0) {
1313 $a_node_id = $a_tree_id;
1316 $query =
'INSERT INTO ' . $this->table_tree .
' (' .
1317 $this->tree_pk .
', child,parent,lft,rgt,depth) ' .
1319 '(%s,%s,%s,%s,%s,%s)';
1320 $res = $this->db->manipulateF(
1322 array(
'integer',
'integer',
'integer',
'integer',
'integer',
'integer'),
1350 $query =
'DELETE FROM ' . $this->table_tree .
1351 ' WHERE ' . $this->tree_pk .
' = %s ';
1352 $this->db->manipulateF($query, array(
'integer'), array($a_tree_id));
1361 public function moveToTrash(
int $a_node_id,
bool $a_set_deleted =
false,
int $a_deleted_by = 0): bool
1365 $user = $DIC->user();
1366 if (!$a_deleted_by) {
1367 $a_deleted_by = $user->getId();
1376 $res = $this->db->query($query);
1380 $subnodes[] = (
int) $row[
'child'];
1383 if (!count($subnodes)) {
1388 if ($a_set_deleted) {
1401 return $this->
isSaved($a_node_id);
1410 if ($this->
isCacheUsed() && isset($this->is_saved_cache[$a_node_id])) {
1411 return $this->is_saved_cache[$a_node_id];
1414 $query =
'SELECT ' . $this->tree_pk .
' FROM ' . $this->table_tree .
' ' .
1415 'WHERE child = %s ';
1416 $res = $this->db->queryF($query, array(
'integer'), array($a_node_id));
1417 $row = $this->db->fetchAssoc(
$res);
1422 $this->is_saved_cache[$a_node_id] =
true;
1427 $this->is_saved_cache[$a_node_id] =
false;
1438 if (!is_array($a_node_ids) || !$this->
isCacheUsed()) {
1442 $query =
'SELECT ' . $this->tree_pk .
', child FROM ' . $this->table_tree .
' ' .
1443 'WHERE ' . $this->db->in(
"child", $a_node_ids,
false,
"integer");
1445 $res = $this->db->query($query);
1446 while ($row = $this->db->fetchAssoc(
$res)) {
1447 if ($row[$this->tree_pk] < 0) {
1449 $this->is_saved_cache[$row[
"child"]] =
true;
1453 $this->is_saved_cache[$row[
"child"]] =
false;
1467 if (!isset($a_parent_id)) {
1473 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1475 'WHERE ' . $this->table_tree .
'.' . $this->tree_pk .
' < %s ' .
1476 'AND ' . $this->table_tree .
'.parent = %s';
1477 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1483 while ($row = $this->db->fetchAssoc(
$res)) {
1497 $query =
'SELECT ' . $this->table_obj_data .
'.obj_id FROM ' . $this->table_tree .
' ' .
1499 'WHERE ' . $this->table_tree .
'.' . $this->tree_pk .
' < ' . $this->db->quote(0,
'integer') .
' ' .
1500 'AND ' . $this->db->in($this->table_obj_data .
'.obj_id', $a_obj_ids,
false,
'integer');
1501 $res = $this->db->query($query);
1503 while ($row = $this->db->fetchAssoc(
$res)) {
1504 $saved[] = (
int) $row[
'obj_id'];
1518 $query =
'SELECT parent FROM ' . $this->table_tree .
' ' .
1519 'WHERE child = %s ';
1520 $res = $this->db->queryF(
1526 $query =
'SELECT parent FROM ' . $this->table_tree .
' ' .
1527 'WHERE child = %s ' .
1528 'AND ' . $this->tree_pk .
' = %s ';
1529 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1535 if ($row = $this->db->fetchObject(
$res)) {
1536 return (
int) $row->parent;
1550 if (!isset($a_node_id)) {
1556 $query =
'SELECT lft FROM ' . $this->table_tree .
' ' .
1557 'WHERE child = %s ' .
1558 'AND ' . $this->tree_pk .
' = %s ';
1559 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1563 $row = $this->db->fetchObject(
$res);
1564 return (
int) $row->lft;
1574 if (!isset($a_node)) {
1581 $query =
'SELECT count(*) cnt FROM ' . $this->table_tree .
' ' .
1583 'WHERE lft <= %s ' .
1585 'AND parent = %s ' .
1586 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ';
1588 $res = $this->db->queryF($query, array(
'integer',
'text',
'integer',
'integer'), array(
1595 $query =
'SELECT count(*) cnt FROM ' . $this->table_tree .
' ' .
1597 'WHERE lft <= %s ' .
1598 'AND parent = %s ' .
1599 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ';
1601 $res = $this->db->queryF($query, array(
'integer',
'integer',
'integer'), array(
1607 $row = $this->db->fetchAssoc(
$res);
1608 return (
int) $row[
"cnt"];
1613 $query =
'SELECT child FROM ' . $this->table_tree .
' ' .
1614 'WHERE parent = %s ' .
1615 'AND ' . $this->tree_pk .
' = %s ';
1616 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1621 if ($row = $this->db->fetchObject(
$res)) {
1622 $this->root_id = (
int) $row->child;
1634 $this->root_id = $a_root_id;
1644 $this->tree_id = $a_tree_id;
1656 $query =
'SELECT lft FROM ' . $this->table_tree .
' ' .
1657 'WHERE ' . $this->table_tree .
'.child = %s ' .
1658 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ';
1659 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1663 $curr_node = $this->db->fetchAssoc(
$res);
1666 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1669 'AND ' . $this->table_obj_data .
'.type = %s ' .
1670 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ' .
1672 $this->db->setLimit(1, 0);
1673 $res = $this->db->queryF($query, array(
'integer',
'text',
'integer'), array(
1679 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1682 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ' .
1684 $this->db->setLimit(1, 0);
1685 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1691 if (
$res->numRows() < 1) {
1694 $row = $this->db->fetchAssoc(
$res);
1707 if (!isset($a_node_id)) {
1714 $query =
'SELECT lft FROM ' . $this->table_tree .
' ' .
1715 'WHERE ' . $this->table_tree .
'.child = %s ' .
1716 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ';
1717 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1722 $curr_node = $this->db->fetchAssoc(
$res);
1725 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1728 'AND ' . $this->table_obj_data .
'.type = %s ' .
1729 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ' .
1730 'ORDER BY lft DESC';
1731 $this->db->setLimit(1, 0);
1732 $res = $this->db->queryF($query, array(
'integer',
'text',
'integer'), array(
1738 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1741 'AND ' . $this->table_tree .
'.' . $this->tree_pk .
' = %s ' .
1742 'ORDER BY lft DESC';
1743 $this->db->setLimit(1, 0);
1744 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1750 if (
$res->numRows() < 1) {
1753 $row = $this->db->fetchAssoc(
$res);
1764 $renumber_callable =
function (
ilDBInterface $db) use ($node_id, $i, &$return) {
1770 $ilAtomQuery = $this->db->buildAtomQuery();
1771 $ilAtomQuery->addTableLock($this->table_tree);
1773 $ilAtomQuery->addQueryCallable($renumber_callable);
1774 $ilAtomQuery->run();
1776 $renumber_callable($this->db);
1789 $query =
'UPDATE ' . $this->table_tree .
' SET lft = %s WHERE child = %s';
1790 $this->db->manipulateF(
1792 array(
'integer',
'integer'),
1799 $query =
'UPDATE ' . $this->table_tree .
' SET lft = %s WHERE child = %s AND tree = %s';
1800 $this->db->manipulateF(
1802 array(
'integer',
'integer',
'integer'),
1811 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1812 'WHERE parent = ' . $this->db->quote($node_id,
'integer') .
' ' .
1814 $res = $this->db->query($query);
1818 $childs[] = (
int) $row->child;
1821 foreach ($childs as $child) {
1827 if (count($childs) > 0) {
1828 $i += $this->gap * 2;
1832 $query =
'UPDATE ' . $this->table_tree .
' SET rgt = %s WHERE child = %s';
1833 $res = $this->db->manipulateF(
1835 array(
'integer',
'integer'),
1842 $query =
'UPDATE ' . $this->table_tree .
' SET rgt = %s WHERE child = %s AND tree = %s';
1843 $res = $this->db->manipulateF($query, array(
'integer',
'integer',
'integer'), array(
1859 $cache_key = $a_ref_id .
'.' . $a_type .
'.' . ((
int) $a_exclude_source_check);
1863 array_key_exists($cache_key, $this->parent_type_cache)) {
1864 return (
int) $this->parent_type_cache[$cache_key];
1868 $do_cache = ($this->
__isMainTree() && count($this->parent_type_cache) < 1000);
1873 $this->parent_type_cache[$cache_key] =
false;
1881 if ($a_exclude_source_check) {
1885 foreach (
$path as $node) {
1887 if ($node[
"type"] == $a_type) {
1889 $this->parent_type_cache[$cache_key] = (
int) $node[
"child"];
1891 return (
int) $node[
"child"];
1896 $this->parent_type_cache[$cache_key] =
false;
1906 public static function _removeEntry(
int $a_tree,
int $a_child,
string $a_db_table =
"tree"): void
1910 $db = $DIC->database();
1912 if ($a_db_table ===
'tree') {
1915 'Tried to delete root node! $a_tree: %s $a_child: %s',
1924 $query =
'DELETE FROM ' . $a_db_table .
' ' .
1925 'WHERE tree = %s ' .
1938 return $this->table_tree ===
'tree';
1950 $this->
logger->debug($query);
1951 $res = $this->db->query($query);
1953 $counter = (
int) $lft_childs = [];
1954 while ($row = $this->db->fetchObject(
$res)) {
1955 $lft_childs[$row->child] = (
int) $row->parent;
1960 if ($counter != count($lft_childs)) {
1961 $message =
'Duplicate entries for "child" in maintree! $a_node_id: ' . $a_node[
'child'];
1968 $parent_childs = [];
1982 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
1983 'WHERE child = %s ' .
1985 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1991 while ($row = $this->db->fetchObject(
$res)) {
1992 $parent_childs[$a_node_id] = (
int) $row->parent;
1997 $message =
'Multiple entries in maintree! $a_node_id: ' . $a_node_id;
2004 $query =
'SELECT * FROM ' . $this->table_tree .
' ' .
2005 'WHERE parent = %s ';
2006 $res = $this->db->queryF($query, array(
'integer'), array($a_node_id));
2008 while ($row = $this->db->fetchObject(
$res)) {
2026 ksort($parent_childs);
2028 $this->
logger->debug(
'left childs ' . print_r($lft_childs,
true));
2029 $this->
logger->debug(
'parent childs ' . print_r($parent_childs,
true));
2031 if (count($lft_childs) != count($parent_childs)) {
2032 $message =
'(COUNT) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2037 foreach ($lft_childs as
$key => $value) {
2038 if ($parent_childs[
$key] != $value) {
2039 $message =
'(COMPARE) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2044 $message =
'(ROOT_FOLDER) Tree is corrupted! Tried to delete root folder';
2060 public function moveTree(
int $a_source_id,
int $a_target_id,
int $a_location = self::POS_LAST_NODE): void
2062 $old_parent_id = $this->
getParentId($a_source_id);
2065 $GLOBALS[
'DIC'][
'ilAppEventHandler']->raise(
2069 'tree' => $this->table_tree,
2070 'source_id' => $a_source_id,
2071 'target_id' => $a_target_id,
2072 'old_parent_id' => $old_parent_id
2093 array $a_fields = [],
2094 array $a_types = [],
2095 bool $a_force_join_reference =
false 2100 $a_force_join_reference,
2107 array $a_fields = [],
2108 array $a_types = [],
2109 bool $a_force_join_reference =
false 2114 $a_force_join_reference,
2128 if (!count($node)) {
2137 if (count($a_fields)) {
2138 $fields = implode(
',', $a_fields);
2141 $query =
"SELECT " . $fields .
2144 " WHERE " . $this->
getTableReference() .
"." . $this->ref_pk .
" IN (" . $query .
")" .
2145 " AND " . $this->db->in($this->
getObjectDataTable() .
"." . $this->obj_pk, $a_obj_ids,
false,
"integer");
2146 $set = $this->db->query($query);
2147 while ($row = $this->db->fetchAssoc($set)) {
2156 $query =
'DELETE FROM tree where ' .
2157 'child = ' . $this->db->quote($a_node_id,
'integer') .
' ' .
2158 'AND tree = ' . $this->db->quote($a_tree_id,
'integer');
2159 $this->db->manipulate($query);
2161 $this->eventHandler->raise(
2165 'tree' => $this->table_tree,
2166 'node_id' => $a_node_id,
2167 'tree_id' => $a_tree_id
2178 $query =
'SELECT DISTINCT(o.type) ' . $this->db->quoteIdentifier(
'type') .
2179 ' FROM tree t JOIN object_reference r ON child = r.ref_id ' .
2180 'JOIN object_data o on r.obj_id = o.obj_id ' .
2181 'WHERE tree < ' . $this->db->quote(0,
'integer') .
' ' .
2182 'AND child = -tree ' .
2184 $res = $this->db->query($query);
2186 $types_deleted = [];
2188 $types_deleted[] = (string) $row->type;
2190 return $types_deleted;
2198 return $this->table_tree ==
'tree';
isRepositoryTree()
check if current tree instance operates on repository tree table
moveTree(int $a_source_id, int $a_target_id, int $a_location=self::POS_LAST_NODE)
Move Tree Implementation public.
Thrown if invalid tree strucutes are found.
setObjectTablePK(string $a_column_name)
set column containing primary key in object table
static setDeletedDates(array $ref_ids, int $user_id)
initTreeImplementation()
Init tree implementation.
fetchSuccessorNode(int $a_node_id, string $a_type="")
get node data of successor node
getNodeData(int $a_node_id, ?int $a_tree_pk=null)
get all information of a node.
static _lookupTitle(int $obj_id)
getNodeTreeData(int $a_node_id)
return all columns of tabel tree
static quoteArray(array $a_array)
Quotes all members of an array for usage in DB query statement.
ilAppEventHandler $eventHandler
manipulateF(string $query, array $types, array $values)
static getLogger(string $a_component_id)
Get component logger.
string $table_obj_data
table name of object_data table
isSaved(int $a_node_id)
Use method isDeleted.
getFilteredChilds(array $a_filter, int $a_node, string $a_order="", string $a_direction="ASC")
get child nodes of given node (exclude filtered obj_types)
deleteNode(int $a_tree_id, int $a_node_id)
checkTreeChilds(bool $a_no_zero_child=true)
check, if all childs of tree nodes exist in object table
getChilds(int $a_node_id, string $a_order="", string $a_direction="ASC")
get child nodes of given node
getNodePath(int $a_endnode_id, int $a_startnode_id=0)
Returns the node path for the specified object reference.
getSavedNodeObjIds(array $a_obj_ids)
get object id of saved/deleted nodes
isInTree(?int $a_node_id)
get all information of a node.
getParentCache()
Get parent cache.
isDeleted(int $a_node_id)
This is a wrapper for isSaved() with a more useful name.
buildJoin()
build join depending on table settings private
useCache(bool $a_use=true)
Use Cache (usually activated)
setTreeTablePK(string $a_column_name)
set column containing primary key in tree table
static lookupTreesForNode(int $node_id)
getSubTreeIds(int $a_ref_id)
Get all ids of subnodes.
deleteTree(array $a_node)
delete node and the whole subtree under this node
quote($value, string $type)
getTreePk()
Get tree primary key.
Base class for nested set path based trees.
addTree(int $a_tree_id, int $a_node_id=-1)
create a new tree to do: ???
const TREE_TYPE_MATERIALIZED_PATH
getPathFull(int $a_endnode_id, int $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
__getSubTreeByParentRelation(int $a_node_id, array &$parent_childs)
int $tree_id
to use different trees in one db-table
getRelationOfNodes(array $a_node_a_arr, array $a_node_b_arr)
get relation of two nodes by node data
isGrandChild(int $a_startnode_id, int $a_querynode_id)
checks if a node is in the path of an other node
getObjectDataTable()
Get object data table.
static _resetDeletedDate(int $ref_id)
validateParentRelations()
Validate parent relations of tree.
getChildsByType(int $a_node_id, string $a_type)
get child nodes of given node by object type
__validateSubtrees(array &$lft_childs, array $parent_childs)
setTableNames(string $a_table_tree, string $a_table_obj_data, string $a_table_obj_reference="")
set table names The primary key of the table containing your object_data must be 'obj_id' You may use...
__checkDelete(array $a_node)
Check for deleteTree() compares a subtree of a given node by checking lft, rgt against parent relatio...
checkForParentType(int $a_ref_id, string $a_type, bool $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...
getSubTreeFilteredByObjIds(int $a_node_id, array $a_obj_ids, array $a_fields=[])
get all node ids in the subtree under specified node id, filter by object ids
fetchTranslationFromObjectDataCache(array $a_obj_ids)
Get translation data from object cache (trigger in object cache on preload)
getDepth(int $a_node_id)
return depth of a node in tree
removeTree(int $a_tree_id)
remove an existing tree
getParentNodeData(int $a_node_id)
get data of parent node from tree and object_data
getTreeTable()
Get tree table name.
getRelation(int $a_node_a, int $a_node_b)
Get relation of two nodes.
checkTree()
check consistence of tree all left & right values are checked if they are exists only once ...
getSavedNodeData(int $a_parent_id)
get data saved/deleted nodes
preloadDeleted(array $a_node_ids)
Preload deleted information.
int $root_id
points to root node (may be a subtree)
renumber(int $node_id=1, int $i=1)
Wrapper for renumber.
lookupTrashedObjectTypes()
Lookup object types in trash.
__renumber(int $node_id=1, int $i=1)
This method is private.
query(string $query)
Run a (read-only) Query on the database.
getRbacSubtreeInfo(int $a_endnode_id)
This method is used for change existing objects and returns all necessary information for this action...
getLeftValue(int $a_node_id)
get left value of given node
getTableReference()
Get reference table if available.
getParentId(int $a_node_id)
get parent id of given node
resetInTreeCache()
reset in tree cache
ilTreeImplementation $tree_impl
getChildsByTypeFilter(int $a_node_id, array $a_types, string $a_order="", string $a_direction="ASC")
get child nodes of given node by object type
setRootId(int $a_root_id)
getFilteredSubTree(int $a_node_id, array $a_filter=[])
get filtered subtree get all subtree nodes beginning at a specific node excluding specific object typ...
insertNodeFromTrash(int $a_source_id, int $a_target_id, int $a_tree_id, int $a_pos=self::POS_LAST_NODE, bool $a_reset_deleted_date=false)
Insert node from trash deletes trash entry.
getDepthCache()
Get depth cache.
string $tree_pk
column name containing tree id in tree table
fetchNodeData(array $a_row)
get data of parent node from tree and object_data
initLangCode()
Do not use it Store user language.
preloadDepthParent(array $a_node_ids)
Preload depth/parent.
const TREE_TYPE_NESTED_SET
getPathId(int $a_endnode_id, int $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
getTrashSubTreeQuery(int $a_node_id, array $a_fields=[], array $a_types=[], bool $a_force_join_reference=false)
getSubTreeQuery(int $a_node_id, array $a_fields=[], array $a_types=[], bool $a_force_join_reference=false)
Get tree subtree query.
insertNode(int $a_node_id, int $a_parent_id, int $a_pos=self::POS_LAST_NODE, bool $a_reset_deletion_date=false)
insert new node with node_id under parent node with parent_id
getMaximumDepth()
Return the current maximum depth in the tree.
static shortenTextExtended(string $a_str, int $a_len, bool $a_dots=false, bool $a_next_blank=false, bool $a_keep_extension=false)
string $obj_pk
column name containing primary key in object table
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
setTreeId(int $a_tree_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
string $ref_pk
column name containing primary key in reference table
isCacheUsed()
Check if cache is active.
getChildSequenceNumber(array $a_node, string $type="")
get sequence number of node in sibling sequence
getSubTree(array $a_node, bool $a_with_data=true, array $a_type=[])
get all nodes in the subtree under specified node
__construct(int $a_tree_id, int $a_root_id=0, ilDBInterface $db=null)
__isMainTree()
Check if operations are done on main tree.
string $table_tree
table name of tree table
string $table_obj_reference
table name of object_reference table
getTreeImplementation()
Get tree implementation.
static _removeEntry(int $a_tree, int $a_child, string $a_db_table="tree")
STATIC METHOD Removes a single entry from a tree.
Interface for tree implementations Currrently nested set or materialized path.
moveToTrash(int $a_node_id, bool $a_set_deleted=false, int $a_deleted_by=0)
Move node to trash bin.
setReferenceTablePK(string $a_column_name)
set column containing primary key in reference table
int $gap
Size of the gaps to be created in the nested sets sequence numbering of the tree nodes.
fetchPredecessorNode(int $a_node_id, string $a_type="")
get node data of predecessor node