ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 $DIC;
151
152 $ilDB = $DIC['ilDB'];
153
154 // set db
155 $this->ilDB = $ilDB;
156
157 $this->lang_code = "en";
158
159 // CREATE LOGGER INSTANCE
160 $this->log = ilLoggerFactory::getLogger('tree');
161
162 if (!isset($a_tree_id) or (func_num_args() == 0)) {
163 $this->log->error("No tree_id given!");
164 $this->log->logStack(ilLogLevel::DEBUG);
165 throw new InvalidArgumentException("No tree_id given!");
166 }
167
168 if (func_num_args() > 2) {
169 $this->log->error("Wrong parameter count!");
170 throw new InvalidArgumentException("Wrong parameter count!");
171 }
172
173 //init variables
174 if (empty($a_root_id)) {
175 $a_root_id = ROOT_FOLDER_ID;
176 }
177
178 $this->tree_id = $a_tree_id;
179 $this->root_id = $a_root_id;
180 $this->table_tree = 'tree';
181 $this->table_obj_data = 'object_data';
182 $this->table_obj_reference = 'object_reference';
183 $this->ref_pk = 'ref_id';
184 $this->obj_pk = 'obj_id';
185 $this->tree_pk = 'tree';
186
187 $this->use_cache = true;
188
189 // If cache is activated, cache object translations to improve performance
190 $this->translation_cache = array();
191 $this->parent_type_cache = array();
192
193 // By default, we create gaps in the tree sequence numbering for 50 nodes
194 $this->gap = 50;
195
196
197 // init tree implementation
198 $this->initTreeImplementation();
199 }
200
204 public function initTreeImplementation()
205 {
206 global $DIC;
207
208 if (!$DIC->isDependencyAvailable('settings') || $DIC->settings()->getModule() != 'common') {
209 include_once './Services/Administration/classes/class.ilSetting.php';
210 $setting = new ilSetting('common');
211 } else {
212 $setting = $DIC->settings();
213 }
214
215 if ($this->__isMainTree()) {
216 if ($setting->get('main_tree_impl', 'ns') == 'ns') {
217 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Using nested set.');
218 include_once './Services/Tree/classes/class.ilNestedSetTree.php';
219 $this->tree_impl = new ilNestedSetTree($this);
220 } else {
221 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Using materialized path.');
222 include_once './Services/Tree/classes/class.ilMaterializedPathTree.php';
223 $this->tree_impl = new ilMaterializedPathTree($this);
224 }
225 } else {
226 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Using netsted set for non main tree.');
227 include_once './Services/Tree/classes/class.ilNestedSetTree.php';
228 $this->tree_impl = new ilNestedSetTree($this);
229 }
230 }
231
236 public function getTreeImplementation()
237 {
238 return $this->tree_impl;
239 }
240
244 public function useCache($a_use = true)
245 {
246 $this->use_cache = $a_use;
247 }
248
253 public function isCacheUsed()
254 {
255 return $this->__isMainTree() and $this->use_cache;
256 }
257
262 public function getDepthCache()
263 {
264 return (array) $this->depth_cache;
265 }
266
271 public function getParentCache()
272 {
273 return (array) $this->parent_cache;
274 }
275
280 public function initLangCode()
281 {
282 global $DIC;
283
284 // lang_code is only required in $this->fetchnodedata
285 try {
286 $ilUser = $DIC['ilUser'];
287 $this->lang_code = $ilUser->getCurrentLanguage();
288 } catch (\InvalidArgumentException $e) {
289 $this->lang_code = "en";
290 }
291 }
292
297 public function getTreeTable()
298 {
299 return $this->table_tree;
300 }
301
306 public function getObjectDataTable()
307 {
309 }
310
315 public function getTreePk()
316 {
317 return $this->tree_pk;
318 }
319
323 public function getTableReference()
324 {
326 }
327
331 public function getGap()
332 {
333 return $this->gap;
334 }
335
336 /***
337 * reset in tree cache
338 */
339 public function resetInTreeCache()
340 {
341 $this->in_tree_cache = array();
342 }
343
344
361 public function setTableNames($a_table_tree, $a_table_obj_data, $a_table_obj_reference = "")
362 {
363 if (!isset($a_table_tree) or !isset($a_table_obj_data)) {
364 $message = "Missing parameter! " .
365 "tree table: " . $a_table_tree . " object data table: " . $a_table_obj_data;
366 $this->log->error($message);
367 throw new InvalidArgumentException($message);
368 }
369
370 $this->table_tree = $a_table_tree;
371 $this->table_obj_data = $a_table_obj_data;
372 $this->table_obj_reference = $a_table_obj_reference;
373
374 $this->initTreeImplementation();
375
376 return true;
377 }
378
386 public function setReferenceTablePK($a_column_name)
387 {
388 if (!isset($a_column_name)) {
389 $message = "No column name given!";
390 $this->log->error($message);
391 throw new InvalidArgumentException($message);
392 }
393
394 $this->ref_pk = $a_column_name;
395 return true;
396 }
397
405 public function setObjectTablePK($a_column_name)
406 {
407 if (!isset($a_column_name)) {
408 $message = "No column name given!";
409 $this->log->error($message);
410 throw new InvalidArgumentException($message);
411 }
412
413 $this->obj_pk = $a_column_name;
414 return true;
415 }
416
424 public function setTreeTablePK($a_column_name)
425 {
426 if (!isset($a_column_name)) {
427 $message = "No column name given!";
428 $this->log->error($message);
429 throw new InvalidArgumentException($message);
430 }
431
432 $this->tree_pk = $a_column_name;
433 return true;
434 }
435
441 public function buildJoin()
442 {
443 if ($this->table_obj_reference) {
444 // Use inner join instead of left join to improve performance
445 return "JOIN " . $this->table_obj_reference . " ON " . $this->table_tree . ".child=" . $this->table_obj_reference . "." . $this->ref_pk . " " .
446 "JOIN " . $this->table_obj_data . " ON " . $this->table_obj_reference . "." . $this->obj_pk . "=" . $this->table_obj_data . "." . $this->obj_pk . " ";
447 } else {
448 // Use inner join instead of left join to improve performance
449 return "JOIN " . $this->table_obj_data . " ON " . $this->table_tree . ".child=" . $this->table_obj_data . "." . $this->obj_pk . " ";
450 }
451 }
452
458 public function getRelation($a_node_a, $a_node_b)
459 {
460 return $this->getRelationOfNodes(
461 $this->getNodeTreeData($a_node_a),
462 $this->getNodeTreeData($a_node_b)
463 );
464 }
465
472 public function getRelationOfNodes($a_node_a_arr, $a_node_b_arr)
473 {
474 return $this->getTreeImplementation()->getRelation($a_node_a_arr, $a_node_b_arr);
475 }
476
483 public function getChildIds($a_node)
484 {
485 global $DIC;
486
487 $ilDB = $DIC['ilDB'];
488
489 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
490 'WHERE parent = ' . $ilDB->quote($a_node, 'integer') . ' ' .
491 'AND tree = ' . $ilDB->quote($this->tree_id, 'integer' . ' ' .
492 'ORDER BY lft');
493 $res = $ilDB->query($query);
494
495 $childs = array();
496 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
497 $childs[] = $row->child;
498 }
499 return $childs;
500 }
501
511 public function getChilds($a_node_id, $a_order = "", $a_direction = "ASC")
512 {
513 global $DIC;
514
515 $ilBench = $DIC['ilBench'];
516 $ilDB = $DIC['ilDB'];
517 $ilObjDataCache = $DIC['ilObjDataCache'];
518 $ilUser = $DIC['ilUser'];
519
520 if (!isset($a_node_id)) {
521 $message = "No node_id given!";
522 $this->log->error($message);
523 throw new InvalidArgumentException($message);
524 }
525
526 // init childs
527 $childs = array();
528
529 // number of childs
530 $count = 0;
531
532 // init order_clause
533 $order_clause = "";
534
535 // set order_clause if sort order parameter is given
536 if (!empty($a_order)) {
537 $order_clause = "ORDER BY " . $a_order . " " . $a_direction;
538 } else {
539 $order_clause = "ORDER BY " . $this->table_tree . ".lft";
540 }
541
542
543 $query = sprintf(
544 'SELECT * FROM ' . $this->table_tree . ' ' .
545 $this->buildJoin() .
546 "WHERE parent = %s " .
547 "AND " . $this->table_tree . "." . $this->tree_pk . " = %s " .
548 $order_clause,
549 $ilDB->quote($a_node_id, 'integer'),
550 $ilDB->quote($this->tree_id, 'integer')
551 );
552
553 $res = $ilDB->query($query);
554
555 if (!$count = $res->numRows()) {
556 return array();
557 }
558
559 // get rows and object ids
560 $rows = array();
561 while ($r = $ilDB->fetchAssoc($res)) {
562 $rows[] = $r;
563 $obj_ids[] = $r["obj_id"];
564 }
565
566 // preload object translation information
567 if ($this->__isMainTree() && $this->isCacheUsed() && is_object($ilObjDataCache) &&
568 is_object($ilUser) && $this->lang_code == $ilUser->getLanguage() && !$this->oc_preloaded[$a_node_id]) {
569 // $ilObjDataCache->preloadTranslations($obj_ids, $this->lang_code);
570 $ilObjDataCache->preloadObjectCache($obj_ids, $this->lang_code);
572 $this->oc_preloaded[$a_node_id] = true;
573 }
574
575 foreach ($rows as $row) {
576 $childs[] = $this->fetchNodeData($row);
577
578 // Update cache of main tree
579 if ($this->__isMainTree()) {
580 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child'].' = true');
581 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
582 }
583 }
584 $childs[$count - 1]["last"] = true;
585 return $childs;
586 }
587
597 public function getFilteredChilds($a_filter, $a_node, $a_order = "", $a_direction = "ASC")
598 {
599 $childs = $this->getChilds($a_node, $a_order, $a_direction);
600
601 foreach ($childs as $child) {
602 if (!in_array($child["type"], $a_filter)) {
603 $filtered[] = $child;
604 }
605 }
606 return $filtered ? $filtered : array();
607 }
608
609
618 public function getChildsByType($a_node_id, $a_type)
619 {
620 global $DIC;
621
622 $ilDB = $DIC['ilDB'];
623
624 if (!isset($a_node_id) or !isset($a_type)) {
625 $message = "Missing parameter! node_id:" . $a_node_id . " type:" . $a_type;
626 $this->log->error($message);
627 throw new InvalidArgumentException($message);
628 }
629
630 if ($a_type == 'rolf' && $this->table_obj_reference) {
631 // Performance optimization: A node can only have exactly one
632 // role folder as its child. Therefore we don't need to sort the
633 // results, and we can let the database know about the expected limit.
634 $ilDB->setLimit(1, 0);
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 $ilDB->quote($a_node_id, 'integer'),
642 $ilDB->quote($this->tree_id, 'integer'),
643 $ilDB->quote($a_type, 'text')
644 );
645 } else {
646 $query = sprintf(
647 "SELECT * FROM " . $this->table_tree . " " .
648 $this->buildJoin() .
649 "WHERE parent = %s " .
650 "AND " . $this->table_tree . "." . $this->tree_pk . " = %s " .
651 "AND " . $this->table_obj_data . ".type = %s " .
652 "ORDER BY " . $this->table_tree . ".lft",
653 $ilDB->quote($a_node_id, 'integer'),
654 $ilDB->quote($this->tree_id, 'integer'),
655 $ilDB->quote($a_type, 'text')
656 );
657 }
658 $res = $ilDB->query($query);
659
660 // init childs
661 $childs = array();
662 while ($row = $ilDB->fetchAssoc($res)) {
663 $childs[] = $this->fetchNodeData($row);
664 }
665
666 return $childs ? $childs : array();
667 }
668
669
678 public function getChildsByTypeFilter($a_node_id, $a_types, $a_order = "", $a_direction = "ASC")
679 {
680 global $DIC;
681
682 $ilDB = $DIC['ilDB'];
683
684 if (!isset($a_node_id) or !$a_types) {
685 $message = "Missing parameter! node_id:" . $a_node_id . " type:" . $a_types;
686 $this->log->error($message);
687 throw new InvalidArgumentException($message);
688 }
689
690 $filter = ' ';
691 if ($a_types) {
692 $filter = 'AND ' . $this->table_obj_data . '.type IN(' . implode(',', ilUtil::quoteArray($a_types)) . ') ';
693 }
694
695 // set order_clause if sort order parameter is given
696 if (!empty($a_order)) {
697 $order_clause = "ORDER BY " . $a_order . " " . $a_direction;
698 } else {
699 $order_clause = "ORDER BY " . $this->table_tree . ".lft";
700 }
701
702 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
703 $this->buildJoin() .
704 'WHERE parent = ' . $ilDB->quote($a_node_id, 'integer') . ' ' .
705 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $ilDB->quote($this->tree_id, 'integer') . ' ' .
706 $filter .
707 $order_clause;
708
709 $res = $ilDB->query($query);
710 while ($row = $ilDB->fetchAssoc($res)) {
711 $childs[] = $this->fetchNodeData($row);
712 }
713
714 return $childs ? $childs : array();
715 }
716
728 public function insertNodeFromTrash($a_source_id, $a_target_id, $a_tree_id, $a_pos = IL_LAST_NODE, $a_reset_deleted_date = false)
729 {
730 global $DIC;
731
732 $ilDB = $DIC['ilDB'];
733
734 if ($this->__isMainTree()) {
735 if ($a_source_id <= 1 or $a_target_id <= 0) {
737 throw new InvalidArgumentException('Invalid parameter given for ilTree::insertNodeFromTrash');
738 }
739 }
740 if (!isset($a_source_id) or !isset($a_target_id)) {
742 throw new InvalidArgumentException('Missing parameter for ilTree::insertNodeFromTrash');
743 }
744 if ($this->isInTree($a_source_id)) {
745 ilLoggerFactory::getLogger('tree')->error('Node already in tree');
747 throw new InvalidArgumentException('Node already in tree.');
748 }
749
750 $query = 'DELETE from tree ' .
751 'WHERE tree = ' . $ilDB->quote($a_tree_id, 'integer') . ' ' .
752 'AND child = ' . $ilDB->quote($a_source_id, 'integer');
753 $ilDB->manipulate($query);
754
755 $this->insertNode($a_source_id, $a_target_id, IL_LAST_NODE, $a_reset_deleted_date);
756 }
757
758
767 public function insertNode($a_node_id, $a_parent_id, $a_pos = IL_LAST_NODE, $a_reset_deletion_date = false)
768 {
769 global $DIC;
770
771 $ilDB = $DIC['ilDB'];
772
773 //echo "+$a_node_id+$a_parent_id+";
774 // CHECK node_id and parent_id > 0 if in main tree
775 if ($this->__isMainTree()) {
776 if ($a_node_id <= 1 or $a_parent_id <= 0) {
777 $message = sprintf(
778 'Invalid parameters! $a_node_id: %s $a_parent_id: %s',
779 $a_node_id,
780 $a_parent_id
781 );
782 $this->log->logStack(ilLogLevel::ERROR, $message);
783 throw new InvalidArgumentException($message);
784 }
785 }
786
787
788 if (!isset($a_node_id) or !isset($a_parent_id)) {
789 $this->log->logStack(ilLogLevel::ERROR);
790 throw new InvalidArgumentException("Missing parameter! " .
791 "node_id: " . $a_node_id . " parent_id: " . $a_parent_id);
792 }
793 if ($this->isInTree($a_node_id)) {
794 throw new InvalidArgumentException("Node " . $a_node_id . " already in tree " .
795 $this->table_tree . "!");
796 }
797
798 $this->getTreeImplementation()->insertNode($a_node_id, $a_parent_id, $a_pos);
799
800 $this->in_tree_cache[$a_node_id] = true;
801
802 // reset deletion date
803 if ($a_reset_deletion_date) {
804 ilObject::_resetDeletedDate($a_node_id);
805 }
806
807 if (isset($GLOBALS['DIC']["ilAppEventHandler"]) && $this->__isMainTree()) {
808 $GLOBALS['DIC']['ilAppEventHandler']->raise(
809 "Services/Tree",
810 "insertNode",
811 array(
812 'tree' => $this->table_tree,
813 'node_id' => $a_node_id,
814 'parent_id' => $a_parent_id)
815 );
816 }
817 }
818
831 public function getFilteredSubTree($a_node_id, $a_filter = array())
832 {
833 $node = $this->getNodeData($a_node_id);
834
835 $first = true;
836 $depth = 0;
837 foreach ($this->getSubTree($node) as $subnode) {
838 if ($depth and $subnode['depth'] > $depth) {
839 continue;
840 }
841 if (!$first and in_array($subnode['type'], $a_filter)) {
842 $depth = $subnode['depth'];
843 $first = false;
844 continue;
845 }
846 $depth = 0;
847 $first = false;
848 $filtered[] = $subnode;
849 }
850 return $filtered ? $filtered : array();
851 }
852
858 public function getSubTreeIds($a_ref_id)
859 {
860 return $this->getTreeImplementation()->getSubTreeIds($a_ref_id);
861 }
862
863
873 public function getSubTree($a_node, $a_with_data = true, $a_type = "")
874 {
875 global $DIC;
876
877 $ilDB = $DIC['ilDB'];
878
879 if (!is_array($a_node)) {
880 $this->log->logStack(ilLogLevel::ERROR);
881 throw new InvalidArgumentException(__METHOD__ . ': wrong datatype for node data given');
882 }
883
884 /*
885 if($a_node['lft'] < 1 or $a_node['rgt'] < 2)
886 {
887 $GLOBALS['DIC']['ilLog']->logStack();
888 $message = sprintf('%s: Invalid node given! $a_node["lft"]: %s $a_node["rgt"]: %s',
889 __METHOD__,
890 $a_node['lft'],
891 $a_node['rgt']);
892
893 throw new InvalidArgumentException($message);
894 }
895 */
896
897 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, $a_type);
898 $res = $ilDB->query($query);
899 while ($row = $ilDB->fetchAssoc($res)) {
900 if ($a_with_data) {
901 $subtree[] = $this->fetchNodeData($row);
902 } else {
903 $subtree[] = $row['child'];
904 }
905 // the lm_data "hack" should be removed in the trunk during an alpha
906 if ($this->__isMainTree() || $this->table_tree == "lm_tree") {
907 $this->in_tree_cache[$row['child']] = true;
908 }
909 }
910 return $subtree ? $subtree : array();
911 }
912
921 public function getSubTreeTypes($a_node, $a_filter = 0)
922 {
923 $a_filter = $a_filter ? $a_filter : array();
924
925 foreach ($this->getSubtree($this->getNodeData($a_node)) as $node) {
926 if (in_array($node["type"], $a_filter)) {
927 continue;
928 }
929 $types["$node[type]"] = $node["type"];
930 }
931 return $types ? $types : array();
932 }
933
941 public function deleteTree($a_node)
942 {
943 global $DIC;
944
945 $ilDB = $DIC['ilDB'];
946
947 $this->log->debug('Delete tree with node ' . $a_node);
948
949 if (!is_array($a_node)) {
950 $this->log->logStack(ilLogLevel::ERROR);
951 throw new InvalidArgumentException(__METHOD__ . ': Wrong datatype for node data!');
952 }
953
954 $this->log->debug($this->tree_pk);
955
956 if ($this->__isMainTree()) {
957 // @todo normally this part is not executed, since the subtree is first
958 // moved to trash and then deleted.
959 if (!$this->__checkDelete($a_node)) {
960 $this->log->logStack(ilLogLevel::ERROR);
961 throw new ilInvalidTreeStructureException('Deletion canceled due to invalid tree structure.' . print_r($a_node, true));
962 }
963 }
964
965 $this->getTreeImplementation()->deleteTree($a_node['child']);
966
967 $this->resetInTreeCache();
968 }
969
974 public function validateParentRelations()
975 {
976 return $this->getTreeImplementation()->validateParentRelations();
977 }
978
989 public function getPathFull($a_endnode_id, $a_startnode_id = 0)
990 {
991 $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
992
993 // We retrieve the full path in a single query to improve performance
994 global $DIC;
995
996 $ilDB = $DIC['ilDB'];
997
998 // Abort if no path ids were found
999 if (count($pathIds) == 0) {
1000 return null;
1001 }
1002
1003 $inClause = 'child IN (';
1004 for ($i = 0; $i < count($pathIds); $i++) {
1005 if ($i > 0) {
1006 $inClause .= ',';
1007 }
1008 $inClause .= $ilDB->quote($pathIds[$i], 'integer');
1009 }
1010 $inClause .= ')';
1011
1012 $q = 'SELECT * ' .
1013 'FROM ' . $this->table_tree . ' ' .
1014 $this->buildJoin() . ' ' .
1015 'WHERE ' . $inClause . ' ' .
1016 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer') . ' ' .
1017 'ORDER BY depth';
1018 $r = $ilDB->query($q);
1019
1020 $pathFull = array();
1021 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1022 $pathFull[] = $this->fetchNodeData($row);
1023
1024 // Update cache
1025 if ($this->__isMainTree()) {
1026 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child']);
1027 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
1028 }
1029 }
1030 return $pathFull;
1031 }
1032
1033
1040 public function preloadDepthParent($a_node_ids)
1041 {
1042 global $DIC;
1043
1044 $ilDB = $DIC['ilDB'];
1045
1046 if (!$this->__isMainTree() || !is_array($a_node_ids) || !$this->isCacheUsed()) {
1047 return;
1048 }
1049
1050 $res = $ilDB->query('SELECT t.depth, t.parent, t.child ' .
1051 'FROM ' . $this->table_tree . ' t ' .
1052 'WHERE ' . $ilDB->in("child", $a_node_ids, false, "integer") .
1053 'AND ' . $this->tree_pk . ' = ' . $ilDB->quote($this->tree_id, "integer"));
1054 while ($row = $ilDB->fetchAssoc($res)) {
1055 $this->depth_cache[$row["child"]] = $row["depth"];
1056 $this->parent_cache[$row["child"]] = $row["parent"];
1057 }
1058 }
1059
1069 public function getPathId($a_endnode_id, $a_startnode_id = 0)
1070 {
1071 if (!$a_endnode_id) {
1072 $this->log->logStack(ilLogLevel::ERROR);
1073 throw new InvalidArgumentException(__METHOD__ . ': No endnode given!');
1074 }
1075
1076 // path id cache
1077 if ($this->isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id])) {
1078 //echo "<br>getPathIdhit";
1079 return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
1080 }
1081 //echo "<br>miss";
1082
1083 $pathIds = $this->getTreeImplementation()->getPathIds($a_endnode_id, $a_startnode_id);
1084
1085 if ($this->__isMainTree()) {
1086 $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
1087 }
1088 return $pathIds;
1089 }
1090
1091 // BEGIN WebDAV: getNodePathForTitlePath function added
1109 public function getNodePathForTitlePath($titlePath, $a_startnode_id = null)
1110 {
1111 global $DIC;
1112
1113 $ilDB = $DIC['ilDB'];
1114 $log = $DIC['log'];
1115 //$log->write('getNodePathForTitlePath('.implode('/',$titlePath));
1116
1117 // handle empty title path
1118 if ($titlePath == null || count($titlePath) == 0) {
1119 if ($a_startnode_id == 0) {
1120 return null;
1121 } else {
1122 return $this->getNodePath($a_startnode_id);
1123 }
1124 }
1125
1126 // fetch the node path up to the startnode
1127 if ($a_startnode_id != null && $a_startnode_id != 0) {
1128 // Start using the node path to the root of the relative path
1129 $nodePath = $this->getNodePath($a_startnode_id);
1130 $parent = $a_startnode_id;
1131 } else {
1132 // Start using the root of the tree
1133 $nodePath = array();
1134 $parent = 0;
1135 }
1136
1137
1138 // Convert title path into Unicode Normal Form C
1139 // This is needed to ensure that we can compare title path strings with
1140 // strings from the database.
1141 require_once('include/Unicode/UtfNormal.php');
1142 include_once './Services/Utilities/classes/class.ilStr.php';
1143 $inClause = 'd.title IN (';
1144 for ($i = 0; $i < count($titlePath); $i++) {
1145 $titlePath[$i] = ilStr::strToLower(UtfNormal::toNFC($titlePath[$i]));
1146 if ($i > 0) {
1147 $inClause .= ',';
1148 }
1149 $inClause .= $ilDB->quote($titlePath[$i], 'text');
1150 }
1151 $inClause .= ')';
1152
1153 // Fetch all rows that are potential path elements
1154 if ($this->table_obj_reference) {
1155 $joinClause = 'JOIN ' . $this->table_obj_reference . ' r ON t.child = r.' . $this->ref_pk . ' ' .
1156 'JOIN ' . $this->table_obj_data . ' d ON r.' . $this->obj_pk . ' = d.' . $this->obj_pk;
1157 } else {
1158 $joinClause = 'JOIN ' . $this->table_obj_data . ' d ON t.child = d.' . $this->obj_pk;
1159 }
1160 // The ORDER BY clause in the following SQL statement ensures that,
1161 // in case of a multiple objects with the same title, always the Object
1162 // with the oldest ref_id is chosen.
1163 // This ensure, that, if a new object with the same title is added,
1164 // WebDAV clients can still work with the older object.
1165 $q = 'SELECT t.depth, t.parent, t.child, d.' . $this->obj_pk . ' obj_id, d.type, d.title ' .
1166 'FROM ' . $this->table_tree . ' t ' .
1167 $joinClause . ' ' .
1168 'WHERE ' . $inClause . ' ' .
1169 'AND t.depth <= ' . (count($titlePath) + count($nodePath)) . ' ' .
1170 'AND t.tree = 1 ' .
1171 'ORDER BY t.depth, t.child ASC';
1172 $r = $ilDB->query($q);
1173
1174 $rows = array();
1175 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1176 $row['title'] = UtfNormal::toNFC($row['title']);
1177 $row['ref_id'] = $row['child'];
1178 $rows[] = $row;
1179 }
1180
1181 // Extract the path elements from the fetched rows
1182 for ($i = 0; $i < count($titlePath); $i++) {
1183 $pathElementFound = false;
1184 foreach ($rows as $row) {
1185 if ($row['parent'] == $parent &&
1186 ilStr::strToLower($row['title']) == $titlePath[$i]) {
1187 // FIXME - We should test here, if the user has
1188 // 'visible' permission for the object.
1189 $nodePath[] = $row;
1190 $parent = $row['child'];
1191 $pathElementFound = true;
1192 break;
1193 }
1194 }
1195 // Abort if we haven't found a path element for the current depth
1196 if (!$pathElementFound) {
1197 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):null');
1198 return null;
1199 }
1200 }
1201 // Return the node path
1202 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):'.var_export($nodePath,true));
1203 return $nodePath;
1204 }
1205 // END WebDAV: getNodePathForTitlePath function added
1206 // END WebDAV: getNodePath function added
1223 public function getNodePath($a_endnode_id, $a_startnode_id = 0)
1224 {
1225 global $DIC;
1226
1227 $ilDB = $DIC['ilDB'];
1228
1229 $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
1230
1231 // Abort if no path ids were found
1232 if (count($pathIds) == 0) {
1233 return null;
1234 }
1235
1236
1237 $types = array();
1238 $data = array();
1239 for ($i = 0; $i < count($pathIds); $i++) {
1240 $types[] = 'integer';
1241 $data[] = $pathIds[$i];
1242 }
1243
1244 $query = 'SELECT t.depth,t.parent,t.child,d.obj_id,d.type,d.title ' .
1245 'FROM ' . $this->table_tree . ' t ' .
1246 'JOIN ' . $this->table_obj_reference . ' r ON r.ref_id = t.child ' .
1247 'JOIN ' . $this->table_obj_data . ' d ON d.obj_id = r.obj_id ' .
1248 'WHERE ' . $ilDB->in('t.child', $data, false, 'integer') . ' ' .
1249 'ORDER BY t.depth ';
1250
1251 $res = $ilDB->queryF($query, $types, $data);
1252
1253 $titlePath = array();
1254 while ($row = $ilDB->fetchAssoc($res)) {
1255 $titlePath[] = $row;
1256 }
1257 return $titlePath;
1258 }
1259 // END WebDAV: getNodePath function added
1260
1268 public function checkTree()
1269 {
1270 global $DIC;
1271
1272 $ilDB = $DIC['ilDB'];
1273
1274 $types = array('integer');
1275 $query = 'SELECT lft,rgt FROM ' . $this->table_tree . ' ' .
1276 'WHERE ' . $this->tree_pk . ' = %s ';
1277
1278 $res = $ilDB->queryF($query, $types, array($this->tree_id));
1279 while ($row = $ilDB->fetchObject($res)) {
1280 $lft[] = $row->lft;
1281 $rgt[] = $row->rgt;
1282 }
1283
1284 $all = array_merge($lft, $rgt);
1285 $uni = array_unique($all);
1286
1287 if (count($all) != count($uni)) {
1288 $message = 'Tree is corrupted!';
1289
1290 $this->log->error($message);
1292 }
1293
1294 return true;
1295 }
1296
1304 public function checkTreeChilds($a_no_zero_child = true)
1305 {
1306 global $DIC;
1307
1308 $ilDB = $DIC['ilDB'];
1309
1310 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1311 'WHERE ' . $this->tree_pk . ' = %s ' .
1312 'ORDER BY lft';
1313 $r1 = $ilDB->queryF($query, array('integer'), array($this->tree_id));
1314
1315 while ($row = $ilDB->fetchAssoc($r1)) {
1316 //echo "tree:".$row[$this->tree_pk].":lft:".$row["lft"].":rgt:".$row["rgt"].":child:".$row["child"].":<br>";
1317 if (($row["child"] == 0) && $a_no_zero_child) {
1318 $message = "Tree contains child with ID 0!";
1319 $this->log->error($message);
1321 }
1322
1323 if ($this->table_obj_reference) {
1324 // get object reference data
1325 $query = 'SELECT * FROM ' . $this->table_obj_reference . ' WHERE ' . $this->ref_pk . ' = %s ';
1326 $r2 = $ilDB->queryF($query, array('integer'), array($row['child']));
1327
1328 //echo "num_childs:".$r2->numRows().":<br>";
1329 if ($r2->numRows() == 0) {
1330 $message = "No Object-to-Reference entry found for ID " . $row["child"] . "!";
1331 $this->log->error($message);
1333 }
1334 if ($r2->numRows() > 1) {
1335 $message = "More Object-to-Reference entries found for ID " . $row["child"] . "!";
1336 $this->log->error($message);
1338 }
1339
1340 // get object data
1341 $obj_ref = $ilDB->fetchAssoc($r2);
1342
1343 $query = 'SELECT * FROM ' . $this->table_obj_data . ' WHERE ' . $this->obj_pk . ' = %s';
1344 $r3 = $ilDB->queryF($query, array('integer'), array($obj_ref[$this->obj_pk]));
1345 if ($r3->numRows() == 0) {
1346 $message = " No child found for ID " . $obj_ref[$this->obj_pk] . "!";
1347 $this->log->error($message);
1349 }
1350 if ($r3->numRows() > 1) {
1351 $message = "More childs found for ID " . $obj_ref[$this->obj_pk] . "!";
1352 $this->log->error($message);
1354 }
1355 } else {
1356 // get only object data
1357 $query = 'SELECT * FROM ' . $this->table_obj_data . ' WHERE ' . $this->obj_pk . ' = %s';
1358 $r2 = $ilDB->queryF($query, array('integer'), array($row['child']));
1359 //echo "num_childs:".$r2->numRows().":<br>";
1360 if ($r2->numRows() == 0) {
1361 $message = "No child found for ID " . $row["child"] . "!";
1362 $this->log->error($message);
1364 }
1365 if ($r2->numRows() > 1) {
1366 $message = "More childs found for ID " . $row["child"] . "!";
1367 $this->log->error($message);
1369 }
1370 }
1371 }
1372
1373 return true;
1374 }
1375
1381 public function getMaximumDepth()
1382 {
1383 global $DIC;
1384
1385 $ilDB = $DIC['ilDB'];
1386
1387 $query = 'SELECT MAX(depth) depth FROM ' . $this->table_tree;
1388 $res = $ilDB->query($query);
1389
1390 $row = $ilDB->fetchAssoc($res);
1391 return $row['depth'];
1392 }
1393
1400 public function getDepth($a_node_id)
1401 {
1402 global $DIC;
1403
1404 $ilDB = $DIC['ilDB'];
1405
1406 if ($a_node_id) {
1407 $query = 'SELECT depth FROM ' . $this->table_tree . ' ' .
1408 'WHERE child = %s ' .
1409 'AND ' . $this->tree_pk . ' = %s ';
1410 $res = $ilDB->queryF($query, array('integer','integer'), array($a_node_id,$this->tree_id));
1411 $row = $ilDB->fetchObject($res);
1412
1413 return $row->depth;
1414 } else {
1415 return 1;
1416 }
1417 }
1418
1426 public function getNodeTreeData($a_node_id)
1427 {
1428 global $DIC;
1429
1430 $ilDB = $DIC['ilDB'];
1431
1432 if (!$a_node_id) {
1433 $this->log->logStack(ilLogLevel::ERROR);
1434 throw new InvalidArgumentException('Missing or empty parameter $a_node_id: ' . $a_node_id);
1435 }
1436
1437 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1438 'WHERE child = ' . $ilDB->quote($a_node_id, 'integer');
1439 $res = $ilDB->query($query);
1440 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1441 return $row;
1442 }
1443 return array();
1444 }
1445
1446
1455 // BEGIN WebDAV: Pass tree id to this method
1456 //function getNodeData($a_node_id)
1457 public function getNodeData($a_node_id, $a_tree_pk = null)
1458 // END PATCH WebDAV: Pass tree id to this method
1459 {
1460 global $DIC;
1461
1462 $ilDB = $DIC['ilDB'];
1463
1464 if (!isset($a_node_id)) {
1465 $this->log->logStack(ilLogLevel::ERROR);
1466 throw new InvalidArgumentException("No node_id given!");
1467 }
1468 if ($this->__isMainTree()) {
1469 if ($a_node_id < 1) {
1470 $message = 'No valid parameter given! $a_node_id: %s' . $a_node_id;
1471
1472 $this->log->error($message);
1473 throw new InvalidArgumentException($message);
1474 }
1475 }
1476
1477 // BEGIN WebDAV: Pass tree id to this method
1478 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1479 $this->buildJoin() .
1480 'WHERE ' . $this->table_tree . '.child = %s ' .
1481 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
1482 $res = $ilDB->queryF($query, array('integer','integer'), array(
1483 $a_node_id,
1484 $a_tree_pk === null ? $this->tree_id : $a_tree_pk));
1485 // END WebDAV: Pass tree id to this method
1486 $row = $ilDB->fetchAssoc($res);
1488
1489 return $this->fetchNodeData($row);
1490 }
1491
1499 public function fetchNodeData($a_row)
1500 {
1501 global $DIC;
1502
1503 $objDefinition = $DIC['objDefinition'];
1504 $lng = $DIC['lng'];
1505 $ilBench = $DIC['ilBench'];
1506 $ilDB = $DIC['ilDB'];
1507
1508 //$ilBench->start("Tree", "fetchNodeData_getRow");
1509 $data = $a_row;
1510 $data["desc"] = $a_row["description"]; // for compability
1511 //$ilBench->stop("Tree", "fetchNodeData_getRow");
1512
1513 // multilingual support systemobjects (sys) & categories (db)
1514 //$ilBench->start("Tree", "fetchNodeData_readDefinition");
1515 if (is_object($objDefinition)) {
1516 $translation_type = $objDefinition->getTranslationType($data["type"]);
1517 }
1518 //$ilBench->stop("Tree", "fetchNodeData_readDefinition");
1519
1520 if ($translation_type == "sys") {
1521 //$ilBench->start("Tree", "fetchNodeData_getLangData");
1522 if ($data["type"] == "rolf" and $data["obj_id"] != ROLE_FOLDER_ID) {
1523 $data["description"] = $lng->txt("obj_" . $data["type"] . "_local_desc") . $data["title"] . $data["desc"];
1524 $data["desc"] = $lng->txt("obj_" . $data["type"] . "_local_desc") . $data["title"] . $data["desc"];
1525 $data["title"] = $lng->txt("obj_" . $data["type"] . "_local");
1526 } else {
1527 $data["title"] = $lng->txt("obj_" . $data["type"]);
1528 $data["description"] = $lng->txt("obj_" . $data["type"] . "_desc");
1529 $data["desc"] = $lng->txt("obj_" . $data["type"] . "_desc");
1530 }
1531 //$ilBench->stop("Tree", "fetchNodeData_getLangData");
1532 } elseif ($translation_type == "db") {
1533
1534 // Try to retrieve object translation from cache
1535 if ($this->isCacheUsed() &&
1536 array_key_exists($data["obj_id"] . '.' . $lang_code, $this->translation_cache)) {
1537 $key = $data["obj_id"] . '.' . $lang_code;
1538 $data["title"] = $this->translation_cache[$key]['title'];
1539 $data["description"] = $this->translation_cache[$key]['description'];
1540 $data["desc"] = $this->translation_cache[$key]['desc'];
1541 } else {
1542 // Object translation is not in cache, read it from database
1543 //$ilBench->start("Tree", "fetchNodeData_getTranslation");
1544 $query = 'SELECT title,description FROM object_translation ' .
1545 'WHERE obj_id = %s ' .
1546 'AND lang_code = %s ' .
1547 'AND NOT lang_default = %s';
1548
1549 $res = $ilDB->queryF($query, array('integer','text','integer'), array(
1550 $data['obj_id'],
1551 $this->lang_code,
1552 1));
1553 $row = $ilDB->fetchObject($res);
1554
1555 if ($row) {
1556 $data["title"] = $row->title;
1557 $data["description"] = ilUtil::shortenText($row->description, ilObject::DESC_LENGTH, true);
1558 $data["desc"] = $row->description;
1559 }
1560 //$ilBench->stop("Tree", "fetchNodeData_getTranslation");
1561
1562 // Store up to 1000 object translations in cache
1563 if ($this->isCacheUsed() && count($this->translation_cache) < 1000) {
1564 $key = $data["obj_id"] . '.' . $lang_code;
1565 $this->translation_cache[$key] = array();
1566 $this->translation_cache[$key]['title'] = $data["title"] ;
1567 $this->translation_cache[$key]['description'] = $data["description"];
1568 $this->translation_cache[$key]['desc'] = $data["desc"];
1569 }
1570 }
1571 }
1572
1573 // TODO: Handle this switch by module.xml definitions
1574 if ($data['type'] == 'crsr' or $data['type'] == 'catr' or $data['type'] == 'grpr') {
1575 include_once('./Services/ContainerReference/classes/class.ilContainerReference.php');
1576 $data['title'] = ilContainerReference::_lookupTitle($data['obj_id']);
1577 }
1578
1579 return $data ? $data : array();
1580 }
1581
1587 protected function fetchTranslationFromObjectDataCache($a_obj_ids)
1588 {
1589 global $DIC;
1590
1591 $ilObjDataCache = $DIC['ilObjDataCache'];
1592
1593 if ($this->isCacheUsed() && is_array($a_obj_ids) && is_object($ilObjDataCache)) {
1594 foreach ($a_obj_ids as $id) {
1595 $this->translation_cache[$id . '.']['title'] = $ilObjDataCache->lookupTitle($id);
1596 $this->translation_cache[$id . '.']['description'] = $ilObjDataCache->lookupDescription($id);
1597 ;
1598 $this->translation_cache[$id . '.']['desc'] =
1599 $this->translation_cache[$id . '.']['description'];
1600 }
1601 }
1602 }
1603
1604
1612 public function isInTree($a_node_id)
1613 {
1614 global $DIC;
1615
1616 $ilDB = $DIC['ilDB'];
1617
1618 if (!isset($a_node_id)) {
1619 return false;
1620 #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1621 }
1622 // is in tree cache
1623 if ($this->isCacheUsed() && isset($this->in_tree_cache[$a_node_id])) {
1624 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Using in tree cache '.$a_node_id);
1625 //echo "<br>in_tree_hit";
1626 return $this->in_tree_cache[$a_node_id];
1627 }
1628
1629 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1630 'WHERE ' . $this->table_tree . '.child = %s ' .
1631 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s';
1632
1633 $res = $ilDB->queryF($query, array('integer','integer'), array(
1634 $a_node_id,
1635 $this->tree_id));
1636
1637 if ($res->numRows() > 0) {
1638 if ($this->__isMainTree()) {
1639 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = true');
1640 $this->in_tree_cache[$a_node_id] = true;
1641 }
1642 return true;
1643 } else {
1644 if ($this->__isMainTree()) {
1645 #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = false');
1646 $this->in_tree_cache[$a_node_id] = false;
1647 }
1648 return false;
1649 }
1650 }
1651
1659 public function getParentNodeData($a_node_id)
1660 {
1661 global $DIC;
1662
1663 $ilDB = $DIC['ilDB'];
1664 global $DIC;
1665
1666 $ilLog = $DIC['ilLog'];
1667
1668 if (!isset($a_node_id)) {
1669 $ilLog->logStack();
1670 throw new InvalidArgumentException(__METHOD__ . ': No node_id given!');
1671 }
1672
1673 if ($this->table_obj_reference) {
1674 // Use inner join instead of left join to improve performance
1675 $innerjoin = "JOIN " . $this->table_obj_reference . " ON v.child=" . $this->table_obj_reference . "." . $this->ref_pk . " " .
1676 "JOIN " . $this->table_obj_data . " ON " . $this->table_obj_reference . "." . $this->obj_pk . "=" . $this->table_obj_data . "." . $this->obj_pk . " ";
1677 } else {
1678 // Use inner join instead of left join to improve performance
1679 $innerjoin = "JOIN " . $this->table_obj_data . " ON v.child=" . $this->table_obj_data . "." . $this->obj_pk . " ";
1680 }
1681
1682 $query = 'SELECT * FROM ' . $this->table_tree . ' s, ' . $this->table_tree . ' v ' .
1683 $innerjoin .
1684 'WHERE s.child = %s ' .
1685 'AND s.parent = v.child ' .
1686 'AND s.' . $this->tree_pk . ' = %s ' .
1687 'AND v.' . $this->tree_pk . ' = %s';
1688 $res = $ilDB->queryF($query, array('integer','integer','integer'), array(
1689 $a_node_id,
1690 $this->tree_id,
1691 $this->tree_id));
1692 $row = $ilDB->fetchAssoc($res);
1693 return $this->fetchNodeData($row);
1694 }
1695
1703 public function isGrandChild($a_startnode_id, $a_querynode_id)
1704 {
1705 return $this->getRelation($a_startnode_id, $a_querynode_id) == self::RELATION_PARENT;
1706 }
1707
1717 public function addTree($a_tree_id, $a_node_id = -1)
1718 {
1719 global $DIC;
1720
1721 $ilDB = $DIC['ilDB'];
1722
1723 // FOR SECURITY addTree() IS NOT ALLOWED ON MAIN TREE
1724 if ($this->__isMainTree()) {
1725 $message = sprintf(
1726 'Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
1727 $a_tree_id,
1728 $a_node_id
1729 );
1730 $this->log->error($message);
1731 throw new InvalidArgumentException($message);
1732 }
1733
1734 if (!isset($a_tree_id)) {
1735 $message = "No tree_id given!";
1736 $this->log->error($message);
1737 throw new InvalidArgumentException($message);
1738 }
1739
1740 if ($a_node_id <= 0) {
1741 $a_node_id = $a_tree_id;
1742 }
1743
1744 $query = 'INSERT INTO ' . $this->table_tree . ' (' .
1745 $this->tree_pk . ', child,parent,lft,rgt,depth) ' .
1746 'VALUES ' .
1747 '(%s,%s,%s,%s,%s,%s)';
1748 $res = $ilDB->manipulateF($query, array('integer','integer','integer','integer','integer','integer'), array(
1749 $a_tree_id,
1750 $a_node_id,
1751 0,
1752 1,
1753 2,
1754 1));
1755
1756 return true;
1757 }
1758
1769 {
1770 global $DIC;
1771
1772 $ilDB = $DIC['ilDB'];
1773
1774 if (!isset($a_type) or (!is_string($a_type))) {
1775 $this->log->logStack(ilLogLevel::ERROR);
1776 throw new InvalidArgumentException('Type not given or wrong datatype');
1777 }
1778
1779 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1780 $this->buildJoin() .
1781 'WHERE ' . $this->table_obj_data . '.type = ' . $this->ilDB->quote($a_type, 'text') .
1782 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer');
1783
1784 $res = $ilDB->query($query);
1785 $data = array();
1786 while ($row = $ilDB->fetchAssoc($res)) {
1787 $data[] = $this->fetchNodeData($row);
1788 }
1789
1790 return $data;
1791 }
1792
1801 public function removeTree($a_tree_id)
1802 {
1803 global $DIC;
1804
1805 $ilDB = $DIC['ilDB'];
1806
1807 // OPERATION NOT ALLOWED ON MAIN TREE
1808 if ($this->__isMainTree()) {
1809 $this->log->logStack(ilLogLevel::ERROR);
1810 throw new InvalidArgumentException('Operation not allowed on main tree');
1811 }
1812 if (!$a_tree_id) {
1813 $this->log->logStack(ilLogLevel::ERROR);
1814 throw new InvalidArgumentException('Missing parameter tree id');
1815 }
1816
1817 $query = 'DELETE FROM ' . $this->table_tree .
1818 ' WHERE ' . $this->tree_pk . ' = %s ';
1819 $ilDB->manipulateF($query, array('integer'), array($a_tree_id));
1820 return true;
1821 }
1822
1830 public function moveToTrash($a_node_id, $a_set_deleted = false)
1831 {
1832 global $DIC;
1833
1834 $ilDB = $DIC['ilDB'];
1835
1836 if (!$a_node_id) {
1837 $this->log->logStack(ilLogLevel::ERROR);
1838 throw new InvalidArgumentException('No valid parameter given! $a_node_id: ' . $a_node_id);
1839 }
1840
1841
1842 $query = $this->getTreeImplementation()->getSubTreeQuery($this->getNodeTreeData($a_node_id), '', false);
1843 $res = $ilDB->query($query);
1844
1845 $subnodes = array();
1846 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1847 $subnodes[] = $row['child'];
1848 }
1849
1850 if (!count($subnodes)) {
1851 // Possibly already deleted
1852 return false;
1853 }
1854
1855 if ($a_set_deleted) {
1856 include_once './Services/Object/classes/class.ilObject.php';
1857 ilObject::setDeletedDates($subnodes);
1858 }
1859
1860 // netsted set <=> mp
1861 $this->getTreeImplementation()->moveToTrash($a_node_id);
1862
1863 return true;
1864 }
1865
1876 public function saveSubTree($a_node_id, $a_set_deleted = false)
1877 {
1878 return $this->moveToTrash($a_node_id, $a_set_deleted);
1879 }
1880
1885 public function isDeleted($a_node_id)
1886 {
1887 return $this->isSaved($a_node_id);
1888 }
1889
1895 public function isSaved($a_node_id)
1896 {
1897 global $DIC;
1898
1899 $ilDB = $DIC['ilDB'];
1900
1901 // is saved cache
1902 if ($this->isCacheUsed() && isset($this->is_saved_cache[$a_node_id])) {
1903 //echo "<br>issavedhit";
1904 return $this->is_saved_cache[$a_node_id];
1905 }
1906
1907 $query = 'SELECT ' . $this->tree_pk . ' FROM ' . $this->table_tree . ' ' .
1908 'WHERE child = %s ';
1909 $res = $ilDB->queryF($query, array('integer'), array($a_node_id));
1910 $row = $ilDB->fetchAssoc($res);
1911
1912 if ($row[$this->tree_pk] < 0) {
1913 if ($this->__isMainTree()) {
1914 $this->is_saved_cache[$a_node_id] = true;
1915 }
1916 return true;
1917 } else {
1918 if ($this->__isMainTree()) {
1919 $this->is_saved_cache[$a_node_id] = false;
1920 }
1921 return false;
1922 }
1923 }
1924
1931 public function preloadDeleted($a_node_ids)
1932 {
1933 global $DIC;
1934
1935 $ilDB = $DIC['ilDB'];
1936
1937 if (!is_array($a_node_ids) || !$this->isCacheUsed()) {
1938 return;
1939 }
1940
1941 $query = 'SELECT ' . $this->tree_pk . ', child FROM ' . $this->table_tree . ' ' .
1942 'WHERE ' . $ilDB->in("child", $a_node_ids, false, "integer");
1943
1944 $res = $ilDB->query($query);
1945 while ($row = $ilDB->fetchAssoc($res)) {
1946 if ($row[$this->tree_pk] < 0) {
1947 if ($this->__isMainTree()) {
1948 $this->is_saved_cache[$row["child"]] = true;
1949 }
1950 } else {
1951 if ($this->__isMainTree()) {
1952 $this->is_saved_cache[$row["child"]] = false;
1953 }
1954 }
1955 }
1956 }
1957
1958
1966 public function getSavedNodeData($a_parent_id)
1967 {
1968 global $DIC;
1969
1970 $ilDB = $DIC['ilDB'];
1971
1972 if (!isset($a_parent_id)) {
1973 $message = "No node_id given!";
1974 $this->log->error($message);
1975 throw new InvalidArgumentException($message);
1976 }
1977
1978 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1979 $this->buildJoin() .
1980 'WHERE ' . $this->table_tree . '.' . $this->tree_pk . ' < %s ' .
1981 'AND ' . $this->table_tree . '.parent = %s';
1982 $res = $ilDB->queryF($query, array('integer','integer'), array(
1983 0,
1984 $a_parent_id));
1985
1986 while ($row = $ilDB->fetchAssoc($res)) {
1987 $saved[] = $this->fetchNodeData($row);
1988 }
1989
1990 return $saved ? $saved : array();
1991 }
1992
1999 public function getSavedNodeObjIds(array $a_obj_ids)
2000 {
2001 global $DIC;
2002
2003 $ilDB = $DIC['ilDB'];
2004
2005 $query = 'SELECT ' . $this->table_obj_data . '.obj_id FROM ' . $this->table_tree . ' ' .
2006 $this->buildJoin() .
2007 'WHERE ' . $this->table_tree . '.' . $this->tree_pk . ' < ' . $ilDB->quote(0, 'integer') . ' ' .
2008 'AND ' . $ilDB->in($this->table_obj_data . '.obj_id', $a_obj_ids, '', 'integer');
2009 $res = $ilDB->query($query);
2010 while ($row = $ilDB->fetchAssoc($res)) {
2011 $saved[] = $row['obj_id'];
2012 }
2013
2014 return $saved ? $saved : array();
2015 }
2016
2024 public function getParentId($a_node_id)
2025 {
2026 global $DIC;
2027
2028 $ilDB = $DIC['ilDB'];
2029
2030 if (!isset($a_node_id)) {
2031 $message = "No node_id given!";
2032 $this->log->error($message);
2033 throw new InvalidArgumentException($message);
2034 }
2035
2036 $query = 'SELECT parent FROM ' . $this->table_tree . ' ' .
2037 'WHERE child = %s ' .
2038 'AND ' . $this->tree_pk . ' = %s ';
2039 $res = $ilDB->queryF($query, array('integer','integer'), array(
2040 $a_node_id,
2041 $this->tree_id));
2042
2043 $row = $ilDB->fetchObject($res);
2044 return $row->parent;
2045 }
2046
2054 public function getLeftValue($a_node_id)
2055 {
2056 global $DIC;
2057
2058 $ilDB = $DIC['ilDB'];
2059
2060 if (!isset($a_node_id)) {
2061 $message = "No node_id given!";
2062 $this->log->error($message);
2063 throw new InvalidArgumentException($message);
2064 }
2065
2066 $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2067 'WHERE child = %s ' .
2068 'AND ' . $this->tree_pk . ' = %s ';
2069 $res = $ilDB->queryF($query, array('integer','integer'), array(
2070 $a_node_id,
2071 $this->tree_id));
2072 $row = $ilDB->fetchObject($res);
2073 return $row->lft;
2074 }
2075
2083 public function getChildSequenceNumber($a_node, $type = "")
2084 {
2085 global $DIC;
2086
2087 $ilDB = $DIC['ilDB'];
2088
2089 if (!isset($a_node)) {
2090 $message = "No node_id given!";
2091 $this->log->error($message);
2092 throw new InvalidArgumentException($message);
2093 }
2094
2095 if ($type) {
2096 $query = 'SELECT count(*) cnt FROM ' . $this->table_tree . ' ' .
2097 $this->buildJoin() .
2098 'WHERE lft <= %s ' .
2099 'AND type = %s ' .
2100 'AND parent = %s ' .
2101 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2102
2103 $res = $ilDB->queryF($query, array('integer','text','integer','integer'), array(
2104 $a_node['lft'],
2105 $type,
2106 $a_node['parent'],
2107 $this->tree_id));
2108 } else {
2109 $query = 'SELECT count(*) cnt FROM ' . $this->table_tree . ' ' .
2110 $this->buildJoin() .
2111 'WHERE lft <= %s ' .
2112 'AND parent = %s ' .
2113 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2114
2115 $res = $ilDB->queryF($query, array('integer','integer','integer'), array(
2116 $a_node['lft'],
2117 $a_node['parent'],
2118 $this->tree_id));
2119 }
2120 $row = $ilDB->fetchAssoc($res);
2121 return $row["cnt"];
2122 }
2123
2130 public function readRootId()
2131 {
2132 global $DIC;
2133
2134 $ilDB = $DIC['ilDB'];
2135
2136 $query = 'SELECT child FROM ' . $this->table_tree . ' ' .
2137 'WHERE parent = %s ' .
2138 'AND ' . $this->tree_pk . ' = %s ';
2139 $res = $ilDB->queryF($query, array('integer','integer'), array(
2140 0,
2141 $this->tree_id));
2142 $row = $ilDB->fetchObject($res);
2143 $this->root_id = $row->child;
2144 return $this->root_id;
2145 }
2146
2152 public function getRootId()
2153 {
2154 return $this->root_id;
2155 }
2156 public function setRootId($a_root_id)
2157 {
2158 $this->root_id = $a_root_id;
2159 }
2160
2166 public function getTreeId()
2167 {
2168 return $this->tree_id;
2169 }
2170
2176 public function setTreeId($a_tree_id)
2177 {
2178 $this->tree_id = $a_tree_id;
2179 }
2180
2189 public function fetchSuccessorNode($a_node_id, $a_type = "")
2190 {
2191 global $DIC;
2192
2193 $ilDB = $DIC['ilDB'];
2194
2195 if (!isset($a_node_id)) {
2196 $message = "No node_id given!";
2197 $this->log->error($message);
2198 throw new InvalidArgumentException($message);
2199 }
2200
2201 // get lft value for current node
2202 $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2203 'WHERE ' . $this->table_tree . '.child = %s ' .
2204 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2205 $res = $ilDB->queryF($query, array('integer','integer'), array(
2206 $a_node_id,
2207 $this->tree_id));
2208 $curr_node = $ilDB->fetchAssoc($res);
2209
2210 if ($a_type) {
2211 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2212 $this->buildJoin() .
2213 'WHERE lft > %s ' .
2214 'AND ' . $this->table_obj_data . '.type = %s ' .
2215 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2216 'ORDER BY lft ';
2217 $ilDB->setLimit(1);
2218 $res = $ilDB->queryF($query, array('integer','text','integer'), array(
2219 $curr_node['lft'],
2220 $a_type,
2221 $this->tree_id));
2222 } else {
2223 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2224 $this->buildJoin() .
2225 'WHERE lft > %s ' .
2226 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2227 'ORDER BY lft ';
2228 $ilDB->setLimit(1);
2229 $res = $ilDB->queryF($query, array('integer','integer'), array(
2230 $curr_node['lft'],
2231 $this->tree_id));
2232 }
2233
2234 if ($res->numRows() < 1) {
2235 return false;
2236 } else {
2237 $row = $ilDB->fetchAssoc($res);
2238 return $this->fetchNodeData($row);
2239 }
2240 }
2241
2250 public function fetchPredecessorNode($a_node_id, $a_type = "")
2251 {
2252 global $DIC;
2253
2254 $ilDB = $DIC['ilDB'];
2255
2256 if (!isset($a_node_id)) {
2257 $message = "No node_id given!";
2258 $this->log->error($message);
2259 throw new InvalidArgumentException($message);
2260 }
2261
2262 // get lft value for current node
2263 $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2264 'WHERE ' . $this->table_tree . '.child = %s ' .
2265 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2266 $res = $ilDB->queryF($query, array('integer','integer'), array(
2267 $a_node_id,
2268 $this->tree_id));
2269
2270 $curr_node = $ilDB->fetchAssoc($res);
2271
2272 if ($a_type) {
2273 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2274 $this->buildJoin() .
2275 'WHERE lft < %s ' .
2276 'AND ' . $this->table_obj_data . '.type = %s ' .
2277 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2278 'ORDER BY lft DESC';
2279 $ilDB->setLimit(1);
2280 $res = $ilDB->queryF($query, array('integer','text','integer'), array(
2281 $curr_node['lft'],
2282 $a_type,
2283 $this->tree_id));
2284 } else {
2285 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2286 $this->buildJoin() .
2287 'WHERE lft < %s ' .
2288 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2289 'ORDER BY lft DESC';
2290 $ilDB->setLimit(1);
2291 $res = $ilDB->queryF($query, array('integer','integer'), array(
2292 $curr_node['lft'],
2293 $this->tree_id));
2294 }
2295
2296 if ($res->numRows() < 1) {
2297 return false;
2298 } else {
2299 $row = $ilDB->fetchAssoc($res);
2300 return $this->fetchNodeData($row);
2301 }
2302 }
2303
2312 public function renumber($node_id = 1, $i = 1)
2313 {
2314 global $DIC;
2315
2316 $ilDB = $DIC['ilDB'];
2317
2318 $renumber_callable = function (ilDBInterface $ilDB) use ($node_id,$i,&$return) {
2319 $return = $this->__renumber($node_id, $i);
2320 };
2321
2322 // LOCKED ###################################
2323 if ($this->__isMainTree()) {
2324 $ilAtomQuery = $ilDB->buildAtomQuery();
2325 $ilAtomQuery->addTableLock($this->table_tree);
2326
2327 $ilAtomQuery->addQueryCallable($renumber_callable);
2328 $ilAtomQuery->run();
2329 } else {
2330 $renumber_callable($ilDB);
2331 }
2332 return $return;
2333 }
2334
2335 // PRIVATE
2345 public function __renumber($node_id = 1, $i = 1)
2346 {
2347 global $DIC;
2348
2349 $ilDB = $DIC['ilDB'];
2350
2351 $query = 'UPDATE ' . $this->table_tree . ' SET lft = %s WHERE child = %s AND tree = %s';
2352 $res = $ilDB->manipulateF($query, array('integer','integer','integer'), array(
2353 $i,
2354 $node_id,
2355 $this->tree_id));
2356
2357 // to much dependencies
2358 //$childs = $this->getChilds($node_id);
2359 $childs = $this->getChildIds($node_id);
2360
2361 foreach ($childs as $child) {
2362 $i = $this->__renumber($child, $i + 1);
2363 }
2364 $i++;
2365
2366 // Insert a gap at the end of node, if the node has children
2367 if (count($childs) > 0) {
2368 $i += $this->gap * 2;
2369 }
2370
2371
2372 $query = 'UPDATE ' . $this->table_tree . ' SET rgt = %s WHERE child = %s AND tree = %s';
2373 $res = $ilDB->manipulateF($query, array('integer','integer', 'integer'), array(
2374 $i,
2375 $node_id,
2376 $this->tree_id));
2377 return $i;
2378 }
2379
2380
2391 public function checkForParentType($a_ref_id, $a_type, $a_exclude_source_check = false)
2392 {
2393 // #12577
2394 $cache_key = $a_ref_id . '.' . $a_type . '.' . ((int) $a_exclude_source_check);
2395
2396 // Try to return a cached result
2397 if ($this->isCacheUsed() &&
2398 array_key_exists($cache_key, $this->parent_type_cache)) {
2399 return $this->parent_type_cache[$cache_key];
2400 }
2401
2402 // Store up to 1000 results in cache
2403 $do_cache = ($this->__isMainTree() && count($this->parent_type_cache) < 1000);
2404
2405 // ref_id is not in tree
2406 if (!$this->isInTree($a_ref_id)) {
2407 if ($do_cache) {
2408 $this->parent_type_cache[$cache_key] = false;
2409 }
2410 return false;
2411 }
2412
2413 $path = array_reverse($this->getPathFull($a_ref_id));
2414
2415 // remove first path entry as it is requested node
2416 if ($a_exclude_source_check) {
2417 array_shift($path);
2418 }
2419
2420 foreach ($path as $node) {
2421 // found matching parent
2422 if ($node["type"] == $a_type) {
2423 if ($do_cache) {
2424 $this->parent_type_cache[$cache_key] = $node["child"];
2425 }
2426 return $node["child"];
2427 }
2428 }
2429
2430 if ($do_cache) {
2431 $this->parent_type_cache[$cache_key] = false;
2432 }
2433 return 0;
2434 }
2435
2446 public static function _removeEntry($a_tree, $a_child, $a_db_table = "tree")
2447 {
2448 global $DIC;
2449
2450 $ilDB = $DIC['ilDB'];
2451
2452 if ($a_db_table === 'tree') {
2453 if ($a_tree == 1 and $a_child == ROOT_FOLDER_ID) {
2454 $message = sprintf(
2455 'Tried to delete root node! $a_tree: %s $a_child: %s',
2456 $a_tree,
2457 $a_child
2458 );
2459 ilLoggerFactory::getLogger('tree')->error($message);
2460 throw new InvalidArgumentException($message);
2461 }
2462 }
2463
2464 $query = 'DELETE FROM ' . $a_db_table . ' ' .
2465 'WHERE tree = %s ' .
2466 'AND child = %s ';
2467 $res = $ilDB->manipulateF($query, array('integer','integer'), array(
2468 $a_tree,
2469 $a_child));
2470 }
2471
2478 public function __isMainTree()
2479 {
2480 return $this->table_tree === 'tree';
2481 }
2482
2494 public function __checkDelete($a_node)
2495 {
2496 global $DIC;
2497
2498 $ilDB = $DIC['ilDB'];
2499
2500
2501 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, array(), false);
2502 $this->log->debug($query);
2503 $res = $ilDB->query($query);
2504
2505 $counter = (int) $lft_childs = array();
2506 while ($row = $ilDB->fetchObject($res)) {
2507 $lft_childs[$row->child] = $row->parent;
2508 ++$counter;
2509 }
2510
2511 // CHECK FOR DUPLICATE CHILD IDS
2512 if ($counter != count($lft_childs)) {
2513 $message = 'Duplicate entries for "child" in maintree! $a_node_id: ' . $a_node['child'];
2514
2515 $this->log->error($message);
2517 }
2518
2519 // GET SUBTREE BY PARENT RELATION
2520 $parent_childs = array();
2521 $this->__getSubTreeByParentRelation($a_node['child'], $parent_childs);
2522 $this->__validateSubtrees($lft_childs, $parent_childs);
2523
2524 return true;
2525 }
2526
2536 public function __getSubTreeByParentRelation($a_node_id, &$parent_childs)
2537 {
2538 global $DIC;
2539
2540 $ilDB = $DIC['ilDB'];
2541
2542 // GET PARENT ID
2543 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2544 'WHERE child = %s ' .
2545 'AND tree = %s ';
2546 $res = $ilDB->queryF($query, array('integer','integer'), array(
2547 $a_node_id,
2548 $this->tree_id));
2549
2550 $counter = 0;
2551 while ($row = $ilDB->fetchObject($res)) {
2552 $parent_childs[$a_node_id] = $row->parent;
2553 ++$counter;
2554 }
2555 // MULTIPLE ENTRIES
2556 if ($counter > 1) {
2557 $message = 'Multiple entries in maintree! $a_node_id: ' . $a_node_id;
2558
2559 $this->log->error($message);
2561 }
2562
2563 // GET ALL CHILDS
2564 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2565 'WHERE parent = %s ';
2566 $res = $ilDB->queryF($query, array('integer'), array($a_node_id));
2567
2568 while ($row = $ilDB->fetchObject($res)) {
2569 // RECURSION
2570 $this->__getSubTreeByParentRelation($row->child, $parent_childs);
2571 }
2572 return true;
2573 }
2574
2582 public function __validateSubtrees(&$lft_childs, $parent_childs)
2583 {
2584 // SORT BY KEY
2585 ksort($lft_childs);
2586 ksort($parent_childs);
2587
2588 $this->log->debug('left childs ' . print_r($lft_childs, true));
2589 $this->log->debug('parent childs ' . print_r($parent_childs, true));
2590
2591 if (count($lft_childs) != count($parent_childs)) {
2592 $message = '(COUNT) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2593 $this->log->error($message);
2595 }
2596
2597
2598 foreach ($lft_childs as $key => $value) {
2599 if ($parent_childs[$key] != $value) {
2600 $message = '(COMPARE) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2601 $this->log->error($message);
2603 }
2604 if ($key == ROOT_FOLDER_ID) {
2605 $message = '(ROOT_FOLDER) Tree is corrupted! Tried to delete root folder';
2606 $this->log->error($message);
2608 }
2609 }
2610 return true;
2611 }
2612
2622 public function moveTree($a_source_id, $a_target_id, $a_location = self::POS_LAST_NODE)
2623 {
2624 $old_parent_id = $this->getParentId($a_source_id);
2625 $this->getTreeImplementation()->moveTree($a_source_id, $a_target_id, $a_location);
2626 if (isset($GLOBALS['DIC']["ilAppEventHandler"]) && $this->__isMainTree()) {
2627 $GLOBALS['DIC']['ilAppEventHandler']->raise(
2628 "Services/Tree",
2629 "moveTree",
2630 array(
2631 'tree' => $this->table_tree,
2632 'source_id' => $a_source_id,
2633 'target_id' => $a_target_id,
2634 'old_parent_id' => $old_parent_id
2635 )
2636 );
2637 }
2638 return true;
2639 }
2640
2641
2642
2643
2651 public function getRbacSubtreeInfo($a_endnode_id)
2652 {
2653 return $this->getTreeImplementation()->getSubtreeInfo($a_endnode_id);
2654 }
2655
2656
2664 public function getSubTreeQuery($a_node_id, $a_fields = array(), $a_types = '', $a_force_join_reference = false)
2665 {
2666 return $this->getTreeImplementation()->getSubTreeQuery(
2667 $this->getNodeTreeData($a_node_id),
2668 $a_types,
2669 $a_force_join_reference,
2670 $a_fields
2671 );
2672 }
2673
2674
2683 public function getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields = array())
2684 {
2685 global $DIC;
2686
2687 $ilDB = $DIC['ilDB'];
2688
2689 $node = $this->getNodeData($a_node_id);
2690 if (!sizeof($node)) {
2691 return;
2692 }
2693
2694 $res = array();
2695
2696 $query = $this->getTreeImplementation()->getSubTreeQuery($node, '', true, array($this->ref_pk));
2697
2698 $fields = '*';
2699 if (count($a_fields)) {
2700 $fields = implode(',', $a_fields);
2701 }
2702
2703 $query = "SELECT " . $fields .
2704 " FROM " . $this->getTreeTable() .
2705 " " . $this->buildJoin() .
2706 " WHERE " . $this->getTableReference() . "." . $this->ref_pk . " IN (" . $query . ")" .
2707 " AND " . $ilDB->in($this->getObjectDataTable() . "." . $this->obj_pk, $a_obj_ids, "", "integer");
2708 $set = $ilDB->query($query);
2709 while ($row = $ilDB->fetchAssoc($set)) {
2710 $res[] = $row;
2711 }
2712
2713 return $res;
2714 }
2715
2716 public function deleteNode($a_tree_id, $a_node_id)
2717 {
2718 global $DIC;
2719
2720 $ilDB = $DIC['ilDB'];
2721 $ilAppEventHandler = $DIC['ilAppEventHandler'];
2722
2723 $query = 'DELETE FROM tree where ' .
2724 'child = ' . $ilDB->quote($a_node_id, 'integer') . ' ' .
2725 'AND tree = ' . $ilDB->quote($a_tree_id, 'integer');
2726 $ilDB->manipulate($query);
2727
2728 $ilAppEventHandler->raise(
2729 "Services/Tree",
2730 "deleteNode",
2731 array('tree' => $this->table_tree,
2732 'node_id' => $a_node_id,
2733 'tree_id' => $a_tree_id
2734 )
2735 );
2736 }
2737
2744 {
2745 global $DIC;
2746
2747 $ilDB = $DIC['ilDB'];
2748
2749 $query = 'SELECT DISTINCT(o.type) ' . $ilDB->quoteIdentifier('type') . ' FROM tree t JOIN object_reference r ON child = r.ref_id ' .
2750 'JOIN object_data o on r.obj_id = o.obj_id ' .
2751 'WHERE tree < ' . $ilDB->quote(0, 'integer') . ' ' .
2752 'AND child = -tree ' .
2753 'GROUP BY o.type';
2754 $res = $ilDB->query($query);
2755
2756 $types_deleted = array();
2757 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
2758 $types_deleted[] = $row->type;
2759 }
2760 return $types_deleted;
2761 }
2762} // END class.tree
$path
Definition: aliased.php:25
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.
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.
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.
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
$r
Definition: example_031.php:79
if(!array_key_exists('StateId', $_REQUEST)) $id
global $ilBench
Definition: ilias.php:18
Interface ilDBInterface.
catch(Exception $e) $message
$row
$GLOBALS['JPEG_Segment_Names']
Global Variable: XMP_tag_captions.
$query
$type
global $DIC
Definition: saml.php:7
$lng
foreach($_POST as $key=> $value) $res
global $ilDB
$ilUser
Definition: imgupload.php:18
$data
Definition: bench.php:6
$a_type
Definition: workflow.php:92
$rows
Definition: xhr_table.php:10