ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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 var $ilias;
43
44
50 var $log;
51
58
65
72
79
86
93
100
107
132 var $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
147 function ilTree($a_tree_id, $a_root_id = 0)
148 {
149 global $ilDB,$ilErr,$ilias,$ilLog;
150
151 // set db & error handler
152 $this->ilDB = $ilDB;
153
154 if (!isset($ilErr))
155 {
156 $ilErr = new ilErrorHandling();
157 $ilErr->setErrorHandling(PEAR_ERROR_CALLBACK,array($ilErr,'errorHandler'));
158 }
159 else
160 {
161 $this->ilErr = $ilErr;
162 }
163
164 $this->lang_code = "en";
165
166 if (!isset($a_tree_id) or (func_num_args() == 0) )
167 {
168 $this->ilErr->raiseError(get_class($this)."::Constructor(): No tree_id given!",$this->ilErr->WARNING);
169 }
170
171 if (func_num_args() > 2)
172 {
173 $this->ilErr->raiseError(get_class($this)."::Constructor(): Wrong parameter count!",$this->ilErr->WARNING);
174 }
175
176 // CREATE LOGGER INSTANCE
177 $this->log = $ilLog;
178
179 //init variables
180 if (empty($a_root_id))
181 {
182 $a_root_id = ROOT_FOLDER_ID;
183 }
184
185 $this->tree_id = $a_tree_id;
186 $this->root_id = $a_root_id;
187 $this->table_tree = 'tree';
188 $this->table_obj_data = 'object_data';
189 $this->table_obj_reference = 'object_reference';
190 $this->ref_pk = 'ref_id';
191 $this->obj_pk = 'obj_id';
192 $this->tree_pk = 'tree';
193
194 $this->use_cache = true;
195
196 // If cache is activated, cache object translations to improve performance
197 $this->translation_cache = array();
198 $this->parent_type_cache = array();
199
200 // By default, we create gaps in the tree sequence numbering for 50 nodes
201 $this->gap = 50;
202
203
204 // init tree implementation
205 $this->initTreeImplementation();
206 }
207
211 public function initTreeImplementation()
212 {
213 global $ilDB;
214
215
216 if(!is_object($GLOBALS['ilSetting']) or $GLOBALS['ilSetting']->getModule() != 'common')
217 {
218 include_once './Services/Administration/classes/class.ilSetting.php';
219 $setting = new ilSetting('common');
220 }
221 else
222 {
223 $setting = $GLOBALS['ilSetting'];
224 }
225
226 if($this->__isMainTree())
227 {
228 if($setting->get('main_tree_impl','ns') == 'ns')
229 {
230 #$GLOBALS['ilLog']->write(__METHOD__.': Using nested set.');
231 include_once './Services/Tree/classes/class.ilNestedSetTree.php';
232 $this->tree_impl = new ilNestedSetTree($this);
233 }
234 else
235 {
236 #$GLOBALS['ilLog']->write(__METHOD__.': Using materialized path.');
237 include_once './Services/Tree/classes/class.ilMaterializedPathTree.php';
238 $this->tree_impl = new ilMaterializedPathTree($this);
239 }
240 }
241 else
242 {
243 #$GLOBALS['ilLog']->write(__METHOD__.': Using netsted set for non main tree.');
244 include_once './Services/Tree/classes/class.ilNestedSetTree.php';
245 $this->tree_impl = new ilNestedSetTree($this);
246 }
247 }
248
253 public function getTreeImplementation()
254 {
255 return $this->tree_impl;
256 }
257
261 public function useCache($a_use = true)
262 {
263 $this->use_cache = $a_use;
264 }
265
270 public function isCacheUsed()
271 {
272 return $this->__isMainTree() and $this->use_cache;
273 }
274
279 public function getDepthCache()
280 {
281 return (array) $this->depth_cache;
282 }
283
288 public function getParentCache()
289 {
290 return (array) $this->parent_cache;
291 }
292
297 function initLangCode()
298 {
299 global $ilUser;
300
301 // lang_code is only required in $this->fetchnodedata
302 if (!is_object($ilUser))
303 {
304 $this->lang_code = "en";
305 }
306 else
307 {
308 $this->lang_code = $ilUser->getCurrentLanguage();
309 }
310 }
311
316 public function getTreeTable()
317 {
318 return $this->table_tree;
319 }
320
325 public function getObjectDataTable()
326 {
328 }
329
334 public function getTreePk()
335 {
336 return $this->tree_pk;
337 }
338
342 public function getTableReference()
343 {
345 }
346
350 public function getGap()
351 {
352 return $this->gap;
353 }
354
355 /***
356 * reset in tree cache
357 */
358 public function resetInTreeCache()
359 {
360 $this->in_tree_cache = array();
361 }
362
363
378 function setTableNames($a_table_tree,$a_table_obj_data,$a_table_obj_reference = "")
379 {
380 if (!isset($a_table_tree) or !isset($a_table_obj_data))
381 {
382 $this->ilErr->raiseError(get_class($this)."::setTableNames(): Missing parameter! ".
383 "tree table: ".$a_table_tree." object data table: ".$a_table_obj_data,$this->ilErr->WARNING);
384 }
385
386 $this->table_tree = $a_table_tree;
387 $this->table_obj_data = $a_table_obj_data;
388 $this->table_obj_reference = $a_table_obj_reference;
389
390 $this->initTreeImplementation();
391
392 return true;
393 }
394
401 function setReferenceTablePK($a_column_name)
402 {
403 if (!isset($a_column_name))
404 {
405 $this->ilErr->raiseError(get_class($this)."::setReferenceTablePK(): No column name given!",$this->ilErr->WARNING);
406 }
407
408 $this->ref_pk = $a_column_name;
409 return true;
410 }
411
418 function setObjectTablePK($a_column_name)
419 {
420 if (!isset($a_column_name))
421 {
422 $this->ilErr->raiseError(get_class($this)."::setObjectTablePK(): No column name given!",$this->ilErr->WARNING);
423 }
424
425 $this->obj_pk = $a_column_name;
426 return true;
427 }
428
435 function setTreeTablePK($a_column_name)
436 {
437 if (!isset($a_column_name))
438 {
439 $this->ilErr->raiseError(get_class($this)."::setTreeTablePK(): No column name given!",$this->ilErr->WARNING);
440 }
441
442 $this->tree_pk = $a_column_name;
443 return true;
444 }
445
451 function buildJoin()
452 {
453 if ($this->table_obj_reference)
454 {
455 // Use inner join instead of left join to improve performance
456 return "JOIN ".$this->table_obj_reference." ON ".$this->table_tree.".child=".$this->table_obj_reference.".".$this->ref_pk." ".
457 "JOIN ".$this->table_obj_data." ON ".$this->table_obj_reference.".".$this->obj_pk."=".$this->table_obj_data.".".$this->obj_pk." ";
458 }
459 else
460 {
461 // Use inner join instead of left join to improve performance
462 return "JOIN ".$this->table_obj_data." ON ".$this->table_tree.".child=".$this->table_obj_data.".".$this->obj_pk." ";
463 }
464 }
465
471 public function getRelation($a_node_a, $a_node_b)
472 {
473 return $this->getRelationOfNodes(
474 $this->getNodeTreeData($a_node_a),
475 $this->getNodeTreeData($a_node_b)
476 );
477 }
478
485 public function getRelationOfNodes($a_node_a_arr, $a_node_b_arr)
486 {
487 return $this->getTreeImplementation()->getRelation($a_node_a_arr, $a_node_b_arr);
488 }
489
496 public function getChildIds($a_node)
497 {
498 global $ilDB;
499
500 $query = 'SELECT * FROM tree '.
501 'WHERE parent = '.$ilDB->quote($a_node,'integer').' '.
502 'AND tree > '.$ilDB->quote(0,'integer');
503 $res = $ilDB->query($query);
504
505 $childs = array();
506 while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
507 {
508 $childs[] = $row->child;
509 }
510 return $childs;
511 }
512
521 function getChilds($a_node_id, $a_order = "", $a_direction = "ASC")
522 {
523 global $ilBench,$ilDB, $ilObjDataCache, $ilUser;
524
525 if (!isset($a_node_id))
526 {
527 $message = get_class($this)."::getChilds(): No node_id given!";
528 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
529 }
530
531 // init childs
532 $childs = array();
533
534 // number of childs
535 $count = 0;
536
537 // init order_clause
538 $order_clause = "";
539
540 // set order_clause if sort order parameter is given
541 if (!empty($a_order))
542 {
543 $order_clause = "ORDER BY ".$a_order." ".$a_direction;
544 }
545 else
546 {
547 $order_clause = "ORDER BY ".$this->table_tree.".lft";
548 }
549
550
551 $query = sprintf('SELECT * FROM '.$this->table_tree.' '.
552 $this->buildJoin().
553 "WHERE parent = %s " .
554 "AND ".$this->table_tree.".".$this->tree_pk." = %s ".
555 $order_clause,
556 $ilDB->quote($a_node_id,'integer'),
557 $ilDB->quote($this->tree_id,'integer'));
558
559 $res = $ilDB->query($query);
560
561 if(!$count = $res->numRows())
562 {
563 return array();
564 }
565
566 // get rows and object ids
567 $rows = array();
568 while($r = $ilDB->fetchAssoc($res))
569 {
570 $rows[] = $r;
571 $obj_ids[] = $r["obj_id"];
572 }
573
574 // preload object translation information
575 if ($this->__isMainTree() && $this->isCacheUsed() && is_object($ilObjDataCache) &&
576 is_object($ilUser) && $this->lang_code == $ilUser->getLanguage() && !$this->oc_preloaded[$a_node_id])
577 {
578// $ilObjDataCache->preloadTranslations($obj_ids, $this->lang_code);
579 $ilObjDataCache->preloadObjectCache($obj_ids, $this->lang_code);
581 $this->oc_preloaded[$a_node_id] = true;
582 }
583
584 foreach ($rows as $row)
585 {
586 $childs[] = $this->fetchNodeData($row);
587
588 // Update cache of main tree
589 if ($this->__isMainTree())
590 {
591 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child'].' = true');
592 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
593 }
594 }
595 $childs[$count - 1]["last"] = true;
596 return $childs;
597 }
598
608 function getFilteredChilds($a_filter,$a_node,$a_order = "",$a_direction = "ASC")
609 {
610 $childs = $this->getChilds($a_node,$a_order,$a_direction);
611
612 foreach($childs as $child)
613 {
614 if(!in_array($child["type"],$a_filter))
615 {
616 $filtered[] = $child;
617 }
618 }
619 return $filtered ? $filtered : array();
620 }
621
622
630 function getChildsByType($a_node_id,$a_type)
631 {
632 global $ilDB;
633
634 if (!isset($a_node_id) or !isset($a_type))
635 {
636 $message = get_class($this)."::getChildsByType(): Missing parameter! node_id:".$a_node_id." type:".$a_type;
637 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
638 }
639
640 if ($a_type=='rolf' && $this->table_obj_reference) {
641 // Performance optimization: A node can only have exactly one
642 // role folder as its child. Therefore we don't need to sort the
643 // results, and we can let the database know about the expected limit.
644 $ilDB->setLimit(1,0);
645 $query = sprintf("SELECT * FROM ".$this->table_tree." ".
646 $this->buildJoin().
647 "WHERE parent = %s ".
648 "AND ".$this->table_tree.".".$this->tree_pk." = %s ".
649 "AND ".$this->table_obj_data.".type = %s ",
650 $ilDB->quote($a_node_id,'integer'),
651 $ilDB->quote($this->tree_id,'integer'),
652 $ilDB->quote($a_type,'text'));
653 } else {
654 $query = sprintf("SELECT * FROM ".$this->table_tree." ".
655 $this->buildJoin().
656 "WHERE parent = %s ".
657 "AND ".$this->table_tree.".".$this->tree_pk." = %s ".
658 "AND ".$this->table_obj_data.".type = %s ".
659 "ORDER BY ".$this->table_tree.".lft",
660 $ilDB->quote($a_node_id,'integer'),
661 $ilDB->quote($this->tree_id,'integer'),
662 $ilDB->quote($a_type,'text'));
663 }
664 $res = $ilDB->query($query);
665
666 // init childs
667 $childs = array();
668 while($row = $ilDB->fetchAssoc($res))
669 {
670 $childs[] = $this->fetchNodeData($row);
671 }
672
673 return $childs ? $childs : array();
674 }
675
676
684 public function getChildsByTypeFilter($a_node_id,$a_types,$a_order = "",$a_direction = "ASC")
685 {
686 global $ilDB;
687
688 if (!isset($a_node_id) or !$a_types)
689 {
690 $message = get_class($this)."::getChildsByType(): Missing parameter! node_id:".$a_node_id." type:".$a_types;
691 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
692 }
693
694 $filter = ' ';
695 if($a_types)
696 {
697 $filter = 'AND '.$this->table_obj_data.'.type IN('.implode(',',ilUtil::quoteArray($a_types)).') ';
698 }
699
700 // set order_clause if sort order parameter is given
701 if (!empty($a_order))
702 {
703 $order_clause = "ORDER BY ".$a_order." ".$a_direction;
704 }
705 else
706 {
707 $order_clause = "ORDER BY ".$this->table_tree.".lft";
708 }
709
710 $query = 'SELECT * FROM '.$this->table_tree.' '.
711 $this->buildJoin().
712 'WHERE parent = '.$ilDB->quote($a_node_id,'integer').' '.
713 'AND '.$this->table_tree.'.'.$this->tree_pk.' = '.$ilDB->quote($this->tree_id,'integer').' '.
714 $filter.
715 $order_clause;
716
717 $res = $ilDB->query($query);
718 while($row = $ilDB->fetchAssoc($res))
719 {
720 $childs[] = $this->fetchNodeData($row);
721 }
722
723 return $childs ? $childs : array();
724 }
725
735 public function insertNodeFromTrash($a_source_id, $a_target_id, $a_tree_id, $a_pos = IL_LAST_NODE, $a_reset_deleted_date = false)
736 {
737 global $ilDB;
738
739 if($this->__isMainTree())
740 {
741 if($a_source_id <= 1 or $a_target_id <= 0)
742 {
744 throw new InvalidArgumentException('Invalid parameter given for ilTree::insertNodeFromTrash');
745 }
746 }
747 if (!isset($a_source_id) or !isset($a_target_id))
748 {
750 throw new InvalidArgumentException('Missing parameter for ilTree::insertNodeFromTrash');
751 }
752 if($this->isInTree($a_source_id))
753 {
754 ilLoggerFactory::getLogger('tree')->error('Node already in tree');
756 throw new InvalidArgumentException('Node already in tree.');
757 }
758
759 $query = 'DELETE from tree '.
760 'WHERE tree = '.$ilDB->quote($a_tree_id,'integer').' '.
761 'AND child = '.$ilDB->quote($a_source_id,'integer');
762 $ilDB->manipulate($query);
763
764 $this->insertNode($a_source_id, $a_target_id, IL_LAST_NODE, $a_reset_deleted_date);
765 }
766
767
775 public function insertNode($a_node_id, $a_parent_id, $a_pos = IL_LAST_NODE, $a_reset_deletion_date = false)
776 {
777 global $ilDB;
778
779//echo "+$a_node_id+$a_parent_id+";
780 // CHECK node_id and parent_id > 0 if in main tree
781 if($this->__isMainTree())
782 {
783 if($a_node_id <= 1 or $a_parent_id <= 0)
784 {
785 $GLOBALS['ilLog']->logStack();
786 $message = sprintf('%s::insertNode(): Invalid parameters! $a_node_id: %s $a_parent_id: %s',
787 get_class($this),
788 $a_node_id,
789 $a_parent_id);
790 $this->log->write($message,$this->log->FATAL);
791 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
792 }
793 }
794
795
796 if (!isset($a_node_id) or !isset($a_parent_id))
797 {
798 $GLOBALS['ilLog']->logStack();
799 $this->ilErr->raiseError(get_class($this)."::insertNode(): Missing parameter! ".
800 "node_id: ".$a_node_id." parent_id: ".$a_parent_id,$this->ilErr->WARNING);
801 }
802 if ($this->isInTree($a_node_id))
803 {
804 $this->ilErr->raiseError(get_class($this)."::insertNode(): Node ".$a_node_id." already in tree ".
805 $this->table_tree."!",$this->ilErr->WARNING);
806 }
807
808 $this->getTreeImplementation()->insertNode($a_node_id, $a_parent_id, $a_pos);
809
810 $this->in_tree_cache[$a_node_id] = true;
811
812 // reset deletion date
813 if ($a_reset_deletion_date)
814 {
815 ilObject::_resetDeletedDate($a_node_id);
816 }
817
818 if (isset($GLOBALS["ilAppEventHandler"]) && $this->__isMainTree()) {
819 $GLOBALS['ilAppEventHandler']->raise(
820 "Services/Tree",
821 "insertNode",
822 array(
823 'tree' => $this->table_tree,
824 'node_id' => $a_node_id,
825 'parent_id' => $a_parent_id)
826 );
827 }
828 }
829
842 public function getFilteredSubTree($a_node_id,$a_filter = array())
843 {
844 $node = $this->getNodeData($a_node_id);
845
846 $first = true;
847 $depth = 0;
848 foreach($this->getSubTree($node) as $subnode)
849 {
850 if($depth and $subnode['depth'] > $depth)
851 {
852 continue;
853 }
854 if(!$first and in_array($subnode['type'],$a_filter))
855 {
856 $depth = $subnode['depth'];
857 $first = false;
858 continue;
859 }
860 $depth = 0;
861 $first = false;
862 $filtered[] = $subnode;
863 }
864 return $filtered ? $filtered : array();
865 }
866
872 public function getSubTreeIds($a_ref_id)
873 {
874 return $this->getTreeImplementation()->getSubTreeIds($a_ref_id);
875 }
876
877
887 function getSubTree($a_node,$a_with_data = true, $a_type = "")
888 {
889 global $ilDB;
890
891 if (!is_array($a_node))
892 {
893 $GLOBALS['ilLog']->logStack();
894 throw new InvalidArgumentException(__METHOD__.': wrong datatype for node data given');
895 }
896
897 /*
898 if($a_node['lft'] < 1 or $a_node['rgt'] < 2)
899 {
900 $GLOBALS['ilLog']->logStack();
901 $message = sprintf('%s: Invalid node given! $a_node["lft"]: %s $a_node["rgt"]: %s',
902 __METHOD__,
903 $a_node['lft'],
904 $a_node['rgt']);
905
906 throw new InvalidArgumentException($message);
907 }
908 */
909
910 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, $a_type);
911 $res = $ilDB->query($query);
912 while($row = $ilDB->fetchAssoc($res))
913 {
914 if($a_with_data)
915 {
916 $subtree[] = $this->fetchNodeData($row);
917 }
918 else
919 {
920 $subtree[] = $row['child'];
921 }
922 // the lm_data "hack" should be removed in the trunk during an alpha
923 if($this->__isMainTree() || $this->table_tree == "lm_tree")
924 {
925 $this->in_tree_cache[$row['child']] = true;
926 }
927 }
928 return $subtree ? $subtree : array();
929 }
930
939 function getSubTreeTypes($a_node,$a_filter = 0)
940 {
941 $a_filter = $a_filter ? $a_filter : array();
942
943 foreach($this->getSubtree($this->getNodeData($a_node)) as $node)
944 {
945 if(in_array($node["type"],$a_filter))
946 {
947 continue;
948 }
949 $types["$node[type]"] = $node["type"];
950 }
951 return $types ? $types : array();
952 }
953
960 function deleteTree($a_node)
961 {
962 global $ilDB;
963
964 $GLOBALS['ilLog']->write(__METHOD__.': Delete tree with node '. $a_node);
965
966 if (!is_array($a_node))
967 {
968 $GLOBALS['ilLog']->logStack();
969 throw new InvalidArgumentException(__METHOD__.': Wrong datatype for node data!');
970 }
971
972 $GLOBALS['ilLog']->write(__METHOD__.': '. $this->tree_pk);
973
974 if($this->__isMainTree() )
975 {
976 // @todo normally this part is not executed, since the subtree is first
977 // moved to trash and then deleted.
978 if(!$this->__checkDelete($a_node))
979 {
980 $GLOBALS['ilLog']->logStack();
981 throw new ilInvalidTreeStructureException('Deletion canceled due to invalid tree structure.' . print_r($a_node,true));
982 }
983 }
984
985 $this->getTreeImplementation()->deleteTree($a_node['child']);
986
987 $this->resetInTreeCache();
988
989 }
990
995 public function validateParentRelations()
996 {
997 return $this->getTreeImplementation()->validateParentRelations();
998 }
999
1010 function getPathFull($a_endnode_id, $a_startnode_id = 0)
1011 {
1012 $pathIds =& $this->getPathId($a_endnode_id, $a_startnode_id);
1013
1014 // We retrieve the full path in a single query to improve performance
1015 global $ilDB;
1016
1017 // Abort if no path ids were found
1018 if (count($pathIds) == 0)
1019 {
1020 return null;
1021 }
1022
1023 $inClause = 'child IN (';
1024 for ($i=0; $i < count($pathIds); $i++)
1025 {
1026 if ($i > 0) $inClause .= ',';
1027 $inClause .= $ilDB->quote($pathIds[$i],'integer');
1028 }
1029 $inClause .= ')';
1030
1031 $q = 'SELECT * '.
1032 'FROM '.$this->table_tree.' '.
1033 $this->buildJoin().' '.
1034 'WHERE '.$inClause.' '.
1035 'AND '.$this->table_tree.'.'.$this->tree_pk.' = '.$this->ilDB->quote($this->tree_id,'integer').' '.
1036 'ORDER BY depth';
1037 $r = $ilDB->query($q);
1038
1039 $pathFull = array();
1040 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
1041 {
1042 $pathFull[] = $this->fetchNodeData($row);
1043
1044 // Update cache
1045 if ($this->__isMainTree())
1046 {
1047 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child']);
1048 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
1049 }
1050 }
1051 return $pathFull;
1052 }
1053
1054
1061 function preloadDepthParent($a_node_ids)
1062 {
1063 global $ilDB;
1064
1065 if (!$this->__isMainTree() || !is_array($a_node_ids) || !$this->isCacheUsed())
1066 {
1067 return;
1068 }
1069
1070 $res = $ilDB->query('SELECT t.depth, t.parent, t.child '.
1071 'FROM '.$this->table_tree.' t '.
1072 'WHERE '.$ilDB->in("child", $a_node_ids, false, "integer").
1073 'AND '.$this->tree_pk.' = '.$ilDB->quote($this->tree_id, "integer"));
1074 while ($row = $ilDB->fetchAssoc($res))
1075 {
1076 $this->depth_cache[$row["child"]] = $row["depth"];
1077 $this->parent_cache[$row["child"]] = $row["parent"];
1078 }
1079 }
1080
1090 public function getPathId($a_endnode_id, $a_startnode_id = 0)
1091 {
1092 if(!$a_endnode_id)
1093 {
1094 $GLOBALS['ilLog']->logStack();
1095 throw new InvalidArgumentException(__METHOD__.': No endnode given!');
1096 }
1097
1098 // path id cache
1099 if ($this->isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id]))
1100 {
1101//echo "<br>getPathIdhit";
1102 return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
1103 }
1104//echo "<br>miss";
1105
1106 $pathIds = $this->getTreeImplementation()->getPathIds($a_endnode_id, $a_startnode_id);
1107
1108 if($this->__isMainTree())
1109 {
1110 $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
1111 }
1112 return $pathIds;
1113 }
1114
1115 // BEGIN WebDAV: getNodePathForTitlePath function added
1133 function getNodePathForTitlePath($titlePath, $a_startnode_id = null)
1134 {
1135 global $ilDB, $log;
1136 //$log->write('getNodePathForTitlePath('.implode('/',$titlePath));
1137
1138 // handle empty title path
1139 if ($titlePath == null || count($titlePath) == 0)
1140 {
1141 if ($a_startnode_id == 0)
1142 {
1143 return null;
1144 }
1145 else
1146 {
1147 return $this->getNodePath($a_startnode_id);
1148 }
1149 }
1150
1151 // fetch the node path up to the startnode
1152 if ($a_startnode_id != null && $a_startnode_id != 0)
1153 {
1154 // Start using the node path to the root of the relative path
1155 $nodePath = $this->getNodePath($a_startnode_id);
1156 $parent = $a_startnode_id;
1157 }
1158 else
1159 {
1160 // Start using the root of the tree
1161 $nodePath = array();
1162 $parent = 0;
1163 }
1164
1165
1166 // Convert title path into Unicode Normal Form C
1167 // This is needed to ensure that we can compare title path strings with
1168 // strings from the database.
1169 require_once('include/Unicode/UtfNormal.php');
1170 include_once './Services/Utilities/classes/class.ilStr.php';
1171 $inClause = 'd.title IN (';
1172 for ($i=0; $i < count($titlePath); $i++)
1173 {
1174 $titlePath[$i] = ilStr::strToLower(UtfNormal::toNFC($titlePath[$i]));
1175 if ($i > 0) $inClause .= ',';
1176 $inClause .= $ilDB->quote($titlePath[$i],'text');
1177 }
1178 $inClause .= ')';
1179
1180 // Fetch all rows that are potential path elements
1181 if ($this->table_obj_reference)
1182 {
1183 $joinClause = 'JOIN '.$this->table_obj_reference.' r ON t.child = r.'.$this->ref_pk.' '.
1184 'JOIN '.$this->table_obj_data.' d ON r.'.$this->obj_pk.' = d.'.$this->obj_pk;
1185 }
1186 else
1187 {
1188 $joinClause = 'JOIN '.$this->table_obj_data.' d ON t.child = d.'.$this->obj_pk;
1189 }
1190 // The ORDER BY clause in the following SQL statement ensures that,
1191 // in case of a multiple objects with the same title, always the Object
1192 // with the oldest ref_id is chosen.
1193 // This ensure, that, if a new object with the same title is added,
1194 // WebDAV clients can still work with the older object.
1195 $q = 'SELECT t.depth, t.parent, t.child, d.'.$this->obj_pk.' obj_id, d.type, d.title '.
1196 'FROM '.$this->table_tree.' t '.
1197 $joinClause.' '.
1198 'WHERE '.$inClause.' '.
1199 'AND t.depth <= '.(count($titlePath)+count($nodePath)).' '.
1200 'AND t.tree = 1 '.
1201 'ORDER BY t.depth, t.child ASC';
1202 $r = $ilDB->query($q);
1203
1204 $rows = array();
1205 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
1206 {
1207 $row['title'] = UtfNormal::toNFC($row['title']);
1208 $row['ref_id'] = $row['child'];
1209 $rows[] = $row;
1210 }
1211
1212 // Extract the path elements from the fetched rows
1213 for ($i = 0; $i < count($titlePath); $i++) {
1214 $pathElementFound = false;
1215 foreach ($rows as $row) {
1216 if ($row['parent'] == $parent &&
1217 ilStr::strToLower($row['title']) == $titlePath[$i])
1218 {
1219 // FIXME - We should test here, if the user has
1220 // 'visible' permission for the object.
1221 $nodePath[] = $row;
1222 $parent = $row['child'];
1223 $pathElementFound = true;
1224 break;
1225 }
1226 }
1227 // Abort if we haven't found a path element for the current depth
1228 if (! $pathElementFound)
1229 {
1230 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):null');
1231 return null;
1232 }
1233 }
1234 // Return the node path
1235 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):'.var_export($nodePath,true));
1236 return $nodePath;
1237 }
1238 // END WebDAV: getNodePathForTitlePath function added
1239 // END WebDAV: getNodePath function added
1256 function getNodePath($a_endnode_id, $a_startnode_id = 0)
1257 {
1258 global $ilDB;
1259
1260 $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
1261
1262 // Abort if no path ids were found
1263 if (count($pathIds) == 0)
1264 {
1265 return null;
1266 }
1267
1268
1269 $types = array();
1270 $data = array();
1271 for ($i = 0; $i < count($pathIds); $i++)
1272 {
1273 $types[] = 'integer';
1274 $data[] = $pathIds[$i];
1275 }
1276
1277 $query = 'SELECT t.depth,t.parent,t.child,d.obj_id,d.type,d.title '.
1278 'FROM '.$this->table_tree.' t '.
1279 'JOIN '.$this->table_obj_reference.' r ON r.ref_id = t.child '.
1280 'JOIN '.$this->table_obj_data.' d ON d.obj_id = r.obj_id '.
1281 'WHERE '.$ilDB->in('t.child',$data,false,'integer').' '.
1282 'ORDER BY t.depth ';
1283
1284 $res = $ilDB->queryF($query,$types,$data);
1285
1286 $titlePath = array();
1287 while ($row = $ilDB->fetchAssoc($res))
1288 {
1289 $titlePath[] = $row;
1290 }
1291 return $titlePath;
1292 }
1293 // END WebDAV: getNodePath function added
1294
1301 function checkTree()
1302 {
1303 global $ilDB;
1304
1305 $types = array('integer');
1306 $query = 'SELECT lft,rgt FROM '.$this->table_tree.' '.
1307 'WHERE '.$this->tree_pk.' = %s ';
1308
1309 $res = $ilDB->queryF($query,$types,array($this->tree_id));
1310 while ($row = $ilDB->fetchObject($res))
1311 {
1312 $lft[] = $row->lft;
1313 $rgt[] = $row->rgt;
1314 }
1315
1316 $all = array_merge($lft,$rgt);
1317 $uni = array_unique($all);
1318
1319 if (count($all) != count($uni))
1320 {
1321 $message = sprintf('%s::checkTree(): Tree is corrupted!',
1322 get_class($this));
1323
1324 $this->log->write($message,$this->log->FATAL);
1325 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
1326 }
1327
1328 return true;
1329 }
1330
1334 function checkTreeChilds($a_no_zero_child = true)
1335 {
1336 global $ilDB;
1337
1338 $query = 'SELECT * FROM '.$this->table_tree.' '.
1339 'WHERE '.$this->tree_pk.' = %s '.
1340 'ORDER BY lft';
1341 $r1 = $ilDB->queryF($query,array('integer'),array($this->tree_id));
1342
1343 while ($row = $ilDB->fetchAssoc($r1))
1344 {
1345//echo "tree:".$row[$this->tree_pk].":lft:".$row["lft"].":rgt:".$row["rgt"].":child:".$row["child"].":<br>";
1346 if (($row["child"] == 0) && $a_no_zero_child)
1347 {
1348 $this->ilErr->raiseError(get_class($this)."::checkTreeChilds(): Tree contains child with ID 0!",$this->ilErr->WARNING);
1349 }
1350
1351 if ($this->table_obj_reference)
1352 {
1353 // get object reference data
1354 $query = 'SELECT * FROM '.$this->table_obj_reference.' WHERE '.$this->ref_pk.' = %s ';
1355 $r2 = $ilDB->queryF($query,array('integer'),array($row['child']));
1356
1357//echo "num_childs:".$r2->numRows().":<br>";
1358 if ($r2->numRows() == 0)
1359 {
1360 $this->ilErr->raiseError(get_class($this)."::checkTree(): No Object-to-Reference entry found for ID ".
1361 $row["child"]."!",$this->ilErr->WARNING);
1362 }
1363 if ($r2->numRows() > 1)
1364 {
1365 $this->ilErr->raiseError(get_class($this)."::checkTree(): More Object-to-Reference entries found for ID ".
1366 $row["child"]."!",$this->ilErr->WARNING);
1367 }
1368
1369 // get object data
1370 $obj_ref = $ilDB->fetchAssoc($r2);
1371
1372 $query = 'SELECT * FROM '.$this->table_obj_data.' WHERE '.$this->obj_pk.' = %s';
1373 $r3 = $ilDB->queryF($query,array('integer'),array($obj_ref[$this->obj_pk]));
1374 if ($r3->numRows() == 0)
1375 {
1376 $this->ilErr->raiseError(get_class($this)."::checkTree(): No child found for ID ".
1377 $obj_ref[$this->obj_pk]."!",$this->ilErr->WARNING);
1378 }
1379 if ($r3->numRows() > 1)
1380 {
1381 $this->ilErr->raiseError(get_class($this)."::checkTree(): More childs found for ID ".
1382 $obj_ref[$this->obj_pk]."!",$this->ilErr->WARNING);
1383 }
1384
1385 }
1386 else
1387 {
1388 // get only object data
1389 $query = 'SELECT * FROM '.$this->table_obj_data.' WHERE '.$this->obj_pk.' = %s';
1390 $r2 = $ilDB->queryF($query,array('integer'),array($row['child']));
1391//echo "num_childs:".$r2->numRows().":<br>";
1392 if ($r2->numRows() == 0)
1393 {
1394 $this->ilErr->raiseError(get_class($this)."::checkTree(): No child found for ID ".
1395 $row["child"]."!",$this->ilErr->WARNING);
1396 }
1397 if ($r2->numRows() > 1)
1398 {
1399 $this->ilErr->raiseError(get_class($this)."::checkTree(): More childs found for ID ".
1400 $row["child"]."!",$this->ilErr->WARNING);
1401 }
1402 }
1403 }
1404
1405 return true;
1406 }
1407
1413 public function getMaximumDepth()
1414 {
1415 global $ilDB;
1416
1417 $query = 'SELECT MAX(depth) depth FROM '.$this->table_tree;
1418 $res = $ilDB->query($query);
1419
1420 $row = $ilDB->fetchAssoc($res);
1421 return $row['depth'];
1422 }
1423
1430 function getDepth($a_node_id)
1431 {
1432 global $ilDB;
1433
1434 if ($a_node_id)
1435 {
1436 $query = 'SELECT depth FROM '.$this->table_tree.' '.
1437 'WHERE child = %s '.
1438 'AND '.$this->tree_pk.' = %s ';
1439 $res = $ilDB->queryF($query,array('integer','integer'),array($a_node_id,$this->tree_id));
1440 $row = $ilDB->fetchObject($res);
1441
1442 return $row->depth;
1443 }
1444 else
1445 {
1446 return 1;
1447 }
1448 }
1449
1457 public function getNodeTreeData($a_node_id)
1458 {
1459 global $ilDB;
1460
1461 if(!$a_node_id)
1462 {
1463 $GLOBALS['ilLog']->logStack();
1464 throw new InvalidArgumentException('Missing or empty parameter $a_node_id: '. $a_node_id);
1465 }
1466
1467 $query = 'SELECT * FROM '.$this->table_tree.' '.
1468 'WHERE child = '.$ilDB->quote($a_node_id,'integer');
1469 $res = $ilDB->query($query);
1470 while($row = $res->fetchRow(DB_FETCHMODE_ASSOC))
1471 {
1472 return $row;
1473 }
1474 return array();
1475 }
1476
1477
1485 // BEGIN WebDAV: Pass tree id to this method
1486 //function getNodeData($a_node_id)
1487 function getNodeData($a_node_id, $a_tree_pk = null)
1488 // END PATCH WebDAV: Pass tree id to this method
1489 {
1490 global $ilDB;
1491
1492 if (!isset($a_node_id))
1493 {
1494 $GLOBALS['ilLog']->logStack();
1495 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1496 }
1497 if($this->__isMainTree())
1498 {
1499 if($a_node_id < 1)
1500 {
1501 $message = sprintf('%s::getNodeData(): No valid parameter given! $a_node_id: %s',
1502 get_class($this),
1503 $a_node_id);
1504
1505 $this->log->write($message,$this->log->FATAL);
1506 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
1507 }
1508 }
1509
1510 // BEGIN WebDAV: Pass tree id to this method
1511 $query = 'SELECT * FROM '.$this->table_tree.' '.
1512 $this->buildJoin().
1513 'WHERE '.$this->table_tree.'.child = %s '.
1514 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
1515 $res = $ilDB->queryF($query,array('integer','integer'),array(
1516 $a_node_id,
1517 $a_tree_pk === null ? $this->tree_id : $a_tree_pk));
1518 // END WebDAV: Pass tree id to this method
1519 $row = $ilDB->fetchAssoc($res);
1521
1522 return $this->fetchNodeData($row);
1523 }
1524
1532 function fetchNodeData($a_row)
1533 {
1534 global $objDefinition, $lng, $ilBench,$ilDB;
1535
1536 //$ilBench->start("Tree", "fetchNodeData_getRow");
1537 $data = $a_row;
1538 $data["desc"] = $a_row["description"]; // for compability
1539 //$ilBench->stop("Tree", "fetchNodeData_getRow");
1540
1541 // multilingual support systemobjects (sys) & categories (db)
1542 //$ilBench->start("Tree", "fetchNodeData_readDefinition");
1543 if (is_object($objDefinition))
1544 {
1545 $translation_type = $objDefinition->getTranslationType($data["type"]);
1546 }
1547 //$ilBench->stop("Tree", "fetchNodeData_readDefinition");
1548
1549 if ($translation_type == "sys")
1550 {
1551 //$ilBench->start("Tree", "fetchNodeData_getLangData");
1552 if ($data["type"] == "rolf" and $data["obj_id"] != ROLE_FOLDER_ID)
1553 {
1554 $data["description"] = $lng->txt("obj_".$data["type"]."_local_desc").$data["title"].$data["desc"];
1555 $data["desc"] = $lng->txt("obj_".$data["type"]."_local_desc").$data["title"].$data["desc"];
1556 $data["title"] = $lng->txt("obj_".$data["type"]."_local");
1557 }
1558 else
1559 {
1560 $data["title"] = $lng->txt("obj_".$data["type"]);
1561 $data["description"] = $lng->txt("obj_".$data["type"]."_desc");
1562 $data["desc"] = $lng->txt("obj_".$data["type"]."_desc");
1563 }
1564 //$ilBench->stop("Tree", "fetchNodeData_getLangData");
1565 }
1566 elseif ($translation_type == "db")
1567 {
1568
1569 // Try to retrieve object translation from cache
1570 if ($this->isCacheUsed() &&
1571 array_key_exists($data["obj_id"].'.'.$lang_code, $this->translation_cache)) {
1572
1573 $key = $data["obj_id"].'.'.$lang_code;
1574 $data["title"] = $this->translation_cache[$key]['title'];
1575 $data["description"] = $this->translation_cache[$key]['description'];
1576 $data["desc"] = $this->translation_cache[$key]['desc'];
1577 }
1578 else
1579 {
1580 // Object translation is not in cache, read it from database
1581 //$ilBench->start("Tree", "fetchNodeData_getTranslation");
1582 $query = 'SELECT title,description FROM object_translation '.
1583 'WHERE obj_id = %s '.
1584 'AND lang_code = %s '.
1585 'AND NOT lang_default = %s';
1586
1587 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
1588 $data['obj_id'],
1589 $this->lang_code,
1590 1));
1591 $row = $ilDB->fetchObject($res);
1592
1593 if ($row)
1594 {
1595 $data["title"] = $row->title;
1596 $data["description"] = ilUtil::shortenText($row->description,ilObject::DESC_LENGTH,true);
1597 $data["desc"] = $row->description;
1598 }
1599 //$ilBench->stop("Tree", "fetchNodeData_getTranslation");
1600
1601 // Store up to 1000 object translations in cache
1602 if ($this->isCacheUsed() && count($this->translation_cache) < 1000)
1603 {
1604 $key = $data["obj_id"].'.'.$lang_code;
1605 $this->translation_cache[$key] = array();
1606 $this->translation_cache[$key]['title'] = $data["title"] ;
1607 $this->translation_cache[$key]['description'] = $data["description"];
1608 $this->translation_cache[$key]['desc'] = $data["desc"];
1609 }
1610 }
1611 }
1612
1613 // TODO: Handle this switch by module.xml definitions
1614 if($data['type'] == 'crsr' or $data['type'] == 'catr')
1615 {
1616 include_once('./Services/ContainerReference/classes/class.ilContainerReference.php');
1617 $data['title'] = ilContainerReference::_lookupTitle($data['obj_id']);
1618 }
1619
1620 return $data ? $data : array();
1621 }
1622
1628 protected function fetchTranslationFromObjectDataCache($a_obj_ids)
1629 {
1630 global $ilObjDataCache;
1631
1632 if ($this->isCacheUsed() && is_array($a_obj_ids) && is_object($ilObjDataCache))
1633 {
1634 foreach ($a_obj_ids as $id)
1635 {
1636 $this->translation_cache[$id.'.']['title'] = $ilObjDataCache->lookupTitle($id);
1637 $this->translation_cache[$id.'.']['description'] = $ilObjDataCache->lookupDescription($id);;
1638 $this->translation_cache[$id.'.']['desc'] =
1639 $this->translation_cache[$id.'.']['description'];
1640 }
1641 }
1642 }
1643
1644
1652 function isInTree($a_node_id)
1653 {
1654 global $ilDB;
1655
1656 if (!isset($a_node_id))
1657 {
1658 return false;
1659 #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1660 }
1661 // is in tree cache
1662 if ($this->isCacheUsed() && isset($this->in_tree_cache[$a_node_id]))
1663 {
1664 #$GLOBALS['ilLog']->write(__METHOD__.': Using in tree cache '.$a_node_id);
1665//echo "<br>in_tree_hit";
1666 return $this->in_tree_cache[$a_node_id];
1667 }
1668
1669 $query = 'SELECT * FROM '.$this->table_tree.' '.
1670 'WHERE '.$this->table_tree.'.child = %s '.
1671 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s';
1672
1673 $res = $ilDB->queryF($query,array('integer','integer'),array(
1674 $a_node_id,
1675 $this->tree_id));
1676
1677 if ($res->numRows() > 0)
1678 {
1679 if($this->__isMainTree())
1680 {
1681 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = true');
1682 $this->in_tree_cache[$a_node_id] = true;
1683 }
1684 return true;
1685 }
1686 else
1687 {
1688 if($this->__isMainTree())
1689 {
1690 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = false');
1691 $this->in_tree_cache[$a_node_id] = false;
1692 }
1693 return false;
1694 }
1695 }
1696
1704 public function getParentNodeData($a_node_id)
1705 {
1706 global $ilDB;
1707 global $ilLog;
1708
1709 if (!isset($a_node_id))
1710 {
1711 $ilLog->logStack();
1712 throw new InvalidArgumentException(__METHOD__.': No node_id given!');
1713 }
1714
1715 if ($this->table_obj_reference)
1716 {
1717 // Use inner join instead of left join to improve performance
1718 $innerjoin = "JOIN ".$this->table_obj_reference." ON v.child=".$this->table_obj_reference.".".$this->ref_pk." ".
1719 "JOIN ".$this->table_obj_data." ON ".$this->table_obj_reference.".".$this->obj_pk."=".$this->table_obj_data.".".$this->obj_pk." ";
1720 }
1721 else
1722 {
1723 // Use inner join instead of left join to improve performance
1724 $innerjoin = "JOIN ".$this->table_obj_data." ON v.child=".$this->table_obj_data.".".$this->obj_pk." ";
1725 }
1726
1727 $query = 'SELECT * FROM '.$this->table_tree.' s, '.$this->table_tree.' v '.
1728 $innerjoin.
1729 'WHERE s.child = %s '.
1730 'AND s.parent = v.child '.
1731 'AND s.'.$this->tree_pk.' = %s '.
1732 'AND v.'.$this->tree_pk.' = %s';
1733 $res = $ilDB->queryF($query,array('integer','integer','integer'),array(
1734 $a_node_id,
1735 $this->tree_id,
1736 $this->tree_id));
1737 $row = $ilDB->fetchAssoc($res);
1738 return $this->fetchNodeData($row);
1739 }
1740
1748 public function isGrandChild($a_startnode_id,$a_querynode_id)
1749 {
1750 return $this->getRelation($a_startnode_id, $a_querynode_id) == self::RELATION_PARENT;
1751 }
1752
1761 function addTree($a_tree_id,$a_node_id = -1)
1762 {
1763 global $ilDB;
1764
1765 // FOR SECURITY addTree() IS NOT ALLOWED ON MAIN TREE
1766 if($this->__isMainTree())
1767 {
1768 $message = sprintf('%s::addTree(): Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
1769 get_class($this),
1770 $a_tree_id,
1771 $a_node_id);
1772 $this->log->write($message,$this->log->FATAL);
1773 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
1774 }
1775
1776 if (!isset($a_tree_id))
1777 {
1778 $this->ilErr->raiseError(get_class($this)."::addTree(): No tree_id given! ",$this->ilErr->WARNING);
1779 }
1780
1781 if ($a_node_id <= 0)
1782 {
1783 $a_node_id = $a_tree_id;
1784 }
1785
1786 $query = 'INSERT INTO '.$this->table_tree.' ('.
1787 $this->tree_pk.', child,parent,lft,rgt,depth) '.
1788 'VALUES '.
1789 '(%s,%s,%s,%s,%s,%s)';
1790 $res = $ilDB->manipulateF($query,array('integer','integer','integer','integer','integer','integer'),array(
1791 $a_tree_id,
1792 $a_node_id,
1793 0,
1794 1,
1795 2,
1796 1));
1797
1798 return true;
1799 }
1800
1809 public function getNodeDataByType($a_type)
1810 {
1811 global $ilDB;
1812
1813 if(!isset($a_type) or (!is_string($a_type)))
1814 {
1815 $GLOBALS['ilLog']->logStack();
1816 throw new InvalidArgumentException('Type not given or wrong datatype');
1817 }
1818
1819 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1820 $this->buildJoin().
1821 'WHERE ' . $this->table_obj_data . '.type = ' . $this->ilDB->quote($a_type, 'text').
1822 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer');
1823
1824 $res = $ilDB->query($query);
1825 $data = array();
1826 while($row = $ilDB->fetchAssoc($res))
1827 {
1828 $data[] = $this->fetchNodeData($row);
1829 }
1830
1831 return $data;
1832 }
1833
1841 public function removeTree($a_tree_id)
1842 {
1843 global $ilDB;
1844
1845 // OPERATION NOT ALLOWED ON MAIN TREE
1846 if($this->__isMainTree())
1847 {
1848 $GLOBALS['ilLog']->logStack();
1849 throw new InvalidArgumentException('Operation not allowed on main tree');
1850 }
1851 if (!$a_tree_id)
1852 {
1853 $GLOBALS['ilLog']->logStack();
1854 throw new InvalidArgumentException('Missing parameter tree id');
1855 }
1856
1857 $query = 'DELETE FROM '.$this->table_tree.
1858 ' WHERE '.$this->tree_pk.' = %s ';
1859 $ilDB->manipulateF($query,array('integer'),array($a_tree_id));
1860 return true;
1861 }
1862
1869 public function moveToTrash($a_node_id, $a_set_deleted = false)
1870 {
1871 return $this->saveSubTree($a_node_id, $a_set_deleted);
1872 }
1873
1884 public function saveSubTree($a_node_id, $a_set_deleted = false)
1885 {
1886 global $ilDB;
1887
1888 if(!$a_node_id)
1889 {
1890 $GLOBALS['ilLog']->logStack();
1891 throw new InvalidArgumentException('No valid parameter given! $a_node_id: '.$a_node_id);
1892 }
1893
1894 // LOCKED ###############################################
1895 if($this->__isMainTree())
1896 {
1897 $ilDB->lockTables(
1898 array(
1899 0 => array('name' => 'tree', 'type' => ilDB::LOCK_WRITE),
1900 1 => array('name' => 'object_reference', 'type' => ilDB::LOCK_WRITE)));
1901 }
1902
1903 $query = $this->getTreeImplementation()->getSubTreeQuery($this->getNodeTreeData($a_node_id),'',false);
1904 $res = $ilDB->query($query);
1905
1906 $subnodes = array();
1907 while($row = $res->fetchRow(DB_FETCHMODE_ASSOC))
1908 {
1909 $subnodes[] = $row['child'];
1910 }
1911
1912 if(!count($subnodes))
1913 {
1914 // Possibly already deleted
1915 // Unlock locked tables before returning
1916 if($this->__isMainTree())
1917 {
1918 $ilDB->unlockTables();
1919 }
1920 return false;
1921 }
1922
1923 if($a_set_deleted)
1924 {
1925 include_once './Services/Object/classes/class.ilObject.php';
1926 ilObject::setDeletedDates($subnodes);
1927 }
1928
1929 // netsted set <=> mp
1930 $this->getTreeImplementation()->moveToTrash($a_node_id);
1931
1932 if($this->__isMainTree())
1933 {
1934 $ilDB->unlockTables();
1935 }
1936
1937 // LOCKED ###############################################
1938 return true;
1939 }
1940
1945 public function isDeleted($a_node_id)
1946 {
1947 return $this->isSaved($a_node_id);
1948 }
1949
1955 public function isSaved($a_node_id)
1956 {
1957 global $ilDB;
1958
1959 // is saved cache
1960 if ($this->isCacheUsed() && isset($this->is_saved_cache[$a_node_id]))
1961 {
1962//echo "<br>issavedhit";
1963 return $this->is_saved_cache[$a_node_id];
1964 }
1965
1966 $query = 'SELECT '.$this->tree_pk.' FROM '.$this->table_tree.' '.
1967 'WHERE child = %s ';
1968 $res = $ilDB->queryF($query,array('integer'),array($a_node_id));
1969 $row = $ilDB->fetchAssoc($res);
1970
1971 if ($row[$this->tree_pk] < 0)
1972 {
1973 if($this->__isMainTree())
1974 {
1975 $this->is_saved_cache[$a_node_id] = true;
1976 }
1977 return true;
1978 }
1979 else
1980 {
1981 if($this->__isMainTree())
1982 {
1983 $this->is_saved_cache[$a_node_id] = false;
1984 }
1985 return false;
1986 }
1987 }
1988
1995 public function preloadDeleted($a_node_ids)
1996 {
1997 global $ilDB;
1998
1999 if (!is_array($a_node_ids) || !$this->isCacheUsed())
2000 {
2001 return;
2002 }
2003
2004 $query = 'SELECT '.$this->tree_pk.', child FROM '.$this->table_tree.' '.
2005 'WHERE '.$ilDB->in("child", $a_node_ids, false, "integer");
2006
2007 $res = $ilDB->query($query);
2008 while ($row = $ilDB->fetchAssoc($res))
2009 {
2010 if ($row[$this->tree_pk] < 0)
2011 {
2012 if($this->__isMainTree())
2013 {
2014 $this->is_saved_cache[$row["child"]] = true;
2015 }
2016 }
2017 else
2018 {
2019 if($this->__isMainTree())
2020 {
2021 $this->is_saved_cache[$row["child"]] = false;
2022 }
2023 }
2024 }
2025 }
2026
2027
2034 function getSavedNodeData($a_parent_id)
2035 {
2036 global $ilDB;
2037
2038 if (!isset($a_parent_id))
2039 {
2040 $this->ilErr->raiseError(get_class($this)."::getSavedNodeData(): No node_id given!",$this->ilErr->WARNING);
2041 }
2042
2043 $query = 'SELECT * FROM '.$this->table_tree.' '.
2044 $this->buildJoin().
2045 'WHERE '.$this->table_tree.'.'.$this->tree_pk.' < %s '.
2046 'AND '.$this->table_tree.'.parent = %s';
2047 $res = $ilDB->queryF($query,array('integer','integer'),array(
2048 0,
2049 $a_parent_id));
2050
2051 while($row = $ilDB->fetchAssoc($res))
2052 {
2053 $saved[] = $this->fetchNodeData($row);
2054 }
2055
2056 return $saved ? $saved : array();
2057 }
2058
2065 function getSavedNodeObjIds(array $a_obj_ids)
2066 {
2067 global $ilDB;
2068
2069 $query = 'SELECT '.$this->table_obj_data.'.obj_id FROM '.$this->table_tree.' '.
2070 $this->buildJoin().
2071 'WHERE '.$this->table_tree.'.'.$this->tree_pk.' < '.$ilDB->quote(0, 'integer').' '.
2072 'AND '.$ilDB->in($this->table_obj_data.'.obj_id', $a_obj_ids, '', 'integer');
2073 $res = $ilDB->query($query);
2074 while($row = $ilDB->fetchAssoc($res))
2075 {
2076 $saved[] = $row['obj_id'];
2077 }
2078
2079 return $saved ? $saved : array();
2080 }
2081
2088 function getParentId($a_node_id)
2089 {
2090 global $ilDB;
2091
2092 if (!isset($a_node_id))
2093 {
2094 $this->ilErr->raiseError(get_class($this)."::getParentId(): No node_id given! ",$this->ilErr->WARNING);
2095 }
2096
2097 $query = 'SELECT parent FROM '.$this->table_tree.' '.
2098 'WHERE child = %s '.
2099 'AND '.$this->tree_pk.' = %s ';
2100 $res = $ilDB->queryF($query,array('integer','integer'),array(
2101 $a_node_id,
2102 $this->tree_id));
2103
2104 $row = $ilDB->fetchObject($res);
2105 return $row->parent;
2106 }
2107
2114 function getLeftValue($a_node_id)
2115 {
2116 global $ilDB;
2117
2118 if (!isset($a_node_id))
2119 {
2120 $this->ilErr->raiseError(get_class($this)."::getLeftValued(): No node_id given! ",$this->ilErr->WARNING);
2121 }
2122
2123 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2124 'WHERE child = %s '.
2125 'AND '.$this->tree_pk.' = %s ';
2126 $res = $ilDB->queryF($query,array('integer','integer'),array(
2127 $a_node_id,
2128 $this->tree_id));
2129 $row = $ilDB->fetchObject($res);
2130 return $row->lft;
2131 }
2132
2139 function getChildSequenceNumber($a_node, $type = "")
2140 {
2141 global $ilDB;
2142
2143 if (!isset($a_node))
2144 {
2145 $this->ilErr->raiseError(get_class($this)."::getChildSequenceNumber(): No node_id given! ",$this->ilErr->WARNING);
2146 }
2147
2148 if($type)
2149 {
2150 $query = 'SELECT count(*) cnt FROM '.$this->table_tree.' '.
2151 $this->buildJoin().
2152 'WHERE lft <= %s '.
2153 'AND type = %s '.
2154 'AND parent = %s '.
2155 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2156
2157 $res = $ilDB->queryF($query,array('integer','text','integer','integer'),array(
2158 $a_node['lft'],
2159 $type,
2160 $a_node['parent'],
2161 $this->tree_id));
2162 }
2163 else
2164 {
2165 $query = 'SELECT count(*) cnt FROM '.$this->table_tree.' '.
2166 $this->buildJoin().
2167 'WHERE lft <= %s '.
2168 'AND parent = %s '.
2169 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2170
2171 $res = $ilDB->queryF($query,array('integer','integer','integer'),array(
2172 $a_node['lft'],
2173 $a_node['parent'],
2174 $this->tree_id));
2175
2176 }
2177 $row = $ilDB->fetchAssoc($res);
2178 return $row["cnt"];
2179 }
2180
2187 function readRootId()
2188 {
2189 global $ilDB;
2190
2191 $query = 'SELECT child FROM '.$this->table_tree.' '.
2192 'WHERE parent = %s '.
2193 'AND '.$this->tree_pk.' = %s ';
2194 $res = $ilDB->queryF($query,array('integer','integer'),array(
2195 0,
2196 $this->tree_id));
2197 $row = $ilDB->fetchObject($res);
2198 $this->root_id = $row->child;
2199 return $this->root_id;
2200 }
2201
2207 function getRootId()
2208 {
2209 return $this->root_id;
2210 }
2211 function setRootId($a_root_id)
2212 {
2213 $this->root_id = $a_root_id;
2214 }
2215
2221 function getTreeId()
2222 {
2223 return $this->tree_id;
2224 }
2225
2231 function setTreeId($a_tree_id)
2232 {
2233 $this->tree_id = $a_tree_id;
2234 }
2235
2243 function fetchSuccessorNode($a_node_id, $a_type = "")
2244 {
2245 global $ilDB;
2246
2247 if (!isset($a_node_id))
2248 {
2249 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
2250 }
2251
2252 // get lft value for current node
2253 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2254 'WHERE '.$this->table_tree.'.child = %s '.
2255 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2256 $res = $ilDB->queryF($query,array('integer','integer'),array(
2257 $a_node_id,
2258 $this->tree_id));
2259 $curr_node = $ilDB->fetchAssoc($res);
2260
2261 if($a_type)
2262 {
2263 $query = 'SELECT * FROM '.$this->table_tree.' '.
2264 $this->buildJoin().
2265 'WHERE lft > %s '.
2266 'AND '.$this->table_obj_data.'.type = %s '.
2267 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2268 'ORDER BY lft ';
2269 $ilDB->setLimit(1);
2270 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
2271 $curr_node['lft'],
2272 $a_type,
2273 $this->tree_id));
2274 }
2275 else
2276 {
2277 $query = 'SELECT * FROM '.$this->table_tree.' '.
2278 $this->buildJoin().
2279 'WHERE lft > %s '.
2280 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2281 'ORDER BY lft ';
2282 $ilDB->setLimit(1);
2283 $res = $ilDB->queryF($query,array('integer','integer'),array(
2284 $curr_node['lft'],
2285 $this->tree_id));
2286 }
2287
2288 if ($res->numRows() < 1)
2289 {
2290 return false;
2291 }
2292 else
2293 {
2294 $row = $ilDB->fetchAssoc($res);
2295 return $this->fetchNodeData($row);
2296 }
2297 }
2298
2306 function fetchPredecessorNode($a_node_id, $a_type = "")
2307 {
2308 global $ilDB;
2309
2310 if (!isset($a_node_id))
2311 {
2312 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
2313 }
2314
2315 // get lft value for current node
2316 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2317 'WHERE '.$this->table_tree.'.child = %s '.
2318 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2319 $res = $ilDB->queryF($query,array('integer','integer'),array(
2320 $a_node_id,
2321 $this->tree_id));
2322
2323 $curr_node = $ilDB->fetchAssoc($res);
2324
2325 if($a_type)
2326 {
2327 $query = 'SELECT * FROM '.$this->table_tree.' '.
2328 $this->buildJoin().
2329 'WHERE lft < %s '.
2330 'AND '.$this->table_obj_data.'.type = %s '.
2331 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2332 'ORDER BY lft DESC';
2333 $ilDB->setLimit(1);
2334 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
2335 $curr_node['lft'],
2336 $a_type,
2337 $this->tree_id));
2338 }
2339 else
2340 {
2341 $query = 'SELECT * FROM '.$this->table_tree.' '.
2342 $this->buildJoin().
2343 'WHERE lft < %s '.
2344 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2345 'ORDER BY lft DESC';
2346 $ilDB->setLimit(1);
2347 $res = $ilDB->queryF($query,array('integer','integer'),array(
2348 $curr_node['lft'],
2349 $this->tree_id));
2350 }
2351
2352 if ($res->numRows() < 1)
2353 {
2354 return false;
2355 }
2356 else
2357 {
2358 $row = $ilDB->fetchAssoc($res);
2359 return $this->fetchNodeData($row);
2360 }
2361 }
2362
2371 function renumber($node_id = 1, $i = 1)
2372 {
2373 global $ilDB;
2374
2375 // LOCKED ###################################
2376 if($this->__isMainTree())
2377 {
2378 /*
2379 ilDB::_lockTables(array($this->table_tree => 'WRITE',
2380 $this->table_obj_data => 'WRITE',
2381 $this->table_obj_reference => 'WRITE',
2382 'object_translation' => 'WRITE',
2383 'object_data od' => 'WRITE',
2384 'container_reference cr' => 'WRITE'));
2385 */
2386 $ilDB->lockTables(
2387 array(
2388 0 => array('name' => $this->table_tree, 'type' => ilDB::LOCK_WRITE),
2389 1 => array('name' => $this->table_obj_data, 'type' => ilDB::LOCK_WRITE),
2390 2 => array('name' => $this->table_obj_reference, 'type' => ilDB::LOCK_WRITE),
2391 3 => array('name' => 'object_translation', 'type' => ilDB::LOCK_WRITE),
2392 4 => array('name' => 'object_data', 'type' => ilDB::LOCK_WRITE, 'alias' => 'od'),
2393 5 => array('name' => 'container_reference', 'type' => ilDB::LOCK_WRITE, 'alias' => 'cr')
2394 ));
2395 }
2396 $return = $this->__renumber($node_id,$i);
2397 if($this->__isMainTree())
2398 {
2399 $ilDB->unlockTables();
2400 }
2401 // LOCKED ###################################
2402 return $return;
2403 }
2404
2405 // PRIVATE
2415 function __renumber($node_id = 1, $i = 1)
2416 {
2417 global $ilDB;
2418
2419 $query = 'UPDATE '.$this->table_tree.' SET lft = %s WHERE child = %s';
2420 $res = $ilDB->manipulateF($query,array('integer','integer'),array(
2421 $i,
2422 $node_id));
2423
2424 // to much dependencies
2425 //$childs = $this->getChilds($node_id);
2426 $childs = $this->getChildIds($node_id);
2427
2428 foreach ($childs as $child)
2429 {
2430 $i = $this->__renumber($child,$i+1);
2431 }
2432 $i++;
2433
2434 // Insert a gap at the end of node, if the node has children
2435 if (count($childs) > 0)
2436 {
2437 $i += $this->gap * 2;
2438 }
2439
2440
2441 $query = 'UPDATE '.$this->table_tree.' SET rgt = %s WHERE child = %s';
2442 $res = $ilDB->manipulateF($query,array('integer','integer'),array(
2443 $i,
2444 $node_id));
2445 return $i;
2446 }
2447
2448
2459 function checkForParentType($a_ref_id,$a_type,$a_exclude_source_check = false)
2460 {
2461 // #12577
2462 $cache_key = $a_ref_id.'.'.$a_type.'.'.((int)$a_exclude_source_check);
2463
2464 // Try to return a cached result
2465 if($this->isCacheUsed() &&
2466 array_key_exists($cache_key, $this->parent_type_cache))
2467 {
2468 return $this->parent_type_cache[$cache_key];
2469 }
2470
2471 // Store up to 1000 results in cache
2472 $do_cache = ($this->__isMainTree() && count($this->parent_type_cache) < 1000);
2473
2474 // ref_id is not in tree
2475 if(!$this->isInTree($a_ref_id))
2476 {
2477 if($do_cache)
2478 {
2479 $this->parent_type_cache[$cache_key] = false;
2480 }
2481 return false;
2482 }
2483
2484 $path = array_reverse($this->getPathFull($a_ref_id));
2485
2486 // remove first path entry as it is requested node
2487 if($a_exclude_source_check)
2488 {
2489 array_shift($path);
2490 }
2491
2492 foreach($path as $node)
2493 {
2494 // found matching parent
2495 if($node["type"] == $a_type)
2496 {
2497 if($do_cache)
2498 {
2499 $this->parent_type_cache[$cache_key] = $node["child"];
2500 }
2501 return $node["child"];
2502 }
2503 }
2504
2505 if($do_cache)
2506 {
2507 $this->parent_type_cache[$cache_key] = false;
2508 }
2509 return 0;
2510 }
2511
2521 function _removeEntry($a_tree,$a_child,$a_db_table = "tree")
2522 {
2523 global $ilDB,$ilLog,$ilErr;
2524
2525 if($a_db_table === 'tree')
2526 {
2527 if($a_tree == 1 and $a_child == ROOT_FOLDER_ID)
2528 {
2529 $message = sprintf('%s::_removeEntry(): Tried to delete root node! $a_tree: %s $a_child: %s',
2530 get_class($this),
2531 $a_tree,
2532 $a_child);
2533 $ilLog->write($message,$ilLog->FATAL);
2534 $ilErr->raiseError($message,$ilErr->WARNING);
2535 }
2536 }
2537
2538 $query = 'DELETE FROM '.$a_db_table.' '.
2539 'WHERE tree = %s '.
2540 'AND child = %s ';
2541 $res = $ilDB->manipulateF($query,array('integer','integer'),array(
2542 $a_tree,
2543 $a_child));
2544
2545 }
2546
2553 public function __isMainTree()
2554 {
2555 return $this->table_tree === 'tree';
2556 }
2557
2568 function __checkDelete($a_node)
2569 {
2570 global $ilDB;
2571
2572
2573 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, array(),false);
2574 $GLOBALS['ilLog']->write(__METHOD__.': '.$query);
2575 $res = $ilDB->query($query);
2576
2577 $counter = (int) $lft_childs = array();
2578 while($row = $ilDB->fetchObject($res))
2579 {
2580 $lft_childs[$row->child] = $row->parent;
2581 ++$counter;
2582 }
2583
2584 // CHECK FOR DUPLICATE CHILD IDS
2585 if($counter != count($lft_childs))
2586 {
2587 $message = sprintf('%s::__checkTree(): Duplicate entries for "child" in maintree! $a_node_id: %s',
2588 get_class($this),
2589 $a_node['child']);
2590 $this->log->write($message,$this->log->FATAL);
2591 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2592 }
2593
2594 // GET SUBTREE BY PARENT RELATION
2595 $parent_childs = array();
2596 $this->__getSubTreeByParentRelation($a_node['child'],$parent_childs);
2597 $this->__validateSubtrees($lft_childs,$parent_childs);
2598
2599 return true;
2600 }
2601
2610 function __getSubTreeByParentRelation($a_node_id,&$parent_childs)
2611 {
2612 global $ilDB;
2613
2614 // GET PARENT ID
2615 $query = 'SELECT * FROM '.$this->table_tree.' '.
2616 'WHERE child = %s '.
2617 'AND tree = %s ';
2618 $res = $ilDB->queryF($query,array('integer','integer'),array(
2619 $a_node_id,
2620 $this->tree_id));
2621
2622 $counter = 0;
2623 while($row = $ilDB->fetchObject($res))
2624 {
2625 $parent_childs[$a_node_id] = $row->parent;
2626 ++$counter;
2627 }
2628 // MULTIPLE ENTRIES
2629 if($counter > 1)
2630 {
2631 $message = sprintf('%s::__getSubTreeByParentRelation(): Multiple entries in maintree! $a_node_id: %s',
2632 get_class($this),
2633 $a_node_id);
2634 $this->log->write($message,$this->log->FATAL);
2635 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2636 }
2637
2638 // GET ALL CHILDS
2639 $query = 'SELECT * FROM '.$this->table_tree.' '.
2640 'WHERE parent = %s ';
2641 $res = $ilDB->queryF($query,array('integer'),array($a_node_id));
2642
2643 while($row = $ilDB->fetchObject($res))
2644 {
2645 // RECURSION
2646 $this->__getSubTreeByParentRelation($row->child,$parent_childs);
2647 }
2648 return true;
2649 }
2650
2651 function __validateSubtrees(&$lft_childs,$parent_childs)
2652 {
2653 // SORT BY KEY
2654 ksort($lft_childs);
2655 ksort($parent_childs);
2656
2657 $GLOBALS['ilLog']->write(__METHOD__.': left childs '. print_r($lft_childs,true));
2658 $GLOBALS['ilLog']->write(__METHOD__.': parent childs '. print_r($parent_childs,true));
2659
2660 if(count($lft_childs) != count($parent_childs))
2661 {
2662 $message = sprintf('%s::__validateSubtrees(): (COUNT) Tree is corrupted! Left/Right subtree does not comply .'.
2663 'with parent relation',
2664 get_class($this));
2665 $this->log->write($message,$this->log->FATAL);
2666 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2667 }
2668
2669
2670 foreach($lft_childs as $key => $value)
2671 {
2672 if($parent_childs[$key] != $value)
2673 {
2674 $message = sprintf('%s::__validateSubtrees(): (COMPARE) Tree is corrupted! Left/Right subtree does not comply '.
2675 'with parent relation',
2676 get_class($this));
2677 $this->log->write($message,$this->log->FATAL);
2678 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2679 }
2680 if($key == ROOT_FOLDER_ID)
2681 {
2682 $message = sprintf('%s::__validateSubtrees(): (ROOT_FOLDER) Tree is corrupted! Tried to delete root folder',
2683 get_class($this));
2684 $this->log->write($message,$this->log->FATAL);
2685 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2686 }
2687 }
2688 return true;
2689 }
2690
2700 public function moveTree($a_source_id, $a_target_id, $a_location = self::POS_LAST_NODE)
2701 {
2702 $old_parent_id = $this->getParentId($a_source_id);
2703 $this->getTreeImplementation()->moveTree($a_source_id,$a_target_id,$a_location);
2704 if (isset($GLOBALS["ilAppEventHandler"]) && $this->__isMainTree()) {
2705 $GLOBALS['ilAppEventHandler']->raise(
2706 "Services/Tree",
2707 "moveTree",
2708 array(
2709 'tree' => $this->table_tree,
2710 'source_id' => $a_source_id,
2711 'target_id' => $a_target_id,
2712 'old_parent_id' => $old_parent_id
2713 )
2714 );
2715 }
2716 return true;
2717 }
2718
2719
2720
2721
2729 public function getRbacSubtreeInfo($a_endnode_id)
2730 {
2731 return $this->getTreeImplementation()->getSubtreeInfo($a_endnode_id);
2732 }
2733
2734
2742 public function getSubTreeQuery($a_node_id,$a_fields = array(), $a_types = '', $a_force_join_reference = false)
2743 {
2744 return $this->getTreeImplementation()->getSubTreeQuery(
2745 $this->getNodeTreeData($a_node_id),
2746 $a_types,
2747 $a_force_join_reference,
2748 $a_fields);
2749 }
2750
2751
2760 public function getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields = array())
2761 {
2762 global $ilDB;
2763
2764 $node = $this->getNodeData($a_node_id);
2765 if(!sizeof($node))
2766 {
2767 return;
2768 }
2769
2770 $res = array();
2771
2772 $query = $this->getTreeImplementation()->getSubTreeQuery($node, '', true, array($this->ref_pk));
2773
2774 $fields = '*';
2775 if(count($a_fields))
2776 {
2777 $fields = implode(',',$a_fields);
2778 }
2779
2780 $query = "SELECT ".$fields.
2781 " FROM ".$this->getTreeTable().
2782 " ".$this->buildJoin().
2783 " WHERE ".$this->getTableReference().".".$this->ref_pk." IN (".$query.")".
2784 " AND ".$ilDB->in($this->getObjectDataTable().".".$this->obj_pk, $a_obj_ids, "", "integer");
2785 $set = $ilDB->query($query);
2786 while($row = $ilDB->fetchAssoc($set))
2787 {
2788 $res[] = $row;
2789 }
2790
2791 return $res;
2792 }
2793
2794 public function deleteNode($a_tree_id,$a_node_id)
2795 {
2796 global $ilDB;
2797
2798 $query = 'DELETE FROM tree where '.
2799 'child = '.$ilDB->quote($a_node_id,'integer').' '.
2800 'AND tree = '.$ilDB->quote($a_tree_id,'integer');
2801 $ilDB->manipulate($query);
2802 }
2803
2810 {
2811 global $ilDB;
2812
2813 $query = 'SELECT DISTINCT(o.type) type FROM tree t JOIN object_reference r ON child = r.ref_id '.
2814 'JOIN object_data o on r.obj_id = o.obj_id '.
2815 'WHERE tree < '.$ilDB->quote(0,'integer').' '.
2816 'AND child = -tree '.
2817 'GROUP BY o.type';
2818 $res = $ilDB->query($query);
2819
2820 $types_deleted = array();
2821 while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
2822 {
2823 $types_deleted[] = $row->type;
2824 }
2825 return $types_deleted;
2826 }
2827
2828
2829} // END class.tree
2830?>
const PEAR_ERROR_CALLBACK
Definition: PEAR.php:35
toNFC( $string)
Convert a UTF-8 string to normal form C, canonical composition.
Definition: UtfNormal.php:157
const DB_FETCHMODE_ASSOC
Definition: class.ilDB.php:10
const DB_FETCHMODE_OBJECT
Definition: class.ilDB.php:11
const IL_LAST_NODE
Definition: class.ilTree.php:4
static _lookupTitle($a_obj_id)
Overwitten from base class.
Database Wrapper.
Definition: class.ilDB.php:29
const LOCK_WRITE
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.
_resetDeletedDate($a_ref_id)
only called in ilObjectGUI::insertSavedNodes
const DESC_LENGTH
ILIAS Setting Class.
static strToLower($a_string)
Definition: class.ilStr.php:91
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.
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
_removeEntry($a_tree, $a_child, $a_db_table="tree")
STATIC METHOD Removes a single entry from a 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
ilTree($a_tree_id, $a_root_id=0)
Constructor @access public.
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.
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 shortenText($a_str, $a_len, $a_dots=false, $a_next_blank=false, $a_keep_extension=false)
shorten a string to given length.
static quoteArray($a_array)
Quotes all members of an array for usage in DB query statement.
$data
$r
Definition: example_031.php:79
$GLOBALS['PHPCAS_CLIENT']
This global variable is used by the interface class phpCAS.
Definition: CAS.php:276
global $ilBench
Definition: ilias.php:18
global $lng
Definition: privfeed.php:40
$path
Definition: index.php:22
global $ilDB
global $ilUser
Definition: imgupload.php:15