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