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 
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 
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  {
321  return $this->table_obj_data;
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);
590  $this->fetchTranslationFromObjectDataCache($obj_ids);
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  {
759  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
760  throw new InvalidArgumentException('Invalid parameter given for ilTree::insertNodeFromTrash');
761  }
762  }
763  if (!isset($a_source_id) or !isset($a_target_id))
764  {
765  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
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');
771  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
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 
1838  public function getNodeDataByType($a_type)
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 
2845  public function lookupTrashedObjectTypes()
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 ?>
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.
static _resetDeletedDate($a_ref_id)
only called in ilObjectGUI::insertSavedNodes
$path
Definition: aliased.php:25
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.
getChilds($a_node_id, $a_order="", $a_direction="ASC")
get child nodes of given node public
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
static _removeEntry($a_tree, $a_child, $a_db_table="tree")
STATIC METHOD Removes a single entry from a tree.
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
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
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...
static strToLower($a_string)
getTreePk()
Get tree primary key.
__validateSubtrees(&$lft_childs, $parent_childs)
Base class for nested set path based trees.
static toNFC( $string)
Convert a UTF-8 string to normal form C, canonical composition.
Definition: UtfNormal.php:154
deleteNode($a_tree_id, $a_node_id)
getSubTreeTypes($a_node, $a_filter=0)
get types of nodes in the subtree under specified node
__construct($a_tree_id, $a_root_id=0)
Constructor public.
getChildsByTypeFilter($a_node_id, $a_types, $a_order="", $a_direction="ASC")
get child nodes of given node by object type public
getFilteredSubTree($a_node_id, $a_filter=array())
get filtered subtree
getObjectDataTable()
Get object data table.
validateParentRelations()
Validate parent relations of tree.
$counter
__checkDelete($a_node)
Check for deleteTree() compares a subtree of a given node by checking lft, rgt against parent relatio...
Interface ilDBInterface.
$a_type
Definition: workflow.php:93
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
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.
$ilUser
Definition: imgupload.php:18
setRootId($a_root_id)
getParentId($a_node_id)
get parent id of given node public
getTableReference()
Get reference table if available.
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.
Create styles array
The data for the language used.
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:29
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:17
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
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.
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