ILIAS  release_5-0 Revision 5.0.0-1144-gc4397b1f870
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
733 public function insertNode($a_node_id, $a_parent_id, $a_pos = IL_LAST_NODE, $a_reset_deletion_date = false)
734 {
735 global $ilDB;
736
737//echo "+$a_node_id+$a_parent_id+";
738 // CHECK node_id and parent_id > 0 if in main tree
739 if($this->__isMainTree())
740 {
741 if($a_node_id <= 1 or $a_parent_id <= 0)
742 {
743 $GLOBALS['ilLog']->logStack();
744 $message = sprintf('%s::insertNode(): Invalid parameters! $a_node_id: %s $a_parent_id: %s',
745 get_class($this),
746 $a_node_id,
747 $a_parent_id);
748 $this->log->write($message,$this->log->FATAL);
749 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
750 }
751 }
752
753
754 if (!isset($a_node_id) or !isset($a_parent_id))
755 {
756 $GLOBALS['ilLog']->logStack();
757 $this->ilErr->raiseError(get_class($this)."::insertNode(): Missing parameter! ".
758 "node_id: ".$a_node_id." parent_id: ".$a_parent_id,$this->ilErr->WARNING);
759 }
760 if ($this->isInTree($a_node_id))
761 {
762 $this->ilErr->raiseError(get_class($this)."::insertNode(): Node ".$a_node_id." already in tree ".
763 $this->table_tree."!",$this->ilErr->WARNING);
764 }
765
766 $this->getTreeImplementation()->insertNode($a_node_id, $a_parent_id, $a_pos);
767
768 $this->in_tree_cache[$a_node_id] = true;
769
770 // reset deletion date
771 if ($a_reset_deletion_date)
772 {
773 ilObject::_resetDeletedDate($a_node_id);
774 }
775 }
776
789 public function getFilteredSubTree($a_node_id,$a_filter = array())
790 {
791 $node = $this->getNodeData($a_node_id);
792
793 $first = true;
794 $depth = 0;
795 foreach($this->getSubTree($node) as $subnode)
796 {
797 if($depth and $subnode['depth'] > $depth)
798 {
799 continue;
800 }
801 if(!$first and in_array($subnode['type'],$a_filter))
802 {
803 $depth = $subnode['depth'];
804 $first = false;
805 continue;
806 }
807 $depth = 0;
808 $first = false;
809 $filtered[] = $subnode;
810 }
811 return $filtered ? $filtered : array();
812 }
813
819 public function getSubTreeIds($a_ref_id)
820 {
821 return $this->getTreeImplementation()->getSubTreeIds($a_ref_id);
822 }
823
824
834 function getSubTree($a_node,$a_with_data = true, $a_type = "")
835 {
836 global $ilDB;
837
838 if (!is_array($a_node))
839 {
840 $GLOBALS['ilLog']->logStack();
841 throw new InvalidArgumentException(__METHOD__.': wrong datatype for node data given');
842 }
843
844 /*
845 if($a_node['lft'] < 1 or $a_node['rgt'] < 2)
846 {
847 $GLOBALS['ilLog']->logStack();
848 $message = sprintf('%s: Invalid node given! $a_node["lft"]: %s $a_node["rgt"]: %s',
849 __METHOD__,
850 $a_node['lft'],
851 $a_node['rgt']);
852
853 throw new InvalidArgumentException($message);
854 }
855 */
856
857 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, $a_type);
858 $res = $ilDB->query($query);
859 while($row = $ilDB->fetchAssoc($res))
860 {
861 if($a_with_data)
862 {
863 $subtree[] = $this->fetchNodeData($row);
864 }
865 else
866 {
867 $subtree[] = $row['child'];
868 }
869 // the lm_data "hack" should be removed in the trunk during an alpha
870 if($this->__isMainTree() || $this->table_tree == "lm_tree")
871 {
872 $this->in_tree_cache[$row['child']] = true;
873 }
874 }
875 return $subtree ? $subtree : array();
876 }
877
886 function getSubTreeTypes($a_node,$a_filter = 0)
887 {
888 $a_filter = $a_filter ? $a_filter : array();
889
890 foreach($this->getSubtree($this->getNodeData($a_node)) as $node)
891 {
892 if(in_array($node["type"],$a_filter))
893 {
894 continue;
895 }
896 $types["$node[type]"] = $node["type"];
897 }
898 return $types ? $types : array();
899 }
900
907 function deleteTree($a_node)
908 {
909 global $ilDB;
910
911 $GLOBALS['ilLog']->write(__METHOD__.': Delete tree with node '. $a_node);
912
913 if (!is_array($a_node))
914 {
915 $GLOBALS['ilLog']->logStack();
916 throw new InvalidArgumentException(__METHOD__.': Wrong datatype for node data!');
917 }
918
919 $GLOBALS['ilLog']->write(__METHOD__.': '. $this->tree_pk);
920
921 if($this->__isMainTree() )
922 {
923 // @todo normally this part is not executed, since the subtree is first
924 // moved to trash and then deleted.
925 if(!$this->__checkDelete($a_node))
926 {
927 $GLOBALS['ilLog']->logStack();
928 throw new ilInvalidTreeStructureException('Deletion canceled due to invalid tree structure.' . print_r($a_node,true));
929 }
930 }
931
932 $this->getTreeImplementation()->deleteTree($a_node['child']);
933
934 $this->resetInTreeCache();
935
936 }
937
948 function getPathFull($a_endnode_id, $a_startnode_id = 0)
949 {
950 $pathIds =& $this->getPathId($a_endnode_id, $a_startnode_id);
951
952 // We retrieve the full path in a single query to improve performance
953 global $ilDB;
954
955 // Abort if no path ids were found
956 if (count($pathIds) == 0)
957 {
958 return null;
959 }
960
961 $inClause = 'child IN (';
962 for ($i=0; $i < count($pathIds); $i++)
963 {
964 if ($i > 0) $inClause .= ',';
965 $inClause .= $ilDB->quote($pathIds[$i],'integer');
966 }
967 $inClause .= ')';
968
969 $q = 'SELECT * '.
970 'FROM '.$this->table_tree.' '.
971 $this->buildJoin().' '.
972 'WHERE '.$inClause.' '.
973 'AND '.$this->table_tree.'.'.$this->tree_pk.' = '.$this->ilDB->quote($this->tree_id,'integer').' '.
974 'ORDER BY depth';
975 $r = $ilDB->query($q);
976
977 $pathFull = array();
978 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
979 {
980 $pathFull[] = $this->fetchNodeData($row);
981
982 // Update cache
983 if ($this->__isMainTree())
984 {
985 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child']);
986 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
987 }
988 }
989 return $pathFull;
990 }
991
992
999 function preloadDepthParent($a_node_ids)
1000 {
1001 global $ilDB;
1002
1003 if (!$this->__isMainTree() || !is_array($a_node_ids) || !$this->isCacheUsed())
1004 {
1005 return;
1006 }
1007
1008 $res = $ilDB->query('SELECT t.depth, t.parent, t.child '.
1009 'FROM '.$this->table_tree.' t '.
1010 'WHERE '.$ilDB->in("child", $a_node_ids, false, "integer").
1011 'AND '.$this->tree_pk.' = '.$ilDB->quote($this->tree_id, "integer"));
1012 while ($row = $ilDB->fetchAssoc($res))
1013 {
1014 $this->depth_cache[$row["child"]] = $row["depth"];
1015 $this->parent_cache[$row["child"]] = $row["parent"];
1016 }
1017 }
1018
1028 public function getPathId($a_endnode_id, $a_startnode_id = 0)
1029 {
1030 if(!$a_endnode_id)
1031 {
1032 $GLOBALS['ilLog']->logStack();
1033 throw new InvalidArgumentException(__METHOD__.': No endnode given!');
1034 }
1035
1036 // path id cache
1037 if ($this->isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id]))
1038 {
1039//echo "<br>getPathIdhit";
1040 return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
1041 }
1042//echo "<br>miss";
1043
1044 $pathIds = $this->getTreeImplementation()->getPathIds($a_endnode_id, $a_startnode_id);
1045
1046 if($this->__isMainTree())
1047 {
1048 $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
1049 }
1050 return $pathIds;
1051 }
1052
1053 // BEGIN WebDAV: getNodePathForTitlePath function added
1071 function getNodePathForTitlePath($titlePath, $a_startnode_id = null)
1072 {
1073 global $ilDB, $log;
1074 //$log->write('getNodePathForTitlePath('.implode('/',$titlePath));
1075
1076 // handle empty title path
1077 if ($titlePath == null || count($titlePath) == 0)
1078 {
1079 if ($a_startnode_id == 0)
1080 {
1081 return null;
1082 }
1083 else
1084 {
1085 return $this->getNodePath($a_startnode_id);
1086 }
1087 }
1088
1089 // fetch the node path up to the startnode
1090 if ($a_startnode_id != null && $a_startnode_id != 0)
1091 {
1092 // Start using the node path to the root of the relative path
1093 $nodePath = $this->getNodePath($a_startnode_id);
1094 $parent = $a_startnode_id;
1095 }
1096 else
1097 {
1098 // Start using the root of the tree
1099 $nodePath = array();
1100 $parent = 0;
1101 }
1102
1103
1104 // Convert title path into Unicode Normal Form C
1105 // This is needed to ensure that we can compare title path strings with
1106 // strings from the database.
1107 require_once('include/Unicode/UtfNormal.php');
1108 include_once './Services/Utilities/classes/class.ilStr.php';
1109 $inClause = 'd.title IN (';
1110 for ($i=0; $i < count($titlePath); $i++)
1111 {
1112 $titlePath[$i] = ilStr::strToLower(UtfNormal::toNFC($titlePath[$i]));
1113 if ($i > 0) $inClause .= ',';
1114 $inClause .= $ilDB->quote($titlePath[$i],'text');
1115 }
1116 $inClause .= ')';
1117
1118 // Fetch all rows that are potential path elements
1119 if ($this->table_obj_reference)
1120 {
1121 $joinClause = 'JOIN '.$this->table_obj_reference.' r ON t.child = r.'.$this->ref_pk.' '.
1122 'JOIN '.$this->table_obj_data.' d ON r.'.$this->obj_pk.' = d.'.$this->obj_pk;
1123 }
1124 else
1125 {
1126 $joinClause = 'JOIN '.$this->table_obj_data.' d ON t.child = d.'.$this->obj_pk;
1127 }
1128 // The ORDER BY clause in the following SQL statement ensures that,
1129 // in case of a multiple objects with the same title, always the Object
1130 // with the oldest ref_id is chosen.
1131 // This ensure, that, if a new object with the same title is added,
1132 // WebDAV clients can still work with the older object.
1133 $q = 'SELECT t.depth, t.parent, t.child, d.'.$this->obj_pk.' obj_id, d.type, d.title '.
1134 'FROM '.$this->table_tree.' t '.
1135 $joinClause.' '.
1136 'WHERE '.$inClause.' '.
1137 'AND t.depth <= '.(count($titlePath)+count($nodePath)).' '.
1138 'AND t.tree = 1 '.
1139 'ORDER BY t.depth, t.child ASC';
1140 $r = $ilDB->query($q);
1141
1142 $rows = array();
1143 while ($row = $r->fetchRow(DB_FETCHMODE_ASSOC))
1144 {
1145 $row['title'] = UtfNormal::toNFC($row['title']);
1146 $row['ref_id'] = $row['child'];
1147 $rows[] = $row;
1148 }
1149
1150 // Extract the path elements from the fetched rows
1151 for ($i = 0; $i < count($titlePath); $i++) {
1152 $pathElementFound = false;
1153 foreach ($rows as $row) {
1154 if ($row['parent'] == $parent &&
1155 ilStr::strToLower($row['title']) == $titlePath[$i])
1156 {
1157 // FIXME - We should test here, if the user has
1158 // 'visible' permission for the object.
1159 $nodePath[] = $row;
1160 $parent = $row['child'];
1161 $pathElementFound = true;
1162 break;
1163 }
1164 }
1165 // Abort if we haven't found a path element for the current depth
1166 if (! $pathElementFound)
1167 {
1168 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):null');
1169 return null;
1170 }
1171 }
1172 // Return the node path
1173 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):'.var_export($nodePath,true));
1174 return $nodePath;
1175 }
1176 // END WebDAV: getNodePathForTitlePath function added
1177 // END WebDAV: getNodePath function added
1194 function getNodePath($a_endnode_id, $a_startnode_id = 0)
1195 {
1196 global $ilDB;
1197
1198 $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
1199
1200 // Abort if no path ids were found
1201 if (count($pathIds) == 0)
1202 {
1203 return null;
1204 }
1205
1206
1207 $types = array();
1208 $data = array();
1209 for ($i = 0; $i < count($pathIds); $i++)
1210 {
1211 $types[] = 'integer';
1212 $data[] = $pathIds[$i];
1213 }
1214
1215 $query = 'SELECT t.depth,t.parent,t.child,d.obj_id,d.type,d.title '.
1216 'FROM '.$this->table_tree.' t '.
1217 'JOIN '.$this->table_obj_reference.' r ON r.ref_id = t.child '.
1218 'JOIN '.$this->table_obj_data.' d ON d.obj_id = r.obj_id '.
1219 'WHERE '.$ilDB->in('t.child',$data,false,'integer').' '.
1220 'ORDER BY t.depth ';
1221
1222 $res = $ilDB->queryF($query,$types,$data);
1223
1224 $titlePath = array();
1225 while ($row = $ilDB->fetchAssoc($res))
1226 {
1227 $titlePath[] = $row;
1228 }
1229 return $titlePath;
1230 }
1231 // END WebDAV: getNodePath function added
1232
1239 function checkTree()
1240 {
1241 global $ilDB;
1242
1243 $types = array('integer');
1244 $query = 'SELECT lft,rgt FROM '.$this->table_tree.' '.
1245 'WHERE '.$this->tree_pk.' = %s ';
1246
1247 $res = $ilDB->queryF($query,$types,array($this->tree_id));
1248 while ($row = $ilDB->fetchObject($res))
1249 {
1250 $lft[] = $row->lft;
1251 $rgt[] = $row->rgt;
1252 }
1253
1254 $all = array_merge($lft,$rgt);
1255 $uni = array_unique($all);
1256
1257 if (count($all) != count($uni))
1258 {
1259 $message = sprintf('%s::checkTree(): Tree is corrupted!',
1260 get_class($this));
1261
1262 $this->log->write($message,$this->log->FATAL);
1263 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
1264 }
1265
1266 return true;
1267 }
1268
1272 function checkTreeChilds($a_no_zero_child = true)
1273 {
1274 global $ilDB;
1275
1276 $query = 'SELECT * FROM '.$this->table_tree.' '.
1277 'WHERE '.$this->tree_pk.' = %s '.
1278 'ORDER BY lft';
1279 $r1 = $ilDB->queryF($query,array('integer'),array($this->tree_id));
1280
1281 while ($row = $ilDB->fetchAssoc($r1))
1282 {
1283//echo "tree:".$row[$this->tree_pk].":lft:".$row["lft"].":rgt:".$row["rgt"].":child:".$row["child"].":<br>";
1284 if (($row["child"] == 0) && $a_no_zero_child)
1285 {
1286 $this->ilErr->raiseError(get_class($this)."::checkTreeChilds(): Tree contains child with ID 0!",$this->ilErr->WARNING);
1287 }
1288
1289 if ($this->table_obj_reference)
1290 {
1291 // get object reference data
1292 $query = 'SELECT * FROM '.$this->table_obj_reference.' WHERE '.$this->ref_pk.' = %s ';
1293 $r2 = $ilDB->queryF($query,array('integer'),array($row['child']));
1294
1295//echo "num_childs:".$r2->numRows().":<br>";
1296 if ($r2->numRows() == 0)
1297 {
1298 $this->ilErr->raiseError(get_class($this)."::checkTree(): No Object-to-Reference entry found for ID ".
1299 $row["child"]."!",$this->ilErr->WARNING);
1300 }
1301 if ($r2->numRows() > 1)
1302 {
1303 $this->ilErr->raiseError(get_class($this)."::checkTree(): More Object-to-Reference entries found for ID ".
1304 $row["child"]."!",$this->ilErr->WARNING);
1305 }
1306
1307 // get object data
1308 $obj_ref = $ilDB->fetchAssoc($r2);
1309
1310 $query = 'SELECT * FROM '.$this->table_obj_data.' WHERE '.$this->obj_pk.' = %s';
1311 $r3 = $ilDB->queryF($query,array('integer'),array($obj_ref[$this->obj_pk]));
1312 if ($r3->numRows() == 0)
1313 {
1314 $this->ilErr->raiseError(get_class($this)."::checkTree(): No child found for ID ".
1315 $obj_ref[$this->obj_pk]."!",$this->ilErr->WARNING);
1316 }
1317 if ($r3->numRows() > 1)
1318 {
1319 $this->ilErr->raiseError(get_class($this)."::checkTree(): More childs found for ID ".
1320 $obj_ref[$this->obj_pk]."!",$this->ilErr->WARNING);
1321 }
1322
1323 }
1324 else
1325 {
1326 // get only object data
1327 $query = 'SELECT * FROM '.$this->table_obj_data.' WHERE '.$this->obj_pk.' = %s';
1328 $r2 = $ilDB->queryF($query,array('integer'),array($row['child']));
1329//echo "num_childs:".$r2->numRows().":<br>";
1330 if ($r2->numRows() == 0)
1331 {
1332 $this->ilErr->raiseError(get_class($this)."::checkTree(): No child found for ID ".
1333 $row["child"]."!",$this->ilErr->WARNING);
1334 }
1335 if ($r2->numRows() > 1)
1336 {
1337 $this->ilErr->raiseError(get_class($this)."::checkTree(): More childs found for ID ".
1338 $row["child"]."!",$this->ilErr->WARNING);
1339 }
1340 }
1341 }
1342
1343 return true;
1344 }
1345
1351 public function getMaximumDepth()
1352 {
1353 global $ilDB;
1354
1355 $query = 'SELECT MAX(depth) depth FROM '.$this->table_tree;
1356 $res = $ilDB->query($query);
1357
1358 $row = $ilDB->fetchAssoc($res);
1359 return $row['depth'];
1360 }
1361
1368 function getDepth($a_node_id)
1369 {
1370 global $ilDB;
1371
1372 if ($a_node_id)
1373 {
1374 $query = 'SELECT depth FROM '.$this->table_tree.' '.
1375 'WHERE child = %s '.
1376 'AND '.$this->tree_pk.' = %s ';
1377 $res = $ilDB->queryF($query,array('integer','integer'),array($a_node_id,$this->tree_id));
1378 $row = $ilDB->fetchObject($res);
1379
1380 return $row->depth;
1381 }
1382 else
1383 {
1384 return 1;
1385 }
1386 }
1387
1395 public function getNodeTreeData($a_node_id)
1396 {
1397 global $ilDB;
1398
1399 if(!$a_node_id)
1400 {
1401 $GLOBALS['ilLog']->logStack();
1402 throw new InvalidArgumentException('Missing or empty parameter $a_node_id: '. $a_node_id);
1403 }
1404
1405 $query = 'SELECT * FROM '.$this->table_tree.' '.
1406 'WHERE child = '.$ilDB->quote($a_node_id,'integer');
1407 $res = $ilDB->query($query);
1408 while($row = $res->fetchRow(DB_FETCHMODE_ASSOC))
1409 {
1410 return $row;
1411 }
1412 return array();
1413 }
1414
1415
1423 // BEGIN WebDAV: Pass tree id to this method
1424 //function getNodeData($a_node_id)
1425 function getNodeData($a_node_id, $a_tree_pk = null)
1426 // END PATCH WebDAV: Pass tree id to this method
1427 {
1428 global $ilDB;
1429
1430 if (!isset($a_node_id))
1431 {
1432 $GLOBALS['ilLog']->logStack();
1433 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1434 }
1435 if($this->__isMainTree())
1436 {
1437 if($a_node_id < 1)
1438 {
1439 $message = sprintf('%s::getNodeData(): No valid parameter given! $a_node_id: %s',
1440 get_class($this),
1441 $a_node_id);
1442
1443 $this->log->write($message,$this->log->FATAL);
1444 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
1445 }
1446 }
1447
1448 // BEGIN WebDAV: Pass tree id to this method
1449 $query = 'SELECT * FROM '.$this->table_tree.' '.
1450 $this->buildJoin().
1451 'WHERE '.$this->table_tree.'.child = %s '.
1452 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
1453 $res = $ilDB->queryF($query,array('integer','integer'),array(
1454 $a_node_id,
1455 $a_tree_pk === null ? $this->tree_id : $a_tree_pk));
1456 // END WebDAV: Pass tree id to this method
1457 $row = $ilDB->fetchAssoc($res);
1459
1460 return $this->fetchNodeData($row);
1461 }
1462
1470 function fetchNodeData($a_row)
1471 {
1472 global $objDefinition, $lng, $ilBench,$ilDB;
1473
1474 //$ilBench->start("Tree", "fetchNodeData_getRow");
1475 $data = $a_row;
1476 $data["desc"] = $a_row["description"]; // for compability
1477 //$ilBench->stop("Tree", "fetchNodeData_getRow");
1478
1479 // multilingual support systemobjects (sys) & categories (db)
1480 //$ilBench->start("Tree", "fetchNodeData_readDefinition");
1481 if (is_object($objDefinition))
1482 {
1483 $translation_type = $objDefinition->getTranslationType($data["type"]);
1484 }
1485 //$ilBench->stop("Tree", "fetchNodeData_readDefinition");
1486
1487 if ($translation_type == "sys")
1488 {
1489 //$ilBench->start("Tree", "fetchNodeData_getLangData");
1490 if ($data["type"] == "rolf" and $data["obj_id"] != ROLE_FOLDER_ID)
1491 {
1492 $data["description"] = $lng->txt("obj_".$data["type"]."_local_desc").$data["title"].$data["desc"];
1493 $data["desc"] = $lng->txt("obj_".$data["type"]."_local_desc").$data["title"].$data["desc"];
1494 $data["title"] = $lng->txt("obj_".$data["type"]."_local");
1495 }
1496 else
1497 {
1498 $data["title"] = $lng->txt("obj_".$data["type"]);
1499 $data["description"] = $lng->txt("obj_".$data["type"]."_desc");
1500 $data["desc"] = $lng->txt("obj_".$data["type"]."_desc");
1501 }
1502 //$ilBench->stop("Tree", "fetchNodeData_getLangData");
1503 }
1504 elseif ($translation_type == "db")
1505 {
1506
1507 // Try to retrieve object translation from cache
1508 if ($this->isCacheUsed() &&
1509 array_key_exists($data["obj_id"].'.'.$lang_code, $this->translation_cache)) {
1510
1511 $key = $data["obj_id"].'.'.$lang_code;
1512 $data["title"] = $this->translation_cache[$key]['title'];
1513 $data["description"] = $this->translation_cache[$key]['description'];
1514 $data["desc"] = $this->translation_cache[$key]['desc'];
1515 }
1516 else
1517 {
1518 // Object translation is not in cache, read it from database
1519 //$ilBench->start("Tree", "fetchNodeData_getTranslation");
1520 $query = 'SELECT title,description FROM object_translation '.
1521 'WHERE obj_id = %s '.
1522 'AND lang_code = %s '.
1523 'AND NOT lang_default = %s';
1524
1525 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
1526 $data['obj_id'],
1527 $this->lang_code,
1528 1));
1529 $row = $ilDB->fetchObject($res);
1530
1531 if ($row)
1532 {
1533 $data["title"] = $row->title;
1534 $data["description"] = ilUtil::shortenText($row->description,ilObject::DESC_LENGTH,true);
1535 $data["desc"] = $row->description;
1536 }
1537 //$ilBench->stop("Tree", "fetchNodeData_getTranslation");
1538
1539 // Store up to 1000 object translations in cache
1540 if ($this->isCacheUsed() && count($this->translation_cache) < 1000)
1541 {
1542 $key = $data["obj_id"].'.'.$lang_code;
1543 $this->translation_cache[$key] = array();
1544 $this->translation_cache[$key]['title'] = $data["title"] ;
1545 $this->translation_cache[$key]['description'] = $data["description"];
1546 $this->translation_cache[$key]['desc'] = $data["desc"];
1547 }
1548 }
1549 }
1550
1551 // TODO: Handle this switch by module.xml definitions
1552 if($data['type'] == 'crsr' or $data['type'] == 'catr')
1553 {
1554 include_once('./Services/ContainerReference/classes/class.ilContainerReference.php');
1555 $data['title'] = ilContainerReference::_lookupTitle($data['obj_id']);
1556 }
1557
1558 return $data ? $data : array();
1559 }
1560
1566 protected function fetchTranslationFromObjectDataCache($a_obj_ids)
1567 {
1568 global $ilObjDataCache;
1569
1570 if ($this->isCacheUsed() && is_array($a_obj_ids) && is_object($ilObjDataCache))
1571 {
1572 foreach ($a_obj_ids as $id)
1573 {
1574 $this->translation_cache[$id.'.']['title'] = $ilObjDataCache->lookupTitle($id);
1575 $this->translation_cache[$id.'.']['description'] = $ilObjDataCache->lookupDescription($id);;
1576 $this->translation_cache[$id.'.']['desc'] =
1577 $this->translation_cache[$id.'.']['description'];
1578 }
1579 }
1580 }
1581
1582
1590 function isInTree($a_node_id)
1591 {
1592 global $ilDB;
1593
1594 if (!isset($a_node_id))
1595 {
1596 return false;
1597 #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1598 }
1599 // is in tree cache
1600 if ($this->isCacheUsed() && isset($this->in_tree_cache[$a_node_id]))
1601 {
1602 #$GLOBALS['ilLog']->write(__METHOD__.': Using in tree cache '.$a_node_id);
1603//echo "<br>in_tree_hit";
1604 return $this->in_tree_cache[$a_node_id];
1605 }
1606
1607 $query = 'SELECT * FROM '.$this->table_tree.' '.
1608 'WHERE '.$this->table_tree.'.child = %s '.
1609 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s';
1610
1611 $res = $ilDB->queryF($query,array('integer','integer'),array(
1612 $a_node_id,
1613 $this->tree_id));
1614
1615 if ($res->numRows() > 0)
1616 {
1617 if($this->__isMainTree())
1618 {
1619 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = true');
1620 $this->in_tree_cache[$a_node_id] = true;
1621 }
1622 return true;
1623 }
1624 else
1625 {
1626 if($this->__isMainTree())
1627 {
1628 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = false');
1629 $this->in_tree_cache[$a_node_id] = false;
1630 }
1631 return false;
1632 }
1633 }
1634
1642 public function getParentNodeData($a_node_id)
1643 {
1644 global $ilDB;
1645 global $ilLog;
1646
1647 if (!isset($a_node_id))
1648 {
1649 $ilLog->logStack();
1650 throw new InvalidArgumentException(__METHOD__.': No node_id given!');
1651 }
1652
1653 if ($this->table_obj_reference)
1654 {
1655 // Use inner join instead of left join to improve performance
1656 $innerjoin = "JOIN ".$this->table_obj_reference." ON v.child=".$this->table_obj_reference.".".$this->ref_pk." ".
1657 "JOIN ".$this->table_obj_data." ON ".$this->table_obj_reference.".".$this->obj_pk."=".$this->table_obj_data.".".$this->obj_pk." ";
1658 }
1659 else
1660 {
1661 // Use inner join instead of left join to improve performance
1662 $innerjoin = "JOIN ".$this->table_obj_data." ON v.child=".$this->table_obj_data.".".$this->obj_pk." ";
1663 }
1664
1665 $query = 'SELECT * FROM '.$this->table_tree.' s, '.$this->table_tree.' v '.
1666 $innerjoin.
1667 'WHERE s.child = %s '.
1668 'AND s.parent = v.child '.
1669 'AND s.'.$this->tree_pk.' = %s '.
1670 'AND v.'.$this->tree_pk.' = %s';
1671 $res = $ilDB->queryF($query,array('integer','integer','integer'),array(
1672 $a_node_id,
1673 $this->tree_id,
1674 $this->tree_id));
1675 $row = $ilDB->fetchAssoc($res);
1676 return $this->fetchNodeData($row);
1677 }
1678
1686 public function isGrandChild($a_startnode_id,$a_querynode_id)
1687 {
1688 return $this->getRelation($a_startnode_id, $a_querynode_id) == self::RELATION_PARENT;
1689 }
1690
1699 function addTree($a_tree_id,$a_node_id = -1)
1700 {
1701 global $ilDB;
1702
1703 // FOR SECURITY addTree() IS NOT ALLOWED ON MAIN TREE
1704 if($this->__isMainTree())
1705 {
1706 $message = sprintf('%s::addTree(): Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
1707 get_class($this),
1708 $a_tree_id,
1709 $a_node_id);
1710 $this->log->write($message,$this->log->FATAL);
1711 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
1712 }
1713
1714 if (!isset($a_tree_id))
1715 {
1716 $this->ilErr->raiseError(get_class($this)."::addTree(): No tree_id given! ",$this->ilErr->WARNING);
1717 }
1718
1719 if ($a_node_id <= 0)
1720 {
1721 $a_node_id = $a_tree_id;
1722 }
1723
1724 $query = 'INSERT INTO '.$this->table_tree.' ('.
1725 $this->tree_pk.', child,parent,lft,rgt,depth) '.
1726 'VALUES '.
1727 '(%s,%s,%s,%s,%s,%s)';
1728 $res = $ilDB->manipulateF($query,array('integer','integer','integer','integer','integer','integer'),array(
1729 $a_tree_id,
1730 $a_node_id,
1731 0,
1732 1,
1733 2,
1734 1));
1735
1736 return true;
1737 }
1738
1747 public function getNodeDataByType($a_type)
1748 {
1749 global $ilDB;
1750
1751 if(!isset($a_type) or (!is_string($a_type)))
1752 {
1753 $GLOBALS['ilLog']->logStack();
1754 throw new InvalidArgumentException('Type not given or wrong datatype');
1755 }
1756
1757 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1758 $this->buildJoin().
1759 'WHERE ' . $this->table_obj_data . '.type = ' . $this->ilDB->quote($a_type, 'text').
1760 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer');
1761
1762 $res = $ilDB->query($query);
1763 $data = array();
1764 while($row = $ilDB->fetchAssoc($res))
1765 {
1766 $data[] = $this->fetchNodeData($row);
1767 }
1768
1769 return $data;
1770 }
1771
1779 public function removeTree($a_tree_id)
1780 {
1781 global $ilDB;
1782
1783 // OPERATION NOT ALLOWED ON MAIN TREE
1784 if($this->__isMainTree())
1785 {
1786 $GLOBALS['ilLog']->logStack();
1787 throw new InvalidArgumentException('Operation not allowed on main tree');
1788 }
1789 if (!$a_tree_id)
1790 {
1791 $GLOBALS['ilLog']->logStack();
1792 throw new InvalidArgumentException('Missing parameter tree id');
1793 }
1794
1795 $query = 'DELETE FROM '.$this->table_tree.
1796 ' WHERE '.$this->tree_pk.' = %s ';
1797 $ilDB->manipulateF($query,array('integer'),array($a_tree_id));
1798 return true;
1799 }
1800
1807 public function moveToTrash($a_node_id, $a_set_deleted = false)
1808 {
1809 return $this->saveSubTree($a_node_id, $a_set_deleted);
1810 }
1811
1822 public function saveSubTree($a_node_id, $a_set_deleted = false)
1823 {
1824 global $ilDB;
1825
1826 if(!$a_node_id)
1827 {
1828 $GLOBALS['ilLog']->logStack();
1829 throw new InvalidArgumentException('No valid parameter given! $a_node_id: '.$a_node_id);
1830 }
1831
1832 // LOCKED ###############################################
1833 if($this->__isMainTree())
1834 {
1835 $ilDB->lockTables(
1836 array(
1837 0 => array('name' => 'tree', 'type' => ilDB::LOCK_WRITE),
1838 1 => array('name' => 'object_reference', 'type' => ilDB::LOCK_WRITE)));
1839 }
1840
1841 $query = $this->getTreeImplementation()->getSubTreeQuery($this->getNodeTreeData($a_node_id),'',false);
1842 $res = $ilDB->query($query);
1843
1844 $subnodes = array();
1845 while($row = $res->fetchRow(DB_FETCHMODE_ASSOC))
1846 {
1847 $subnodes[] = $row['child'];
1848 }
1849
1850 if(!count($subnodes))
1851 {
1852 // Possibly already deleted
1853 // Unlock locked tables before returning
1854 if($this->__isMainTree())
1855 {
1856 $ilDB->unlockTables();
1857 }
1858 return false;
1859 }
1860
1861 if($a_set_deleted)
1862 {
1863 include_once './Services/Object/classes/class.ilObject.php';
1864 ilObject::setDeletedDates($subnodes);
1865 }
1866
1867 // netsted set <=> mp
1868 $this->getTreeImplementation()->moveToTrash($a_node_id);
1869
1870 if($this->__isMainTree())
1871 {
1872 $ilDB->unlockTables();
1873 }
1874
1875 // LOCKED ###############################################
1876 return true;
1877 }
1878
1883 public function isDeleted($a_node_id)
1884 {
1885 return $this->isSaved($a_node_id);
1886 }
1887
1893 public function isSaved($a_node_id)
1894 {
1895 global $ilDB;
1896
1897 // is saved cache
1898 if ($this->isCacheUsed() && isset($this->is_saved_cache[$a_node_id]))
1899 {
1900//echo "<br>issavedhit";
1901 return $this->is_saved_cache[$a_node_id];
1902 }
1903
1904 $query = 'SELECT '.$this->tree_pk.' FROM '.$this->table_tree.' '.
1905 'WHERE child = %s ';
1906 $res = $ilDB->queryF($query,array('integer'),array($a_node_id));
1907 $row = $ilDB->fetchAssoc($res);
1908
1909 if ($row[$this->tree_pk] < 0)
1910 {
1911 if($this->__isMainTree())
1912 {
1913 $this->is_saved_cache[$a_node_id] = true;
1914 }
1915 return true;
1916 }
1917 else
1918 {
1919 if($this->__isMainTree())
1920 {
1921 $this->is_saved_cache[$a_node_id] = false;
1922 }
1923 return false;
1924 }
1925 }
1926
1933 public function preloadDeleted($a_node_ids)
1934 {
1935 global $ilDB;
1936
1937 if (!is_array($a_node_ids) || !$this->isCacheUsed())
1938 {
1939 return;
1940 }
1941
1942 $query = 'SELECT '.$this->tree_pk.', child FROM '.$this->table_tree.' '.
1943 'WHERE '.$ilDB->in("child", $a_node_ids, false, "integer");
1944
1945 $res = $ilDB->query($query);
1946 while ($row = $ilDB->fetchAssoc($res))
1947 {
1948 if ($row[$this->tree_pk] < 0)
1949 {
1950 if($this->__isMainTree())
1951 {
1952 $this->is_saved_cache[$row["child"]] = true;
1953 }
1954 }
1955 else
1956 {
1957 if($this->__isMainTree())
1958 {
1959 $this->is_saved_cache[$row["child"]] = false;
1960 }
1961 }
1962 }
1963 }
1964
1965
1972 function getSavedNodeData($a_parent_id)
1973 {
1974 global $ilDB;
1975
1976 if (!isset($a_parent_id))
1977 {
1978 $this->ilErr->raiseError(get_class($this)."::getSavedNodeData(): No node_id given!",$this->ilErr->WARNING);
1979 }
1980
1981 $query = 'SELECT * FROM '.$this->table_tree.' '.
1982 $this->buildJoin().
1983 'WHERE '.$this->table_tree.'.'.$this->tree_pk.' < %s '.
1984 'AND '.$this->table_tree.'.parent = %s';
1985 $res = $ilDB->queryF($query,array('integer','integer'),array(
1986 0,
1987 $a_parent_id));
1988
1989 while($row = $ilDB->fetchAssoc($res))
1990 {
1991 $saved[] = $this->fetchNodeData($row);
1992 }
1993
1994 return $saved ? $saved : array();
1995 }
1996
2003 function getSavedNodeObjIds(array $a_obj_ids)
2004 {
2005 global $ilDB;
2006
2007 $query = 'SELECT '.$this->table_obj_data.'.obj_id FROM '.$this->table_tree.' '.
2008 $this->buildJoin().
2009 'WHERE '.$this->table_tree.'.'.$this->tree_pk.' < '.$ilDB->quote(0, 'integer').' '.
2010 'AND '.$ilDB->in($this->table_obj_data.'.obj_id', $a_obj_ids, '', 'integer');
2011 $res = $ilDB->query($query);
2012 while($row = $ilDB->fetchAssoc($res))
2013 {
2014 $saved[] = $row['obj_id'];
2015 }
2016
2017 return $saved ? $saved : array();
2018 }
2019
2026 function getParentId($a_node_id)
2027 {
2028 global $ilDB;
2029
2030 if (!isset($a_node_id))
2031 {
2032 $this->ilErr->raiseError(get_class($this)."::getParentId(): No node_id given! ",$this->ilErr->WARNING);
2033 }
2034
2035 $query = 'SELECT parent FROM '.$this->table_tree.' '.
2036 'WHERE child = %s '.
2037 'AND '.$this->tree_pk.' = %s ';
2038 $res = $ilDB->queryF($query,array('integer','integer'),array(
2039 $a_node_id,
2040 $this->tree_id));
2041
2042 $row = $ilDB->fetchObject($res);
2043 return $row->parent;
2044 }
2045
2052 function getLeftValue($a_node_id)
2053 {
2054 global $ilDB;
2055
2056 if (!isset($a_node_id))
2057 {
2058 $this->ilErr->raiseError(get_class($this)."::getLeftValued(): No node_id given! ",$this->ilErr->WARNING);
2059 }
2060
2061 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2062 'WHERE child = %s '.
2063 'AND '.$this->tree_pk.' = %s ';
2064 $res = $ilDB->queryF($query,array('integer','integer'),array(
2065 $a_node_id,
2066 $this->tree_id));
2067 $row = $ilDB->fetchObject($res);
2068 return $row->lft;
2069 }
2070
2077 function getChildSequenceNumber($a_node, $type = "")
2078 {
2079 global $ilDB;
2080
2081 if (!isset($a_node))
2082 {
2083 $this->ilErr->raiseError(get_class($this)."::getChildSequenceNumber(): No node_id given! ",$this->ilErr->WARNING);
2084 }
2085
2086 if($type)
2087 {
2088 $query = 'SELECT count(*) cnt FROM '.$this->table_tree.' '.
2089 $this->buildJoin().
2090 'WHERE lft <= %s '.
2091 'AND type = %s '.
2092 'AND parent = %s '.
2093 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2094
2095 $res = $ilDB->queryF($query,array('integer','text','integer','integer'),array(
2096 $a_node['lft'],
2097 $type,
2098 $a_node['parent'],
2099 $this->tree_id));
2100 }
2101 else
2102 {
2103 $query = 'SELECT count(*) cnt FROM '.$this->table_tree.' '.
2104 $this->buildJoin().
2105 'WHERE lft <= %s '.
2106 'AND parent = %s '.
2107 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2108
2109 $res = $ilDB->queryF($query,array('integer','integer','integer'),array(
2110 $a_node['lft'],
2111 $a_node['parent'],
2112 $this->tree_id));
2113
2114 }
2115 $row = $ilDB->fetchAssoc($res);
2116 return $row["cnt"];
2117 }
2118
2125 function readRootId()
2126 {
2127 global $ilDB;
2128
2129 $query = 'SELECT child FROM '.$this->table_tree.' '.
2130 'WHERE parent = %s '.
2131 'AND '.$this->tree_pk.' = %s ';
2132 $res = $ilDB->queryF($query,array('integer','integer'),array(
2133 0,
2134 $this->tree_id));
2135 $row = $ilDB->fetchObject($res);
2136 $this->root_id = $row->child;
2137 return $this->root_id;
2138 }
2139
2145 function getRootId()
2146 {
2147 return $this->root_id;
2148 }
2149 function setRootId($a_root_id)
2150 {
2151 $this->root_id = $a_root_id;
2152 }
2153
2159 function getTreeId()
2160 {
2161 return $this->tree_id;
2162 }
2163
2169 function setTreeId($a_tree_id)
2170 {
2171 $this->tree_id = $a_tree_id;
2172 }
2173
2181 function fetchSuccessorNode($a_node_id, $a_type = "")
2182 {
2183 global $ilDB;
2184
2185 if (!isset($a_node_id))
2186 {
2187 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
2188 }
2189
2190 // get lft value for current node
2191 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2192 'WHERE '.$this->table_tree.'.child = %s '.
2193 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2194 $res = $ilDB->queryF($query,array('integer','integer'),array(
2195 $a_node_id,
2196 $this->tree_id));
2197 $curr_node = $ilDB->fetchAssoc($res);
2198
2199 if($a_type)
2200 {
2201 $query = 'SELECT * FROM '.$this->table_tree.' '.
2202 $this->buildJoin().
2203 'WHERE lft > %s '.
2204 'AND '.$this->table_obj_data.'.type = %s '.
2205 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2206 'ORDER BY lft ';
2207 $ilDB->setLimit(1);
2208 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
2209 $curr_node['lft'],
2210 $a_type,
2211 $this->tree_id));
2212 }
2213 else
2214 {
2215 $query = 'SELECT * FROM '.$this->table_tree.' '.
2216 $this->buildJoin().
2217 'WHERE lft > %s '.
2218 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2219 'ORDER BY lft ';
2220 $ilDB->setLimit(1);
2221 $res = $ilDB->queryF($query,array('integer','integer'),array(
2222 $curr_node['lft'],
2223 $this->tree_id));
2224 }
2225
2226 if ($res->numRows() < 1)
2227 {
2228 return false;
2229 }
2230 else
2231 {
2232 $row = $ilDB->fetchAssoc($res);
2233 return $this->fetchNodeData($row);
2234 }
2235 }
2236
2244 function fetchPredecessorNode($a_node_id, $a_type = "")
2245 {
2246 global $ilDB;
2247
2248 if (!isset($a_node_id))
2249 {
2250 $this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
2251 }
2252
2253 // get lft value for current node
2254 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2255 'WHERE '.$this->table_tree.'.child = %s '.
2256 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2257 $res = $ilDB->queryF($query,array('integer','integer'),array(
2258 $a_node_id,
2259 $this->tree_id));
2260
2261 $curr_node = $ilDB->fetchAssoc($res);
2262
2263 if($a_type)
2264 {
2265 $query = 'SELECT * FROM '.$this->table_tree.' '.
2266 $this->buildJoin().
2267 'WHERE lft < %s '.
2268 'AND '.$this->table_obj_data.'.type = %s '.
2269 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2270 'ORDER BY lft DESC';
2271 $ilDB->setLimit(1);
2272 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
2273 $curr_node['lft'],
2274 $a_type,
2275 $this->tree_id));
2276 }
2277 else
2278 {
2279 $query = 'SELECT * FROM '.$this->table_tree.' '.
2280 $this->buildJoin().
2281 'WHERE lft < %s '.
2282 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2283 'ORDER BY lft DESC';
2284 $ilDB->setLimit(1);
2285 $res = $ilDB->queryF($query,array('integer','integer'),array(
2286 $curr_node['lft'],
2287 $this->tree_id));
2288 }
2289
2290 if ($res->numRows() < 1)
2291 {
2292 return false;
2293 }
2294 else
2295 {
2296 $row = $ilDB->fetchAssoc($res);
2297 return $this->fetchNodeData($row);
2298 }
2299 }
2300
2309 function renumber($node_id = 1, $i = 1)
2310 {
2311 global $ilDB;
2312
2313 // LOCKED ###################################
2314 if($this->__isMainTree())
2315 {
2316 /*
2317 ilDB::_lockTables(array($this->table_tree => 'WRITE',
2318 $this->table_obj_data => 'WRITE',
2319 $this->table_obj_reference => 'WRITE',
2320 'object_translation' => 'WRITE',
2321 'object_data od' => 'WRITE',
2322 'container_reference cr' => 'WRITE'));
2323 */
2324 $ilDB->lockTables(
2325 array(
2326 0 => array('name' => $this->table_tree, 'type' => ilDB::LOCK_WRITE),
2327 1 => array('name' => $this->table_obj_data, 'type' => ilDB::LOCK_WRITE),
2328 2 => array('name' => $this->table_obj_reference, 'type' => ilDB::LOCK_WRITE),
2329 3 => array('name' => 'object_translation', 'type' => ilDB::LOCK_WRITE),
2330 4 => array('name' => 'object_data', 'type' => ilDB::LOCK_WRITE, 'alias' => 'od'),
2331 5 => array('name' => 'container_reference', 'type' => ilDB::LOCK_WRITE, 'alias' => 'cr')
2332 ));
2333 }
2334 $return = $this->__renumber($node_id,$i);
2335 if($this->__isMainTree())
2336 {
2337 $ilDB->unlockTables();
2338 }
2339 // LOCKED ###################################
2340 return $return;
2341 }
2342
2343 // PRIVATE
2353 function __renumber($node_id = 1, $i = 1)
2354 {
2355 global $ilDB;
2356
2357 $query = 'UPDATE '.$this->table_tree.' SET lft = %s WHERE child = %s';
2358 $res = $ilDB->manipulateF($query,array('integer','integer'),array(
2359 $i,
2360 $node_id));
2361
2362 $childs = $this->getChilds($node_id);
2363
2364 foreach ($childs as $child)
2365 {
2366 $i = $this->__renumber($child["child"],$i+1);
2367 }
2368 $i++;
2369
2370 // Insert a gap at the end of node, if the node has children
2371 if (count($childs) > 0)
2372 {
2373 $i += $this->gap * 2;
2374 }
2375
2376
2377 $query = 'UPDATE '.$this->table_tree.' SET rgt = %s WHERE child = %s';
2378 $res = $ilDB->manipulateF($query,array('integer','integer'),array(
2379 $i,
2380 $node_id));
2381 return $i;
2382 }
2383
2384
2395 function checkForParentType($a_ref_id,$a_type,$a_exclude_source_check = false)
2396 {
2397 // #12577
2398 $cache_key = $a_ref_id.'.'.$a_type.'.'.((int)$a_exclude_source_check);
2399
2400 // Try to return a cached result
2401 if($this->isCacheUsed() &&
2402 array_key_exists($cache_key, $this->parent_type_cache))
2403 {
2404 return $this->parent_type_cache[$cache_key];
2405 }
2406
2407 // Store up to 1000 results in cache
2408 $do_cache = ($this->__isMainTree() && count($this->parent_type_cache) < 1000);
2409
2410 // ref_id is not in tree
2411 if(!$this->isInTree($a_ref_id))
2412 {
2413 if($do_cache)
2414 {
2415 $this->parent_type_cache[$cache_key] = false;
2416 }
2417 return false;
2418 }
2419
2420 $path = array_reverse($this->getPathFull($a_ref_id));
2421
2422 // remove first path entry as it is requested node
2423 if($a_exclude_source_check)
2424 {
2425 array_shift($path);
2426 }
2427
2428 foreach($path as $node)
2429 {
2430 // found matching parent
2431 if($node["type"] == $a_type)
2432 {
2433 if($do_cache)
2434 {
2435 $this->parent_type_cache[$cache_key] = $node["child"];
2436 }
2437 return $node["child"];
2438 }
2439 }
2440
2441 if($do_cache)
2442 {
2443 $this->parent_type_cache[$cache_key] = false;
2444 }
2445 return 0;
2446 }
2447
2457 function _removeEntry($a_tree,$a_child,$a_db_table = "tree")
2458 {
2459 global $ilDB,$ilLog,$ilErr;
2460
2461 if($a_db_table === 'tree')
2462 {
2463 if($a_tree == 1 and $a_child == ROOT_FOLDER_ID)
2464 {
2465 $message = sprintf('%s::_removeEntry(): Tried to delete root node! $a_tree: %s $a_child: %s',
2466 get_class($this),
2467 $a_tree,
2468 $a_child);
2469 $ilLog->write($message,$ilLog->FATAL);
2470 $ilErr->raiseError($message,$ilErr->WARNING);
2471 }
2472 }
2473
2474 $query = 'DELETE FROM '.$a_db_table.' '.
2475 'WHERE tree = %s '.
2476 'AND child = %s ';
2477 $res = $ilDB->manipulateF($query,array('integer','integer'),array(
2478 $a_tree,
2479 $a_child));
2480
2481 }
2482
2489 public function __isMainTree()
2490 {
2491 return $this->table_tree === 'tree';
2492 }
2493
2504 function __checkDelete($a_node)
2505 {
2506 global $ilDB;
2507
2508
2509 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, array(),false);
2510 $GLOBALS['ilLog']->write(__METHOD__.': '.$query);
2511 $res = $ilDB->query($query);
2512
2513 $counter = (int) $lft_childs = array();
2514 while($row = $ilDB->fetchObject($res))
2515 {
2516 $lft_childs[$row->child] = $row->parent;
2517 ++$counter;
2518 }
2519
2520 // CHECK FOR DUPLICATE CHILD IDS
2521 if($counter != count($lft_childs))
2522 {
2523 $message = sprintf('%s::__checkTree(): Duplicate entries for "child" in maintree! $a_node_id: %s',
2524 get_class($this),
2525 $a_node['child']);
2526 $this->log->write($message,$this->log->FATAL);
2527 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2528 }
2529
2530 // GET SUBTREE BY PARENT RELATION
2531 $parent_childs = array();
2532 $this->__getSubTreeByParentRelation($a_node['child'],$parent_childs);
2533 $this->__validateSubtrees($lft_childs,$parent_childs);
2534
2535 return true;
2536 }
2537
2546 function __getSubTreeByParentRelation($a_node_id,&$parent_childs)
2547 {
2548 global $ilDB;
2549
2550 // GET PARENT ID
2551 $query = 'SELECT * FROM '.$this->table_tree.' '.
2552 'WHERE child = %s '.
2553 'AND tree = %s ';
2554 $res = $ilDB->queryF($query,array('integer','integer'),array(
2555 $a_node_id,
2556 $this->tree_id));
2557
2558 $counter = 0;
2559 while($row = $ilDB->fetchObject($res))
2560 {
2561 $parent_childs[$a_node_id] = $row->parent;
2562 ++$counter;
2563 }
2564 // MULTIPLE ENTRIES
2565 if($counter > 1)
2566 {
2567 $message = sprintf('%s::__getSubTreeByParentRelation(): Multiple entries in maintree! $a_node_id: %s',
2568 get_class($this),
2569 $a_node_id);
2570 $this->log->write($message,$this->log->FATAL);
2571 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2572 }
2573
2574 // GET ALL CHILDS
2575 $query = 'SELECT * FROM '.$this->table_tree.' '.
2576 'WHERE parent = %s ';
2577 $res = $ilDB->queryF($query,array('integer'),array($a_node_id));
2578
2579 while($row = $ilDB->fetchObject($res))
2580 {
2581 // RECURSION
2582 $this->__getSubTreeByParentRelation($row->child,$parent_childs);
2583 }
2584 return true;
2585 }
2586
2587 function __validateSubtrees(&$lft_childs,$parent_childs)
2588 {
2589 // SORT BY KEY
2590 ksort($lft_childs);
2591 ksort($parent_childs);
2592
2593 $GLOBALS['ilLog']->write(__METHOD__.': left childs '. print_r($lft_childs,true));
2594 $GLOBALS['ilLog']->write(__METHOD__.': parent childs '. print_r($parent_childs,true));
2595
2596 if(count($lft_childs) != count($parent_childs))
2597 {
2598 $message = sprintf('%s::__validateSubtrees(): (COUNT) Tree is corrupted! Left/Right subtree does not comply .'.
2599 'with parent relation',
2600 get_class($this));
2601 $this->log->write($message,$this->log->FATAL);
2602 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2603 }
2604
2605
2606 foreach($lft_childs as $key => $value)
2607 {
2608 if($parent_childs[$key] != $value)
2609 {
2610 $message = sprintf('%s::__validateSubtrees(): (COMPARE) Tree is corrupted! Left/Right subtree does not comply '.
2611 'with parent relation',
2612 get_class($this));
2613 $this->log->write($message,$this->log->FATAL);
2614 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2615 }
2616 if($key == ROOT_FOLDER_ID)
2617 {
2618 $message = sprintf('%s::__validateSubtrees(): (ROOT_FOLDER) Tree is corrupted! Tried to delete root folder',
2619 get_class($this));
2620 $this->log->write($message,$this->log->FATAL);
2621 $this->ilErr->raiseError($message,$this->ilErr->WARNING);
2622 }
2623 }
2624 return true;
2625 }
2626
2636 public function moveTree($a_source_id, $a_target_id, $a_location = self::POS_LAST_NODE)
2637 {
2638 $this->getTreeImplementation()->moveTree($a_source_id,$a_target_id,$a_location);
2639 $GLOBALS['ilAppEventHandler']->raise(
2640 "Services/Tree",
2641 "moveTree",
2642 array(
2643 'tree' => $this->table_tree,
2644 'source_id' => $a_source_id,
2645 'target_id' => $a_target_id)
2646 );
2647 return true;
2648 }
2649
2650
2651
2652
2660 public function getRbacSubtreeInfo($a_endnode_id)
2661 {
2662 return $this->getTreeImplementation()->getSubtreeInfo($a_endnode_id);
2663 }
2664
2665
2673 public function getSubTreeQuery($a_node_id,$a_fields = array(), $a_types = '', $a_force_join_reference = false)
2674 {
2675 return $this->getTreeImplementation()->getSubTreeQuery(
2676 $this->getNodeTreeData($a_node_id),
2677 $a_types,
2678 $a_force_join_reference,
2679 $a_fields);
2680 }
2681
2682
2691 public function getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields = array())
2692 {
2693 global $ilDB;
2694
2695 $node = $this->getNodeData($a_node_id);
2696 if(!sizeof($node))
2697 {
2698 return;
2699 }
2700
2701 $res = array();
2702
2703 $query = $this->getTreeImplementation()->getSubTreeQuery($node, '', true, array($this->ref_pk));
2704
2705 $fields = '*';
2706 if(count($a_fields))
2707 {
2708 $fields = implode(',',$a_fields);
2709 }
2710
2711 $query = "SELECT ".$fields.
2712 " FROM ".$this->getTreeTable().
2713 " ".$this->buildJoin().
2714 " WHERE ".$this->getTableReference().".".$this->ref_pk." IN (".$query.")".
2715 " AND ".$ilDB->in($this->getObjectDataTable().".".$this->obj_pk, $a_obj_ids, "", "integer");
2716 $set = $ilDB->query($query);
2717 while($row = $ilDB->fetchAssoc($set))
2718 {
2719 $res[] = $row;
2720 }
2721
2722 return $res;
2723 }
2724
2725 public function deleteNode($a_tree_id,$a_node_id)
2726 {
2727 global $ilDB;
2728
2729 $query = 'DELETE FROM tree where '.
2730 'child = '.$ilDB->quote($a_node_id,'integer').' '.
2731 'AND tree = '.$ilDB->quote($a_tree_id,'integer');
2732 $ilDB->manipulate($query);
2733 }
2734
2735
2736} // END class.tree
2737?>
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.
Error Handling & global info handling uses PEAR error class.
Thrown if invalid tree strucutes are found.
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:89
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
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
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)
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.
$GLOBALS['ct_recipient']
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