ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.ilTree.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4define("IL_LAST_NODE", -2);
5define("IL_FIRST_NODE", -1);
6
7include_once './Services/Tree/exceptions/class.ilInvalidTreeStructureException.php';
8
24class ilTree
25{
26 const POS_LAST_NODE = -2;
27 const POS_FIRST_NODE = -1;
28
29
30 const RELATION_CHILD = 1; // including grand child
31 const RELATION_PARENT = 2; // including grand child
33 const RELATION_EQUALS = 4;
34 const RELATION_NONE = 5;
35
36
42 public $ilias;
43
44
50 public $log;
51
57 public $root_id;
58
64 public $tree_id;
65
72
79
86
92 public $ref_pk;
93
99 public $obj_pk;
100
106 public $tree_pk;
107
132 public $gap;
133
134 protected $depth_cache = array();
135 protected $parent_cache = array();
136 protected $in_tree_cache = array();
137
138 private $tree_impl = null;
139
140
148 public function __construct($a_tree_id, $a_root_id = 0)
149 {
150 global $ilDB;
151
152 // set db
153 $this->ilDB = $ilDB;
154
155 $this->lang_code = "en";
156
157 // CREATE LOGGER INSTANCE
158 $this->log = ilLoggerFactory::getLogger('tree');
159
160 if (!isset($a_tree_id) or (func_num_args() == 0)) {
161 $this->log->error("No tree_id given!");
162 $this->log->logStack(ilLogLevel::DEBUG);
163 throw new InvalidArgumentException("No tree_id given!");
164 }
165
166 if (func_num_args() > 2) {
167 $this->log->error("Wrong parameter count!");
168 throw new InvalidArgumentException("Wrong parameter count!");
169 }
170
171 //init variables
172 if (empty($a_root_id)) {
173 $a_root_id = ROOT_FOLDER_ID;
174 }
175
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';
184
185 $this->use_cache = true;
186
187 // If cache is activated, cache object translations to improve performance
188 $this->translation_cache = array();
189 $this->parent_type_cache = array();
190
191 // By default, we create gaps in the tree sequence numbering for 50 nodes
192 $this->gap = 50;
193
194
195 // init tree implementation
196 $this->initTreeImplementation();
197 }
198
202 public function initTreeImplementation()
203 {
204 global $ilDB;
205
206
207 if (!is_object($GLOBALS['ilSetting']) or $GLOBALS['ilSetting']->getModule() != 'common') {
208 include_once './Services/Administration/classes/class.ilSetting.php';
209 $setting = new ilSetting('common');
210 } else {
211 $setting = $GLOBALS['ilSetting'];
212 }
213
214 if ($this->__isMainTree()) {
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';
218 $this->tree_impl = new ilNestedSetTree($this);
219 } else {
220 #$GLOBALS['ilLog']->write(__METHOD__.': Using materialized path.');
221 include_once './Services/Tree/classes/class.ilMaterializedPathTree.php';
222 $this->tree_impl = new ilMaterializedPathTree($this);
223 }
224 } else {
225 #$GLOBALS['ilLog']->write(__METHOD__.': Using netsted set for non main tree.');
226 include_once './Services/Tree/classes/class.ilNestedSetTree.php';
227 $this->tree_impl = new ilNestedSetTree($this);
228 }
229 }
230
235 public function getTreeImplementation()
236 {
237 return $this->tree_impl;
238 }
239
243 public function useCache($a_use = true)
244 {
245 $this->use_cache = $a_use;
246 }
247
252 public function isCacheUsed()
253 {
254 return $this->__isMainTree() and $this->use_cache;
255 }
256
261 public function getDepthCache()
262 {
263 return (array) $this->depth_cache;
264 }
265
270 public function getParentCache()
271 {
272 return (array) $this->parent_cache;
273 }
274
279 public function initLangCode()
280 {
281 global $ilUser;
282
283 // lang_code is only required in $this->fetchnodedata
284 if (!is_object($ilUser)) {
285 $this->lang_code = "en";
286 } else {
287 $this->lang_code = $ilUser->getCurrentLanguage();
288 }
289 }
290
295 public function getTreeTable()
296 {
297 return $this->table_tree;
298 }
299
304 public function getObjectDataTable()
305 {
307 }
308
313 public function getTreePk()
314 {
315 return $this->tree_pk;
316 }
317
321 public function getTableReference()
322 {
324 }
325
329 public function getGap()
330 {
331 return $this->gap;
332 }
333
334 /***
335 * reset in tree cache
336 */
337 public function resetInTreeCache()
338 {
339 $this->in_tree_cache = array();
340 }
341
342
359 public function setTableNames($a_table_tree, $a_table_obj_data, $a_table_obj_reference = "")
360 {
361 if (!isset($a_table_tree) or !isset($a_table_obj_data)) {
362 $message = "Missing parameter! " .
363 "tree table: " . $a_table_tree . " object data table: " . $a_table_obj_data;
364 $this->log->error($message);
365 throw new InvalidArgumentException($message);
366 }
367
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;
371
372 $this->initTreeImplementation();
373
374 return true;
375 }
376
384 public function setReferenceTablePK($a_column_name)
385 {
386 if (!isset($a_column_name)) {
387 $message = "No column name given!";
388 $this->log->error($message);
389 throw new InvalidArgumentException($message);
390 }
391
392 $this->ref_pk = $a_column_name;
393 return true;
394 }
395
403 public function setObjectTablePK($a_column_name)
404 {
405 if (!isset($a_column_name)) {
406 $message = "No column name given!";
407 $this->log->error($message);
408 throw new InvalidArgumentException($message);
409 }
410
411 $this->obj_pk = $a_column_name;
412 return true;
413 }
414
422 public function setTreeTablePK($a_column_name)
423 {
424 if (!isset($a_column_name)) {
425 $message = "No column name given!";
426 $this->log->error($message);
427 throw new InvalidArgumentException($message);
428 }
429
430 $this->tree_pk = $a_column_name;
431 return true;
432 }
433
439 public function buildJoin()
440 {
441 if ($this->table_obj_reference) {
442 // Use inner join instead of left join to improve performance
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 . " ";
445 } else {
446 // Use inner join instead of left join to improve performance
447 return "JOIN " . $this->table_obj_data . " ON " . $this->table_tree . ".child=" . $this->table_obj_data . "." . $this->obj_pk . " ";
448 }
449 }
450
456 public function getRelation($a_node_a, $a_node_b)
457 {
458 return $this->getRelationOfNodes(
459 $this->getNodeTreeData($a_node_a),
460 $this->getNodeTreeData($a_node_b)
461 );
462 }
463
470 public function getRelationOfNodes($a_node_a_arr, $a_node_b_arr)
471 {
472 return $this->getTreeImplementation()->getRelation($a_node_a_arr, $a_node_b_arr);
473 }
474
481 public function getChildIds($a_node)
482 {
483 global $ilDB;
484
485 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
486 'WHERE parent = ' . $ilDB->quote($a_node, 'integer') . ' ' .
487 'AND tree = ' . $ilDB->quote($this->tree_id, 'integer' . ' ' .
488 'ORDER BY lft');
489 $res = $ilDB->query($query);
490
491 $childs = array();
492 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
493 $childs[] = $row->child;
494 }
495 return $childs;
496 }
497
507 public function getChilds($a_node_id, $a_order = "", $a_direction = "ASC")
508 {
509 global $ilBench,$ilDB, $ilObjDataCache, $ilUser;
510
511 if (!isset($a_node_id)) {
512 $message = "No node_id given!";
513 $this->log->error($message);
514 throw new InvalidArgumentException($message);
515 }
516
517 // init childs
518 $childs = array();
519
520 // number of childs
521 $count = 0;
522
523 // init order_clause
524 $order_clause = "";
525
526 // set order_clause if sort order parameter is given
527 if (!empty($a_order)) {
528 $order_clause = "ORDER BY " . $a_order . " " . $a_direction;
529 } else {
530 $order_clause = "ORDER BY " . $this->table_tree . ".lft";
531 }
532
533
534 $query = sprintf(
535 'SELECT * FROM ' . $this->table_tree . ' ' .
536 $this->buildJoin() .
537 "WHERE parent = %s " .
538 "AND " . $this->table_tree . "." . $this->tree_pk . " = %s " .
539 $order_clause,
540 $ilDB->quote($a_node_id, 'integer'),
541 $ilDB->quote($this->tree_id, 'integer')
542 );
543
544 $res = $ilDB->query($query);
545
546 if (!$count = $res->numRows()) {
547 return array();
548 }
549
550 // get rows and object ids
551 $rows = array();
552 while ($r = $ilDB->fetchAssoc($res)) {
553 $rows[] = $r;
554 $obj_ids[] = $r["obj_id"];
555 }
556
557 // preload object translation information
558 if ($this->__isMainTree() && $this->isCacheUsed() && is_object($ilObjDataCache) &&
559 is_object($ilUser) && $this->lang_code == $ilUser->getLanguage() && !$this->oc_preloaded[$a_node_id]) {
560 // $ilObjDataCache->preloadTranslations($obj_ids, $this->lang_code);
561 $ilObjDataCache->preloadObjectCache($obj_ids, $this->lang_code);
563 $this->oc_preloaded[$a_node_id] = true;
564 }
565
566 foreach ($rows as $row) {
567 $childs[] = $this->fetchNodeData($row);
568
569 // Update cache of main tree
570 if ($this->__isMainTree()) {
571 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child'].' = true');
572 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
573 }
574 }
575 $childs[$count - 1]["last"] = true;
576 return $childs;
577 }
578
588 public function getFilteredChilds($a_filter, $a_node, $a_order = "", $a_direction = "ASC")
589 {
590 $childs = $this->getChilds($a_node, $a_order, $a_direction);
591
592 foreach ($childs as $child) {
593 if (!in_array($child["type"], $a_filter)) {
594 $filtered[] = $child;
595 }
596 }
597 return $filtered ? $filtered : array();
598 }
599
600
609 public function getChildsByType($a_node_id, $a_type)
610 {
611 global $ilDB;
612
613 if (!isset($a_node_id) or !isset($a_type)) {
614 $message = "Missing parameter! node_id:" . $a_node_id . " type:" . $a_type;
615 $this->log->error($message);
616 throw new InvalidArgumentException($message);
617 }
618
619 if ($a_type=='rolf' && $this->table_obj_reference) {
620 // Performance optimization: A node can only have exactly one
621 // role folder as its child. Therefore we don't need to sort the
622 // results, and we can let the database know about the expected limit.
623 $ilDB->setLimit(1, 0);
624 $query = sprintf(
625 "SELECT * FROM " . $this->table_tree . " " .
626 $this->buildJoin() .
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'),
632 $ilDB->quote($a_type, 'text')
633 );
634 } else {
635 $query = sprintf(
636 "SELECT * FROM " . $this->table_tree . " " .
637 $this->buildJoin() .
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'),
644 $ilDB->quote($a_type, 'text')
645 );
646 }
647 $res = $ilDB->query($query);
648
649 // init childs
650 $childs = array();
651 while ($row = $ilDB->fetchAssoc($res)) {
652 $childs[] = $this->fetchNodeData($row);
653 }
654
655 return $childs ? $childs : array();
656 }
657
658
667 public function getChildsByTypeFilter($a_node_id, $a_types, $a_order = "", $a_direction = "ASC")
668 {
669 global $ilDB;
670
671 if (!isset($a_node_id) or !$a_types) {
672 $message = "Missing parameter! node_id:" . $a_node_id . " type:" . $a_types;
673 $this->log->error($message);
674 throw new InvalidArgumentException($message);
675 }
676
677 $filter = ' ';
678 if ($a_types) {
679 $filter = 'AND ' . $this->table_obj_data . '.type IN(' . implode(',', ilUtil::quoteArray($a_types)) . ') ';
680 }
681
682 // set order_clause if sort order parameter is given
683 if (!empty($a_order)) {
684 $order_clause = "ORDER BY " . $a_order . " " . $a_direction;
685 } else {
686 $order_clause = "ORDER BY " . $this->table_tree . ".lft";
687 }
688
689 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
690 $this->buildJoin() .
691 'WHERE parent = ' . $ilDB->quote($a_node_id, 'integer') . ' ' .
692 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $ilDB->quote($this->tree_id, 'integer') . ' ' .
693 $filter .
694 $order_clause;
695
696 $res = $ilDB->query($query);
697 while ($row = $ilDB->fetchAssoc($res)) {
698 $childs[] = $this->fetchNodeData($row);
699 }
700
701 return $childs ? $childs : array();
702 }
703
715 public function insertNodeFromTrash($a_source_id, $a_target_id, $a_tree_id, $a_pos = IL_LAST_NODE, $a_reset_deleted_date = false)
716 {
717 global $ilDB;
718
719 if ($this->__isMainTree()) {
720 if ($a_source_id <= 1 or $a_target_id <= 0) {
722 throw new InvalidArgumentException('Invalid parameter given for ilTree::insertNodeFromTrash');
723 }
724 }
725 if (!isset($a_source_id) or !isset($a_target_id)) {
727 throw new InvalidArgumentException('Missing parameter for ilTree::insertNodeFromTrash');
728 }
729 if ($this->isInTree($a_source_id)) {
730 ilLoggerFactory::getLogger('tree')->error('Node already in tree');
732 throw new InvalidArgumentException('Node already in tree.');
733 }
734
735 $query = 'DELETE from tree ' .
736 'WHERE tree = ' . $ilDB->quote($a_tree_id, 'integer') . ' ' .
737 'AND child = ' . $ilDB->quote($a_source_id, 'integer');
738 $ilDB->manipulate($query);
739
740 $this->insertNode($a_source_id, $a_target_id, IL_LAST_NODE, $a_reset_deleted_date);
741 }
742
743
752 public function insertNode($a_node_id, $a_parent_id, $a_pos = IL_LAST_NODE, $a_reset_deletion_date = false)
753 {
754 global $ilDB;
755
756 //echo "+$a_node_id+$a_parent_id+";
757 // CHECK node_id and parent_id > 0 if in main tree
758 if ($this->__isMainTree()) {
759 if ($a_node_id <= 1 or $a_parent_id <= 0) {
761 'Invalid parameters! $a_node_id: %s $a_parent_id: %s',
762 $a_node_id,
763 $a_parent_id
764 );
765 $this->log->logStack(ilLogLevel::ERROR, $message);
766 throw new InvalidArgumentException($message);
767 }
768 }
769
770
771 if (!isset($a_node_id) or !isset($a_parent_id)) {
772 $this->log->logStack(ilLogLevel::ERROR);
773 throw new InvalidArgumentException("Missing parameter! " .
774 "node_id: " . $a_node_id . " parent_id: " . $a_parent_id);
775 }
776 if ($this->isInTree($a_node_id)) {
777 throw new InvalidArgumentException("Node " . $a_node_id . " already in tree " .
778 $this->table_tree . "!");
779 }
780
781 $this->getTreeImplementation()->insertNode($a_node_id, $a_parent_id, $a_pos);
782
783 $this->in_tree_cache[$a_node_id] = true;
784
785 // reset deletion date
786 if ($a_reset_deletion_date) {
787 ilObject::_resetDeletedDate($a_node_id);
788 }
789
790 if (isset($GLOBALS["ilAppEventHandler"]) && $this->__isMainTree()) {
791 $GLOBALS['ilAppEventHandler']->raise(
792 "Services/Tree",
793 "insertNode",
794 array(
795 'tree' => $this->table_tree,
796 'node_id' => $a_node_id,
797 'parent_id' => $a_parent_id)
798 );
799 }
800 }
801
814 public function getFilteredSubTree($a_node_id, $a_filter = array())
815 {
816 $node = $this->getNodeData($a_node_id);
817
818 $first = true;
819 $depth = 0;
820 foreach ($this->getSubTree($node) as $subnode) {
821 if ($depth and $subnode['depth'] > $depth) {
822 continue;
823 }
824 if (!$first and in_array($subnode['type'], $a_filter)) {
825 $depth = $subnode['depth'];
826 $first = false;
827 continue;
828 }
829 $depth = 0;
830 $first = false;
831 $filtered[] = $subnode;
832 }
833 return $filtered ? $filtered : array();
834 }
835
841 public function getSubTreeIds($a_ref_id)
842 {
843 return $this->getTreeImplementation()->getSubTreeIds($a_ref_id);
844 }
845
846
856 public function getSubTree($a_node, $a_with_data = true, $a_type = "")
857 {
858 global $ilDB;
859
860 if (!is_array($a_node)) {
861 $this->log->logStack(ilLogLevel::ERROR);
862 throw new InvalidArgumentException(__METHOD__ . ': wrong datatype for node data given');
863 }
864
865 /*
866 if($a_node['lft'] < 1 or $a_node['rgt'] < 2)
867 {
868 $GLOBALS['ilLog']->logStack();
869 $message = sprintf('%s: Invalid node given! $a_node["lft"]: %s $a_node["rgt"]: %s',
870 __METHOD__,
871 $a_node['lft'],
872 $a_node['rgt']);
873
874 throw new InvalidArgumentException($message);
875 }
876 */
877
878 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, $a_type);
879 $res = $ilDB->query($query);
880 while ($row = $ilDB->fetchAssoc($res)) {
881 if ($a_with_data) {
882 $subtree[] = $this->fetchNodeData($row);
883 } else {
884 $subtree[] = $row['child'];
885 }
886 // the lm_data "hack" should be removed in the trunk during an alpha
887 if ($this->__isMainTree() || $this->table_tree == "lm_tree") {
888 $this->in_tree_cache[$row['child']] = true;
889 }
890 }
891 return $subtree ? $subtree : array();
892 }
893
902 public function getSubTreeTypes($a_node, $a_filter = 0)
903 {
904 $a_filter = $a_filter ? $a_filter : array();
905
906 foreach ($this->getSubtree($this->getNodeData($a_node)) as $node) {
907 if (in_array($node["type"], $a_filter)) {
908 continue;
909 }
910 $types["$node[type]"] = $node["type"];
911 }
912 return $types ? $types : array();
913 }
914
922 public function deleteTree($a_node)
923 {
924 global $ilDB;
925
926 $this->log->debug('Delete tree with node ' . $a_node);
927
928 if (!is_array($a_node)) {
929 $this->log->logStack(ilLogLevel::ERROR);
930 throw new InvalidArgumentException(__METHOD__ . ': Wrong datatype for node data!');
931 }
932
933 $this->log->debug($this->tree_pk);
934
935 if ($this->__isMainTree()) {
936 // @todo normally this part is not executed, since the subtree is first
937 // moved to trash and then deleted.
938 if (!$this->__checkDelete($a_node)) {
939 $this->log->logStack(ilLogLevel::ERROR);
940 throw new ilInvalidTreeStructureException('Deletion canceled due to invalid tree structure.' . print_r($a_node, true));
941 }
942 }
943
944 $this->getTreeImplementation()->deleteTree($a_node['child']);
945
946 $this->resetInTreeCache();
947 }
948
953 public function validateParentRelations()
954 {
955 return $this->getTreeImplementation()->validateParentRelations();
956 }
957
968 public function getPathFull($a_endnode_id, $a_startnode_id = 0)
969 {
970 $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
971
972 // We retrieve the full path in a single query to improve performance
973 global $ilDB;
974
975 // Abort if no path ids were found
976 if (count($pathIds) == 0) {
977 return null;
978 }
979
980 $inClause = 'child IN (';
981 for ($i=0; $i < count($pathIds); $i++) {
982 if ($i > 0) {
983 $inClause .= ',';
984 }
985 $inClause .= $ilDB->quote($pathIds[$i], 'integer');
986 }
987 $inClause .= ')';
988
989 $q = 'SELECT * ' .
990 'FROM ' . $this->table_tree . ' ' .
991 $this->buildJoin() . ' ' .
992 'WHERE ' . $inClause . ' ' .
993 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer') . ' ' .
994 'ORDER BY depth';
995 $r = $ilDB->query($q);
996
997 $pathFull = array();
998 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
999 $pathFull[] = $this->fetchNodeData($row);
1000
1001 // Update cache
1002 if ($this->__isMainTree()) {
1003 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child']);
1004 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
1005 }
1006 }
1007 return $pathFull;
1008 }
1009
1010
1017 public function preloadDepthParent($a_node_ids)
1018 {
1019 global $ilDB;
1020
1021 if (!$this->__isMainTree() || !is_array($a_node_ids) || !$this->isCacheUsed()) {
1022 return;
1023 }
1024
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"));
1029 while ($row = $ilDB->fetchAssoc($res)) {
1030 $this->depth_cache[$row["child"]] = $row["depth"];
1031 $this->parent_cache[$row["child"]] = $row["parent"];
1032 }
1033 }
1034
1044 public function getPathId($a_endnode_id, $a_startnode_id = 0)
1045 {
1046 if (!$a_endnode_id) {
1047 $this->log->logStack(ilLogLevel::ERROR);
1048 throw new InvalidArgumentException(__METHOD__ . ': No endnode given!');
1049 }
1050
1051 // path id cache
1052 if ($this->isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id])) {
1053 //echo "<br>getPathIdhit";
1054 return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
1055 }
1056 //echo "<br>miss";
1057
1058 $pathIds = $this->getTreeImplementation()->getPathIds($a_endnode_id, $a_startnode_id);
1059
1060 if ($this->__isMainTree()) {
1061 $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
1062 }
1063 return $pathIds;
1064 }
1065
1066 // BEGIN WebDAV: getNodePathForTitlePath function added
1084 public function getNodePathForTitlePath($titlePath, $a_startnode_id = null)
1085 {
1086 global $ilDB, $log;
1087 //$log->write('getNodePathForTitlePath('.implode('/',$titlePath));
1088
1089 // handle empty title path
1090 if ($titlePath == null || count($titlePath) == 0) {
1091 if ($a_startnode_id == 0) {
1092 return null;
1093 } else {
1094 return $this->getNodePath($a_startnode_id);
1095 }
1096 }
1097
1098 // fetch the node path up to the startnode
1099 if ($a_startnode_id != null && $a_startnode_id != 0) {
1100 // Start using the node path to the root of the relative path
1101 $nodePath = $this->getNodePath($a_startnode_id);
1102 $parent = $a_startnode_id;
1103 } else {
1104 // Start using the root of the tree
1105 $nodePath = array();
1106 $parent = 0;
1107 }
1108
1109
1110 // Convert title path into Unicode Normal Form C
1111 // This is needed to ensure that we can compare title path strings with
1112 // strings from the database.
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++) {
1117 $titlePath[$i] = ilStr::strToLower(UtfNormal::toNFC($titlePath[$i]));
1118 if ($i > 0) {
1119 $inClause .= ',';
1120 }
1121 $inClause .= $ilDB->quote($titlePath[$i], 'text');
1122 }
1123 $inClause .= ')';
1124
1125 // Fetch all rows that are potential path elements
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;
1129 } else {
1130 $joinClause = 'JOIN ' . $this->table_obj_data . ' d ON t.child = d.' . $this->obj_pk;
1131 }
1132 // The ORDER BY clause in the following SQL statement ensures that,
1133 // in case of a multiple objects with the same title, always the Object
1134 // with the oldest ref_id is chosen.
1135 // This ensure, that, if a new object with the same title is added,
1136 // WebDAV clients can still work with the older object.
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 ' .
1139 $joinClause . ' ' .
1140 'WHERE ' . $inClause . ' ' .
1141 'AND t.depth <= ' . (count($titlePath)+count($nodePath)) . ' ' .
1142 'AND t.tree = 1 ' .
1143 'ORDER BY t.depth, t.child ASC';
1144 $r = $ilDB->query($q);
1145
1146 $rows = array();
1147 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1148 $row['title'] = UtfNormal::toNFC($row['title']);
1149 $row['ref_id'] = $row['child'];
1150 $rows[] = $row;
1151 }
1152
1153 // Extract the path elements from the fetched rows
1154 for ($i = 0; $i < count($titlePath); $i++) {
1155 $pathElementFound = false;
1156 foreach ($rows as $row) {
1157 if ($row['parent'] == $parent &&
1158 ilStr::strToLower($row['title']) == $titlePath[$i]) {
1159 // FIXME - We should test here, if the user has
1160 // 'visible' permission for the object.
1161 $nodePath[] = $row;
1162 $parent = $row['child'];
1163 $pathElementFound = true;
1164 break;
1165 }
1166 }
1167 // Abort if we haven't found a path element for the current depth
1168 if (!$pathElementFound) {
1169 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):null');
1170 return null;
1171 }
1172 }
1173 // Return the node path
1174 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):'.var_export($nodePath,true));
1175 return $nodePath;
1176 }
1177 // END WebDAV: getNodePathForTitlePath function added
1178 // END WebDAV: getNodePath function added
1195 public function getNodePath($a_endnode_id, $a_startnode_id = 0)
1196 {
1197 global $ilDB;
1198
1199 $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
1200
1201 // Abort if no path ids were found
1202 if (count($pathIds) == 0) {
1203 return null;
1204 }
1205
1206
1207 $types = array();
1208 $data = array();
1209 for ($i = 0; $i < count($pathIds); $i++) {
1210 $types[] = 'integer';
1211 $data[] = $pathIds[$i];
1212 }
1213
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 ';
1220
1221 $res = $ilDB->queryF($query, $types, $data);
1222
1223 $titlePath = array();
1224 while ($row = $ilDB->fetchAssoc($res)) {
1225 $titlePath[] = $row;
1226 }
1227 return $titlePath;
1228 }
1229 // END WebDAV: getNodePath function added
1230
1238 public function checkTree()
1239 {
1240 global $ilDB;
1241
1242 $types = array('integer');
1243 $query = 'SELECT lft,rgt FROM ' . $this->table_tree . ' ' .
1244 'WHERE ' . $this->tree_pk . ' = %s ';
1245
1246 $res = $ilDB->queryF($query, $types, array($this->tree_id));
1247 while ($row = $ilDB->fetchObject($res)) {
1248 $lft[] = $row->lft;
1249 $rgt[] = $row->rgt;
1250 }
1251
1252 $all = array_merge($lft, $rgt);
1253 $uni = array_unique($all);
1254
1255 if (count($all) != count($uni)) {
1256 $message = 'Tree is corrupted!';
1257
1258 $this->log->error($message);
1260 }
1261
1262 return true;
1263 }
1264
1272 public function checkTreeChilds($a_no_zero_child = true)
1273 {
1274 global $ilDB;
1275
1276 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1277 'WHERE ' . $this->tree_pk . ' = %s ' .
1278 'ORDER BY lft';
1279 $r1 = $ilDB->queryF($query, array('integer'), array($this->tree_id));
1280
1281 while ($row = $ilDB->fetchAssoc($r1)) {
1282 //echo "tree:".$row[$this->tree_pk].":lft:".$row["lft"].":rgt:".$row["rgt"].":child:".$row["child"].":<br>";
1283 if (($row["child"] == 0) && $a_no_zero_child) {
1284 $message = "Tree contains child with ID 0!";
1285 $this->log->error($message);
1287 }
1288
1289 if ($this->table_obj_reference) {
1290 // get object reference data
1291 $query = 'SELECT * FROM ' . $this->table_obj_reference . ' WHERE ' . $this->ref_pk . ' = %s ';
1292 $r2 = $ilDB->queryF($query, array('integer'), array($row['child']));
1293
1294 //echo "num_childs:".$r2->numRows().":<br>";
1295 if ($r2->numRows() == 0) {
1296 $message = "No Object-to-Reference entry found for ID " . $row["child"] . "!";
1297 $this->log->error($message);
1299 }
1300 if ($r2->numRows() > 1) {
1301 $message = "More Object-to-Reference entries found for ID " . $row["child"] . "!";
1302 $this->log->error($message);
1304 }
1305
1306 // get object data
1307 $obj_ref = $ilDB->fetchAssoc($r2);
1308
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) {
1312 $message = " No child found for ID " . $obj_ref[$this->obj_pk] . "!";
1313 $this->log->error($message);
1315 }
1316 if ($r3->numRows() > 1) {
1317 $message = "More childs found for ID " . $obj_ref[$this->obj_pk] . "!";
1318 $this->log->error($message);
1320 }
1321 } else {
1322 // get only object data
1323 $query = 'SELECT * FROM ' . $this->table_obj_data . ' WHERE ' . $this->obj_pk . ' = %s';
1324 $r2 = $ilDB->queryF($query, array('integer'), array($row['child']));
1325 //echo "num_childs:".$r2->numRows().":<br>";
1326 if ($r2->numRows() == 0) {
1327 $message = "No child found for ID " . $row["child"] . "!";
1328 $this->log->error($message);
1330 }
1331 if ($r2->numRows() > 1) {
1332 $message = "More childs found for ID " . $row["child"] . "!";
1333 $this->log->error($message);
1335 }
1336 }
1337 }
1338
1339 return true;
1340 }
1341
1347 public function getMaximumDepth()
1348 {
1349 global $ilDB;
1350
1351 $query = 'SELECT MAX(depth) depth FROM ' . $this->table_tree;
1352 $res = $ilDB->query($query);
1353
1354 $row = $ilDB->fetchAssoc($res);
1355 return $row['depth'];
1356 }
1357
1364 public function getDepth($a_node_id)
1365 {
1366 global $ilDB;
1367
1368 if ($a_node_id) {
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));
1373 $row = $ilDB->fetchObject($res);
1374
1375 return $row->depth;
1376 } else {
1377 return 1;
1378 }
1379 }
1380
1388 public function getNodeTreeData($a_node_id)
1389 {
1390 global $ilDB;
1391
1392 if (!$a_node_id) {
1393 $this->log->logStack(ilLogLevel::ERROR);
1394 throw new InvalidArgumentException('Missing or empty parameter $a_node_id: ' . $a_node_id);
1395 }
1396
1397 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1398 'WHERE child = ' . $ilDB->quote($a_node_id, 'integer');
1399 $res = $ilDB->query($query);
1400 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1401 return $row;
1402 }
1403 return array();
1404 }
1405
1406
1415 // BEGIN WebDAV: Pass tree id to this method
1416 //function getNodeData($a_node_id)
1417 public function getNodeData($a_node_id, $a_tree_pk = null)
1418 // END PATCH WebDAV: Pass tree id to this method
1419 {
1420 global $ilDB;
1421
1422 if (!isset($a_node_id)) {
1423 $this->log->logStack(ilLogLevel::ERROR);
1424 throw new InvalidArgumentException("No node_id given!");
1425 }
1426 if ($this->__isMainTree()) {
1427 if ($a_node_id < 1) {
1428 $message = 'No valid parameter given! $a_node_id: %s' . $a_node_id;
1429
1430 $this->log->error($message);
1431 throw new InvalidArgumentException($message);
1432 }
1433 }
1434
1435 // BEGIN WebDAV: Pass tree id to this method
1436 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1437 $this->buildJoin() .
1438 'WHERE ' . $this->table_tree . '.child = %s ' .
1439 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
1440 $res = $ilDB->queryF($query, array('integer','integer'), array(
1441 $a_node_id,
1442 $a_tree_pk === null ? $this->tree_id : $a_tree_pk));
1443 // END WebDAV: Pass tree id to this method
1444 $row = $ilDB->fetchAssoc($res);
1446
1447 return $this->fetchNodeData($row);
1448 }
1449
1457 public function fetchNodeData($a_row)
1458 {
1459 global $objDefinition, $lng, $ilBench,$ilDB;
1460
1461 //$ilBench->start("Tree", "fetchNodeData_getRow");
1462 $data = $a_row;
1463 $data["desc"] = $a_row["description"]; // for compability
1464 //$ilBench->stop("Tree", "fetchNodeData_getRow");
1465
1466 // multilingual support systemobjects (sys) & categories (db)
1467 //$ilBench->start("Tree", "fetchNodeData_readDefinition");
1468 if (is_object($objDefinition)) {
1469 $translation_type = $objDefinition->getTranslationType($data["type"]);
1470 }
1471 //$ilBench->stop("Tree", "fetchNodeData_readDefinition");
1472
1473 if ($translation_type == "sys") {
1474 //$ilBench->start("Tree", "fetchNodeData_getLangData");
1475 if ($data["type"] == "rolf" and $data["obj_id"] != ROLE_FOLDER_ID) {
1476 $data["description"] = $lng->txt("obj_" . $data["type"] . "_local_desc") . $data["title"] . $data["desc"];
1477 $data["desc"] = $lng->txt("obj_" . $data["type"] . "_local_desc") . $data["title"] . $data["desc"];
1478 $data["title"] = $lng->txt("obj_" . $data["type"] . "_local");
1479 } else {
1480 $data["title"] = $lng->txt("obj_" . $data["type"]);
1481 $data["description"] = $lng->txt("obj_" . $data["type"] . "_desc");
1482 $data["desc"] = $lng->txt("obj_" . $data["type"] . "_desc");
1483 }
1484 //$ilBench->stop("Tree", "fetchNodeData_getLangData");
1485 } elseif ($translation_type == "db") {
1486
1487 // Try to retrieve object translation from cache
1488 if ($this->isCacheUsed() &&
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'];
1494 } else {
1495 // Object translation is not in cache, read it from database
1496 //$ilBench->start("Tree", "fetchNodeData_getTranslation");
1497 $query = 'SELECT title,description FROM object_translation ' .
1498 'WHERE obj_id = %s ' .
1499 'AND lang_code = %s ' .
1500 'AND NOT lang_default = %s';
1501
1502 $res = $ilDB->queryF($query, array('integer','text','integer'), array(
1503 $data['obj_id'],
1504 $this->lang_code,
1505 1));
1506 $row = $ilDB->fetchObject($res);
1507
1508 if ($row) {
1509 $data["title"] = $row->title;
1510 $data["description"] = ilUtil::shortenText($row->description, ilObject::DESC_LENGTH, true);
1511 $data["desc"] = $row->description;
1512 }
1513 //$ilBench->stop("Tree", "fetchNodeData_getTranslation");
1514
1515 // Store up to 1000 object translations in cache
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"];
1522 }
1523 }
1524 }
1525
1526 // TODO: Handle this switch by module.xml definitions
1527 if ($data['type'] == 'crsr' or $data['type'] == 'catr' or $data['type'] == 'grpr') {
1528 include_once('./Services/ContainerReference/classes/class.ilContainerReference.php');
1529 $data['title'] = ilContainerReference::_lookupTitle($data['obj_id']);
1530 }
1531
1532 return $data ? $data : array();
1533 }
1534
1540 protected function fetchTranslationFromObjectDataCache($a_obj_ids)
1541 {
1542 global $ilObjDataCache;
1543
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);
1548 ;
1549 $this->translation_cache[$id . '.']['desc'] =
1550 $this->translation_cache[$id . '.']['description'];
1551 }
1552 }
1553 }
1554
1555
1563 public function isInTree($a_node_id)
1564 {
1565 global $ilDB;
1566
1567 if (!isset($a_node_id)) {
1568 return false;
1569 #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1570 }
1571 // is in tree cache
1572 if ($this->isCacheUsed() && isset($this->in_tree_cache[$a_node_id])) {
1573 #$GLOBALS['ilLog']->write(__METHOD__.': Using in tree cache '.$a_node_id);
1574 //echo "<br>in_tree_hit";
1575 return $this->in_tree_cache[$a_node_id];
1576 }
1577
1578 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1579 'WHERE ' . $this->table_tree . '.child = %s ' .
1580 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s';
1581
1582 $res = $ilDB->queryF($query, array('integer','integer'), array(
1583 $a_node_id,
1584 $this->tree_id));
1585
1586 if ($res->numRows() > 0) {
1587 if ($this->__isMainTree()) {
1588 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = true');
1589 $this->in_tree_cache[$a_node_id] = true;
1590 }
1591 return true;
1592 } else {
1593 if ($this->__isMainTree()) {
1594 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = false');
1595 $this->in_tree_cache[$a_node_id] = false;
1596 }
1597 return false;
1598 }
1599 }
1600
1608 public function getParentNodeData($a_node_id)
1609 {
1610 global $ilDB;
1611 global $ilLog;
1612
1613 if (!isset($a_node_id)) {
1614 $ilLog->logStack();
1615 throw new InvalidArgumentException(__METHOD__ . ': No node_id given!');
1616 }
1617
1618 if ($this->table_obj_reference) {
1619 // Use inner join instead of left join to improve performance
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 . " ";
1622 } else {
1623 // Use inner join instead of left join to improve performance
1624 $innerjoin = "JOIN " . $this->table_obj_data . " ON v.child=" . $this->table_obj_data . "." . $this->obj_pk . " ";
1625 }
1626
1627 $query = 'SELECT * FROM ' . $this->table_tree . ' s, ' . $this->table_tree . ' v ' .
1628 $innerjoin .
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(
1634 $a_node_id,
1635 $this->tree_id,
1636 $this->tree_id));
1637 $row = $ilDB->fetchAssoc($res);
1638 return $this->fetchNodeData($row);
1639 }
1640
1648 public function isGrandChild($a_startnode_id, $a_querynode_id)
1649 {
1650 return $this->getRelation($a_startnode_id, $a_querynode_id) == self::RELATION_PARENT;
1651 }
1652
1662 public function addTree($a_tree_id, $a_node_id = -1)
1663 {
1664 global $ilDB;
1665
1666 // FOR SECURITY addTree() IS NOT ALLOWED ON MAIN TREE
1667 if ($this->__isMainTree()) {
1668 $message = sprintf(
1669 'Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
1670 $a_tree_id,
1671 $a_node_id
1672 );
1673 $this->log->error($message);
1674 throw new InvalidArgumentException($message);
1675 }
1676
1677 if (!isset($a_tree_id)) {
1678 $message = "No tree_id given!";
1679 $this->log->error($message);
1680 throw new InvalidArgumentException($message);
1681 }
1682
1683 if ($a_node_id <= 0) {
1684 $a_node_id = $a_tree_id;
1685 }
1686
1687 $query = 'INSERT INTO ' . $this->table_tree . ' (' .
1688 $this->tree_pk . ', child,parent,lft,rgt,depth) ' .
1689 'VALUES ' .
1690 '(%s,%s,%s,%s,%s,%s)';
1691 $res = $ilDB->manipulateF($query, array('integer','integer','integer','integer','integer','integer'), array(
1692 $a_tree_id,
1693 $a_node_id,
1694 0,
1695 1,
1696 2,
1697 1));
1698
1699 return true;
1700 }
1701
1712 {
1713 global $ilDB;
1714
1715 if (!isset($a_type) or (!is_string($a_type))) {
1716 $this->log->logStack(ilLogLevel::ERROR);
1717 throw new InvalidArgumentException('Type not given or wrong datatype');
1718 }
1719
1720 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1721 $this->buildJoin() .
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');
1724
1725 $res = $ilDB->query($query);
1726 $data = array();
1727 while ($row = $ilDB->fetchAssoc($res)) {
1728 $data[] = $this->fetchNodeData($row);
1729 }
1730
1731 return $data;
1732 }
1733
1742 public function removeTree($a_tree_id)
1743 {
1744 global $ilDB;
1745
1746 // OPERATION NOT ALLOWED ON MAIN TREE
1747 if ($this->__isMainTree()) {
1748 $this->log->logStack(ilLogLevel::ERROR);
1749 throw new InvalidArgumentException('Operation not allowed on main tree');
1750 }
1751 if (!$a_tree_id) {
1752 $this->log->logStack(ilLogLevel::ERROR);
1753 throw new InvalidArgumentException('Missing parameter tree id');
1754 }
1755
1756 $query = 'DELETE FROM ' . $this->table_tree .
1757 ' WHERE ' . $this->tree_pk . ' = %s ';
1758 $ilDB->manipulateF($query, array('integer'), array($a_tree_id));
1759 return true;
1760 }
1761
1769 public function moveToTrash($a_node_id, $a_set_deleted = false)
1770 {
1771 global $ilDB;
1772
1773 if (!$a_node_id) {
1774 $this->log->logStack(ilLogLevel::ERROR);
1775 throw new InvalidArgumentException('No valid parameter given! $a_node_id: ' . $a_node_id);
1776 }
1777
1778
1779 $query = $this->getTreeImplementation()->getSubTreeQuery($this->getNodeTreeData($a_node_id), '', false);
1780 $res = $ilDB->query($query);
1781
1782 $subnodes = array();
1783 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1784 $subnodes[] = $row['child'];
1785 }
1786
1787 if (!count($subnodes)) {
1788 // Possibly already deleted
1789 return false;
1790 }
1791
1792 if ($a_set_deleted) {
1793 include_once './Services/Object/classes/class.ilObject.php';
1794 ilObject::setDeletedDates($subnodes);
1795 }
1796
1797 // netsted set <=> mp
1798 $this->getTreeImplementation()->moveToTrash($a_node_id);
1799
1800 return true;
1801 }
1802
1813 public function saveSubTree($a_node_id, $a_set_deleted = false)
1814 {
1815 return $this->moveToTrash($a_node_id, $a_set_deleted);
1816 }
1817
1822 public function isDeleted($a_node_id)
1823 {
1824 return $this->isSaved($a_node_id);
1825 }
1826
1832 public function isSaved($a_node_id)
1833 {
1834 global $ilDB;
1835
1836 // is saved cache
1837 if ($this->isCacheUsed() && isset($this->is_saved_cache[$a_node_id])) {
1838 //echo "<br>issavedhit";
1839 return $this->is_saved_cache[$a_node_id];
1840 }
1841
1842 $query = 'SELECT ' . $this->tree_pk . ' FROM ' . $this->table_tree . ' ' .
1843 'WHERE child = %s ';
1844 $res = $ilDB->queryF($query, array('integer'), array($a_node_id));
1845 $row = $ilDB->fetchAssoc($res);
1846
1847 if ($row[$this->tree_pk] < 0) {
1848 if ($this->__isMainTree()) {
1849 $this->is_saved_cache[$a_node_id] = true;
1850 }
1851 return true;
1852 } else {
1853 if ($this->__isMainTree()) {
1854 $this->is_saved_cache[$a_node_id] = false;
1855 }
1856 return false;
1857 }
1858 }
1859
1866 public function preloadDeleted($a_node_ids)
1867 {
1868 global $ilDB;
1869
1870 if (!is_array($a_node_ids) || !$this->isCacheUsed()) {
1871 return;
1872 }
1873
1874 $query = 'SELECT ' . $this->tree_pk . ', child FROM ' . $this->table_tree . ' ' .
1875 'WHERE ' . $ilDB->in("child", $a_node_ids, false, "integer");
1876
1877 $res = $ilDB->query($query);
1878 while ($row = $ilDB->fetchAssoc($res)) {
1879 if ($row[$this->tree_pk] < 0) {
1880 if ($this->__isMainTree()) {
1881 $this->is_saved_cache[$row["child"]] = true;
1882 }
1883 } else {
1884 if ($this->__isMainTree()) {
1885 $this->is_saved_cache[$row["child"]] = false;
1886 }
1887 }
1888 }
1889 }
1890
1891
1899 public function getSavedNodeData($a_parent_id)
1900 {
1901 global $ilDB;
1902
1903 if (!isset($a_parent_id)) {
1904 $message = "No node_id given!";
1905 $this->log->error($message);
1906 throw new InvalidArgumentException($message);
1907 }
1908
1909 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1910 $this->buildJoin() .
1911 'WHERE ' . $this->table_tree . '.' . $this->tree_pk . ' < %s ' .
1912 'AND ' . $this->table_tree . '.parent = %s';
1913 $res = $ilDB->queryF($query, array('integer','integer'), array(
1914 0,
1915 $a_parent_id));
1916
1917 while ($row = $ilDB->fetchAssoc($res)) {
1918 $saved[] = $this->fetchNodeData($row);
1919 }
1920
1921 return $saved ? $saved : array();
1922 }
1923
1930 public function getSavedNodeObjIds(array $a_obj_ids)
1931 {
1932 global $ilDB;
1933
1934 $query = 'SELECT ' . $this->table_obj_data . '.obj_id FROM ' . $this->table_tree . ' ' .
1935 $this->buildJoin() .
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');
1938 $res = $ilDB->query($query);
1939 while ($row = $ilDB->fetchAssoc($res)) {
1940 $saved[] = $row['obj_id'];
1941 }
1942
1943 return $saved ? $saved : array();
1944 }
1945
1953 public function getParentId($a_node_id)
1954 {
1955 global $ilDB;
1956
1957 if (!isset($a_node_id)) {
1958 $message = "No node_id given!";
1959 $this->log->error($message);
1960 throw new InvalidArgumentException($message);
1961 }
1962
1963 $query = 'SELECT parent FROM ' . $this->table_tree . ' ' .
1964 'WHERE child = %s ' .
1965 'AND ' . $this->tree_pk . ' = %s ';
1966 $res = $ilDB->queryF($query, array('integer','integer'), array(
1967 $a_node_id,
1968 $this->tree_id));
1969
1970 $row = $ilDB->fetchObject($res);
1971 return $row->parent;
1972 }
1973
1981 public function getLeftValue($a_node_id)
1982 {
1983 global $ilDB;
1984
1985 if (!isset($a_node_id)) {
1986 $message = "No node_id given!";
1987 $this->log->error($message);
1988 throw new InvalidArgumentException($message);
1989 }
1990
1991 $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
1992 'WHERE child = %s ' .
1993 'AND ' . $this->tree_pk . ' = %s ';
1994 $res = $ilDB->queryF($query, array('integer','integer'), array(
1995 $a_node_id,
1996 $this->tree_id));
1997 $row = $ilDB->fetchObject($res);
1998 return $row->lft;
1999 }
2000
2008 public function getChildSequenceNumber($a_node, $type = "")
2009 {
2010 global $ilDB;
2011
2012 if (!isset($a_node)) {
2013 $message = "No node_id given!";
2014 $this->log->error($message);
2015 throw new InvalidArgumentException($message);
2016 }
2017
2018 if ($type) {
2019 $query = 'SELECT count(*) cnt FROM ' . $this->table_tree . ' ' .
2020 $this->buildJoin() .
2021 'WHERE lft <= %s ' .
2022 'AND type = %s ' .
2023 'AND parent = %s ' .
2024 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2025
2026 $res = $ilDB->queryF($query, array('integer','text','integer','integer'), array(
2027 $a_node['lft'],
2028 $type,
2029 $a_node['parent'],
2030 $this->tree_id));
2031 } else {
2032 $query = 'SELECT count(*) cnt FROM ' . $this->table_tree . ' ' .
2033 $this->buildJoin() .
2034 'WHERE lft <= %s ' .
2035 'AND parent = %s ' .
2036 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2037
2038 $res = $ilDB->queryF($query, array('integer','integer','integer'), array(
2039 $a_node['lft'],
2040 $a_node['parent'],
2041 $this->tree_id));
2042 }
2043 $row = $ilDB->fetchAssoc($res);
2044 return $row["cnt"];
2045 }
2046
2053 public function readRootId()
2054 {
2055 global $ilDB;
2056
2057 $query = 'SELECT child FROM ' . $this->table_tree . ' ' .
2058 'WHERE parent = %s ' .
2059 'AND ' . $this->tree_pk . ' = %s ';
2060 $res = $ilDB->queryF($query, array('integer','integer'), array(
2061 0,
2062 $this->tree_id));
2063 $row = $ilDB->fetchObject($res);
2064 $this->root_id = $row->child;
2065 return $this->root_id;
2066 }
2067
2073 public function getRootId()
2074 {
2075 return $this->root_id;
2076 }
2077 public function setRootId($a_root_id)
2078 {
2079 $this->root_id = $a_root_id;
2080 }
2081
2087 public function getTreeId()
2088 {
2089 return $this->tree_id;
2090 }
2091
2097 public function setTreeId($a_tree_id)
2098 {
2099 $this->tree_id = $a_tree_id;
2100 }
2101
2110 public function fetchSuccessorNode($a_node_id, $a_type = "")
2111 {
2112 global $ilDB;
2113
2114 if (!isset($a_node_id)) {
2115 $message = "No node_id given!";
2116 $this->log->error($message);
2117 throw new InvalidArgumentException($message);
2118 }
2119
2120 // get lft value for current node
2121 $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2122 'WHERE ' . $this->table_tree . '.child = %s ' .
2123 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2124 $res = $ilDB->queryF($query, array('integer','integer'), array(
2125 $a_node_id,
2126 $this->tree_id));
2127 $curr_node = $ilDB->fetchAssoc($res);
2128
2129 if ($a_type) {
2130 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2131 $this->buildJoin() .
2132 'WHERE lft > %s ' .
2133 'AND ' . $this->table_obj_data . '.type = %s ' .
2134 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2135 'ORDER BY lft ';
2136 $ilDB->setLimit(1);
2137 $res = $ilDB->queryF($query, array('integer','text','integer'), array(
2138 $curr_node['lft'],
2139 $a_type,
2140 $this->tree_id));
2141 } else {
2142 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2143 $this->buildJoin() .
2144 'WHERE lft > %s ' .
2145 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2146 'ORDER BY lft ';
2147 $ilDB->setLimit(1);
2148 $res = $ilDB->queryF($query, array('integer','integer'), array(
2149 $curr_node['lft'],
2150 $this->tree_id));
2151 }
2152
2153 if ($res->numRows() < 1) {
2154 return false;
2155 } else {
2156 $row = $ilDB->fetchAssoc($res);
2157 return $this->fetchNodeData($row);
2158 }
2159 }
2160
2169 public function fetchPredecessorNode($a_node_id, $a_type = "")
2170 {
2171 global $ilDB;
2172
2173 if (!isset($a_node_id)) {
2174 $message = "No node_id given!";
2175 $this->log->error($message);
2176 throw new InvalidArgumentException($message);
2177 }
2178
2179 // get lft value for current node
2180 $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2181 'WHERE ' . $this->table_tree . '.child = %s ' .
2182 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2183 $res = $ilDB->queryF($query, array('integer','integer'), array(
2184 $a_node_id,
2185 $this->tree_id));
2186
2187 $curr_node = $ilDB->fetchAssoc($res);
2188
2189 if ($a_type) {
2190 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2191 $this->buildJoin() .
2192 'WHERE lft < %s ' .
2193 'AND ' . $this->table_obj_data . '.type = %s ' .
2194 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2195 'ORDER BY lft DESC';
2196 $ilDB->setLimit(1);
2197 $res = $ilDB->queryF($query, array('integer','text','integer'), array(
2198 $curr_node['lft'],
2199 $a_type,
2200 $this->tree_id));
2201 } else {
2202 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2203 $this->buildJoin() .
2204 'WHERE lft < %s ' .
2205 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2206 'ORDER BY lft DESC';
2207 $ilDB->setLimit(1);
2208 $res = $ilDB->queryF($query, array('integer','integer'), array(
2209 $curr_node['lft'],
2210 $this->tree_id));
2211 }
2212
2213 if ($res->numRows() < 1) {
2214 return false;
2215 } else {
2216 $row = $ilDB->fetchAssoc($res);
2217 return $this->fetchNodeData($row);
2218 }
2219 }
2220
2229 public function renumber($node_id = 1, $i = 1)
2230 {
2231 global $ilDB;
2232
2233 $renumber_callable = function (ilDBInterface $ilDB) use ($node_id,$i,&$return) {
2234 $return = $this->__renumber($node_id, $i);
2235 };
2236
2237 // LOCKED ###################################
2238 if ($this->__isMainTree()) {
2239 $ilAtomQuery = $ilDB->buildAtomQuery();
2240 $ilAtomQuery->addTableLock($this->table_tree);
2241
2242 $ilAtomQuery->addQueryCallable($renumber_callable);
2243 $ilAtomQuery->run();
2244 } else {
2245 $renumber_callable($ilDB);
2246 }
2247 return $return;
2248 }
2249
2250 // PRIVATE
2260 public function __renumber($node_id = 1, $i = 1)
2261 {
2262 global $ilDB;
2263
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(
2266 $i,
2267 $node_id,
2268 $this->tree_id));
2269
2270 // to much dependencies
2271 //$childs = $this->getChilds($node_id);
2272 $childs = $this->getChildIds($node_id);
2273
2274 foreach ($childs as $child) {
2275 $i = $this->__renumber($child, $i+1);
2276 }
2277 $i++;
2278
2279 // Insert a gap at the end of node, if the node has children
2280 if (count($childs) > 0) {
2281 $i += $this->gap * 2;
2282 }
2283
2284
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(
2287 $i,
2288 $node_id,
2289 $this->tree_id));
2290 return $i;
2291 }
2292
2293
2304 public function checkForParentType($a_ref_id, $a_type, $a_exclude_source_check = false)
2305 {
2306 // #12577
2307 $cache_key = $a_ref_id . '.' . $a_type . '.' . ((int) $a_exclude_source_check);
2308
2309 // Try to return a cached result
2310 if ($this->isCacheUsed() &&
2311 array_key_exists($cache_key, $this->parent_type_cache)) {
2312 return $this->parent_type_cache[$cache_key];
2313 }
2314
2315 // Store up to 1000 results in cache
2316 $do_cache = ($this->__isMainTree() && count($this->parent_type_cache) < 1000);
2317
2318 // ref_id is not in tree
2319 if (!$this->isInTree($a_ref_id)) {
2320 if ($do_cache) {
2321 $this->parent_type_cache[$cache_key] = false;
2322 }
2323 return false;
2324 }
2325
2326 $path = array_reverse($this->getPathFull($a_ref_id));
2327
2328 // remove first path entry as it is requested node
2329 if ($a_exclude_source_check) {
2330 array_shift($path);
2331 }
2332
2333 foreach ($path as $node) {
2334 // found matching parent
2335 if ($node["type"] == $a_type) {
2336 if ($do_cache) {
2337 $this->parent_type_cache[$cache_key] = $node["child"];
2338 }
2339 return $node["child"];
2340 }
2341 }
2342
2343 if ($do_cache) {
2344 $this->parent_type_cache[$cache_key] = false;
2345 }
2346 return 0;
2347 }
2348
2359 public static function _removeEntry($a_tree, $a_child, $a_db_table = "tree")
2360 {
2361 global $ilDB;
2362
2363 if ($a_db_table === 'tree') {
2364 if ($a_tree == 1 and $a_child == ROOT_FOLDER_ID) {
2365 $message = sprintf(
2366 'Tried to delete root node! $a_tree: %s $a_child: %s',
2367 $a_tree,
2368 $a_child
2369 );
2370 ilLoggerFactory::getLogger('tree')->error($message);
2371 throw new InvalidArgumentException($message);
2372 }
2373 }
2374
2375 $query = 'DELETE FROM ' . $a_db_table . ' ' .
2376 'WHERE tree = %s ' .
2377 'AND child = %s ';
2378 $res = $ilDB->manipulateF($query, array('integer','integer'), array(
2379 $a_tree,
2380 $a_child));
2381 }
2382
2389 public function __isMainTree()
2390 {
2391 return $this->table_tree === 'tree';
2392 }
2393
2405 public function __checkDelete($a_node)
2406 {
2407 global $ilDB;
2408
2409
2410 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, array(), false);
2411 $this->log->debug($query);
2412 $res = $ilDB->query($query);
2413
2414 $counter = (int) $lft_childs = array();
2415 while ($row = $ilDB->fetchObject($res)) {
2416 $lft_childs[$row->child] = $row->parent;
2417 ++$counter;
2418 }
2419
2420 // CHECK FOR DUPLICATE CHILD IDS
2421 if ($counter != count($lft_childs)) {
2422 $message = 'Duplicate entries for "child" in maintree! $a_node_id: ' . $a_node['child'];
2423
2424 $this->log->error($message);
2426 }
2427
2428 // GET SUBTREE BY PARENT RELATION
2429 $parent_childs = array();
2430 $this->__getSubTreeByParentRelation($a_node['child'], $parent_childs);
2431 $this->__validateSubtrees($lft_childs, $parent_childs);
2432
2433 return true;
2434 }
2435
2445 public function __getSubTreeByParentRelation($a_node_id, &$parent_childs)
2446 {
2447 global $ilDB;
2448
2449 // GET PARENT ID
2450 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2451 'WHERE child = %s ' .
2452 'AND tree = %s ';
2453 $res = $ilDB->queryF($query, array('integer','integer'), array(
2454 $a_node_id,
2455 $this->tree_id));
2456
2457 $counter = 0;
2458 while ($row = $ilDB->fetchObject($res)) {
2459 $parent_childs[$a_node_id] = $row->parent;
2460 ++$counter;
2461 }
2462 // MULTIPLE ENTRIES
2463 if ($counter > 1) {
2464 $message = 'Multiple entries in maintree! $a_node_id: ' . $a_node_id;
2465
2466 $this->log->error($message);
2468 }
2469
2470 // GET ALL CHILDS
2471 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2472 'WHERE parent = %s ';
2473 $res = $ilDB->queryF($query, array('integer'), array($a_node_id));
2474
2475 while ($row = $ilDB->fetchObject($res)) {
2476 // RECURSION
2477 $this->__getSubTreeByParentRelation($row->child, $parent_childs);
2478 }
2479 return true;
2480 }
2481
2489 public function __validateSubtrees(&$lft_childs, $parent_childs)
2490 {
2491 // SORT BY KEY
2492 ksort($lft_childs);
2493 ksort($parent_childs);
2494
2495 $this->log->debug('left childs ' . print_r($lft_childs, true));
2496 $this->log->debug('parent childs ' . print_r($parent_childs, true));
2497
2498 if (count($lft_childs) != count($parent_childs)) {
2499 $message = '(COUNT) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2500 $this->log->error($message);
2502 }
2503
2504
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';
2508 $this->log->error($message);
2510 }
2511 if ($key == ROOT_FOLDER_ID) {
2512 $message = '(ROOT_FOLDER) Tree is corrupted! Tried to delete root folder';
2513 $this->log->error($message);
2515 }
2516 }
2517 return true;
2518 }
2519
2529 public function moveTree($a_source_id, $a_target_id, $a_location = self::POS_LAST_NODE)
2530 {
2531 $old_parent_id = $this->getParentId($a_source_id);
2532 $this->getTreeImplementation()->moveTree($a_source_id, $a_target_id, $a_location);
2533 if (isset($GLOBALS["ilAppEventHandler"]) && $this->__isMainTree()) {
2534 $GLOBALS['ilAppEventHandler']->raise(
2535 "Services/Tree",
2536 "moveTree",
2537 array(
2538 'tree' => $this->table_tree,
2539 'source_id' => $a_source_id,
2540 'target_id' => $a_target_id,
2541 'old_parent_id' => $old_parent_id
2542 )
2543 );
2544 }
2545 return true;
2546 }
2547
2548
2549
2550
2558 public function getRbacSubtreeInfo($a_endnode_id)
2559 {
2560 return $this->getTreeImplementation()->getSubtreeInfo($a_endnode_id);
2561 }
2562
2563
2571 public function getSubTreeQuery($a_node_id, $a_fields = array(), $a_types = '', $a_force_join_reference = false)
2572 {
2573 return $this->getTreeImplementation()->getSubTreeQuery(
2574 $this->getNodeTreeData($a_node_id),
2575 $a_types,
2576 $a_force_join_reference,
2577 $a_fields
2578 );
2579 }
2580
2581
2590 public function getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields = array())
2591 {
2592 global $ilDB;
2593
2594 $node = $this->getNodeData($a_node_id);
2595 if (!sizeof($node)) {
2596 return;
2597 }
2598
2599 $res = array();
2600
2601 $query = $this->getTreeImplementation()->getSubTreeQuery($node, '', true, array($this->ref_pk));
2602
2603 $fields = '*';
2604 if (count($a_fields)) {
2605 $fields = implode(',', $a_fields);
2606 }
2607
2608 $query = "SELECT " . $fields .
2609 " FROM " . $this->getTreeTable() .
2610 " " . $this->buildJoin() .
2611 " WHERE " . $this->getTableReference() . "." . $this->ref_pk . " IN (" . $query . ")" .
2612 " AND " . $ilDB->in($this->getObjectDataTable() . "." . $this->obj_pk, $a_obj_ids, "", "integer");
2613 $set = $ilDB->query($query);
2614 while ($row = $ilDB->fetchAssoc($set)) {
2615 $res[] = $row;
2616 }
2617
2618 return $res;
2619 }
2620
2621 public function deleteNode($a_tree_id, $a_node_id)
2622 {
2623 global $ilDB, $ilAppEventHandler;
2624
2625 $query = 'DELETE FROM tree where ' .
2626 'child = ' . $ilDB->quote($a_node_id, 'integer') . ' ' .
2627 'AND tree = ' . $ilDB->quote($a_tree_id, 'integer');
2628 $ilDB->manipulate($query);
2629
2630 $ilAppEventHandler->raise(
2631 "Services/Tree",
2632 "deleteNode",
2633 array('tree' => $this->table_tree,
2634 'node_id' => $a_node_id,
2635 'tree_id' => $a_tree_id
2636 )
2637 );
2638 }
2639
2646 {
2647 global $ilDB;
2648
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 ' .
2653 'GROUP BY o.type';
2654 $res = $ilDB->query($query);
2655
2656 $types_deleted = array();
2657 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
2658 $types_deleted[] = $row->type;
2659 }
2660 return $types_deleted;
2661 }
2662} // END class.tree
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.
Definition: UtfNormal.php:157
const IL_LAST_NODE
Definition: class.ilTree.php:4
static _lookupTitle($a_obj_id)
Overwitten from base class.
Database Wrapper.
Definition: class.ilDB.php:30
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
const DESC_LENGTH
ILIAS Setting Class.
static strToLower($a_string)
Definition: class.ilStr.php:87
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
const RELATION_EQUALS
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
const POS_LAST_NODE
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.
resetInTreeCache()
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)
$table_obj_reference
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.
const POS_FIRST_NODE
isSaved($a_node_id)
Use method isDeleted check if node is saved.
const RELATION_PARENT
initLangCode()
Store user language.
const RELATION_NONE
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.
const RELATION_SIBLING
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 *.
const RELATION_CHILD
__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
setRootId($a_root_id)
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.
$counter
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
$r
Definition: example_031.php:79
if(!array_key_exists('StateId', $_REQUEST)) $id
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
global $ilBench
Definition: ilias.php:18
Interface ilDBInterface.
catch(Exception $e) $message
global $lng
Definition: privfeed.php:17
$query
$type
foreach($_POST as $key=> $value) $res
global $ilDB
$ilUser
Definition: imgupload.php:18
$a_type
Definition: workflow.php:92
$rows
Definition: xhr_table.php:10