ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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
148 function __construct($a_tree_id, $a_root_id = 0)
149 {
150 global $ilDB;
151
152 // set db
153 $this->ilDB = $ilDB;
154
155 $this->lang_code = "en";
156
157 // CREATE LOGGER INSTANCE
158 $this->log = ilLoggerFactory::getLogger('tree');
159
160 if (!isset($a_tree_id) or (func_num_args() == 0) )
161 {
162 $this->log->error("No tree_id given!");
163 $this->log->logStack(ilLogLevel::DEBUG);
164 throw new InvalidArgumentException("No tree_id given!");
165 }
166
167 if (func_num_args() > 2)
168 {
169 $this->log->error("Wrong parameter count!");
170 throw new InvalidArgumentException("Wrong parameter count!");
171 }
172
173 //init variables
174 if (empty($a_root_id))
175 {
176 $a_root_id = ROOT_FOLDER_ID;
177 }
178
179 $this->tree_id = $a_tree_id;
180 $this->root_id = $a_root_id;
181 $this->table_tree = 'tree';
182 $this->table_obj_data = 'object_data';
183 $this->table_obj_reference = 'object_reference';
184 $this->ref_pk = 'ref_id';
185 $this->obj_pk = 'obj_id';
186 $this->tree_pk = 'tree';
187
188 $this->use_cache = true;
189
190 // If cache is activated, cache object translations to improve performance
191 $this->translation_cache = array();
192 $this->parent_type_cache = array();
193
194 // By default, we create gaps in the tree sequence numbering for 50 nodes
195 $this->gap = 50;
196
197
198 // init tree implementation
199 $this->initTreeImplementation();
200 }
201
205 public function initTreeImplementation()
206 {
207 global $ilDB;
208
209
210 if(!is_object($GLOBALS['ilSetting']) or $GLOBALS['ilSetting']->getModule() != 'common')
211 {
212 include_once './Services/Administration/classes/class.ilSetting.php';
213 $setting = new ilSetting('common');
214 }
215 else
216 {
217 $setting = $GLOBALS['ilSetting'];
218 }
219
220 if($this->__isMainTree())
221 {
222 if($setting->get('main_tree_impl','ns') == 'ns')
223 {
224 #$GLOBALS['ilLog']->write(__METHOD__.': Using nested set.');
225 include_once './Services/Tree/classes/class.ilNestedSetTree.php';
226 $this->tree_impl = new ilNestedSetTree($this);
227 }
228 else
229 {
230 #$GLOBALS['ilLog']->write(__METHOD__.': Using materialized path.');
231 include_once './Services/Tree/classes/class.ilMaterializedPathTree.php';
232 $this->tree_impl = new ilMaterializedPathTree($this);
233 }
234 }
235 else
236 {
237 #$GLOBALS['ilLog']->write(__METHOD__.': Using netsted set for non main tree.');
238 include_once './Services/Tree/classes/class.ilNestedSetTree.php';
239 $this->tree_impl = new ilNestedSetTree($this);
240 }
241 }
242
247 public function getTreeImplementation()
248 {
249 return $this->tree_impl;
250 }
251
255 public function useCache($a_use = true)
256 {
257 $this->use_cache = $a_use;
258 }
259
264 public function isCacheUsed()
265 {
266 return $this->__isMainTree() and $this->use_cache;
267 }
268
273 public function getDepthCache()
274 {
275 return (array) $this->depth_cache;
276 }
277
282 public function getParentCache()
283 {
284 return (array) $this->parent_cache;
285 }
286
291 function initLangCode()
292 {
293 global $ilUser;
294
295 // lang_code is only required in $this->fetchnodedata
296 if (!is_object($ilUser))
297 {
298 $this->lang_code = "en";
299 }
300 else
301 {
302 $this->lang_code = $ilUser->getCurrentLanguage();
303 }
304 }
305
310 public function getTreeTable()
311 {
312 return $this->table_tree;
313 }
314
319 public function getObjectDataTable()
320 {
322 }
323
328 public function getTreePk()
329 {
330 return $this->tree_pk;
331 }
332
336 public function getTableReference()
337 {
339 }
340
344 public function getGap()
345 {
346 return $this->gap;
347 }
348
349 /***
350 * reset in tree cache
351 */
352 public function resetInTreeCache()
353 {
354 $this->in_tree_cache = array();
355 }
356
357
374 function setTableNames($a_table_tree,$a_table_obj_data,$a_table_obj_reference = "")
375 {
376 if (!isset($a_table_tree) or !isset($a_table_obj_data))
377 {
378 $message = "Missing parameter! ".
379 "tree table: ".$a_table_tree." object data table: ".$a_table_obj_data;
380 $this->log->error($message);
381 throw new InvalidArgumentException($message);
382 }
383
384 $this->table_tree = $a_table_tree;
385 $this->table_obj_data = $a_table_obj_data;
386 $this->table_obj_reference = $a_table_obj_reference;
387
388 $this->initTreeImplementation();
389
390 return true;
391 }
392
400 function setReferenceTablePK($a_column_name)
401 {
402 if (!isset($a_column_name))
403 {
404 $message = "No column name given!";
405 $this->log->error($message);
406 throw new InvalidArgumentException($message);
407 }
408
409 $this->ref_pk = $a_column_name;
410 return true;
411 }
412
420 function setObjectTablePK($a_column_name)
421 {
422 if (!isset($a_column_name))
423 {
424 $message = "No column name given!";
425 $this->log->error($message);
426 throw new InvalidArgumentException($message);
427 }
428
429 $this->obj_pk = $a_column_name;
430 return true;
431 }
432
440 function setTreeTablePK($a_column_name)
441 {
442 if (!isset($a_column_name))
443 {
444 $message = "No column name given!";
445 $this->log->error($message);
446 throw new InvalidArgumentException($message);
447 }
448
449 $this->tree_pk = $a_column_name;
450 return true;
451 }
452
458 function buildJoin()
459 {
460 if ($this->table_obj_reference)
461 {
462 // Use inner join instead of left join to improve performance
463 return "JOIN ".$this->table_obj_reference." ON ".$this->table_tree.".child=".$this->table_obj_reference.".".$this->ref_pk." ".
464 "JOIN ".$this->table_obj_data." ON ".$this->table_obj_reference.".".$this->obj_pk."=".$this->table_obj_data.".".$this->obj_pk." ";
465 }
466 else
467 {
468 // Use inner join instead of left join to improve performance
469 return "JOIN ".$this->table_obj_data." ON ".$this->table_tree.".child=".$this->table_obj_data.".".$this->obj_pk." ";
470 }
471 }
472
478 public function getRelation($a_node_a, $a_node_b)
479 {
480 return $this->getRelationOfNodes(
481 $this->getNodeTreeData($a_node_a),
482 $this->getNodeTreeData($a_node_b)
483 );
484 }
485
492 public function getRelationOfNodes($a_node_a_arr, $a_node_b_arr)
493 {
494 return $this->getTreeImplementation()->getRelation($a_node_a_arr, $a_node_b_arr);
495 }
496
503 public function getChildIds($a_node)
504 {
505 global $ilDB;
506
507 $query = 'SELECT * FROM '. $this->table_tree . ' ' .
508 'WHERE parent = '.$ilDB->quote($a_node,'integer').' '.
509 'AND tree = '.$ilDB->quote($this->tree_id,'integer' . ' ' .
510 'ORDER BY lft');
511 $res = $ilDB->query($query);
512
513 $childs = array();
514 while($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
515 {
516 $childs[] = $row->child;
517 }
518 return $childs;
519 }
520
530 function getChilds($a_node_id, $a_order = "", $a_direction = "ASC")
531 {
532 global $ilBench,$ilDB, $ilObjDataCache, $ilUser;
533
534 if (!isset($a_node_id))
535 {
536 $message = "No node_id given!";
537 $this->log->error($message);
538 throw new InvalidArgumentException($message);
539 }
540
541 // init childs
542 $childs = array();
543
544 // number of childs
545 $count = 0;
546
547 // init order_clause
548 $order_clause = "";
549
550 // set order_clause if sort order parameter is given
551 if (!empty($a_order))
552 {
553 $order_clause = "ORDER BY ".$a_order." ".$a_direction;
554 }
555 else
556 {
557 $order_clause = "ORDER BY ".$this->table_tree.".lft";
558 }
559
560
561 $query = sprintf('SELECT * FROM '.$this->table_tree.' '.
562 $this->buildJoin().
563 "WHERE parent = %s " .
564 "AND ".$this->table_tree.".".$this->tree_pk." = %s ".
565 $order_clause,
566 $ilDB->quote($a_node_id,'integer'),
567 $ilDB->quote($this->tree_id,'integer'));
568
569 $res = $ilDB->query($query);
570
571 if(!$count = $res->numRows())
572 {
573 return array();
574 }
575
576 // get rows and object ids
577 $rows = array();
578 while($r = $ilDB->fetchAssoc($res))
579 {
580 $rows[] = $r;
581 $obj_ids[] = $r["obj_id"];
582 }
583
584 // preload object translation information
585 if ($this->__isMainTree() && $this->isCacheUsed() && is_object($ilObjDataCache) &&
586 is_object($ilUser) && $this->lang_code == $ilUser->getLanguage() && !$this->oc_preloaded[$a_node_id])
587 {
588// $ilObjDataCache->preloadTranslations($obj_ids, $this->lang_code);
589 $ilObjDataCache->preloadObjectCache($obj_ids, $this->lang_code);
591 $this->oc_preloaded[$a_node_id] = true;
592 }
593
594 foreach ($rows as $row)
595 {
596 $childs[] = $this->fetchNodeData($row);
597
598 // Update cache of main tree
599 if ($this->__isMainTree())
600 {
601 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child'].' = true');
602 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
603 }
604 }
605 $childs[$count - 1]["last"] = true;
606 return $childs;
607 }
608
618 function getFilteredChilds($a_filter,$a_node,$a_order = "",$a_direction = "ASC")
619 {
620 $childs = $this->getChilds($a_node,$a_order,$a_direction);
621
622 foreach($childs as $child)
623 {
624 if(!in_array($child["type"],$a_filter))
625 {
626 $filtered[] = $child;
627 }
628 }
629 return $filtered ? $filtered : array();
630 }
631
632
641 function getChildsByType($a_node_id,$a_type)
642 {
643 global $ilDB;
644
645 if (!isset($a_node_id) or !isset($a_type))
646 {
647 $message = "Missing parameter! node_id:".$a_node_id." type:".$a_type;
648 $this->log->error($message);
649 throw new InvalidArgumentException($message);
650 }
651
652 if ($a_type=='rolf' && $this->table_obj_reference) {
653 // Performance optimization: A node can only have exactly one
654 // role folder as its child. Therefore we don't need to sort the
655 // results, and we can let the database know about the expected limit.
656 $ilDB->setLimit(1,0);
657 $query = sprintf("SELECT * FROM ".$this->table_tree." ".
658 $this->buildJoin().
659 "WHERE parent = %s ".
660 "AND ".$this->table_tree.".".$this->tree_pk." = %s ".
661 "AND ".$this->table_obj_data.".type = %s ",
662 $ilDB->quote($a_node_id,'integer'),
663 $ilDB->quote($this->tree_id,'integer'),
664 $ilDB->quote($a_type,'text'));
665 } else {
666 $query = sprintf("SELECT * FROM ".$this->table_tree." ".
667 $this->buildJoin().
668 "WHERE parent = %s ".
669 "AND ".$this->table_tree.".".$this->tree_pk." = %s ".
670 "AND ".$this->table_obj_data.".type = %s ".
671 "ORDER BY ".$this->table_tree.".lft",
672 $ilDB->quote($a_node_id,'integer'),
673 $ilDB->quote($this->tree_id,'integer'),
674 $ilDB->quote($a_type,'text'));
675 }
676 $res = $ilDB->query($query);
677
678 // init childs
679 $childs = array();
680 while($row = $ilDB->fetchAssoc($res))
681 {
682 $childs[] = $this->fetchNodeData($row);
683 }
684
685 return $childs ? $childs : array();
686 }
687
688
697 public function getChildsByTypeFilter($a_node_id,$a_types,$a_order = "",$a_direction = "ASC")
698 {
699 global $ilDB;
700
701 if (!isset($a_node_id) or !$a_types)
702 {
703 $message = "Missing parameter! node_id:".$a_node_id." type:".$a_types;
704 $this->log->error($message);
705 throw new InvalidArgumentException($message);
706 }
707
708 $filter = ' ';
709 if($a_types)
710 {
711 $filter = 'AND '.$this->table_obj_data.'.type IN('.implode(',',ilUtil::quoteArray($a_types)).') ';
712 }
713
714 // set order_clause if sort order parameter is given
715 if (!empty($a_order))
716 {
717 $order_clause = "ORDER BY ".$a_order." ".$a_direction;
718 }
719 else
720 {
721 $order_clause = "ORDER BY ".$this->table_tree.".lft";
722 }
723
724 $query = 'SELECT * FROM '.$this->table_tree.' '.
725 $this->buildJoin().
726 'WHERE parent = '.$ilDB->quote($a_node_id,'integer').' '.
727 'AND '.$this->table_tree.'.'.$this->tree_pk.' = '.$ilDB->quote($this->tree_id,'integer').' '.
728 $filter.
729 $order_clause;
730
731 $res = $ilDB->query($query);
732 while($row = $ilDB->fetchAssoc($res))
733 {
734 $childs[] = $this->fetchNodeData($row);
735 }
736
737 return $childs ? $childs : array();
738 }
739
751 public function insertNodeFromTrash($a_source_id, $a_target_id, $a_tree_id, $a_pos = IL_LAST_NODE, $a_reset_deleted_date = false)
752 {
753 global $ilDB;
754
755 if($this->__isMainTree())
756 {
757 if($a_source_id <= 1 or $a_target_id <= 0)
758 {
760 throw new InvalidArgumentException('Invalid parameter given for ilTree::insertNodeFromTrash');
761 }
762 }
763 if (!isset($a_source_id) or !isset($a_target_id))
764 {
766 throw new InvalidArgumentException('Missing parameter for ilTree::insertNodeFromTrash');
767 }
768 if($this->isInTree($a_source_id))
769 {
770 ilLoggerFactory::getLogger('tree')->error('Node already in tree');
772 throw new InvalidArgumentException('Node already in tree.');
773 }
774
775 $query = 'DELETE from tree '.
776 'WHERE tree = '.$ilDB->quote($a_tree_id,'integer').' '.
777 'AND child = '.$ilDB->quote($a_source_id,'integer');
778 $ilDB->manipulate($query);
779
780 $this->insertNode($a_source_id, $a_target_id, IL_LAST_NODE, $a_reset_deleted_date);
781 }
782
783
792 public function insertNode($a_node_id, $a_parent_id, $a_pos = IL_LAST_NODE, $a_reset_deletion_date = false)
793 {
794 global $ilDB;
795
796//echo "+$a_node_id+$a_parent_id+";
797 // CHECK node_id and parent_id > 0 if in main tree
798 if($this->__isMainTree())
799 {
800 if($a_node_id <= 1 or $a_parent_id <= 0)
801 {
802 $message = sprintf('Invalid parameters! $a_node_id: %s $a_parent_id: %s',
803 $a_node_id,
804 $a_parent_id);
805 $this->log->logStack(ilLogLevel::ERROR, $message);
806 throw new InvalidArgumentException($message);
807 }
808 }
809
810
811 if (!isset($a_node_id) or !isset($a_parent_id))
812 {
813 $this->log->logStack(ilLogLevel::ERROR);
814 throw new InvalidArgumentException("Missing parameter! ".
815 "node_id: ".$a_node_id." parent_id: ".$a_parent_id);
816 }
817 if ($this->isInTree($a_node_id))
818 {
819 throw new InvalidArgumentException("Node ".$a_node_id." already in tree ".
820 $this->table_tree."!");
821 }
822
823 $this->getTreeImplementation()->insertNode($a_node_id, $a_parent_id, $a_pos);
824
825 $this->in_tree_cache[$a_node_id] = true;
826
827 // reset deletion date
828 if ($a_reset_deletion_date)
829 {
830 ilObject::_resetDeletedDate($a_node_id);
831 }
832
833 if (isset($GLOBALS["ilAppEventHandler"]) && $this->__isMainTree()) {
834 $GLOBALS['ilAppEventHandler']->raise(
835 "Services/Tree",
836 "insertNode",
837 array(
838 'tree' => $this->table_tree,
839 'node_id' => $a_node_id,
840 'parent_id' => $a_parent_id)
841 );
842 }
843 }
844
857 public function getFilteredSubTree($a_node_id,$a_filter = array())
858 {
859 $node = $this->getNodeData($a_node_id);
860
861 $first = true;
862 $depth = 0;
863 foreach($this->getSubTree($node) as $subnode)
864 {
865 if($depth and $subnode['depth'] > $depth)
866 {
867 continue;
868 }
869 if(!$first and in_array($subnode['type'],$a_filter))
870 {
871 $depth = $subnode['depth'];
872 $first = false;
873 continue;
874 }
875 $depth = 0;
876 $first = false;
877 $filtered[] = $subnode;
878 }
879 return $filtered ? $filtered : array();
880 }
881
887 public function getSubTreeIds($a_ref_id)
888 {
889 return $this->getTreeImplementation()->getSubTreeIds($a_ref_id);
890 }
891
892
902 function getSubTree($a_node,$a_with_data = true, $a_type = "")
903 {
904 global $ilDB;
905
906 if (!is_array($a_node))
907 {
908 $this->log->logStack(ilLogLevel::ERROR);
909 throw new InvalidArgumentException(__METHOD__.': wrong datatype for node data given');
910 }
911
912 /*
913 if($a_node['lft'] < 1 or $a_node['rgt'] < 2)
914 {
915 $GLOBALS['ilLog']->logStack();
916 $message = sprintf('%s: Invalid node given! $a_node["lft"]: %s $a_node["rgt"]: %s',
917 __METHOD__,
918 $a_node['lft'],
919 $a_node['rgt']);
920
921 throw new InvalidArgumentException($message);
922 }
923 */
924
925 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, $a_type);
926 $res = $ilDB->query($query);
927 while($row = $ilDB->fetchAssoc($res))
928 {
929 if($a_with_data)
930 {
931 $subtree[] = $this->fetchNodeData($row);
932 }
933 else
934 {
935 $subtree[] = $row['child'];
936 }
937 // the lm_data "hack" should be removed in the trunk during an alpha
938 if($this->__isMainTree() || $this->table_tree == "lm_tree")
939 {
940 $this->in_tree_cache[$row['child']] = true;
941 }
942 }
943 return $subtree ? $subtree : array();
944 }
945
954 function getSubTreeTypes($a_node,$a_filter = 0)
955 {
956 $a_filter = $a_filter ? $a_filter : array();
957
958 foreach($this->getSubtree($this->getNodeData($a_node)) as $node)
959 {
960 if(in_array($node["type"],$a_filter))
961 {
962 continue;
963 }
964 $types["$node[type]"] = $node["type"];
965 }
966 return $types ? $types : array();
967 }
968
976 function deleteTree($a_node)
977 {
978 global $ilDB;
979
980 $this->log->debug('Delete tree with node '. $a_node);
981
982 if (!is_array($a_node))
983 {
984 $this->log->logStack(ilLogLevel::ERROR);
985 throw new InvalidArgumentException(__METHOD__.': Wrong datatype for node data!');
986 }
987
988 $this->log->debug($this->tree_pk);
989
990 if($this->__isMainTree() )
991 {
992 // @todo normally this part is not executed, since the subtree is first
993 // moved to trash and then deleted.
994 if(!$this->__checkDelete($a_node))
995 {
996 $this->log->logStack(ilLogLevel::ERROR);
997 throw new ilInvalidTreeStructureException('Deletion canceled due to invalid tree structure.' . print_r($a_node,true));
998 }
999 }
1000
1001 $this->getTreeImplementation()->deleteTree($a_node['child']);
1002
1003 $this->resetInTreeCache();
1004 }
1005
1010 public function validateParentRelations()
1011 {
1012 return $this->getTreeImplementation()->validateParentRelations();
1013 }
1014
1025 function getPathFull($a_endnode_id, $a_startnode_id = 0)
1026 {
1027 $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
1028
1029 // We retrieve the full path in a single query to improve performance
1030 global $ilDB;
1031
1032 // Abort if no path ids were found
1033 if (count($pathIds) == 0)
1034 {
1035 return null;
1036 }
1037
1038 $inClause = 'child IN (';
1039 for ($i=0; $i < count($pathIds); $i++)
1040 {
1041 if ($i > 0) $inClause .= ',';
1042 $inClause .= $ilDB->quote($pathIds[$i],'integer');
1043 }
1044 $inClause .= ')';
1045
1046 $q = 'SELECT * '.
1047 'FROM '.$this->table_tree.' '.
1048 $this->buildJoin().' '.
1049 'WHERE '.$inClause.' '.
1050 'AND '.$this->table_tree.'.'.$this->tree_pk.' = '.$this->ilDB->quote($this->tree_id,'integer').' '.
1051 'ORDER BY depth';
1052 $r = $ilDB->query($q);
1053
1054 $pathFull = array();
1055 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC))
1056 {
1057 $pathFull[] = $this->fetchNodeData($row);
1058
1059 // Update cache
1060 if ($this->__isMainTree())
1061 {
1062 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child']);
1063 $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
1064 }
1065 }
1066 return $pathFull;
1067 }
1068
1069
1076 function preloadDepthParent($a_node_ids)
1077 {
1078 global $ilDB;
1079
1080 if (!$this->__isMainTree() || !is_array($a_node_ids) || !$this->isCacheUsed())
1081 {
1082 return;
1083 }
1084
1085 $res = $ilDB->query('SELECT t.depth, t.parent, t.child '.
1086 'FROM '.$this->table_tree.' t '.
1087 'WHERE '.$ilDB->in("child", $a_node_ids, false, "integer").
1088 'AND '.$this->tree_pk.' = '.$ilDB->quote($this->tree_id, "integer"));
1089 while ($row = $ilDB->fetchAssoc($res))
1090 {
1091 $this->depth_cache[$row["child"]] = $row["depth"];
1092 $this->parent_cache[$row["child"]] = $row["parent"];
1093 }
1094 }
1095
1105 public function getPathId($a_endnode_id, $a_startnode_id = 0)
1106 {
1107 if(!$a_endnode_id)
1108 {
1109 $this->log->logStack(ilLogLevel::ERROR);
1110 throw new InvalidArgumentException(__METHOD__.': No endnode given!');
1111 }
1112
1113 // path id cache
1114 if ($this->isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id]))
1115 {
1116//echo "<br>getPathIdhit";
1117 return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
1118 }
1119//echo "<br>miss";
1120
1121 $pathIds = $this->getTreeImplementation()->getPathIds($a_endnode_id, $a_startnode_id);
1122
1123 if($this->__isMainTree())
1124 {
1125 $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
1126 }
1127 return $pathIds;
1128 }
1129
1130 // BEGIN WebDAV: getNodePathForTitlePath function added
1148 function getNodePathForTitlePath($titlePath, $a_startnode_id = null)
1149 {
1150 global $ilDB, $log;
1151 //$log->write('getNodePathForTitlePath('.implode('/',$titlePath));
1152
1153 // handle empty title path
1154 if ($titlePath == null || count($titlePath) == 0)
1155 {
1156 if ($a_startnode_id == 0)
1157 {
1158 return null;
1159 }
1160 else
1161 {
1162 return $this->getNodePath($a_startnode_id);
1163 }
1164 }
1165
1166 // fetch the node path up to the startnode
1167 if ($a_startnode_id != null && $a_startnode_id != 0)
1168 {
1169 // Start using the node path to the root of the relative path
1170 $nodePath = $this->getNodePath($a_startnode_id);
1171 $parent = $a_startnode_id;
1172 }
1173 else
1174 {
1175 // Start using the root of the tree
1176 $nodePath = array();
1177 $parent = 0;
1178 }
1179
1180
1181 // Convert title path into Unicode Normal Form C
1182 // This is needed to ensure that we can compare title path strings with
1183 // strings from the database.
1184 require_once('include/Unicode/UtfNormal.php');
1185 include_once './Services/Utilities/classes/class.ilStr.php';
1186 $inClause = 'd.title IN (';
1187 for ($i=0; $i < count($titlePath); $i++)
1188 {
1189 $titlePath[$i] = ilStr::strToLower(UtfNormal::toNFC($titlePath[$i]));
1190 if ($i > 0) $inClause .= ',';
1191 $inClause .= $ilDB->quote($titlePath[$i],'text');
1192 }
1193 $inClause .= ')';
1194
1195 // Fetch all rows that are potential path elements
1196 if ($this->table_obj_reference)
1197 {
1198 $joinClause = 'JOIN '.$this->table_obj_reference.' r ON t.child = r.'.$this->ref_pk.' '.
1199 'JOIN '.$this->table_obj_data.' d ON r.'.$this->obj_pk.' = d.'.$this->obj_pk;
1200 }
1201 else
1202 {
1203 $joinClause = 'JOIN '.$this->table_obj_data.' d ON t.child = d.'.$this->obj_pk;
1204 }
1205 // The ORDER BY clause in the following SQL statement ensures that,
1206 // in case of a multiple objects with the same title, always the Object
1207 // with the oldest ref_id is chosen.
1208 // This ensure, that, if a new object with the same title is added,
1209 // WebDAV clients can still work with the older object.
1210 $q = 'SELECT t.depth, t.parent, t.child, d.'.$this->obj_pk.' obj_id, d.type, d.title '.
1211 'FROM '.$this->table_tree.' t '.
1212 $joinClause.' '.
1213 'WHERE '.$inClause.' '.
1214 'AND t.depth <= '.(count($titlePath)+count($nodePath)).' '.
1215 'AND t.tree = 1 '.
1216 'ORDER BY t.depth, t.child ASC';
1217 $r = $ilDB->query($q);
1218
1219 $rows = array();
1220 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC))
1221 {
1222 $row['title'] = UtfNormal::toNFC($row['title']);
1223 $row['ref_id'] = $row['child'];
1224 $rows[] = $row;
1225 }
1226
1227 // Extract the path elements from the fetched rows
1228 for ($i = 0; $i < count($titlePath); $i++) {
1229 $pathElementFound = false;
1230 foreach ($rows as $row) {
1231 if ($row['parent'] == $parent &&
1232 ilStr::strToLower($row['title']) == $titlePath[$i])
1233 {
1234 // FIXME - We should test here, if the user has
1235 // 'visible' permission for the object.
1236 $nodePath[] = $row;
1237 $parent = $row['child'];
1238 $pathElementFound = true;
1239 break;
1240 }
1241 }
1242 // Abort if we haven't found a path element for the current depth
1243 if (! $pathElementFound)
1244 {
1245 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):null');
1246 return null;
1247 }
1248 }
1249 // Return the node path
1250 //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):'.var_export($nodePath,true));
1251 return $nodePath;
1252 }
1253 // END WebDAV: getNodePathForTitlePath function added
1254 // END WebDAV: getNodePath function added
1271 function getNodePath($a_endnode_id, $a_startnode_id = 0)
1272 {
1273 global $ilDB;
1274
1275 $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
1276
1277 // Abort if no path ids were found
1278 if (count($pathIds) == 0)
1279 {
1280 return null;
1281 }
1282
1283
1284 $types = array();
1285 $data = array();
1286 for ($i = 0; $i < count($pathIds); $i++)
1287 {
1288 $types[] = 'integer';
1289 $data[] = $pathIds[$i];
1290 }
1291
1292 $query = 'SELECT t.depth,t.parent,t.child,d.obj_id,d.type,d.title '.
1293 'FROM '.$this->table_tree.' t '.
1294 'JOIN '.$this->table_obj_reference.' r ON r.ref_id = t.child '.
1295 'JOIN '.$this->table_obj_data.' d ON d.obj_id = r.obj_id '.
1296 'WHERE '.$ilDB->in('t.child',$data,false,'integer').' '.
1297 'ORDER BY t.depth ';
1298
1299 $res = $ilDB->queryF($query,$types,$data);
1300
1301 $titlePath = array();
1302 while ($row = $ilDB->fetchAssoc($res))
1303 {
1304 $titlePath[] = $row;
1305 }
1306 return $titlePath;
1307 }
1308 // END WebDAV: getNodePath function added
1309
1317 function checkTree()
1318 {
1319 global $ilDB;
1320
1321 $types = array('integer');
1322 $query = 'SELECT lft,rgt FROM '.$this->table_tree.' '.
1323 'WHERE '.$this->tree_pk.' = %s ';
1324
1325 $res = $ilDB->queryF($query,$types,array($this->tree_id));
1326 while ($row = $ilDB->fetchObject($res))
1327 {
1328 $lft[] = $row->lft;
1329 $rgt[] = $row->rgt;
1330 }
1331
1332 $all = array_merge($lft,$rgt);
1333 $uni = array_unique($all);
1334
1335 if (count($all) != count($uni))
1336 {
1337 $message = 'Tree is corrupted!';
1338
1339 $this->log->error($message);
1340 throw new ilInvalidTreeStructureException($message);
1341 }
1342
1343 return true;
1344 }
1345
1353 function checkTreeChilds($a_no_zero_child = true)
1354 {
1355 global $ilDB;
1356
1357 $query = 'SELECT * FROM '.$this->table_tree.' '.
1358 'WHERE '.$this->tree_pk.' = %s '.
1359 'ORDER BY lft';
1360 $r1 = $ilDB->queryF($query,array('integer'),array($this->tree_id));
1361
1362 while ($row = $ilDB->fetchAssoc($r1))
1363 {
1364//echo "tree:".$row[$this->tree_pk].":lft:".$row["lft"].":rgt:".$row["rgt"].":child:".$row["child"].":<br>";
1365 if (($row["child"] == 0) && $a_no_zero_child)
1366 {
1367 $message = "Tree contains child with ID 0!";
1368 $this->log->error($message);
1369 throw new ilInvalidTreeStructureException($message);
1370 }
1371
1372 if ($this->table_obj_reference)
1373 {
1374 // get object reference data
1375 $query = 'SELECT * FROM '.$this->table_obj_reference.' WHERE '.$this->ref_pk.' = %s ';
1376 $r2 = $ilDB->queryF($query,array('integer'),array($row['child']));
1377
1378//echo "num_childs:".$r2->numRows().":<br>";
1379 if ($r2->numRows() == 0)
1380 {
1381 $message = "No Object-to-Reference entry found for ID ". $row["child"]."!";
1382 $this->log->error($message);
1383 throw new ilInvalidTreeStructureException($message);
1384 }
1385 if ($r2->numRows() > 1)
1386 {
1387 $message = "More Object-to-Reference entries found for ID ". $row["child"]."!";
1388 $this->log->error($message);
1389 throw new ilInvalidTreeStructureException($message);
1390 }
1391
1392 // get object data
1393 $obj_ref = $ilDB->fetchAssoc($r2);
1394
1395 $query = 'SELECT * FROM '.$this->table_obj_data.' WHERE '.$this->obj_pk.' = %s';
1396 $r3 = $ilDB->queryF($query,array('integer'),array($obj_ref[$this->obj_pk]));
1397 if ($r3->numRows() == 0)
1398 {
1399 $message = " No child found for ID ". $obj_ref[$this->obj_pk]."!";
1400 $this->log->error($message);
1401 throw new ilInvalidTreeStructureException($message);
1402 }
1403 if ($r3->numRows() > 1)
1404 {
1405 $message = "More childs found for ID ". $obj_ref[$this->obj_pk]."!";
1406 $this->log->error($message);
1407 throw new ilInvalidTreeStructureException($message);
1408 }
1409
1410 }
1411 else
1412 {
1413 // get only object data
1414 $query = 'SELECT * FROM '.$this->table_obj_data.' WHERE '.$this->obj_pk.' = %s';
1415 $r2 = $ilDB->queryF($query,array('integer'),array($row['child']));
1416//echo "num_childs:".$r2->numRows().":<br>";
1417 if ($r2->numRows() == 0)
1418 {
1419 $message = "No child found for ID ". $row["child"]."!";
1420 $this->log->error($message);
1421 throw new ilInvalidTreeStructureException($message);
1422 }
1423 if ($r2->numRows() > 1)
1424 {
1425 $message = "More childs found for ID ". $row["child"]."!";
1426 $this->log->error($message);
1427 throw new ilInvalidTreeStructureException($message);
1428 }
1429 }
1430 }
1431
1432 return true;
1433 }
1434
1440 public function getMaximumDepth()
1441 {
1442 global $ilDB;
1443
1444 $query = 'SELECT MAX(depth) depth FROM '.$this->table_tree;
1445 $res = $ilDB->query($query);
1446
1447 $row = $ilDB->fetchAssoc($res);
1448 return $row['depth'];
1449 }
1450
1457 function getDepth($a_node_id)
1458 {
1459 global $ilDB;
1460
1461 if ($a_node_id)
1462 {
1463 $query = 'SELECT depth FROM '.$this->table_tree.' '.
1464 'WHERE child = %s '.
1465 'AND '.$this->tree_pk.' = %s ';
1466 $res = $ilDB->queryF($query,array('integer','integer'),array($a_node_id,$this->tree_id));
1467 $row = $ilDB->fetchObject($res);
1468
1469 return $row->depth;
1470 }
1471 else
1472 {
1473 return 1;
1474 }
1475 }
1476
1484 public function getNodeTreeData($a_node_id)
1485 {
1486 global $ilDB;
1487
1488 if(!$a_node_id)
1489 {
1490 $this->log->logStack(ilLogLevel::ERROR);
1491 throw new InvalidArgumentException('Missing or empty parameter $a_node_id: '. $a_node_id);
1492 }
1493
1494 $query = 'SELECT * FROM '.$this->table_tree.' '.
1495 'WHERE child = '.$ilDB->quote($a_node_id,'integer');
1496 $res = $ilDB->query($query);
1497 while($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC))
1498 {
1499 return $row;
1500 }
1501 return array();
1502 }
1503
1504
1513 // BEGIN WebDAV: Pass tree id to this method
1514 //function getNodeData($a_node_id)
1515 function getNodeData($a_node_id, $a_tree_pk = null)
1516 // END PATCH WebDAV: Pass tree id to this method
1517 {
1518 global $ilDB;
1519
1520 if (!isset($a_node_id))
1521 {
1522 $this->log->logStack(ilLogLevel::ERROR);
1523 throw new InvalidArgumentException("No node_id given!");
1524 }
1525 if($this->__isMainTree())
1526 {
1527 if($a_node_id < 1)
1528 {
1529 $message = 'No valid parameter given! $a_node_id: %s'.$a_node_id;
1530
1531 $this->log->error($message);
1532 throw new InvalidArgumentException($message);
1533 }
1534 }
1535
1536 // BEGIN WebDAV: Pass tree id to this method
1537 $query = 'SELECT * FROM '.$this->table_tree.' '.
1538 $this->buildJoin().
1539 'WHERE '.$this->table_tree.'.child = %s '.
1540 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
1541 $res = $ilDB->queryF($query,array('integer','integer'),array(
1542 $a_node_id,
1543 $a_tree_pk === null ? $this->tree_id : $a_tree_pk));
1544 // END WebDAV: Pass tree id to this method
1545 $row = $ilDB->fetchAssoc($res);
1547
1548 return $this->fetchNodeData($row);
1549 }
1550
1558 function fetchNodeData($a_row)
1559 {
1560 global $objDefinition, $lng, $ilBench,$ilDB;
1561
1562 //$ilBench->start("Tree", "fetchNodeData_getRow");
1563 $data = $a_row;
1564 $data["desc"] = $a_row["description"]; // for compability
1565 //$ilBench->stop("Tree", "fetchNodeData_getRow");
1566
1567 // multilingual support systemobjects (sys) & categories (db)
1568 //$ilBench->start("Tree", "fetchNodeData_readDefinition");
1569 if (is_object($objDefinition))
1570 {
1571 $translation_type = $objDefinition->getTranslationType($data["type"]);
1572 }
1573 //$ilBench->stop("Tree", "fetchNodeData_readDefinition");
1574
1575 if ($translation_type == "sys")
1576 {
1577 //$ilBench->start("Tree", "fetchNodeData_getLangData");
1578 if ($data["type"] == "rolf" and $data["obj_id"] != ROLE_FOLDER_ID)
1579 {
1580 $data["description"] = $lng->txt("obj_".$data["type"]."_local_desc").$data["title"].$data["desc"];
1581 $data["desc"] = $lng->txt("obj_".$data["type"]."_local_desc").$data["title"].$data["desc"];
1582 $data["title"] = $lng->txt("obj_".$data["type"]."_local");
1583 }
1584 else
1585 {
1586 $data["title"] = $lng->txt("obj_".$data["type"]);
1587 $data["description"] = $lng->txt("obj_".$data["type"]."_desc");
1588 $data["desc"] = $lng->txt("obj_".$data["type"]."_desc");
1589 }
1590 //$ilBench->stop("Tree", "fetchNodeData_getLangData");
1591 }
1592 elseif ($translation_type == "db")
1593 {
1594
1595 // Try to retrieve object translation from cache
1596 if ($this->isCacheUsed() &&
1597 array_key_exists($data["obj_id"].'.'.$lang_code, $this->translation_cache)) {
1598
1599 $key = $data["obj_id"].'.'.$lang_code;
1600 $data["title"] = $this->translation_cache[$key]['title'];
1601 $data["description"] = $this->translation_cache[$key]['description'];
1602 $data["desc"] = $this->translation_cache[$key]['desc'];
1603 }
1604 else
1605 {
1606 // Object translation is not in cache, read it from database
1607 //$ilBench->start("Tree", "fetchNodeData_getTranslation");
1608 $query = 'SELECT title,description FROM object_translation '.
1609 'WHERE obj_id = %s '.
1610 'AND lang_code = %s '.
1611 'AND NOT lang_default = %s';
1612
1613 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
1614 $data['obj_id'],
1615 $this->lang_code,
1616 1));
1617 $row = $ilDB->fetchObject($res);
1618
1619 if ($row)
1620 {
1621 $data["title"] = $row->title;
1622 $data["description"] = ilUtil::shortenText($row->description,ilObject::DESC_LENGTH,true);
1623 $data["desc"] = $row->description;
1624 }
1625 //$ilBench->stop("Tree", "fetchNodeData_getTranslation");
1626
1627 // Store up to 1000 object translations in cache
1628 if ($this->isCacheUsed() && count($this->translation_cache) < 1000)
1629 {
1630 $key = $data["obj_id"].'.'.$lang_code;
1631 $this->translation_cache[$key] = array();
1632 $this->translation_cache[$key]['title'] = $data["title"] ;
1633 $this->translation_cache[$key]['description'] = $data["description"];
1634 $this->translation_cache[$key]['desc'] = $data["desc"];
1635 }
1636 }
1637 }
1638
1639 // TODO: Handle this switch by module.xml definitions
1640 if($data['type'] == 'crsr' or $data['type'] == 'catr' or $data['type'] == 'grpr')
1641 {
1642 include_once('./Services/ContainerReference/classes/class.ilContainerReference.php');
1643 $data['title'] = ilContainerReference::_lookupTitle($data['obj_id']);
1644 }
1645
1646 return $data ? $data : array();
1647 }
1648
1654 protected function fetchTranslationFromObjectDataCache($a_obj_ids)
1655 {
1656 global $ilObjDataCache;
1657
1658 if ($this->isCacheUsed() && is_array($a_obj_ids) && is_object($ilObjDataCache))
1659 {
1660 foreach ($a_obj_ids as $id)
1661 {
1662 $this->translation_cache[$id.'.']['title'] = $ilObjDataCache->lookupTitle($id);
1663 $this->translation_cache[$id.'.']['description'] = $ilObjDataCache->lookupDescription($id);;
1664 $this->translation_cache[$id.'.']['desc'] =
1665 $this->translation_cache[$id.'.']['description'];
1666 }
1667 }
1668 }
1669
1670
1678 function isInTree($a_node_id)
1679 {
1680 global $ilDB;
1681
1682 if (!isset($a_node_id))
1683 {
1684 return false;
1685 #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1686 }
1687 // is in tree cache
1688 if ($this->isCacheUsed() && isset($this->in_tree_cache[$a_node_id]))
1689 {
1690 #$GLOBALS['ilLog']->write(__METHOD__.': Using in tree cache '.$a_node_id);
1691//echo "<br>in_tree_hit";
1692 return $this->in_tree_cache[$a_node_id];
1693 }
1694
1695 $query = 'SELECT * FROM '.$this->table_tree.' '.
1696 'WHERE '.$this->table_tree.'.child = %s '.
1697 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s';
1698
1699 $res = $ilDB->queryF($query,array('integer','integer'),array(
1700 $a_node_id,
1701 $this->tree_id));
1702
1703 if ($res->numRows() > 0)
1704 {
1705 if($this->__isMainTree())
1706 {
1707 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = true');
1708 $this->in_tree_cache[$a_node_id] = true;
1709 }
1710 return true;
1711 }
1712 else
1713 {
1714 if($this->__isMainTree())
1715 {
1716 #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = false');
1717 $this->in_tree_cache[$a_node_id] = false;
1718 }
1719 return false;
1720 }
1721 }
1722
1730 public function getParentNodeData($a_node_id)
1731 {
1732 global $ilDB;
1733 global $ilLog;
1734
1735 if (!isset($a_node_id))
1736 {
1737 $ilLog->logStack();
1738 throw new InvalidArgumentException(__METHOD__.': No node_id given!');
1739 }
1740
1741 if ($this->table_obj_reference)
1742 {
1743 // Use inner join instead of left join to improve performance
1744 $innerjoin = "JOIN ".$this->table_obj_reference." ON v.child=".$this->table_obj_reference.".".$this->ref_pk." ".
1745 "JOIN ".$this->table_obj_data." ON ".$this->table_obj_reference.".".$this->obj_pk."=".$this->table_obj_data.".".$this->obj_pk." ";
1746 }
1747 else
1748 {
1749 // Use inner join instead of left join to improve performance
1750 $innerjoin = "JOIN ".$this->table_obj_data." ON v.child=".$this->table_obj_data.".".$this->obj_pk." ";
1751 }
1752
1753 $query = 'SELECT * FROM '.$this->table_tree.' s, '.$this->table_tree.' v '.
1754 $innerjoin.
1755 'WHERE s.child = %s '.
1756 'AND s.parent = v.child '.
1757 'AND s.'.$this->tree_pk.' = %s '.
1758 'AND v.'.$this->tree_pk.' = %s';
1759 $res = $ilDB->queryF($query,array('integer','integer','integer'),array(
1760 $a_node_id,
1761 $this->tree_id,
1762 $this->tree_id));
1763 $row = $ilDB->fetchAssoc($res);
1764 return $this->fetchNodeData($row);
1765 }
1766
1774 public function isGrandChild($a_startnode_id,$a_querynode_id)
1775 {
1776 return $this->getRelation($a_startnode_id, $a_querynode_id) == self::RELATION_PARENT;
1777 }
1778
1788 function addTree($a_tree_id,$a_node_id = -1)
1789 {
1790 global $ilDB;
1791
1792 // FOR SECURITY addTree() IS NOT ALLOWED ON MAIN TREE
1793 if($this->__isMainTree())
1794 {
1795 $message = sprintf('Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
1796 $a_tree_id,
1797 $a_node_id);
1798 $this->log->error($message);
1799 throw new InvalidArgumentException($message);
1800 }
1801
1802 if (!isset($a_tree_id))
1803 {
1804 $message = "No tree_id given!";
1805 $this->log->error($message);
1806 throw new InvalidArgumentException($message);
1807 }
1808
1809 if ($a_node_id <= 0)
1810 {
1811 $a_node_id = $a_tree_id;
1812 }
1813
1814 $query = 'INSERT INTO '.$this->table_tree.' ('.
1815 $this->tree_pk.', child,parent,lft,rgt,depth) '.
1816 'VALUES '.
1817 '(%s,%s,%s,%s,%s,%s)';
1818 $res = $ilDB->manipulateF($query,array('integer','integer','integer','integer','integer','integer'),array(
1819 $a_tree_id,
1820 $a_node_id,
1821 0,
1822 1,
1823 2,
1824 1));
1825
1826 return true;
1827 }
1828
1839 {
1840 global $ilDB;
1841
1842 if(!isset($a_type) or (!is_string($a_type)))
1843 {
1844 $this->log->logStack(ilLogLevel::ERROR);
1845 throw new InvalidArgumentException('Type not given or wrong datatype');
1846 }
1847
1848 $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1849 $this->buildJoin().
1850 'WHERE ' . $this->table_obj_data . '.type = ' . $this->ilDB->quote($a_type, 'text').
1851 'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer');
1852
1853 $res = $ilDB->query($query);
1854 $data = array();
1855 while($row = $ilDB->fetchAssoc($res))
1856 {
1857 $data[] = $this->fetchNodeData($row);
1858 }
1859
1860 return $data;
1861 }
1862
1871 public function removeTree($a_tree_id)
1872 {
1873 global $ilDB;
1874
1875 // OPERATION NOT ALLOWED ON MAIN TREE
1876 if($this->__isMainTree())
1877 {
1878 $this->log->logStack(ilLogLevel::ERROR);
1879 throw new InvalidArgumentException('Operation not allowed on main tree');
1880 }
1881 if (!$a_tree_id)
1882 {
1883 $this->log->logStack(ilLogLevel::ERROR);
1884 throw new InvalidArgumentException('Missing parameter tree id');
1885 }
1886
1887 $query = 'DELETE FROM '.$this->table_tree.
1888 ' WHERE '.$this->tree_pk.' = %s ';
1889 $ilDB->manipulateF($query,array('integer'),array($a_tree_id));
1890 return true;
1891 }
1892
1900 public function moveToTrash($a_node_id, $a_set_deleted = false)
1901 {
1902 global $ilDB;
1903
1904 if(!$a_node_id)
1905 {
1906 $this->log->logStack(ilLogLevel::ERROR);
1907 throw new InvalidArgumentException('No valid parameter given! $a_node_id: '.$a_node_id);
1908 }
1909
1910
1911 $query = $this->getTreeImplementation()->getSubTreeQuery($this->getNodeTreeData($a_node_id),'',false);
1912 $res = $ilDB->query($query);
1913
1914 $subnodes = array();
1915 while($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC))
1916 {
1917 $subnodes[] = $row['child'];
1918 }
1919
1920 if(!count($subnodes))
1921 {
1922 // Possibly already deleted
1923 return false;
1924 }
1925
1926 if($a_set_deleted)
1927 {
1928 include_once './Services/Object/classes/class.ilObject.php';
1929 ilObject::setDeletedDates($subnodes);
1930 }
1931
1932 // netsted set <=> mp
1933 $this->getTreeImplementation()->moveToTrash($a_node_id);
1934
1935 return true;
1936 }
1937
1948 public function saveSubTree($a_node_id, $a_set_deleted = false)
1949 {
1950 return $this->moveToTrash($a_node_id, $a_set_deleted);
1951 }
1952
1957 public function isDeleted($a_node_id)
1958 {
1959 return $this->isSaved($a_node_id);
1960 }
1961
1967 public function isSaved($a_node_id)
1968 {
1969 global $ilDB;
1970
1971 // is saved cache
1972 if ($this->isCacheUsed() && isset($this->is_saved_cache[$a_node_id]))
1973 {
1974//echo "<br>issavedhit";
1975 return $this->is_saved_cache[$a_node_id];
1976 }
1977
1978 $query = 'SELECT '.$this->tree_pk.' FROM '.$this->table_tree.' '.
1979 'WHERE child = %s ';
1980 $res = $ilDB->queryF($query,array('integer'),array($a_node_id));
1981 $row = $ilDB->fetchAssoc($res);
1982
1983 if ($row[$this->tree_pk] < 0)
1984 {
1985 if($this->__isMainTree())
1986 {
1987 $this->is_saved_cache[$a_node_id] = true;
1988 }
1989 return true;
1990 }
1991 else
1992 {
1993 if($this->__isMainTree())
1994 {
1995 $this->is_saved_cache[$a_node_id] = false;
1996 }
1997 return false;
1998 }
1999 }
2000
2007 public function preloadDeleted($a_node_ids)
2008 {
2009 global $ilDB;
2010
2011 if (!is_array($a_node_ids) || !$this->isCacheUsed())
2012 {
2013 return;
2014 }
2015
2016 $query = 'SELECT '.$this->tree_pk.', child FROM '.$this->table_tree.' '.
2017 'WHERE '.$ilDB->in("child", $a_node_ids, false, "integer");
2018
2019 $res = $ilDB->query($query);
2020 while ($row = $ilDB->fetchAssoc($res))
2021 {
2022 if ($row[$this->tree_pk] < 0)
2023 {
2024 if($this->__isMainTree())
2025 {
2026 $this->is_saved_cache[$row["child"]] = true;
2027 }
2028 }
2029 else
2030 {
2031 if($this->__isMainTree())
2032 {
2033 $this->is_saved_cache[$row["child"]] = false;
2034 }
2035 }
2036 }
2037 }
2038
2039
2047 function getSavedNodeData($a_parent_id)
2048 {
2049 global $ilDB;
2050
2051 if (!isset($a_parent_id))
2052 {
2053 $message = "No node_id given!";
2054 $this->log->error($message);
2055 throw new InvalidArgumentException($message);
2056 }
2057
2058 $query = 'SELECT * FROM '.$this->table_tree.' '.
2059 $this->buildJoin().
2060 'WHERE '.$this->table_tree.'.'.$this->tree_pk.' < %s '.
2061 'AND '.$this->table_tree.'.parent = %s';
2062 $res = $ilDB->queryF($query,array('integer','integer'),array(
2063 0,
2064 $a_parent_id));
2065
2066 while($row = $ilDB->fetchAssoc($res))
2067 {
2068 $saved[] = $this->fetchNodeData($row);
2069 }
2070
2071 return $saved ? $saved : array();
2072 }
2073
2080 function getSavedNodeObjIds(array $a_obj_ids)
2081 {
2082 global $ilDB;
2083
2084 $query = 'SELECT '.$this->table_obj_data.'.obj_id FROM '.$this->table_tree.' '.
2085 $this->buildJoin().
2086 'WHERE '.$this->table_tree.'.'.$this->tree_pk.' < '.$ilDB->quote(0, 'integer').' '.
2087 'AND '.$ilDB->in($this->table_obj_data.'.obj_id', $a_obj_ids, '', 'integer');
2088 $res = $ilDB->query($query);
2089 while($row = $ilDB->fetchAssoc($res))
2090 {
2091 $saved[] = $row['obj_id'];
2092 }
2093
2094 return $saved ? $saved : array();
2095 }
2096
2104 function getParentId($a_node_id)
2105 {
2106 global $ilDB;
2107
2108 if (!isset($a_node_id))
2109 {
2110 $message = "No node_id given!";
2111 $this->log->error($message);
2112 throw new InvalidArgumentException($message);
2113 }
2114
2115 $query = 'SELECT parent FROM '.$this->table_tree.' '.
2116 'WHERE child = %s '.
2117 'AND '.$this->tree_pk.' = %s ';
2118 $res = $ilDB->queryF($query,array('integer','integer'),array(
2119 $a_node_id,
2120 $this->tree_id));
2121
2122 $row = $ilDB->fetchObject($res);
2123 return $row->parent;
2124 }
2125
2133 function getLeftValue($a_node_id)
2134 {
2135 global $ilDB;
2136
2137 if (!isset($a_node_id))
2138 {
2139 $message = "No node_id given!";
2140 $this->log->error($message);
2141 throw new InvalidArgumentException($message);
2142 }
2143
2144 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2145 'WHERE child = %s '.
2146 'AND '.$this->tree_pk.' = %s ';
2147 $res = $ilDB->queryF($query,array('integer','integer'),array(
2148 $a_node_id,
2149 $this->tree_id));
2150 $row = $ilDB->fetchObject($res);
2151 return $row->lft;
2152 }
2153
2161 function getChildSequenceNumber($a_node, $type = "")
2162 {
2163 global $ilDB;
2164
2165 if (!isset($a_node))
2166 {
2167 $message = "No node_id given!";
2168 $this->log->error($message);
2169 throw new InvalidArgumentException($message);
2170 }
2171
2172 if($type)
2173 {
2174 $query = 'SELECT count(*) cnt FROM '.$this->table_tree.' '.
2175 $this->buildJoin().
2176 'WHERE lft <= %s '.
2177 'AND type = %s '.
2178 'AND parent = %s '.
2179 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2180
2181 $res = $ilDB->queryF($query,array('integer','text','integer','integer'),array(
2182 $a_node['lft'],
2183 $type,
2184 $a_node['parent'],
2185 $this->tree_id));
2186 }
2187 else
2188 {
2189 $query = 'SELECT count(*) cnt FROM '.$this->table_tree.' '.
2190 $this->buildJoin().
2191 'WHERE lft <= %s '.
2192 'AND parent = %s '.
2193 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2194
2195 $res = $ilDB->queryF($query,array('integer','integer','integer'),array(
2196 $a_node['lft'],
2197 $a_node['parent'],
2198 $this->tree_id));
2199
2200 }
2201 $row = $ilDB->fetchAssoc($res);
2202 return $row["cnt"];
2203 }
2204
2211 function readRootId()
2212 {
2213 global $ilDB;
2214
2215 $query = 'SELECT child FROM '.$this->table_tree.' '.
2216 'WHERE parent = %s '.
2217 'AND '.$this->tree_pk.' = %s ';
2218 $res = $ilDB->queryF($query,array('integer','integer'),array(
2219 0,
2220 $this->tree_id));
2221 $row = $ilDB->fetchObject($res);
2222 $this->root_id = $row->child;
2223 return $this->root_id;
2224 }
2225
2231 function getRootId()
2232 {
2233 return $this->root_id;
2234 }
2235 function setRootId($a_root_id)
2236 {
2237 $this->root_id = $a_root_id;
2238 }
2239
2245 function getTreeId()
2246 {
2247 return $this->tree_id;
2248 }
2249
2255 function setTreeId($a_tree_id)
2256 {
2257 $this->tree_id = $a_tree_id;
2258 }
2259
2268 function fetchSuccessorNode($a_node_id, $a_type = "")
2269 {
2270 global $ilDB;
2271
2272 if (!isset($a_node_id))
2273 {
2274 $message = "No node_id given!";
2275 $this->log->error($message);
2276 throw new InvalidArgumentException($message);
2277 }
2278
2279 // get lft value for current node
2280 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2281 'WHERE '.$this->table_tree.'.child = %s '.
2282 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2283 $res = $ilDB->queryF($query,array('integer','integer'),array(
2284 $a_node_id,
2285 $this->tree_id));
2286 $curr_node = $ilDB->fetchAssoc($res);
2287
2288 if($a_type)
2289 {
2290 $query = 'SELECT * FROM '.$this->table_tree.' '.
2291 $this->buildJoin().
2292 'WHERE lft > %s '.
2293 'AND '.$this->table_obj_data.'.type = %s '.
2294 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2295 'ORDER BY lft ';
2296 $ilDB->setLimit(1);
2297 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
2298 $curr_node['lft'],
2299 $a_type,
2300 $this->tree_id));
2301 }
2302 else
2303 {
2304 $query = 'SELECT * FROM '.$this->table_tree.' '.
2305 $this->buildJoin().
2306 'WHERE lft > %s '.
2307 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2308 'ORDER BY lft ';
2309 $ilDB->setLimit(1);
2310 $res = $ilDB->queryF($query,array('integer','integer'),array(
2311 $curr_node['lft'],
2312 $this->tree_id));
2313 }
2314
2315 if ($res->numRows() < 1)
2316 {
2317 return false;
2318 }
2319 else
2320 {
2321 $row = $ilDB->fetchAssoc($res);
2322 return $this->fetchNodeData($row);
2323 }
2324 }
2325
2334 function fetchPredecessorNode($a_node_id, $a_type = "")
2335 {
2336 global $ilDB;
2337
2338 if (!isset($a_node_id))
2339 {
2340 $message = "No node_id given!";
2341 $this->log->error($message);
2342 throw new InvalidArgumentException($message);
2343 }
2344
2345 // get lft value for current node
2346 $query = 'SELECT lft FROM '.$this->table_tree.' '.
2347 'WHERE '.$this->table_tree.'.child = %s '.
2348 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s ';
2349 $res = $ilDB->queryF($query,array('integer','integer'),array(
2350 $a_node_id,
2351 $this->tree_id));
2352
2353 $curr_node = $ilDB->fetchAssoc($res);
2354
2355 if($a_type)
2356 {
2357 $query = 'SELECT * FROM '.$this->table_tree.' '.
2358 $this->buildJoin().
2359 'WHERE lft < %s '.
2360 'AND '.$this->table_obj_data.'.type = %s '.
2361 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2362 'ORDER BY lft DESC';
2363 $ilDB->setLimit(1);
2364 $res = $ilDB->queryF($query,array('integer','text','integer'),array(
2365 $curr_node['lft'],
2366 $a_type,
2367 $this->tree_id));
2368 }
2369 else
2370 {
2371 $query = 'SELECT * FROM '.$this->table_tree.' '.
2372 $this->buildJoin().
2373 'WHERE lft < %s '.
2374 'AND '.$this->table_tree.'.'.$this->tree_pk.' = %s '.
2375 'ORDER BY lft DESC';
2376 $ilDB->setLimit(1);
2377 $res = $ilDB->queryF($query,array('integer','integer'),array(
2378 $curr_node['lft'],
2379 $this->tree_id));
2380 }
2381
2382 if ($res->numRows() < 1)
2383 {
2384 return false;
2385 }
2386 else
2387 {
2388 $row = $ilDB->fetchAssoc($res);
2389 return $this->fetchNodeData($row);
2390 }
2391 }
2392
2401 function renumber($node_id = 1, $i = 1)
2402 {
2403 global $ilDB;
2404
2405 $renumber_callable = function(ilDBInterface $ilDB) use($node_id,$i,&$return)
2406 {
2407
2408 $return = $this->__renumber($node_id,$i);
2409
2410 };
2411
2412 // LOCKED ###################################
2413 if($this->__isMainTree())
2414 {
2415 $ilAtomQuery = $ilDB->buildAtomQuery();
2416 $ilAtomQuery->addTableLock( $this->table_tree );
2417
2418 $ilAtomQuery->addQueryCallable($renumber_callable);
2419 $ilAtomQuery->run();
2420 }
2421 else
2422 {
2423 $renumber_callable($ilDB);
2424 }
2425 return $return;
2426 }
2427
2428 // PRIVATE
2438 function __renumber($node_id = 1, $i = 1)
2439 {
2440 global $ilDB;
2441
2442 $query = 'UPDATE '.$this->table_tree.' SET lft = %s WHERE child = %s AND tree = %s';
2443 $res = $ilDB->manipulateF($query,array('integer','integer','integer'),array(
2444 $i,
2445 $node_id,
2446 $this->tree_id));
2447
2448 // to much dependencies
2449 //$childs = $this->getChilds($node_id);
2450 $childs = $this->getChildIds($node_id);
2451
2452 foreach ($childs as $child)
2453 {
2454 $i = $this->__renumber($child,$i+1);
2455 }
2456 $i++;
2457
2458 // Insert a gap at the end of node, if the node has children
2459 if (count($childs) > 0)
2460 {
2461 $i += $this->gap * 2;
2462 }
2463
2464
2465 $query = 'UPDATE '.$this->table_tree.' SET rgt = %s WHERE child = %s AND tree = %s';
2466 $res = $ilDB->manipulateF($query,array('integer','integer', 'integer'),array(
2467 $i,
2468 $node_id,
2469 $this->tree_id));
2470 return $i;
2471 }
2472
2473
2484 function checkForParentType($a_ref_id,$a_type,$a_exclude_source_check = false)
2485 {
2486 // #12577
2487 $cache_key = $a_ref_id.'.'.$a_type.'.'.((int)$a_exclude_source_check);
2488
2489 // Try to return a cached result
2490 if($this->isCacheUsed() &&
2491 array_key_exists($cache_key, $this->parent_type_cache))
2492 {
2493 return $this->parent_type_cache[$cache_key];
2494 }
2495
2496 // Store up to 1000 results in cache
2497 $do_cache = ($this->__isMainTree() && count($this->parent_type_cache) < 1000);
2498
2499 // ref_id is not in tree
2500 if(!$this->isInTree($a_ref_id))
2501 {
2502 if($do_cache)
2503 {
2504 $this->parent_type_cache[$cache_key] = false;
2505 }
2506 return false;
2507 }
2508
2509 $path = array_reverse($this->getPathFull($a_ref_id));
2510
2511 // remove first path entry as it is requested node
2512 if($a_exclude_source_check)
2513 {
2514 array_shift($path);
2515 }
2516
2517 foreach($path as $node)
2518 {
2519 // found matching parent
2520 if($node["type"] == $a_type)
2521 {
2522 if($do_cache)
2523 {
2524 $this->parent_type_cache[$cache_key] = $node["child"];
2525 }
2526 return $node["child"];
2527 }
2528 }
2529
2530 if($do_cache)
2531 {
2532 $this->parent_type_cache[$cache_key] = false;
2533 }
2534 return 0;
2535 }
2536
2547 static function _removeEntry($a_tree,$a_child,$a_db_table = "tree")
2548 {
2549 global $ilDB;
2550
2551 if($a_db_table === 'tree')
2552 {
2553 if($a_tree == 1 and $a_child == ROOT_FOLDER_ID)
2554 {
2555 $message = sprintf('Tried to delete root node! $a_tree: %s $a_child: %s',
2556 $a_tree,
2557 $a_child);
2558 ilLoggerFactory::getLogger('tree')->error($message);
2559 throw new InvalidArgumentException($message);
2560 }
2561 }
2562
2563 $query = 'DELETE FROM '.$a_db_table.' '.
2564 'WHERE tree = %s '.
2565 'AND child = %s ';
2566 $res = $ilDB->manipulateF($query,array('integer','integer'),array(
2567 $a_tree,
2568 $a_child));
2569
2570 }
2571
2578 public function __isMainTree()
2579 {
2580 return $this->table_tree === 'tree';
2581 }
2582
2594 function __checkDelete($a_node)
2595 {
2596 global $ilDB;
2597
2598
2599 $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, array(),false);
2600 $this->log->debug($query);
2601 $res = $ilDB->query($query);
2602
2603 $counter = (int) $lft_childs = array();
2604 while($row = $ilDB->fetchObject($res))
2605 {
2606 $lft_childs[$row->child] = $row->parent;
2607 ++$counter;
2608 }
2609
2610 // CHECK FOR DUPLICATE CHILD IDS
2611 if($counter != count($lft_childs))
2612 {
2613 $message = 'Duplicate entries for "child" in maintree! $a_node_id: '.$a_node['child'];
2614
2615 $this->log->error($message);
2616 throw new ilInvalidTreeStructureException($message);
2617 }
2618
2619 // GET SUBTREE BY PARENT RELATION
2620 $parent_childs = array();
2621 $this->__getSubTreeByParentRelation($a_node['child'],$parent_childs);
2622 $this->__validateSubtrees($lft_childs,$parent_childs);
2623
2624 return true;
2625 }
2626
2636 function __getSubTreeByParentRelation($a_node_id,&$parent_childs)
2637 {
2638 global $ilDB;
2639
2640 // GET PARENT ID
2641 $query = 'SELECT * FROM '.$this->table_tree.' '.
2642 'WHERE child = %s '.
2643 'AND tree = %s ';
2644 $res = $ilDB->queryF($query,array('integer','integer'),array(
2645 $a_node_id,
2646 $this->tree_id));
2647
2648 $counter = 0;
2649 while($row = $ilDB->fetchObject($res))
2650 {
2651 $parent_childs[$a_node_id] = $row->parent;
2652 ++$counter;
2653 }
2654 // MULTIPLE ENTRIES
2655 if($counter > 1)
2656 {
2657 $message = 'Multiple entries in maintree! $a_node_id: '. $a_node_id;
2658
2659 $this->log->error($message);
2660 throw new ilInvalidTreeStructureException($message);
2661 }
2662
2663 // GET ALL CHILDS
2664 $query = 'SELECT * FROM '.$this->table_tree.' '.
2665 'WHERE parent = %s ';
2666 $res = $ilDB->queryF($query,array('integer'),array($a_node_id));
2667
2668 while($row = $ilDB->fetchObject($res))
2669 {
2670 // RECURSION
2671 $this->__getSubTreeByParentRelation($row->child,$parent_childs);
2672 }
2673 return true;
2674 }
2675
2683 function __validateSubtrees(&$lft_childs,$parent_childs)
2684 {
2685 // SORT BY KEY
2686 ksort($lft_childs);
2687 ksort($parent_childs);
2688
2689 $this->log->debug('left childs '. print_r($lft_childs,true));
2690 $this->log->debug('parent childs '. print_r($parent_childs,true));
2691
2692 if(count($lft_childs) != count($parent_childs))
2693 {
2694 $message = '(COUNT) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2695 $this->log->error($message);
2696 throw new ilInvalidTreeStructureException($message);
2697 }
2698
2699
2700 foreach($lft_childs as $key => $value)
2701 {
2702 if($parent_childs[$key] != $value)
2703 {
2704 $message = '(COMPARE) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2705 $this->log->error($message);
2706 throw new ilInvalidTreeStructureException($message);
2707 }
2708 if($key == ROOT_FOLDER_ID)
2709 {
2710 $message = '(ROOT_FOLDER) Tree is corrupted! Tried to delete root folder';
2711 $this->log->error($message);
2712 throw new ilInvalidTreeStructureException($message);
2713 }
2714 }
2715 return true;
2716 }
2717
2727 public function moveTree($a_source_id, $a_target_id, $a_location = self::POS_LAST_NODE)
2728 {
2729 $old_parent_id = $this->getParentId($a_source_id);
2730 $this->getTreeImplementation()->moveTree($a_source_id,$a_target_id,$a_location);
2731 if (isset($GLOBALS["ilAppEventHandler"]) && $this->__isMainTree()) {
2732 $GLOBALS['ilAppEventHandler']->raise(
2733 "Services/Tree",
2734 "moveTree",
2735 array(
2736 'tree' => $this->table_tree,
2737 'source_id' => $a_source_id,
2738 'target_id' => $a_target_id,
2739 'old_parent_id' => $old_parent_id
2740 )
2741 );
2742 }
2743 return true;
2744 }
2745
2746
2747
2748
2756 public function getRbacSubtreeInfo($a_endnode_id)
2757 {
2758 return $this->getTreeImplementation()->getSubtreeInfo($a_endnode_id);
2759 }
2760
2761
2769 public function getSubTreeQuery($a_node_id,$a_fields = array(), $a_types = '', $a_force_join_reference = false)
2770 {
2771 return $this->getTreeImplementation()->getSubTreeQuery(
2772 $this->getNodeTreeData($a_node_id),
2773 $a_types,
2774 $a_force_join_reference,
2775 $a_fields);
2776 }
2777
2778
2787 public function getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields = array())
2788 {
2789 global $ilDB;
2790
2791 $node = $this->getNodeData($a_node_id);
2792 if(!sizeof($node))
2793 {
2794 return;
2795 }
2796
2797 $res = array();
2798
2799 $query = $this->getTreeImplementation()->getSubTreeQuery($node, '', true, array($this->ref_pk));
2800
2801 $fields = '*';
2802 if(count($a_fields))
2803 {
2804 $fields = implode(',',$a_fields);
2805 }
2806
2807 $query = "SELECT ".$fields.
2808 " FROM ".$this->getTreeTable().
2809 " ".$this->buildJoin().
2810 " WHERE ".$this->getTableReference().".".$this->ref_pk." IN (".$query.")".
2811 " AND ".$ilDB->in($this->getObjectDataTable().".".$this->obj_pk, $a_obj_ids, "", "integer");
2812 $set = $ilDB->query($query);
2813 while($row = $ilDB->fetchAssoc($set))
2814 {
2815 $res[] = $row;
2816 }
2817
2818 return $res;
2819 }
2820
2821 public function deleteNode($a_tree_id,$a_node_id)
2822 {
2823 global $ilDB, $ilAppEventHandler;
2824
2825 $query = 'DELETE FROM tree where '.
2826 'child = '.$ilDB->quote($a_node_id,'integer').' '.
2827 'AND tree = '.$ilDB->quote($a_tree_id,'integer');
2828 $ilDB->manipulate($query);
2829
2830 $ilAppEventHandler->raise(
2831 "Services/Tree",
2832 "deleteNode",
2833 array('tree' => $this->table_tree,
2834 'node_id' => $a_node_id,
2835 'tree_id' => $a_tree_id
2836 )
2837 );
2838 }
2839
2846 {
2847 global $ilDB;
2848
2849 $query = 'SELECT DISTINCT(o.type) '.$ilDB->quoteIdentifier('type').' FROM tree t JOIN object_reference r ON child = r.ref_id '.
2850 'JOIN object_data o on r.obj_id = o.obj_id '.
2851 'WHERE tree < '.$ilDB->quote(0,'integer').' '.
2852 'AND child = -tree '.
2853 'GROUP BY o.type';
2854 $res = $ilDB->query($query);
2855
2856 $types_deleted = array();
2857 while($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
2858 {
2859 $types_deleted[] = $row->type;
2860 }
2861 return $types_deleted;
2862 }
2863
2864
2865} // END class.tree
2866?>
sprintf('%.4f', $callTime)
$path
Definition: aliased.php:25
An exception for terminatinating execution or to throw for unit testing.
static toNFC( $string)
Convert a UTF-8 string to normal form C, canonical composition.
Definition: UtfNormal.php:154
const IL_LAST_NODE
Definition: class.ilTree.php:4
static _lookupTitle($a_obj_id)
Overwitten from base class.
Database Wrapper.
Definition: class.ilDB.php:30
quote($a_query, $a_type=null)
Wrapper for quote method.
Thrown if invalid tree strucutes are found.
static getLogger($a_component_id)
Get component logger.
Base class for materialize path based trees Based on implementation of Werner Randelshofer.
Base class for nested set path based trees.
static setDeletedDates($a_ref_ids)
Set deleted date @global type $ilDB.
static _resetDeletedDate($a_ref_id)
only called in ilObjectGUI::insertSavedNodes
const DESC_LENGTH
ILIAS Setting Class.
static strToLower($a_string)
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
isCacheUsed()
Check if cache is active.
fetchPredecessorNode($a_node_id, $a_type="")
get node data of predecessor node
lookupTrashedObjectTypes()
Lookup object types in trash @global type $ilDB.
getRelation($a_node_a, $a_node_b)
Get relation of two nodes.
moveToTrash($a_node_id, $a_set_deleted=false)
Wrapper for saveSubTree.
getSubTree($a_node, $a_with_data=true, $a_type="")
get all nodes in the subtree under specified node
getFilteredChilds($a_filter, $a_node, $a_order="", $a_direction="ASC")
get child nodes of given node (exclude filtered obj_types) @access public
isGrandChild($a_startnode_id, $a_querynode_id)
checks if a node is in the path of an other node @access public
getSubTreeTypes($a_node, $a_filter=0)
get types of nodes in the subtree under specified node
getRelationOfNodes($a_node_a_arr, $a_node_b_arr)
get relation of two nodes by node data
setObjectTablePK($a_column_name)
set column containing primary key in object table @access public
getTreePk()
Get tree primary key.
getParentId($a_node_id)
get parent id of given node @access public
getRbacSubtreeInfo($a_endnode_id)
This method is used for change existing objects and returns all necessary information for this action...
getParentCache()
Get parent cache.
setReferenceTablePK($a_column_name)
set column containing primary key in reference table @access public
saveSubTree($a_node_id, $a_set_deleted=false)
Use the wrapper moveToTrash save subtree: delete a subtree (defined by node_id) to a new tree with $t...
checkTreeChilds($a_no_zero_child=true)
check, if all childs of tree nodes exist in object table
getDepth($a_node_id)
return depth of a node in tree @access private
getSavedNodeData($a_parent_id)
get data saved/deleted nodes
getChildSequenceNumber($a_node, $type="")
get sequence number of node in sibling sequence @access public
const RELATION_EQUALS
insertNodeFromTrash($a_source_id, $a_target_id, $a_tree_id, $a_pos=IL_LAST_NODE, $a_reset_deleted_date=false)
Insert node from trash deletes trash entry.
useCache($a_use=true)
Use Cache (usually activated)
deleteTree($a_node)
delete node and the whole subtree under this node @access public
getLeftValue($a_node_id)
get left value of given node @access public
__checkDelete($a_node)
Check for deleteTree() compares a subtree of a given node by checking lft, rgt against parent relatio...
fetchSuccessorNode($a_node_id, $a_type="")
get node data of successor node
getDepthCache()
Get depth cache.
getTreeId()
get tree id @access public
getChildIds($a_node)
Get node child ids @global type $ilDB.
getNodeDataByType($a_type)
get nodes by type
getTreeTable()
Get tree table name.
static _removeEntry($a_tree, $a_child, $a_db_table="tree")
STATIC METHOD Removes a single entry from a tree.
getSubTreeQuery($a_node_id, $a_fields=array(), $a_types='', $a_force_join_reference=false)
Get tree subtree query.
__renumber($node_id=1, $i=1)
This method is private.
setTreeTablePK($a_column_name)
set column containing primary key in tree table @access public
const POS_LAST_NODE
isDeleted($a_node_id)
This is a wrapper for isSaved() with a more useful name.
getNodeTreeData($a_node_id)
return all columns of tabel tree
setTableNames($a_table_tree, $a_table_obj_data, $a_table_obj_reference="")
set table names The primary key of the table containing your object_data must be 'obj_id' You may use...
getTableReference()
Get reference table if available.
fetchTranslationFromObjectDataCache($a_obj_ids)
Get translation data from object cache (trigger in object cache on preload)
readRootId()
read root id from database
removeTree($a_tree_id)
remove an existing tree
fetchNodeData($a_row)
get data of parent node from tree and object_data @access private
checkTree()
check consistence of tree all left & right values are checked if they are exists only once @access pu...
getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields=array())
get all node ids in the subtree under specified node id, filter by object ids
renumber($node_id=1, $i=1)
Wrapper for renumber.
getNodePath($a_endnode_id, $a_startnode_id=0)
Returns the node path for the specified object reference.
resetInTreeCache()
getChildsByType($a_node_id, $a_type)
get child nodes of given node by object type @access public
getNodeData($a_node_id, $a_tree_pk=null)
get all information of a node.
setTreeId($a_tree_id)
set tree id @access public
getSavedNodeObjIds(array $a_obj_ids)
get object id of saved/deleted nodes
getChildsByTypeFilter($a_node_id, $a_types, $a_order="", $a_direction="ASC")
get child nodes of given node by object type @access public
deleteNode($a_tree_id, $a_node_id)
$table_obj_reference
getMaximumDepth()
Return the current maximum depth in the tree @access public.
moveTree($a_source_id, $a_target_id, $a_location=self::POS_LAST_NODE)
Move Tree Implementation.
preloadDepthParent($a_node_ids)
Preload depth/parent.
const POS_FIRST_NODE
isSaved($a_node_id)
Use method isDeleted check if node is saved.
const RELATION_PARENT
initLangCode()
Store user language.
const RELATION_NONE
buildJoin()
build join depending on table settings @access private
getTreeImplementation()
Get tree implementation.
getNodePathForTitlePath($titlePath, $a_startnode_id=null)
Converts a path consisting of object titles into a path consisting of tree nodes.
initTreeImplementation()
Init tree implementation.
const RELATION_SIBLING
preloadDeleted($a_node_ids)
Preload deleted information.
__construct($a_tree_id, $a_root_id=0)
Constructor @access public.
getFilteredSubTree($a_node_id, $a_filter=array())
get filtered subtree
getObjectDataTable()
Get object data table.
getRootId()
get the root id of tree @access public
insertNode($a_node_id, $a_parent_id, $a_pos=IL_LAST_NODE, $a_reset_deletion_date=false)
insert new node with node_id under parent node with parent_id @access public
checkForParentType($a_ref_id, $a_type, $a_exclude_source_check=false)
Check for parent type e.g check if a folder (ref_id 3) is in a parent course obj => checkForParentTyp...
__isMainTree()
Check if operations are done on main tree.
__getSubTreeByParentRelation($a_node_id, &$parent_childs)
@global type $ilDB
isInTree($a_node_id)
get all information of a node.
getGap()
Get default gap *.
const RELATION_CHILD
__validateSubtrees(&$lft_childs, $parent_childs)
getPathId($a_endnode_id, $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
addTree($a_tree_id, $a_node_id=-1)
create a new tree to do: ???
getPathFull($a_endnode_id, $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
getParentNodeData($a_node_id)
get data of parent node from tree and object_data @access public
getChilds($a_node_id, $a_order="", $a_direction="ASC")
get child nodes of given node @access public
setRootId($a_root_id)
validateParentRelations()
Validate parent relations of tree.
getSubTreeIds($a_ref_id)
Get all ids of subnodes.
static 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.
$counter
$r
Definition: example_031.php:79
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
global $ilBench
Definition: ilias.php:18
Interface ilDBInterface.
global $lng
Definition: privfeed.php:17
global $ilDB
$ilUser
Definition: imgupload.php:18
$a_type
Definition: workflow.php:93