ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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  public $ilias;
43 
44 
50  public $log;
51 
57  public $root_id;
58 
64  public $tree_id;
65 
71  public $table_tree;
72 
79 
86 
92  public $ref_pk;
93 
99  public $obj_pk;
100 
106  public $tree_pk;
107 
132  public $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  public function __construct($a_tree_id, $a_root_id = 0)
149  {
150  global $DIC;
151 
152  $ilDB = $DIC['ilDB'];
153 
154  // set db
155  $this->ilDB = $ilDB;
156 
157  $this->lang_code = "en";
158 
159  // CREATE LOGGER INSTANCE
160  $this->log = ilLoggerFactory::getLogger('tree');
161 
162  if (!isset($a_tree_id) or (func_num_args() == 0)) {
163  $this->log->error("No tree_id given!");
164  $this->log->logStack(ilLogLevel::DEBUG);
165  throw new InvalidArgumentException("No tree_id given!");
166  }
167 
168  if (func_num_args() > 2) {
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  $a_root_id = ROOT_FOLDER_ID;
176  }
177 
178  $this->tree_id = $a_tree_id;
179  $this->root_id = $a_root_id;
180  $this->table_tree = 'tree';
181  $this->table_obj_data = 'object_data';
182  $this->table_obj_reference = 'object_reference';
183  $this->ref_pk = 'ref_id';
184  $this->obj_pk = 'obj_id';
185  $this->tree_pk = 'tree';
186 
187  $this->use_cache = true;
188 
189  // If cache is activated, cache object translations to improve performance
190  $this->translation_cache = array();
191  $this->parent_type_cache = array();
192 
193  // By default, we create gaps in the tree sequence numbering for 50 nodes
194  $this->gap = 50;
195 
196 
197  // init tree implementation
198  $this->initTreeImplementation();
199  }
200 
204  public function initTreeImplementation()
205  {
206  global $DIC;
207 
208  if (!$DIC->isDependencyAvailable('settings') || $DIC->settings()->getModule() != 'common') {
209  include_once './Services/Administration/classes/class.ilSetting.php';
210  $setting = new ilSetting('common');
211  } else {
212  $setting = $DIC->settings();
213  }
214 
215  if ($this->__isMainTree()) {
216  if ($setting->get('main_tree_impl', 'ns') == 'ns') {
217  #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Using nested set.');
218  include_once './Services/Tree/classes/class.ilNestedSetTree.php';
219  $this->tree_impl = new ilNestedSetTree($this);
220  } else {
221  #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Using materialized path.');
222  include_once './Services/Tree/classes/class.ilMaterializedPathTree.php';
223  $this->tree_impl = new ilMaterializedPathTree($this);
224  }
225  } else {
226  #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Using netsted set for non main tree.');
227  include_once './Services/Tree/classes/class.ilNestedSetTree.php';
228  $this->tree_impl = new ilNestedSetTree($this);
229  }
230  }
231 
236  public function getTreeImplementation()
237  {
238  return $this->tree_impl;
239  }
240 
244  public function useCache($a_use = true)
245  {
246  $this->use_cache = $a_use;
247  }
248 
253  public function isCacheUsed()
254  {
255  return $this->__isMainTree() and $this->use_cache;
256  }
257 
262  public function getDepthCache()
263  {
264  return (array) $this->depth_cache;
265  }
266 
271  public function getParentCache()
272  {
273  return (array) $this->parent_cache;
274  }
275 
280  public function initLangCode()
281  {
282  global $DIC;
283 
284  // lang_code is only required in $this->fetchnodedata
285  try {
286  $ilUser = $DIC['ilUser'];
287  $this->lang_code = $ilUser->getCurrentLanguage();
288  } catch (\InvalidArgumentException $e) {
289  $this->lang_code = "en";
290  }
291  }
292 
297  public function getTreeTable()
298  {
299  return $this->table_tree;
300  }
301 
306  public function getObjectDataTable()
307  {
308  return $this->table_obj_data;
309  }
310 
315  public function getTreePk()
316  {
317  return $this->tree_pk;
318  }
319 
323  public function getTableReference()
324  {
326  }
327 
331  public function getGap()
332  {
333  return $this->gap;
334  }
335 
336  /***
337  * reset in tree cache
338  */
339  public function resetInTreeCache()
340  {
341  $this->in_tree_cache = array();
342  }
343 
344 
361  public function setTableNames($a_table_tree, $a_table_obj_data, $a_table_obj_reference = "")
362  {
363  if (!isset($a_table_tree) or !isset($a_table_obj_data)) {
364  $message = "Missing parameter! " .
365  "tree table: " . $a_table_tree . " object data table: " . $a_table_obj_data;
366  $this->log->error($message);
368  }
369 
370  $this->table_tree = $a_table_tree;
371  $this->table_obj_data = $a_table_obj_data;
372  $this->table_obj_reference = $a_table_obj_reference;
373 
374  $this->initTreeImplementation();
375 
376  return true;
377  }
378 
386  public function setReferenceTablePK($a_column_name)
387  {
388  if (!isset($a_column_name)) {
389  $message = "No column name given!";
390  $this->log->error($message);
392  }
393 
394  $this->ref_pk = $a_column_name;
395  return true;
396  }
397 
405  public function setObjectTablePK($a_column_name)
406  {
407  if (!isset($a_column_name)) {
408  $message = "No column name given!";
409  $this->log->error($message);
411  }
412 
413  $this->obj_pk = $a_column_name;
414  return true;
415  }
416 
424  public function setTreeTablePK($a_column_name)
425  {
426  if (!isset($a_column_name)) {
427  $message = "No column name given!";
428  $this->log->error($message);
430  }
431 
432  $this->tree_pk = $a_column_name;
433  return true;
434  }
435 
441  public function buildJoin()
442  {
443  if ($this->table_obj_reference) {
444  // Use inner join instead of left join to improve performance
445  return "JOIN " . $this->table_obj_reference . " ON " . $this->table_tree . ".child=" . $this->table_obj_reference . "." . $this->ref_pk . " " .
446  "JOIN " . $this->table_obj_data . " ON " . $this->table_obj_reference . "." . $this->obj_pk . "=" . $this->table_obj_data . "." . $this->obj_pk . " ";
447  } else {
448  // Use inner join instead of left join to improve performance
449  return "JOIN " . $this->table_obj_data . " ON " . $this->table_tree . ".child=" . $this->table_obj_data . "." . $this->obj_pk . " ";
450  }
451  }
452 
458  public function getRelation($a_node_a, $a_node_b)
459  {
460  return $this->getRelationOfNodes(
461  $this->getNodeTreeData($a_node_a),
462  $this->getNodeTreeData($a_node_b)
463  );
464  }
465 
472  public function getRelationOfNodes($a_node_a_arr, $a_node_b_arr)
473  {
474  return $this->getTreeImplementation()->getRelation($a_node_a_arr, $a_node_b_arr);
475  }
476 
483  public function getChildIds($a_node)
484  {
485  global $DIC;
486 
487  $ilDB = $DIC['ilDB'];
488 
489  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
490  'WHERE parent = ' . $ilDB->quote($a_node, 'integer') . ' ' .
491  'AND tree = ' . $ilDB->quote($this->tree_id, 'integer' . ' ' .
492  'ORDER BY lft');
493  $res = $ilDB->query($query);
494 
495  $childs = array();
496  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
497  $childs[] = $row->child;
498  }
499  return $childs;
500  }
501 
511  public function getChilds($a_node_id, $a_order = "", $a_direction = "ASC")
512  {
513  global $DIC;
514 
515  $ilBench = $DIC['ilBench'];
516  $ilDB = $DIC['ilDB'];
517  $ilObjDataCache = $DIC['ilObjDataCache'];
518  $ilUser = $DIC['ilUser'];
519 
520  if (!isset($a_node_id)) {
521  $message = "No node_id given!";
522  $this->log->error($message);
524  }
525 
526  // init childs
527  $childs = array();
528 
529  // number of childs
530  $count = 0;
531 
532  // init order_clause
533  $order_clause = "";
534 
535  // set order_clause if sort order parameter is given
536  if (!empty($a_order)) {
537  $order_clause = "ORDER BY " . $a_order . " " . $a_direction;
538  } else {
539  $order_clause = "ORDER BY " . $this->table_tree . ".lft";
540  }
541 
542 
543  $query = sprintf(
544  'SELECT * FROM ' . $this->table_tree . ' ' .
545  $this->buildJoin() .
546  "WHERE parent = %s " .
547  "AND " . $this->table_tree . "." . $this->tree_pk . " = %s " .
548  $order_clause,
549  $ilDB->quote($a_node_id, 'integer'),
550  $ilDB->quote($this->tree_id, 'integer')
551  );
552 
553  $res = $ilDB->query($query);
554 
555  if (!$count = $res->numRows()) {
556  return array();
557  }
558 
559  // get rows and object ids
560  $rows = array();
561  while ($r = $ilDB->fetchAssoc($res)) {
562  $rows[] = $r;
563  $obj_ids[] = $r["obj_id"];
564  }
565 
566  // preload object translation information
567  if ($this->__isMainTree() && $this->isCacheUsed() && is_object($ilObjDataCache) &&
568  is_object($ilUser) && $this->lang_code == $ilUser->getLanguage() && !$this->oc_preloaded[$a_node_id]) {
569  // $ilObjDataCache->preloadTranslations($obj_ids, $this->lang_code);
570  $ilObjDataCache->preloadObjectCache($obj_ids, $this->lang_code);
571  $this->fetchTranslationFromObjectDataCache($obj_ids);
572  $this->oc_preloaded[$a_node_id] = true;
573  }
574 
575  foreach ($rows as $row) {
576  $childs[] = $this->fetchNodeData($row);
577 
578  // Update cache of main tree
579  if ($this->__isMainTree()) {
580  #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child'].' = true');
581  $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
582  }
583  }
584  $childs[$count - 1]["last"] = true;
585  return $childs;
586  }
587 
597  public function getFilteredChilds($a_filter, $a_node, $a_order = "", $a_direction = "ASC")
598  {
599  $childs = $this->getChilds($a_node, $a_order, $a_direction);
600 
601  foreach ($childs as $child) {
602  if (!in_array($child["type"], $a_filter)) {
603  $filtered[] = $child;
604  }
605  }
606  return $filtered ? $filtered : array();
607  }
608 
609 
618  public function getChildsByType($a_node_id, $a_type)
619  {
620  global $DIC;
621 
622  $ilDB = $DIC['ilDB'];
623 
624  if (!isset($a_node_id) or !isset($a_type)) {
625  $message = "Missing parameter! node_id:" . $a_node_id . " type:" . $a_type;
626  $this->log->error($message);
628  }
629 
630  if ($a_type == 'rolf' && $this->table_obj_reference) {
631  // Performance optimization: A node can only have exactly one
632  // role folder as its child. Therefore we don't need to sort the
633  // results, and we can let the database know about the expected limit.
634  $ilDB->setLimit(1, 0);
635  $query = sprintf(
636  "SELECT * FROM " . $this->table_tree . " " .
637  $this->buildJoin() .
638  "WHERE parent = %s " .
639  "AND " . $this->table_tree . "." . $this->tree_pk . " = %s " .
640  "AND " . $this->table_obj_data . ".type = %s ",
641  $ilDB->quote($a_node_id, 'integer'),
642  $ilDB->quote($this->tree_id, 'integer'),
643  $ilDB->quote($a_type, 'text')
644  );
645  } else {
646  $query = sprintf(
647  "SELECT * FROM " . $this->table_tree . " " .
648  $this->buildJoin() .
649  "WHERE parent = %s " .
650  "AND " . $this->table_tree . "." . $this->tree_pk . " = %s " .
651  "AND " . $this->table_obj_data . ".type = %s " .
652  "ORDER BY " . $this->table_tree . ".lft",
653  $ilDB->quote($a_node_id, 'integer'),
654  $ilDB->quote($this->tree_id, 'integer'),
655  $ilDB->quote($a_type, 'text')
656  );
657  }
658  $res = $ilDB->query($query);
659 
660  // init childs
661  $childs = array();
662  while ($row = $ilDB->fetchAssoc($res)) {
663  $childs[] = $this->fetchNodeData($row);
664  }
665 
666  return $childs ? $childs : array();
667  }
668 
669 
678  public function getChildsByTypeFilter($a_node_id, $a_types, $a_order = "", $a_direction = "ASC")
679  {
680  global $DIC;
681 
682  $ilDB = $DIC['ilDB'];
683 
684  if (!isset($a_node_id) or !$a_types) {
685  $message = "Missing parameter! node_id:" . $a_node_id . " type:" . $a_types;
686  $this->log->error($message);
688  }
689 
690  $filter = ' ';
691  if ($a_types) {
692  $filter = 'AND ' . $this->table_obj_data . '.type IN(' . implode(',', ilUtil::quoteArray($a_types)) . ') ';
693  }
694 
695  // set order_clause if sort order parameter is given
696  if (!empty($a_order)) {
697  $order_clause = "ORDER BY " . $a_order . " " . $a_direction;
698  } else {
699  $order_clause = "ORDER BY " . $this->table_tree . ".lft";
700  }
701 
702  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
703  $this->buildJoin() .
704  'WHERE parent = ' . $ilDB->quote($a_node_id, 'integer') . ' ' .
705  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $ilDB->quote($this->tree_id, 'integer') . ' ' .
706  $filter .
707  $order_clause;
708 
709  $res = $ilDB->query($query);
710  while ($row = $ilDB->fetchAssoc($res)) {
711  $childs[] = $this->fetchNodeData($row);
712  }
713 
714  return $childs ? $childs : array();
715  }
716 
728  public function insertNodeFromTrash($a_source_id, $a_target_id, $a_tree_id, $a_pos = IL_LAST_NODE, $a_reset_deleted_date = false)
729  {
730  global $DIC;
731 
732  $ilDB = $DIC['ilDB'];
733 
734  if ($this->__isMainTree()) {
735  if ($a_source_id <= 1 or $a_target_id <= 0) {
736  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
737  throw new InvalidArgumentException('Invalid parameter given for ilTree::insertNodeFromTrash');
738  }
739  }
740  if (!isset($a_source_id) or !isset($a_target_id)) {
741  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
742  throw new InvalidArgumentException('Missing parameter for ilTree::insertNodeFromTrash');
743  }
744  if ($this->isInTree($a_source_id)) {
745  ilLoggerFactory::getLogger('tree')->error('Node already in tree');
746  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
747  throw new InvalidArgumentException('Node already in tree.');
748  }
749 
750  $query = 'DELETE from tree ' .
751  'WHERE tree = ' . $ilDB->quote($a_tree_id, 'integer') . ' ' .
752  'AND child = ' . $ilDB->quote($a_source_id, 'integer');
753  $ilDB->manipulate($query);
754 
755  $this->insertNode($a_source_id, $a_target_id, IL_LAST_NODE, $a_reset_deleted_date);
756  }
757 
758 
767  public function insertNode($a_node_id, $a_parent_id, $a_pos = IL_LAST_NODE, $a_reset_deletion_date = false)
768  {
769  global $DIC;
770 
771  $ilDB = $DIC['ilDB'];
772 
773  //echo "+$a_node_id+$a_parent_id+";
774  // CHECK node_id and parent_id > 0 if in main tree
775  if ($this->__isMainTree()) {
776  if ($a_node_id <= 1 or $a_parent_id <= 0) {
777  $message = sprintf(
778  'Invalid parameters! $a_node_id: %s $a_parent_id: %s',
779  $a_node_id,
780  $a_parent_id
781  );
782  $this->log->logStack(ilLogLevel::ERROR, $message);
784  }
785  }
786 
787 
788  if (!isset($a_node_id) or !isset($a_parent_id)) {
789  $this->log->logStack(ilLogLevel::ERROR);
790  throw new InvalidArgumentException("Missing parameter! " .
791  "node_id: " . $a_node_id . " parent_id: " . $a_parent_id);
792  }
793  if ($this->isInTree($a_node_id)) {
794  throw new InvalidArgumentException("Node " . $a_node_id . " already in tree " .
795  $this->table_tree . "!");
796  }
797 
798  $this->getTreeImplementation()->insertNode($a_node_id, $a_parent_id, $a_pos);
799 
800  $this->in_tree_cache[$a_node_id] = true;
801 
802  // reset deletion date
803  if ($a_reset_deletion_date) {
804  ilObject::_resetDeletedDate($a_node_id);
805  }
806 
807  if (isset($GLOBALS['DIC']["ilAppEventHandler"]) && $this->__isMainTree()) {
808  $GLOBALS['DIC']['ilAppEventHandler']->raise(
809  "Services/Tree",
810  "insertNode",
811  array(
812  'tree' => $this->table_tree,
813  'node_id' => $a_node_id,
814  'parent_id' => $a_parent_id)
815  );
816  }
817  }
818 
831  public function getFilteredSubTree($a_node_id, $a_filter = array())
832  {
833  $node = $this->getNodeData($a_node_id);
834 
835  $first = true;
836  $depth = 0;
837  foreach ($this->getSubTree($node) as $subnode) {
838  if ($depth and $subnode['depth'] > $depth) {
839  continue;
840  }
841  if (!$first and in_array($subnode['type'], $a_filter)) {
842  $depth = $subnode['depth'];
843  $first = false;
844  continue;
845  }
846  $depth = 0;
847  $first = false;
848  $filtered[] = $subnode;
849  }
850  return $filtered ? $filtered : array();
851  }
852 
858  public function getSubTreeIds($a_ref_id)
859  {
860  return $this->getTreeImplementation()->getSubTreeIds($a_ref_id);
861  }
862 
863 
873  public function getSubTree($a_node, $a_with_data = true, $a_type = "")
874  {
875  global $DIC;
876 
877  $ilDB = $DIC['ilDB'];
878 
879  if (!is_array($a_node)) {
880  $this->log->logStack(ilLogLevel::ERROR);
881  throw new InvalidArgumentException(__METHOD__ . ': wrong datatype for node data given');
882  }
883 
884  /*
885  if($a_node['lft'] < 1 or $a_node['rgt'] < 2)
886  {
887  $GLOBALS['DIC']['ilLog']->logStack();
888  $message = sprintf('%s: Invalid node given! $a_node["lft"]: %s $a_node["rgt"]: %s',
889  __METHOD__,
890  $a_node['lft'],
891  $a_node['rgt']);
892 
893  throw new InvalidArgumentException($message);
894  }
895  */
896 
897  $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, $a_type);
898  $res = $ilDB->query($query);
899  while ($row = $ilDB->fetchAssoc($res)) {
900  if ($a_with_data) {
901  $subtree[] = $this->fetchNodeData($row);
902  } else {
903  $subtree[] = $row['child'];
904  }
905  // the lm_data "hack" should be removed in the trunk during an alpha
906  if ($this->__isMainTree() || $this->table_tree == "lm_tree") {
907  $this->in_tree_cache[$row['child']] = true;
908  }
909  }
910  return $subtree ? $subtree : array();
911  }
912 
921  public function getSubTreeTypes($a_node, $a_filter = 0)
922  {
923  $a_filter = $a_filter ? $a_filter : array();
924 
925  foreach ($this->getSubtree($this->getNodeData($a_node)) as $node) {
926  if (in_array($node["type"], $a_filter)) {
927  continue;
928  }
929  $types["$node[type]"] = $node["type"];
930  }
931  return $types ? $types : array();
932  }
933 
941  public function deleteTree($a_node)
942  {
943  global $DIC;
944 
945  $ilDB = $DIC['ilDB'];
946 
947  $this->log->debug('Delete tree with node ' . $a_node);
948 
949  if (!is_array($a_node)) {
950  $this->log->logStack(ilLogLevel::ERROR);
951  throw new InvalidArgumentException(__METHOD__ . ': Wrong datatype for node data!');
952  }
953 
954  $this->log->debug($this->tree_pk);
955 
956  if ($this->__isMainTree()) {
957  // @todo normally this part is not executed, since the subtree is first
958  // moved to trash and then deleted.
959  if (!$this->__checkDelete($a_node)) {
960  $this->log->logStack(ilLogLevel::ERROR);
961  throw new ilInvalidTreeStructureException('Deletion canceled due to invalid tree structure.' . print_r($a_node, true));
962  }
963  }
964 
965  $this->getTreeImplementation()->deleteTree($a_node['child']);
966 
967  $this->resetInTreeCache();
968  }
969 
974  public function validateParentRelations()
975  {
976  return $this->getTreeImplementation()->validateParentRelations();
977  }
978 
989  public function getPathFull($a_endnode_id, $a_startnode_id = 0)
990  {
991  $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
992 
993  // We retrieve the full path in a single query to improve performance
994  global $DIC;
995 
996  $ilDB = $DIC['ilDB'];
997 
998  // Abort if no path ids were found
999  if (count($pathIds) == 0) {
1000  return null;
1001  }
1002 
1003  $inClause = 'child IN (';
1004  for ($i = 0; $i < count($pathIds); $i++) {
1005  if ($i > 0) {
1006  $inClause .= ',';
1007  }
1008  $inClause .= $ilDB->quote($pathIds[$i], 'integer');
1009  }
1010  $inClause .= ')';
1011 
1012  $q = 'SELECT * ' .
1013  'FROM ' . $this->table_tree . ' ' .
1014  $this->buildJoin() . ' ' .
1015  'WHERE ' . $inClause . ' ' .
1016  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer') . ' ' .
1017  'ORDER BY depth';
1018  $r = $ilDB->query($q);
1019 
1020  $pathFull = array();
1021  while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1022  $pathFull[] = $this->fetchNodeData($row);
1023 
1024  // Update cache
1025  if ($this->__isMainTree()) {
1026  #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child']);
1027  $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
1028  }
1029  }
1030  return $pathFull;
1031  }
1032 
1033 
1040  public function preloadDepthParent($a_node_ids)
1041  {
1042  global $DIC;
1043 
1044  $ilDB = $DIC['ilDB'];
1045 
1046  if (!$this->__isMainTree() || !is_array($a_node_ids) || !$this->isCacheUsed()) {
1047  return;
1048  }
1049 
1050  $res = $ilDB->query('SELECT t.depth, t.parent, t.child ' .
1051  'FROM ' . $this->table_tree . ' t ' .
1052  'WHERE ' . $ilDB->in("child", $a_node_ids, false, "integer") .
1053  'AND ' . $this->tree_pk . ' = ' . $ilDB->quote($this->tree_id, "integer"));
1054  while ($row = $ilDB->fetchAssoc($res)) {
1055  $this->depth_cache[$row["child"]] = $row["depth"];
1056  $this->parent_cache[$row["child"]] = $row["parent"];
1057  }
1058  }
1059 
1069  public function getPathId($a_endnode_id, $a_startnode_id = 0)
1070  {
1071  if (!$a_endnode_id) {
1072  $this->log->logStack(ilLogLevel::ERROR);
1073  throw new InvalidArgumentException(__METHOD__ . ': No endnode given!');
1074  }
1075 
1076  // path id cache
1077  if ($this->isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id])) {
1078  //echo "<br>getPathIdhit";
1079  return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
1080  }
1081  //echo "<br>miss";
1082 
1083  $pathIds = $this->getTreeImplementation()->getPathIds($a_endnode_id, $a_startnode_id);
1084 
1085  if ($this->__isMainTree()) {
1086  $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
1087  }
1088  return $pathIds;
1089  }
1090 
1091  // BEGIN WebDAV: getNodePathForTitlePath function added
1109  public function getNodePathForTitlePath($titlePath, $a_startnode_id = null)
1110  {
1111  global $DIC;
1112 
1113  $ilDB = $DIC['ilDB'];
1114  $log = $DIC['log'];
1115  //$log->write('getNodePathForTitlePath('.implode('/',$titlePath));
1116 
1117  // handle empty title path
1118  if ($titlePath == null || count($titlePath) == 0) {
1119  if ($a_startnode_id == 0) {
1120  return null;
1121  } else {
1122  return $this->getNodePath($a_startnode_id);
1123  }
1124  }
1125 
1126  // fetch the node path up to the startnode
1127  if ($a_startnode_id != null && $a_startnode_id != 0) {
1128  // Start using the node path to the root of the relative path
1129  $nodePath = $this->getNodePath($a_startnode_id);
1130  $parent = $a_startnode_id;
1131  } else {
1132  // Start using the root of the tree
1133  $nodePath = array();
1134  $parent = 0;
1135  }
1136 
1137 
1138  // Convert title path into Unicode Normal Form C
1139  // This is needed to ensure that we can compare title path strings with
1140  // strings from the database.
1141  require_once('include/Unicode/UtfNormal.php');
1142  include_once './Services/Utilities/classes/class.ilStr.php';
1143  $inClause = 'd.title IN (';
1144  for ($i = 0; $i < count($titlePath); $i++) {
1145  $titlePath[$i] = ilStr::strToLower(UtfNormal::toNFC($titlePath[$i]));
1146  if ($i > 0) {
1147  $inClause .= ',';
1148  }
1149  $inClause .= $ilDB->quote($titlePath[$i], 'text');
1150  }
1151  $inClause .= ')';
1152 
1153  // Fetch all rows that are potential path elements
1154  if ($this->table_obj_reference) {
1155  $joinClause = 'JOIN ' . $this->table_obj_reference . ' r ON t.child = r.' . $this->ref_pk . ' ' .
1156  'JOIN ' . $this->table_obj_data . ' d ON r.' . $this->obj_pk . ' = d.' . $this->obj_pk;
1157  } else {
1158  $joinClause = 'JOIN ' . $this->table_obj_data . ' d ON t.child = d.' . $this->obj_pk;
1159  }
1160  // The ORDER BY clause in the following SQL statement ensures that,
1161  // in case of a multiple objects with the same title, always the Object
1162  // with the oldest ref_id is chosen.
1163  // This ensure, that, if a new object with the same title is added,
1164  // WebDAV clients can still work with the older object.
1165  $q = 'SELECT t.depth, t.parent, t.child, d.' . $this->obj_pk . ' obj_id, d.type, d.title ' .
1166  'FROM ' . $this->table_tree . ' t ' .
1167  $joinClause . ' ' .
1168  'WHERE ' . $inClause . ' ' .
1169  'AND t.depth <= ' . (count($titlePath) + count($nodePath)) . ' ' .
1170  'AND t.tree = 1 ' .
1171  'ORDER BY t.depth, t.child ASC';
1172  $r = $ilDB->query($q);
1173 
1174  $rows = array();
1175  while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1176  $row['title'] = UtfNormal::toNFC($row['title']);
1177  $row['ref_id'] = $row['child'];
1178  $rows[] = $row;
1179  }
1180 
1181  // Extract the path elements from the fetched rows
1182  for ($i = 0; $i < count($titlePath); $i++) {
1183  $pathElementFound = false;
1184  foreach ($rows as $row) {
1185  if ($row['parent'] == $parent &&
1186  ilStr::strToLower($row['title']) == $titlePath[$i]) {
1187  // FIXME - We should test here, if the user has
1188  // 'visible' permission for the object.
1189  $nodePath[] = $row;
1190  $parent = $row['child'];
1191  $pathElementFound = true;
1192  break;
1193  }
1194  }
1195  // Abort if we haven't found a path element for the current depth
1196  if (!$pathElementFound) {
1197  //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):null');
1198  return null;
1199  }
1200  }
1201  // Return the node path
1202  //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):'.var_export($nodePath,true));
1203  return $nodePath;
1204  }
1205  // END WebDAV: getNodePathForTitlePath function added
1206  // END WebDAV: getNodePath function added
1223  public function getNodePath($a_endnode_id, $a_startnode_id = 0)
1224  {
1225  global $DIC;
1226 
1227  $ilDB = $DIC['ilDB'];
1228 
1229  $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
1230 
1231  // Abort if no path ids were found
1232  if (count($pathIds) == 0) {
1233  return null;
1234  }
1235 
1236 
1237  $types = array();
1238  $data = array();
1239  for ($i = 0; $i < count($pathIds); $i++) {
1240  $types[] = 'integer';
1241  $data[] = $pathIds[$i];
1242  }
1243 
1244  $query = 'SELECT t.depth,t.parent,t.child,d.obj_id,d.type,d.title ' .
1245  'FROM ' . $this->table_tree . ' t ' .
1246  'JOIN ' . $this->table_obj_reference . ' r ON r.ref_id = t.child ' .
1247  'JOIN ' . $this->table_obj_data . ' d ON d.obj_id = r.obj_id ' .
1248  'WHERE ' . $ilDB->in('t.child', $data, false, 'integer') . ' ' .
1249  'ORDER BY t.depth ';
1250 
1251  $res = $ilDB->queryF($query, $types, $data);
1252 
1253  $titlePath = array();
1254  while ($row = $ilDB->fetchAssoc($res)) {
1255  $titlePath[] = $row;
1256  }
1257  return $titlePath;
1258  }
1259  // END WebDAV: getNodePath function added
1260 
1268  public function checkTree()
1269  {
1270  global $DIC;
1271 
1272  $ilDB = $DIC['ilDB'];
1273 
1274  $types = array('integer');
1275  $query = 'SELECT lft,rgt FROM ' . $this->table_tree . ' ' .
1276  'WHERE ' . $this->tree_pk . ' = %s ';
1277 
1278  $res = $ilDB->queryF($query, $types, array($this->tree_id));
1279  while ($row = $ilDB->fetchObject($res)) {
1280  $lft[] = $row->lft;
1281  $rgt[] = $row->rgt;
1282  }
1283 
1284  $all = array_merge($lft, $rgt);
1285  $uni = array_unique($all);
1286 
1287  if (count($all) != count($uni)) {
1288  $message = 'Tree is corrupted!';
1289 
1290  $this->log->error($message);
1292  }
1293 
1294  return true;
1295  }
1296 
1304  public function checkTreeChilds($a_no_zero_child = true)
1305  {
1306  global $DIC;
1307 
1308  $ilDB = $DIC['ilDB'];
1309 
1310  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1311  'WHERE ' . $this->tree_pk . ' = %s ' .
1312  'ORDER BY lft';
1313  $r1 = $ilDB->queryF($query, array('integer'), array($this->tree_id));
1314 
1315  while ($row = $ilDB->fetchAssoc($r1)) {
1316  //echo "tree:".$row[$this->tree_pk].":lft:".$row["lft"].":rgt:".$row["rgt"].":child:".$row["child"].":<br>";
1317  if (($row["child"] == 0) && $a_no_zero_child) {
1318  $message = "Tree contains child with ID 0!";
1319  $this->log->error($message);
1321  }
1322 
1323  if ($this->table_obj_reference) {
1324  // get object reference data
1325  $query = 'SELECT * FROM ' . $this->table_obj_reference . ' WHERE ' . $this->ref_pk . ' = %s ';
1326  $r2 = $ilDB->queryF($query, array('integer'), array($row['child']));
1327 
1328  //echo "num_childs:".$r2->numRows().":<br>";
1329  if ($r2->numRows() == 0) {
1330  $message = "No Object-to-Reference entry found for ID " . $row["child"] . "!";
1331  $this->log->error($message);
1333  }
1334  if ($r2->numRows() > 1) {
1335  $message = "More Object-to-Reference entries found for ID " . $row["child"] . "!";
1336  $this->log->error($message);
1338  }
1339 
1340  // get object data
1341  $obj_ref = $ilDB->fetchAssoc($r2);
1342 
1343  $query = 'SELECT * FROM ' . $this->table_obj_data . ' WHERE ' . $this->obj_pk . ' = %s';
1344  $r3 = $ilDB->queryF($query, array('integer'), array($obj_ref[$this->obj_pk]));
1345  if ($r3->numRows() == 0) {
1346  $message = " No child found for ID " . $obj_ref[$this->obj_pk] . "!";
1347  $this->log->error($message);
1349  }
1350  if ($r3->numRows() > 1) {
1351  $message = "More childs found for ID " . $obj_ref[$this->obj_pk] . "!";
1352  $this->log->error($message);
1354  }
1355  } else {
1356  // get only object data
1357  $query = 'SELECT * FROM ' . $this->table_obj_data . ' WHERE ' . $this->obj_pk . ' = %s';
1358  $r2 = $ilDB->queryF($query, array('integer'), array($row['child']));
1359  //echo "num_childs:".$r2->numRows().":<br>";
1360  if ($r2->numRows() == 0) {
1361  $message = "No child found for ID " . $row["child"] . "!";
1362  $this->log->error($message);
1364  }
1365  if ($r2->numRows() > 1) {
1366  $message = "More childs found for ID " . $row["child"] . "!";
1367  $this->log->error($message);
1369  }
1370  }
1371  }
1372 
1373  return true;
1374  }
1375 
1381  public function getMaximumDepth()
1382  {
1383  global $DIC;
1384 
1385  $ilDB = $DIC['ilDB'];
1386 
1387  $query = 'SELECT MAX(depth) depth FROM ' . $this->table_tree;
1388  $res = $ilDB->query($query);
1389 
1390  $row = $ilDB->fetchAssoc($res);
1391  return $row['depth'];
1392  }
1393 
1400  public function getDepth($a_node_id)
1401  {
1402  global $DIC;
1403 
1404  $ilDB = $DIC['ilDB'];
1405 
1406  if ($a_node_id) {
1407  $query = 'SELECT depth FROM ' . $this->table_tree . ' ' .
1408  'WHERE child = %s ' .
1409  'AND ' . $this->tree_pk . ' = %s ';
1410  $res = $ilDB->queryF($query, array('integer','integer'), array($a_node_id,$this->tree_id));
1411  $row = $ilDB->fetchObject($res);
1412 
1413  return $row->depth;
1414  } else {
1415  return 1;
1416  }
1417  }
1418 
1426  public function getNodeTreeData($a_node_id)
1427  {
1428  global $DIC;
1429 
1430  $ilDB = $DIC['ilDB'];
1431 
1432  if (!$a_node_id) {
1433  $this->log->logStack(ilLogLevel::ERROR);
1434  throw new InvalidArgumentException('Missing or empty parameter $a_node_id: ' . $a_node_id);
1435  }
1436 
1437  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1438  'WHERE child = ' . $ilDB->quote($a_node_id, 'integer');
1439  $res = $ilDB->query($query);
1440  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1441  return $row;
1442  }
1443  return array();
1444  }
1445 
1446 
1455  // BEGIN WebDAV: Pass tree id to this method
1456  //function getNodeData($a_node_id)
1457  public function getNodeData($a_node_id, $a_tree_pk = null)
1458  // END PATCH WebDAV: Pass tree id to this method
1459  {
1460  global $DIC;
1461 
1462  $ilDB = $DIC['ilDB'];
1463 
1464  if (!isset($a_node_id)) {
1465  $this->log->logStack(ilLogLevel::ERROR);
1466  throw new InvalidArgumentException("No node_id given!");
1467  }
1468  if ($this->__isMainTree()) {
1469  if ($a_node_id < 1) {
1470  $message = 'No valid parameter given! $a_node_id: %s' . $a_node_id;
1471 
1472  $this->log->error($message);
1474  }
1475  }
1476 
1477  // BEGIN WebDAV: Pass tree id to this method
1478  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1479  $this->buildJoin() .
1480  'WHERE ' . $this->table_tree . '.child = %s ' .
1481  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
1482  $res = $ilDB->queryF($query, array('integer','integer'), array(
1483  $a_node_id,
1484  $a_tree_pk === null ? $this->tree_id : $a_tree_pk));
1485  // END WebDAV: Pass tree id to this method
1486  $row = $ilDB->fetchAssoc($res);
1488 
1489  return $this->fetchNodeData($row);
1490  }
1491 
1499  public function fetchNodeData($a_row)
1500  {
1501  global $DIC;
1502 
1503  $objDefinition = $DIC['objDefinition'];
1504  $lng = $DIC['lng'];
1505  $ilBench = $DIC['ilBench'];
1506  $ilDB = $DIC['ilDB'];
1507 
1508  //$ilBench->start("Tree", "fetchNodeData_getRow");
1509  $data = $a_row;
1510  $data["desc"] = $a_row["description"]; // for compability
1511  //$ilBench->stop("Tree", "fetchNodeData_getRow");
1512 
1513  // multilingual support systemobjects (sys) & categories (db)
1514  //$ilBench->start("Tree", "fetchNodeData_readDefinition");
1515  if (is_object($objDefinition)) {
1516  $translation_type = $objDefinition->getTranslationType($data["type"]);
1517  }
1518  //$ilBench->stop("Tree", "fetchNodeData_readDefinition");
1519 
1520  if ($translation_type == "sys") {
1521  //$ilBench->start("Tree", "fetchNodeData_getLangData");
1522  if ($data["type"] == "rolf" and $data["obj_id"] != ROLE_FOLDER_ID) {
1523  $data["description"] = $lng->txt("obj_" . $data["type"] . "_local_desc") . $data["title"] . $data["desc"];
1524  $data["desc"] = $lng->txt("obj_" . $data["type"] . "_local_desc") . $data["title"] . $data["desc"];
1525  $data["title"] = $lng->txt("obj_" . $data["type"] . "_local");
1526  } else {
1527  $data["title"] = $lng->txt("obj_" . $data["type"]);
1528  $data["description"] = $lng->txt("obj_" . $data["type"] . "_desc");
1529  $data["desc"] = $lng->txt("obj_" . $data["type"] . "_desc");
1530  }
1531  //$ilBench->stop("Tree", "fetchNodeData_getLangData");
1532  } elseif ($translation_type == "db") {
1533 
1534  // Try to retrieve object translation from cache
1535  if ($this->isCacheUsed() &&
1536  array_key_exists($data["obj_id"] . '.' . $lang_code, $this->translation_cache)) {
1537  $key = $data["obj_id"] . '.' . $lang_code;
1538  $data["title"] = $this->translation_cache[$key]['title'];
1539  $data["description"] = $this->translation_cache[$key]['description'];
1540  $data["desc"] = $this->translation_cache[$key]['desc'];
1541  } else {
1542  // Object translation is not in cache, read it from database
1543  //$ilBench->start("Tree", "fetchNodeData_getTranslation");
1544  $query = 'SELECT title,description FROM object_translation ' .
1545  'WHERE obj_id = %s ' .
1546  'AND lang_code = %s ' .
1547  'AND NOT lang_default = %s';
1548 
1549  $res = $ilDB->queryF($query, array('integer','text','integer'), array(
1550  $data['obj_id'],
1551  $this->lang_code,
1552  1));
1553  $row = $ilDB->fetchObject($res);
1554 
1555  if ($row) {
1556  $data["title"] = $row->title;
1557  $data["description"] = ilUtil::shortenText($row->description, ilObject::DESC_LENGTH, true);
1558  $data["desc"] = $row->description;
1559  }
1560  //$ilBench->stop("Tree", "fetchNodeData_getTranslation");
1561 
1562  // Store up to 1000 object translations in cache
1563  if ($this->isCacheUsed() && count($this->translation_cache) < 1000) {
1564  $key = $data["obj_id"] . '.' . $lang_code;
1565  $this->translation_cache[$key] = array();
1566  $this->translation_cache[$key]['title'] = $data["title"] ;
1567  $this->translation_cache[$key]['description'] = $data["description"];
1568  $this->translation_cache[$key]['desc'] = $data["desc"];
1569  }
1570  }
1571  }
1572 
1573  // TODO: Handle this switch by module.xml definitions
1574  if ($data['type'] == 'crsr' or $data['type'] == 'catr' or $data['type'] == 'grpr') {
1575  include_once('./Services/ContainerReference/classes/class.ilContainerReference.php');
1576  $data['title'] = ilContainerReference::_lookupTitle($data['obj_id']);
1577  }
1578 
1579  return $data ? $data : array();
1580  }
1581 
1587  protected function fetchTranslationFromObjectDataCache($a_obj_ids)
1588  {
1589  global $DIC;
1590 
1591  $ilObjDataCache = $DIC['ilObjDataCache'];
1592 
1593  if ($this->isCacheUsed() && is_array($a_obj_ids) && is_object($ilObjDataCache)) {
1594  foreach ($a_obj_ids as $id) {
1595  $this->translation_cache[$id . '.']['title'] = $ilObjDataCache->lookupTitle($id);
1596  $this->translation_cache[$id . '.']['description'] = $ilObjDataCache->lookupDescription($id);
1597  ;
1598  $this->translation_cache[$id . '.']['desc'] =
1599  $this->translation_cache[$id . '.']['description'];
1600  }
1601  }
1602  }
1603 
1604 
1612  public function isInTree($a_node_id)
1613  {
1614  global $DIC;
1615 
1616  $ilDB = $DIC['ilDB'];
1617 
1618  if (!isset($a_node_id)) {
1619  return false;
1620  #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1621  }
1622  // is in tree cache
1623  if ($this->isCacheUsed() && isset($this->in_tree_cache[$a_node_id])) {
1624  #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Using in tree cache '.$a_node_id);
1625  //echo "<br>in_tree_hit";
1626  return $this->in_tree_cache[$a_node_id];
1627  }
1628 
1629  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1630  'WHERE ' . $this->table_tree . '.child = %s ' .
1631  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s';
1632 
1633  $res = $ilDB->queryF($query, array('integer','integer'), array(
1634  $a_node_id,
1635  $this->tree_id));
1636 
1637  if ($res->numRows() > 0) {
1638  if ($this->__isMainTree()) {
1639  #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = true');
1640  $this->in_tree_cache[$a_node_id] = true;
1641  }
1642  return true;
1643  } else {
1644  if ($this->__isMainTree()) {
1645  #$GLOBALS['DIC']['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = false');
1646  $this->in_tree_cache[$a_node_id] = false;
1647  }
1648  return false;
1649  }
1650  }
1651 
1659  public function getParentNodeData($a_node_id)
1660  {
1661  global $DIC;
1662 
1663  $ilDB = $DIC['ilDB'];
1664  global $DIC;
1665 
1666  $ilLog = $DIC['ilLog'];
1667 
1668  if (!isset($a_node_id)) {
1669  $ilLog->logStack();
1670  throw new InvalidArgumentException(__METHOD__ . ': No node_id given!');
1671  }
1672 
1673  if ($this->table_obj_reference) {
1674  // Use inner join instead of left join to improve performance
1675  $innerjoin = "JOIN " . $this->table_obj_reference . " ON v.child=" . $this->table_obj_reference . "." . $this->ref_pk . " " .
1676  "JOIN " . $this->table_obj_data . " ON " . $this->table_obj_reference . "." . $this->obj_pk . "=" . $this->table_obj_data . "." . $this->obj_pk . " ";
1677  } else {
1678  // Use inner join instead of left join to improve performance
1679  $innerjoin = "JOIN " . $this->table_obj_data . " ON v.child=" . $this->table_obj_data . "." . $this->obj_pk . " ";
1680  }
1681 
1682  $query = 'SELECT * FROM ' . $this->table_tree . ' s, ' . $this->table_tree . ' v ' .
1683  $innerjoin .
1684  'WHERE s.child = %s ' .
1685  'AND s.parent = v.child ' .
1686  'AND s.' . $this->tree_pk . ' = %s ' .
1687  'AND v.' . $this->tree_pk . ' = %s';
1688  $res = $ilDB->queryF($query, array('integer','integer','integer'), array(
1689  $a_node_id,
1690  $this->tree_id,
1691  $this->tree_id));
1692  $row = $ilDB->fetchAssoc($res);
1693  return $this->fetchNodeData($row);
1694  }
1695 
1703  public function isGrandChild($a_startnode_id, $a_querynode_id)
1704  {
1705  return $this->getRelation($a_startnode_id, $a_querynode_id) == self::RELATION_PARENT;
1706  }
1707 
1717  public function addTree($a_tree_id, $a_node_id = -1)
1718  {
1719  global $DIC;
1720 
1721  $ilDB = $DIC['ilDB'];
1722 
1723  // FOR SECURITY addTree() IS NOT ALLOWED ON MAIN TREE
1724  if ($this->__isMainTree()) {
1725  $message = sprintf(
1726  'Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
1727  $a_tree_id,
1728  $a_node_id
1729  );
1730  $this->log->error($message);
1732  }
1733 
1734  if (!isset($a_tree_id)) {
1735  $message = "No tree_id given!";
1736  $this->log->error($message);
1738  }
1739 
1740  if ($a_node_id <= 0) {
1741  $a_node_id = $a_tree_id;
1742  }
1743 
1744  $query = 'INSERT INTO ' . $this->table_tree . ' (' .
1745  $this->tree_pk . ', child,parent,lft,rgt,depth) ' .
1746  'VALUES ' .
1747  '(%s,%s,%s,%s,%s,%s)';
1748  $res = $ilDB->manipulateF($query, array('integer','integer','integer','integer','integer','integer'), array(
1749  $a_tree_id,
1750  $a_node_id,
1751  0,
1752  1,
1753  2,
1754  1));
1755 
1756  return true;
1757  }
1758 
1768  public function getNodeDataByType($a_type)
1769  {
1770  global $DIC;
1771 
1772  $ilDB = $DIC['ilDB'];
1773 
1774  if (!isset($a_type) or (!is_string($a_type))) {
1775  $this->log->logStack(ilLogLevel::ERROR);
1776  throw new InvalidArgumentException('Type not given or wrong datatype');
1777  }
1778 
1779  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1780  $this->buildJoin() .
1781  'WHERE ' . $this->table_obj_data . '.type = ' . $this->ilDB->quote($a_type, 'text') .
1782  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer');
1783 
1784  $res = $ilDB->query($query);
1785  $data = array();
1786  while ($row = $ilDB->fetchAssoc($res)) {
1787  $data[] = $this->fetchNodeData($row);
1788  }
1789 
1790  return $data;
1791  }
1792 
1801  public function removeTree($a_tree_id)
1802  {
1803  global $DIC;
1804 
1805  $ilDB = $DIC['ilDB'];
1806 
1807  // OPERATION NOT ALLOWED ON MAIN TREE
1808  if ($this->__isMainTree()) {
1809  $this->log->logStack(ilLogLevel::ERROR);
1810  throw new InvalidArgumentException('Operation not allowed on main tree');
1811  }
1812  if (!$a_tree_id) {
1813  $this->log->logStack(ilLogLevel::ERROR);
1814  throw new InvalidArgumentException('Missing parameter tree id');
1815  }
1816 
1817  $query = 'DELETE FROM ' . $this->table_tree .
1818  ' WHERE ' . $this->tree_pk . ' = %s ';
1819  $ilDB->manipulateF($query, array('integer'), array($a_tree_id));
1820  return true;
1821  }
1822 
1830  public function moveToTrash($a_node_id, $a_set_deleted = false)
1831  {
1832  global $DIC;
1833 
1834  $ilDB = $DIC['ilDB'];
1835 
1836  if (!$a_node_id) {
1837  $this->log->logStack(ilLogLevel::ERROR);
1838  throw new InvalidArgumentException('No valid parameter given! $a_node_id: ' . $a_node_id);
1839  }
1840 
1841 
1842  $query = $this->getTreeImplementation()->getSubTreeQuery($this->getNodeTreeData($a_node_id), '', false);
1843  $res = $ilDB->query($query);
1844 
1845  $subnodes = array();
1846  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1847  $subnodes[] = $row['child'];
1848  }
1849 
1850  if (!count($subnodes)) {
1851  // Possibly already deleted
1852  return false;
1853  }
1854 
1855  if ($a_set_deleted) {
1856  include_once './Services/Object/classes/class.ilObject.php';
1857  ilObject::setDeletedDates($subnodes);
1858  }
1859 
1860  // netsted set <=> mp
1861  $this->getTreeImplementation()->moveToTrash($a_node_id);
1862 
1863  return true;
1864  }
1865 
1876  public function saveSubTree($a_node_id, $a_set_deleted = false)
1877  {
1878  return $this->moveToTrash($a_node_id, $a_set_deleted);
1879  }
1880 
1885  public function isDeleted($a_node_id)
1886  {
1887  return $this->isSaved($a_node_id);
1888  }
1889 
1895  public function isSaved($a_node_id)
1896  {
1897  global $DIC;
1898 
1899  $ilDB = $DIC['ilDB'];
1900 
1901  // is saved cache
1902  if ($this->isCacheUsed() && isset($this->is_saved_cache[$a_node_id])) {
1903  //echo "<br>issavedhit";
1904  return $this->is_saved_cache[$a_node_id];
1905  }
1906 
1907  $query = 'SELECT ' . $this->tree_pk . ' FROM ' . $this->table_tree . ' ' .
1908  'WHERE child = %s ';
1909  $res = $ilDB->queryF($query, array('integer'), array($a_node_id));
1910  $row = $ilDB->fetchAssoc($res);
1911 
1912  if ($row[$this->tree_pk] < 0) {
1913  if ($this->__isMainTree()) {
1914  $this->is_saved_cache[$a_node_id] = true;
1915  }
1916  return true;
1917  } else {
1918  if ($this->__isMainTree()) {
1919  $this->is_saved_cache[$a_node_id] = false;
1920  }
1921  return false;
1922  }
1923  }
1924 
1931  public function preloadDeleted($a_node_ids)
1932  {
1933  global $DIC;
1934 
1935  $ilDB = $DIC['ilDB'];
1936 
1937  if (!is_array($a_node_ids) || !$this->isCacheUsed()) {
1938  return;
1939  }
1940 
1941  $query = 'SELECT ' . $this->tree_pk . ', child FROM ' . $this->table_tree . ' ' .
1942  'WHERE ' . $ilDB->in("child", $a_node_ids, false, "integer");
1943 
1944  $res = $ilDB->query($query);
1945  while ($row = $ilDB->fetchAssoc($res)) {
1946  if ($row[$this->tree_pk] < 0) {
1947  if ($this->__isMainTree()) {
1948  $this->is_saved_cache[$row["child"]] = true;
1949  }
1950  } else {
1951  if ($this->__isMainTree()) {
1952  $this->is_saved_cache[$row["child"]] = false;
1953  }
1954  }
1955  }
1956  }
1957 
1958 
1966  public function getSavedNodeData($a_parent_id)
1967  {
1968  global $DIC;
1969 
1970  $ilDB = $DIC['ilDB'];
1971 
1972  if (!isset($a_parent_id)) {
1973  $message = "No node_id given!";
1974  $this->log->error($message);
1976  }
1977 
1978  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1979  $this->buildJoin() .
1980  'WHERE ' . $this->table_tree . '.' . $this->tree_pk . ' < %s ' .
1981  'AND ' . $this->table_tree . '.parent = %s';
1982  $res = $ilDB->queryF($query, array('integer','integer'), array(
1983  0,
1984  $a_parent_id));
1985 
1986  while ($row = $ilDB->fetchAssoc($res)) {
1987  $saved[] = $this->fetchNodeData($row);
1988  }
1989 
1990  return $saved ? $saved : array();
1991  }
1992 
1999  public function getSavedNodeObjIds(array $a_obj_ids)
2000  {
2001  global $DIC;
2002 
2003  $ilDB = $DIC['ilDB'];
2004 
2005  $query = 'SELECT ' . $this->table_obj_data . '.obj_id FROM ' . $this->table_tree . ' ' .
2006  $this->buildJoin() .
2007  'WHERE ' . $this->table_tree . '.' . $this->tree_pk . ' < ' . $ilDB->quote(0, 'integer') . ' ' .
2008  'AND ' . $ilDB->in($this->table_obj_data . '.obj_id', $a_obj_ids, '', 'integer');
2009  $res = $ilDB->query($query);
2010  while ($row = $ilDB->fetchAssoc($res)) {
2011  $saved[] = $row['obj_id'];
2012  }
2013 
2014  return $saved ? $saved : array();
2015  }
2016 
2024  public function getParentId($a_node_id)
2025  {
2026  global $DIC;
2027 
2028  $ilDB = $DIC['ilDB'];
2029 
2030  if (!isset($a_node_id)) {
2031  $message = "No node_id given!";
2032  $this->log->error($message);
2034  }
2035 
2036  $query = 'SELECT parent FROM ' . $this->table_tree . ' ' .
2037  'WHERE child = %s ' .
2038  'AND ' . $this->tree_pk . ' = %s ';
2039  $res = $ilDB->queryF($query, array('integer','integer'), array(
2040  $a_node_id,
2041  $this->tree_id));
2042 
2043  $row = $ilDB->fetchObject($res);
2044  return $row->parent;
2045  }
2046 
2054  public function getLeftValue($a_node_id)
2055  {
2056  global $DIC;
2057 
2058  $ilDB = $DIC['ilDB'];
2059 
2060  if (!isset($a_node_id)) {
2061  $message = "No node_id given!";
2062  $this->log->error($message);
2064  }
2065 
2066  $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2067  'WHERE child = %s ' .
2068  'AND ' . $this->tree_pk . ' = %s ';
2069  $res = $ilDB->queryF($query, array('integer','integer'), array(
2070  $a_node_id,
2071  $this->tree_id));
2072  $row = $ilDB->fetchObject($res);
2073  return $row->lft;
2074  }
2075 
2083  public function getChildSequenceNumber($a_node, $type = "")
2084  {
2085  global $DIC;
2086 
2087  $ilDB = $DIC['ilDB'];
2088 
2089  if (!isset($a_node)) {
2090  $message = "No node_id given!";
2091  $this->log->error($message);
2093  }
2094 
2095  if ($type) {
2096  $query = 'SELECT count(*) cnt FROM ' . $this->table_tree . ' ' .
2097  $this->buildJoin() .
2098  'WHERE lft <= %s ' .
2099  'AND type = %s ' .
2100  'AND parent = %s ' .
2101  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2102 
2103  $res = $ilDB->queryF($query, array('integer','text','integer','integer'), array(
2104  $a_node['lft'],
2105  $type,
2106  $a_node['parent'],
2107  $this->tree_id));
2108  } else {
2109  $query = 'SELECT count(*) cnt FROM ' . $this->table_tree . ' ' .
2110  $this->buildJoin() .
2111  'WHERE lft <= %s ' .
2112  'AND parent = %s ' .
2113  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2114 
2115  $res = $ilDB->queryF($query, array('integer','integer','integer'), array(
2116  $a_node['lft'],
2117  $a_node['parent'],
2118  $this->tree_id));
2119  }
2120  $row = $ilDB->fetchAssoc($res);
2121  return $row["cnt"];
2122  }
2123 
2130  public function readRootId()
2131  {
2132  global $DIC;
2133 
2134  $ilDB = $DIC['ilDB'];
2135 
2136  $query = 'SELECT child FROM ' . $this->table_tree . ' ' .
2137  'WHERE parent = %s ' .
2138  'AND ' . $this->tree_pk . ' = %s ';
2139  $res = $ilDB->queryF($query, array('integer','integer'), array(
2140  0,
2141  $this->tree_id));
2142  $row = $ilDB->fetchObject($res);
2143  $this->root_id = $row->child;
2144  return $this->root_id;
2145  }
2146 
2152  public function getRootId()
2153  {
2154  return $this->root_id;
2155  }
2156  public function setRootId($a_root_id)
2157  {
2158  $this->root_id = $a_root_id;
2159  }
2160 
2166  public function getTreeId()
2167  {
2168  return $this->tree_id;
2169  }
2170 
2176  public function setTreeId($a_tree_id)
2177  {
2178  $this->tree_id = $a_tree_id;
2179  }
2180 
2189  public function fetchSuccessorNode($a_node_id, $a_type = "")
2190  {
2191  global $DIC;
2192 
2193  $ilDB = $DIC['ilDB'];
2194 
2195  if (!isset($a_node_id)) {
2196  $message = "No node_id given!";
2197  $this->log->error($message);
2199  }
2200 
2201  // get lft value for current node
2202  $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2203  'WHERE ' . $this->table_tree . '.child = %s ' .
2204  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2205  $res = $ilDB->queryF($query, array('integer','integer'), array(
2206  $a_node_id,
2207  $this->tree_id));
2208  $curr_node = $ilDB->fetchAssoc($res);
2209 
2210  if ($a_type) {
2211  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2212  $this->buildJoin() .
2213  'WHERE lft > %s ' .
2214  'AND ' . $this->table_obj_data . '.type = %s ' .
2215  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2216  'ORDER BY lft ';
2217  $ilDB->setLimit(1);
2218  $res = $ilDB->queryF($query, array('integer','text','integer'), array(
2219  $curr_node['lft'],
2220  $a_type,
2221  $this->tree_id));
2222  } else {
2223  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2224  $this->buildJoin() .
2225  'WHERE lft > %s ' .
2226  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2227  'ORDER BY lft ';
2228  $ilDB->setLimit(1);
2229  $res = $ilDB->queryF($query, array('integer','integer'), array(
2230  $curr_node['lft'],
2231  $this->tree_id));
2232  }
2233 
2234  if ($res->numRows() < 1) {
2235  return false;
2236  } else {
2237  $row = $ilDB->fetchAssoc($res);
2238  return $this->fetchNodeData($row);
2239  }
2240  }
2241 
2250  public function fetchPredecessorNode($a_node_id, $a_type = "")
2251  {
2252  global $DIC;
2253 
2254  $ilDB = $DIC['ilDB'];
2255 
2256  if (!isset($a_node_id)) {
2257  $message = "No node_id given!";
2258  $this->log->error($message);
2260  }
2261 
2262  // get lft value for current node
2263  $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2264  'WHERE ' . $this->table_tree . '.child = %s ' .
2265  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2266  $res = $ilDB->queryF($query, array('integer','integer'), array(
2267  $a_node_id,
2268  $this->tree_id));
2269 
2270  $curr_node = $ilDB->fetchAssoc($res);
2271 
2272  if ($a_type) {
2273  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2274  $this->buildJoin() .
2275  'WHERE lft < %s ' .
2276  'AND ' . $this->table_obj_data . '.type = %s ' .
2277  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2278  'ORDER BY lft DESC';
2279  $ilDB->setLimit(1);
2280  $res = $ilDB->queryF($query, array('integer','text','integer'), array(
2281  $curr_node['lft'],
2282  $a_type,
2283  $this->tree_id));
2284  } else {
2285  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2286  $this->buildJoin() .
2287  'WHERE lft < %s ' .
2288  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2289  'ORDER BY lft DESC';
2290  $ilDB->setLimit(1);
2291  $res = $ilDB->queryF($query, array('integer','integer'), array(
2292  $curr_node['lft'],
2293  $this->tree_id));
2294  }
2295 
2296  if ($res->numRows() < 1) {
2297  return false;
2298  } else {
2299  $row = $ilDB->fetchAssoc($res);
2300  return $this->fetchNodeData($row);
2301  }
2302  }
2303 
2312  public function renumber($node_id = 1, $i = 1)
2313  {
2314  global $DIC;
2315 
2316  $ilDB = $DIC['ilDB'];
2317 
2318  $renumber_callable = function (ilDBInterface $ilDB) use ($node_id,$i,&$return) {
2319  $return = $this->__renumber($node_id, $i);
2320  };
2321 
2322  // LOCKED ###################################
2323  if ($this->__isMainTree()) {
2324  $ilAtomQuery = $ilDB->buildAtomQuery();
2325  $ilAtomQuery->addTableLock($this->table_tree);
2326 
2327  $ilAtomQuery->addQueryCallable($renumber_callable);
2328  $ilAtomQuery->run();
2329  } else {
2330  $renumber_callable($ilDB);
2331  }
2332  return $return;
2333  }
2334 
2335  // PRIVATE
2345  public function __renumber($node_id = 1, $i = 1)
2346  {
2347  global $DIC;
2348 
2349  $ilDB = $DIC['ilDB'];
2350 
2351  $query = 'UPDATE ' . $this->table_tree . ' SET lft = %s WHERE child = %s AND tree = %s';
2352  $res = $ilDB->manipulateF($query, array('integer','integer','integer'), array(
2353  $i,
2354  $node_id,
2355  $this->tree_id));
2356 
2357  // to much dependencies
2358  //$childs = $this->getChilds($node_id);
2359  $childs = $this->getChildIds($node_id);
2360 
2361  foreach ($childs as $child) {
2362  $i = $this->__renumber($child, $i + 1);
2363  }
2364  $i++;
2365 
2366  // Insert a gap at the end of node, if the node has children
2367  if (count($childs) > 0) {
2368  $i += $this->gap * 2;
2369  }
2370 
2371 
2372  $query = 'UPDATE ' . $this->table_tree . ' SET rgt = %s WHERE child = %s AND tree = %s';
2373  $res = $ilDB->manipulateF($query, array('integer','integer', 'integer'), array(
2374  $i,
2375  $node_id,
2376  $this->tree_id));
2377  return $i;
2378  }
2379 
2380 
2391  public function checkForParentType($a_ref_id, $a_type, $a_exclude_source_check = false)
2392  {
2393  // #12577
2394  $cache_key = $a_ref_id . '.' . $a_type . '.' . ((int) $a_exclude_source_check);
2395 
2396  // Try to return a cached result
2397  if ($this->isCacheUsed() &&
2398  array_key_exists($cache_key, $this->parent_type_cache)) {
2399  return $this->parent_type_cache[$cache_key];
2400  }
2401 
2402  // Store up to 1000 results in cache
2403  $do_cache = ($this->__isMainTree() && count($this->parent_type_cache) < 1000);
2404 
2405  // ref_id is not in tree
2406  if (!$this->isInTree($a_ref_id)) {
2407  if ($do_cache) {
2408  $this->parent_type_cache[$cache_key] = false;
2409  }
2410  return false;
2411  }
2412 
2413  $path = array_reverse($this->getPathFull($a_ref_id));
2414 
2415  // remove first path entry as it is requested node
2416  if ($a_exclude_source_check) {
2417  array_shift($path);
2418  }
2419 
2420  foreach ($path as $node) {
2421  // found matching parent
2422  if ($node["type"] == $a_type) {
2423  if ($do_cache) {
2424  $this->parent_type_cache[$cache_key] = $node["child"];
2425  }
2426  return $node["child"];
2427  }
2428  }
2429 
2430  if ($do_cache) {
2431  $this->parent_type_cache[$cache_key] = false;
2432  }
2433  return 0;
2434  }
2435 
2446  public static function _removeEntry($a_tree, $a_child, $a_db_table = "tree")
2447  {
2448  global $DIC;
2449 
2450  $ilDB = $DIC['ilDB'];
2451 
2452  if ($a_db_table === 'tree') {
2453  if ($a_tree == 1 and $a_child == ROOT_FOLDER_ID) {
2454  $message = sprintf(
2455  'Tried to delete root node! $a_tree: %s $a_child: %s',
2456  $a_tree,
2457  $a_child
2458  );
2459  ilLoggerFactory::getLogger('tree')->error($message);
2461  }
2462  }
2463 
2464  $query = 'DELETE FROM ' . $a_db_table . ' ' .
2465  'WHERE tree = %s ' .
2466  'AND child = %s ';
2467  $res = $ilDB->manipulateF($query, array('integer','integer'), array(
2468  $a_tree,
2469  $a_child));
2470  }
2471 
2478  public function __isMainTree()
2479  {
2480  return $this->table_tree === 'tree';
2481  }
2482 
2494  public function __checkDelete($a_node)
2495  {
2496  global $DIC;
2497 
2498  $ilDB = $DIC['ilDB'];
2499 
2500 
2501  $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, array(), false);
2502  $this->log->debug($query);
2503  $res = $ilDB->query($query);
2504 
2505  $counter = (int) $lft_childs = array();
2506  while ($row = $ilDB->fetchObject($res)) {
2507  $lft_childs[$row->child] = $row->parent;
2508  ++$counter;
2509  }
2510 
2511  // CHECK FOR DUPLICATE CHILD IDS
2512  if ($counter != count($lft_childs)) {
2513  $message = 'Duplicate entries for "child" in maintree! $a_node_id: ' . $a_node['child'];
2514 
2515  $this->log->error($message);
2517  }
2518 
2519  // GET SUBTREE BY PARENT RELATION
2520  $parent_childs = array();
2521  $this->__getSubTreeByParentRelation($a_node['child'], $parent_childs);
2522  $this->__validateSubtrees($lft_childs, $parent_childs);
2523 
2524  return true;
2525  }
2526 
2536  public function __getSubTreeByParentRelation($a_node_id, &$parent_childs)
2537  {
2538  global $DIC;
2539 
2540  $ilDB = $DIC['ilDB'];
2541 
2542  // GET PARENT ID
2543  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2544  'WHERE child = %s ' .
2545  'AND tree = %s ';
2546  $res = $ilDB->queryF($query, array('integer','integer'), array(
2547  $a_node_id,
2548  $this->tree_id));
2549 
2550  $counter = 0;
2551  while ($row = $ilDB->fetchObject($res)) {
2552  $parent_childs[$a_node_id] = $row->parent;
2553  ++$counter;
2554  }
2555  // MULTIPLE ENTRIES
2556  if ($counter > 1) {
2557  $message = 'Multiple entries in maintree! $a_node_id: ' . $a_node_id;
2558 
2559  $this->log->error($message);
2561  }
2562 
2563  // GET ALL CHILDS
2564  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2565  'WHERE parent = %s ';
2566  $res = $ilDB->queryF($query, array('integer'), array($a_node_id));
2567 
2568  while ($row = $ilDB->fetchObject($res)) {
2569  // RECURSION
2570  $this->__getSubTreeByParentRelation($row->child, $parent_childs);
2571  }
2572  return true;
2573  }
2574 
2582  public function __validateSubtrees(&$lft_childs, $parent_childs)
2583  {
2584  // SORT BY KEY
2585  ksort($lft_childs);
2586  ksort($parent_childs);
2587 
2588  $this->log->debug('left childs ' . print_r($lft_childs, true));
2589  $this->log->debug('parent childs ' . print_r($parent_childs, true));
2590 
2591  if (count($lft_childs) != count($parent_childs)) {
2592  $message = '(COUNT) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2593  $this->log->error($message);
2595  }
2596 
2597 
2598  foreach ($lft_childs as $key => $value) {
2599  if ($parent_childs[$key] != $value) {
2600  $message = '(COMPARE) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2601  $this->log->error($message);
2603  }
2604  if ($key == ROOT_FOLDER_ID) {
2605  $message = '(ROOT_FOLDER) Tree is corrupted! Tried to delete root folder';
2606  $this->log->error($message);
2608  }
2609  }
2610  return true;
2611  }
2612 
2622  public function moveTree($a_source_id, $a_target_id, $a_location = self::POS_LAST_NODE)
2623  {
2624  $old_parent_id = $this->getParentId($a_source_id);
2625  $this->getTreeImplementation()->moveTree($a_source_id, $a_target_id, $a_location);
2626  if (isset($GLOBALS['DIC']["ilAppEventHandler"]) && $this->__isMainTree()) {
2627  $GLOBALS['DIC']['ilAppEventHandler']->raise(
2628  "Services/Tree",
2629  "moveTree",
2630  array(
2631  'tree' => $this->table_tree,
2632  'source_id' => $a_source_id,
2633  'target_id' => $a_target_id,
2634  'old_parent_id' => $old_parent_id
2635  )
2636  );
2637  }
2638  return true;
2639  }
2640 
2641 
2642 
2643 
2651  public function getRbacSubtreeInfo($a_endnode_id)
2652  {
2653  return $this->getTreeImplementation()->getSubtreeInfo($a_endnode_id);
2654  }
2655 
2656 
2664  public function getSubTreeQuery($a_node_id, $a_fields = array(), $a_types = '', $a_force_join_reference = false)
2665  {
2666  return $this->getTreeImplementation()->getSubTreeQuery(
2667  $this->getNodeTreeData($a_node_id),
2668  $a_types,
2669  $a_force_join_reference,
2670  $a_fields
2671  );
2672  }
2673 
2674 
2683  public function getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields = array())
2684  {
2685  global $DIC;
2686 
2687  $ilDB = $DIC['ilDB'];
2688 
2689  $node = $this->getNodeData($a_node_id);
2690  if (!sizeof($node)) {
2691  return;
2692  }
2693 
2694  $res = array();
2695 
2696  $query = $this->getTreeImplementation()->getSubTreeQuery($node, '', true, array($this->ref_pk));
2697 
2698  $fields = '*';
2699  if (count($a_fields)) {
2700  $fields = implode(',', $a_fields);
2701  }
2702 
2703  $query = "SELECT " . $fields .
2704  " FROM " . $this->getTreeTable() .
2705  " " . $this->buildJoin() .
2706  " WHERE " . $this->getTableReference() . "." . $this->ref_pk . " IN (" . $query . ")" .
2707  " AND " . $ilDB->in($this->getObjectDataTable() . "." . $this->obj_pk, $a_obj_ids, "", "integer");
2708  $set = $ilDB->query($query);
2709  while ($row = $ilDB->fetchAssoc($set)) {
2710  $res[] = $row;
2711  }
2712 
2713  return $res;
2714  }
2715 
2716  public function deleteNode($a_tree_id, $a_node_id)
2717  {
2718  global $DIC;
2719 
2720  $ilDB = $DIC['ilDB'];
2721  $ilAppEventHandler = $DIC['ilAppEventHandler'];
2722 
2723  $query = 'DELETE FROM tree where ' .
2724  'child = ' . $ilDB->quote($a_node_id, 'integer') . ' ' .
2725  'AND tree = ' . $ilDB->quote($a_tree_id, 'integer');
2726  $ilDB->manipulate($query);
2727 
2728  $ilAppEventHandler->raise(
2729  "Services/Tree",
2730  "deleteNode",
2731  array('tree' => $this->table_tree,
2732  'node_id' => $a_node_id,
2733  'tree_id' => $a_tree_id
2734  )
2735  );
2736  }
2737 
2743  public function lookupTrashedObjectTypes()
2744  {
2745  global $DIC;
2746 
2747  $ilDB = $DIC['ilDB'];
2748 
2749  $query = 'SELECT DISTINCT(o.type) ' . $ilDB->quoteIdentifier('type') . ' FROM tree t JOIN object_reference r ON child = r.ref_id ' .
2750  'JOIN object_data o on r.obj_id = o.obj_id ' .
2751  'WHERE tree < ' . $ilDB->quote(0, 'integer') . ' ' .
2752  'AND child = -tree ' .
2753  'GROUP BY o.type';
2754  $res = $ilDB->query($query);
2755 
2756  $types_deleted = array();
2757  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
2758  $types_deleted[] = $row->type;
2759  }
2760  return $types_deleted;
2761  }
2762 } // END class.tree
removeTree($a_tree_id)
remove an existing tree
Thrown if invalid tree strucutes are found.
static setDeletedDates($a_ref_ids)
Set deleted date.
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
static shortenText( $a_str, $a_len, $a_dots=false, $a_next_blank=false, $a_keep_extension=false)
shorten a string to given length.
preloadDeleted($a_node_ids)
Preload deleted information.
$type
global $DIC
Definition: saml.php:7
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
getTreeId()
get tree id public
getParentCache()
Get parent cache.
if(!array_key_exists('StateId', $_REQUEST)) $id
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
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)
Definition: class.ilStr.php:87
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
__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.
__checkDelete($a_node)
Check for deleteTree() compares a subtree of a given node by checking lft, rgt against parent relatio...
static toNFC($string)
Convert a UTF-8 string to normal form C, canonical composition.
Definition: UtfNormal.php:157
$a_type
Definition: workflow.php:92
const POS_FIRST_NODE
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
catch(Exception $e) $message
foreach($_POST as $key=> $value) $res
getLeftValue($a_node_id)
get left value of given node public
static _lookupTitle($a_obj_id)
Overwitten from base class.
$lng
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.
$query
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
$row
$rows
Definition: xhr_table.php:10
__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
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...
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.
$i
Definition: disco.tpl.php:19
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: ???
$key
Definition: croninfo.php:18
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.
$GLOBALS['JPEG_Segment_Names']
Global Variable: XMP_tag_captions.
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
$data
Definition: bench.php:6
readRootId()
read root id from database