ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
All Data Structures Namespaces Files Functions Variables Typedefs Modules Pages
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 $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  $this->log->error("No tree_id given!");
162  $this->log->logStack(ilLogLevel::DEBUG);
163  throw new InvalidArgumentException("No tree_id given!");
164  }
165 
166  if (func_num_args() > 2) {
167  $this->log->error("Wrong parameter count!");
168  throw new InvalidArgumentException("Wrong parameter count!");
169  }
170 
171  //init variables
172  if (empty($a_root_id)) {
173  $a_root_id = ROOT_FOLDER_ID;
174  }
175 
176  $this->tree_id = $a_tree_id;
177  $this->root_id = $a_root_id;
178  $this->table_tree = 'tree';
179  $this->table_obj_data = 'object_data';
180  $this->table_obj_reference = 'object_reference';
181  $this->ref_pk = 'ref_id';
182  $this->obj_pk = 'obj_id';
183  $this->tree_pk = 'tree';
184 
185  $this->use_cache = true;
186 
187  // If cache is activated, cache object translations to improve performance
188  $this->translation_cache = array();
189  $this->parent_type_cache = array();
190 
191  // By default, we create gaps in the tree sequence numbering for 50 nodes
192  $this->gap = 50;
193 
194 
195  // init tree implementation
196  $this->initTreeImplementation();
197  }
198 
202  public function initTreeImplementation()
203  {
204  global $ilDB;
205 
206 
207  if (!is_object($GLOBALS['ilSetting']) or $GLOBALS['ilSetting']->getModule() != 'common') {
208  include_once './Services/Administration/classes/class.ilSetting.php';
209  $setting = new ilSetting('common');
210  } else {
211  $setting = $GLOBALS['ilSetting'];
212  }
213 
214  if ($this->__isMainTree()) {
215  if ($setting->get('main_tree_impl', 'ns') == 'ns') {
216  #$GLOBALS['ilLog']->write(__METHOD__.': Using nested set.');
217  include_once './Services/Tree/classes/class.ilNestedSetTree.php';
218  $this->tree_impl = new ilNestedSetTree($this);
219  } else {
220  #$GLOBALS['ilLog']->write(__METHOD__.': Using materialized path.');
221  include_once './Services/Tree/classes/class.ilMaterializedPathTree.php';
222  $this->tree_impl = new ilMaterializedPathTree($this);
223  }
224  } else {
225  #$GLOBALS['ilLog']->write(__METHOD__.': Using netsted set for non main tree.');
226  include_once './Services/Tree/classes/class.ilNestedSetTree.php';
227  $this->tree_impl = new ilNestedSetTree($this);
228  }
229  }
230 
235  public function getTreeImplementation()
236  {
237  return $this->tree_impl;
238  }
239 
243  public function useCache($a_use = true)
244  {
245  $this->use_cache = $a_use;
246  }
247 
252  public function isCacheUsed()
253  {
254  return $this->__isMainTree() and $this->use_cache;
255  }
256 
261  public function getDepthCache()
262  {
263  return (array) $this->depth_cache;
264  }
265 
270  public function getParentCache()
271  {
272  return (array) $this->parent_cache;
273  }
274 
279  public function initLangCode()
280  {
281  global $ilUser;
282 
283  // lang_code is only required in $this->fetchnodedata
284  if (!is_object($ilUser)) {
285  $this->lang_code = "en";
286  } else {
287  $this->lang_code = $ilUser->getCurrentLanguage();
288  }
289  }
290 
295  public function getTreeTable()
296  {
297  return $this->table_tree;
298  }
299 
304  public function getObjectDataTable()
305  {
306  return $this->table_obj_data;
307  }
308 
313  public function getTreePk()
314  {
315  return $this->tree_pk;
316  }
317 
321  public function getTableReference()
322  {
324  }
325 
329  public function getGap()
330  {
331  return $this->gap;
332  }
333 
334  /***
335  * reset in tree cache
336  */
337  public function resetInTreeCache()
338  {
339  $this->in_tree_cache = array();
340  }
341 
342 
359  public function setTableNames($a_table_tree, $a_table_obj_data, $a_table_obj_reference = "")
360  {
361  if (!isset($a_table_tree) or !isset($a_table_obj_data)) {
362  $message = "Missing parameter! " .
363  "tree table: " . $a_table_tree . " object data table: " . $a_table_obj_data;
364  $this->log->error($message);
366  }
367 
368  $this->table_tree = $a_table_tree;
369  $this->table_obj_data = $a_table_obj_data;
370  $this->table_obj_reference = $a_table_obj_reference;
371 
372  $this->initTreeImplementation();
373 
374  return true;
375  }
376 
384  public function setReferenceTablePK($a_column_name)
385  {
386  if (!isset($a_column_name)) {
387  $message = "No column name given!";
388  $this->log->error($message);
390  }
391 
392  $this->ref_pk = $a_column_name;
393  return true;
394  }
395 
403  public function setObjectTablePK($a_column_name)
404  {
405  if (!isset($a_column_name)) {
406  $message = "No column name given!";
407  $this->log->error($message);
409  }
410 
411  $this->obj_pk = $a_column_name;
412  return true;
413  }
414 
422  public function setTreeTablePK($a_column_name)
423  {
424  if (!isset($a_column_name)) {
425  $message = "No column name given!";
426  $this->log->error($message);
428  }
429 
430  $this->tree_pk = $a_column_name;
431  return true;
432  }
433 
439  public function buildJoin()
440  {
441  if ($this->table_obj_reference) {
442  // Use inner join instead of left join to improve performance
443  return "JOIN " . $this->table_obj_reference . " ON " . $this->table_tree . ".child=" . $this->table_obj_reference . "." . $this->ref_pk . " " .
444  "JOIN " . $this->table_obj_data . " ON " . $this->table_obj_reference . "." . $this->obj_pk . "=" . $this->table_obj_data . "." . $this->obj_pk . " ";
445  } else {
446  // Use inner join instead of left join to improve performance
447  return "JOIN " . $this->table_obj_data . " ON " . $this->table_tree . ".child=" . $this->table_obj_data . "." . $this->obj_pk . " ";
448  }
449  }
450 
456  public function getRelation($a_node_a, $a_node_b)
457  {
458  return $this->getRelationOfNodes(
459  $this->getNodeTreeData($a_node_a),
460  $this->getNodeTreeData($a_node_b)
461  );
462  }
463 
470  public function getRelationOfNodes($a_node_a_arr, $a_node_b_arr)
471  {
472  return $this->getTreeImplementation()->getRelation($a_node_a_arr, $a_node_b_arr);
473  }
474 
481  public function getChildIds($a_node)
482  {
483  global $ilDB;
484 
485  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
486  'WHERE parent = ' . $ilDB->quote($a_node, 'integer') . ' ' .
487  'AND tree = ' . $ilDB->quote($this->tree_id, 'integer' . ' ' .
488  'ORDER BY lft');
489  $res = $ilDB->query($query);
490 
491  $childs = array();
492  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
493  $childs[] = $row->child;
494  }
495  return $childs;
496  }
497 
507  public function getChilds($a_node_id, $a_order = "", $a_direction = "ASC")
508  {
509  global $ilBench,$ilDB, $ilObjDataCache, $ilUser;
510 
511  if (!isset($a_node_id)) {
512  $message = "No node_id given!";
513  $this->log->error($message);
515  }
516 
517  // init childs
518  $childs = array();
519 
520  // number of childs
521  $count = 0;
522 
523  // init order_clause
524  $order_clause = "";
525 
526  // set order_clause if sort order parameter is given
527  if (!empty($a_order)) {
528  $order_clause = "ORDER BY " . $a_order . " " . $a_direction;
529  } else {
530  $order_clause = "ORDER BY " . $this->table_tree . ".lft";
531  }
532 
533 
534  $query = sprintf(
535  'SELECT * FROM ' . $this->table_tree . ' ' .
536  $this->buildJoin() .
537  "WHERE parent = %s " .
538  "AND " . $this->table_tree . "." . $this->tree_pk . " = %s " .
539  $order_clause,
540  $ilDB->quote($a_node_id, 'integer'),
541  $ilDB->quote($this->tree_id, 'integer')
542  );
543 
544  $res = $ilDB->query($query);
545 
546  if (!$count = $res->numRows()) {
547  return array();
548  }
549 
550  // get rows and object ids
551  $rows = array();
552  while ($r = $ilDB->fetchAssoc($res)) {
553  $rows[] = $r;
554  $obj_ids[] = $r["obj_id"];
555  }
556 
557  // preload object translation information
558  if ($this->__isMainTree() && $this->isCacheUsed() && is_object($ilObjDataCache) &&
559  is_object($ilUser) && $this->lang_code == $ilUser->getLanguage() && !$this->oc_preloaded[$a_node_id]) {
560  // $ilObjDataCache->preloadTranslations($obj_ids, $this->lang_code);
561  $ilObjDataCache->preloadObjectCache($obj_ids, $this->lang_code);
562  $this->fetchTranslationFromObjectDataCache($obj_ids);
563  $this->oc_preloaded[$a_node_id] = true;
564  }
565 
566  foreach ($rows as $row) {
567  $childs[] = $this->fetchNodeData($row);
568 
569  // Update cache of main tree
570  if ($this->__isMainTree()) {
571  #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child'].' = true');
572  $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
573  }
574  }
575  $childs[$count - 1]["last"] = true;
576  return $childs;
577  }
578 
588  public function getFilteredChilds($a_filter, $a_node, $a_order = "", $a_direction = "ASC")
589  {
590  $childs = $this->getChilds($a_node, $a_order, $a_direction);
591 
592  foreach ($childs as $child) {
593  if (!in_array($child["type"], $a_filter)) {
594  $filtered[] = $child;
595  }
596  }
597  return $filtered ? $filtered : array();
598  }
599 
600 
609  public function getChildsByType($a_node_id, $a_type)
610  {
611  global $ilDB;
612 
613  if (!isset($a_node_id) or !isset($a_type)) {
614  $message = "Missing parameter! node_id:" . $a_node_id . " type:" . $a_type;
615  $this->log->error($message);
617  }
618 
619  if ($a_type=='rolf' && $this->table_obj_reference) {
620  // Performance optimization: A node can only have exactly one
621  // role folder as its child. Therefore we don't need to sort the
622  // results, and we can let the database know about the expected limit.
623  $ilDB->setLimit(1, 0);
624  $query = sprintf(
625  "SELECT * FROM " . $this->table_tree . " " .
626  $this->buildJoin() .
627  "WHERE parent = %s " .
628  "AND " . $this->table_tree . "." . $this->tree_pk . " = %s " .
629  "AND " . $this->table_obj_data . ".type = %s ",
630  $ilDB->quote($a_node_id, 'integer'),
631  $ilDB->quote($this->tree_id, 'integer'),
632  $ilDB->quote($a_type, 'text')
633  );
634  } else {
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  "ORDER BY " . $this->table_tree . ".lft",
642  $ilDB->quote($a_node_id, 'integer'),
643  $ilDB->quote($this->tree_id, 'integer'),
644  $ilDB->quote($a_type, 'text')
645  );
646  }
647  $res = $ilDB->query($query);
648 
649  // init childs
650  $childs = array();
651  while ($row = $ilDB->fetchAssoc($res)) {
652  $childs[] = $this->fetchNodeData($row);
653  }
654 
655  return $childs ? $childs : array();
656  }
657 
658 
667  public function getChildsByTypeFilter($a_node_id, $a_types, $a_order = "", $a_direction = "ASC")
668  {
669  global $ilDB;
670 
671  if (!isset($a_node_id) or !$a_types) {
672  $message = "Missing parameter! node_id:" . $a_node_id . " type:" . $a_types;
673  $this->log->error($message);
675  }
676 
677  $filter = ' ';
678  if ($a_types) {
679  $filter = 'AND ' . $this->table_obj_data . '.type IN(' . implode(',', ilUtil::quoteArray($a_types)) . ') ';
680  }
681 
682  // set order_clause if sort order parameter is given
683  if (!empty($a_order)) {
684  $order_clause = "ORDER BY " . $a_order . " " . $a_direction;
685  } else {
686  $order_clause = "ORDER BY " . $this->table_tree . ".lft";
687  }
688 
689  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
690  $this->buildJoin() .
691  'WHERE parent = ' . $ilDB->quote($a_node_id, 'integer') . ' ' .
692  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $ilDB->quote($this->tree_id, 'integer') . ' ' .
693  $filter .
694  $order_clause;
695 
696  $res = $ilDB->query($query);
697  while ($row = $ilDB->fetchAssoc($res)) {
698  $childs[] = $this->fetchNodeData($row);
699  }
700 
701  return $childs ? $childs : array();
702  }
703 
715  public function insertNodeFromTrash($a_source_id, $a_target_id, $a_tree_id, $a_pos = IL_LAST_NODE, $a_reset_deleted_date = false)
716  {
717  global $ilDB;
718 
719  if ($this->__isMainTree()) {
720  if ($a_source_id <= 1 or $a_target_id <= 0) {
721  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
722  throw new InvalidArgumentException('Invalid parameter given for ilTree::insertNodeFromTrash');
723  }
724  }
725  if (!isset($a_source_id) or !isset($a_target_id)) {
726  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
727  throw new InvalidArgumentException('Missing parameter for ilTree::insertNodeFromTrash');
728  }
729  if ($this->isInTree($a_source_id)) {
730  ilLoggerFactory::getLogger('tree')->error('Node already in tree');
731  ilLoggerFactory::getLogger('tree')->logStack(ilLogLevel::INFO);
732  throw new InvalidArgumentException('Node already in tree.');
733  }
734 
735  $query = 'DELETE from tree ' .
736  'WHERE tree = ' . $ilDB->quote($a_tree_id, 'integer') . ' ' .
737  'AND child = ' . $ilDB->quote($a_source_id, 'integer');
738  $ilDB->manipulate($query);
739 
740  $this->insertNode($a_source_id, $a_target_id, IL_LAST_NODE, $a_reset_deleted_date);
741  }
742 
743 
752  public function insertNode($a_node_id, $a_parent_id, $a_pos = IL_LAST_NODE, $a_reset_deletion_date = false)
753  {
754  global $ilDB;
755 
756  //echo "+$a_node_id+$a_parent_id+";
757  // CHECK node_id and parent_id > 0 if in main tree
758  if ($this->__isMainTree()) {
759  if ($a_node_id <= 1 or $a_parent_id <= 0) {
760  $message = sprintf(
761  'Invalid parameters! $a_node_id: %s $a_parent_id: %s',
762  $a_node_id,
763  $a_parent_id
764  );
765  $this->log->logStack(ilLogLevel::ERROR, $message);
767  }
768  }
769 
770 
771  if (!isset($a_node_id) or !isset($a_parent_id)) {
772  $this->log->logStack(ilLogLevel::ERROR);
773  throw new InvalidArgumentException("Missing parameter! " .
774  "node_id: " . $a_node_id . " parent_id: " . $a_parent_id);
775  }
776  if ($this->isInTree($a_node_id)) {
777  throw new InvalidArgumentException("Node " . $a_node_id . " already in tree " .
778  $this->table_tree . "!");
779  }
780 
781  $this->getTreeImplementation()->insertNode($a_node_id, $a_parent_id, $a_pos);
782 
783  $this->in_tree_cache[$a_node_id] = true;
784 
785  // reset deletion date
786  if ($a_reset_deletion_date) {
787  ilObject::_resetDeletedDate($a_node_id);
788  }
789 
790  if (isset($GLOBALS["ilAppEventHandler"]) && $this->__isMainTree()) {
791  $GLOBALS['ilAppEventHandler']->raise(
792  "Services/Tree",
793  "insertNode",
794  array(
795  'tree' => $this->table_tree,
796  'node_id' => $a_node_id,
797  'parent_id' => $a_parent_id)
798  );
799  }
800  }
801 
814  public function getFilteredSubTree($a_node_id, $a_filter = array())
815  {
816  $node = $this->getNodeData($a_node_id);
817 
818  $first = true;
819  $depth = 0;
820  foreach ($this->getSubTree($node) as $subnode) {
821  if ($depth and $subnode['depth'] > $depth) {
822  continue;
823  }
824  if (!$first and in_array($subnode['type'], $a_filter)) {
825  $depth = $subnode['depth'];
826  $first = false;
827  continue;
828  }
829  $depth = 0;
830  $first = false;
831  $filtered[] = $subnode;
832  }
833  return $filtered ? $filtered : array();
834  }
835 
841  public function getSubTreeIds($a_ref_id)
842  {
843  return $this->getTreeImplementation()->getSubTreeIds($a_ref_id);
844  }
845 
846 
856  public function getSubTree($a_node, $a_with_data = true, $a_type = "")
857  {
858  global $ilDB;
859 
860  if (!is_array($a_node)) {
861  $this->log->logStack(ilLogLevel::ERROR);
862  throw new InvalidArgumentException(__METHOD__ . ': wrong datatype for node data given');
863  }
864 
865  /*
866  if($a_node['lft'] < 1 or $a_node['rgt'] < 2)
867  {
868  $GLOBALS['ilLog']->logStack();
869  $message = sprintf('%s: Invalid node given! $a_node["lft"]: %s $a_node["rgt"]: %s',
870  __METHOD__,
871  $a_node['lft'],
872  $a_node['rgt']);
873 
874  throw new InvalidArgumentException($message);
875  }
876  */
877 
878  $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, $a_type);
879  $res = $ilDB->query($query);
880  while ($row = $ilDB->fetchAssoc($res)) {
881  if ($a_with_data) {
882  $subtree[] = $this->fetchNodeData($row);
883  } else {
884  $subtree[] = $row['child'];
885  }
886  // the lm_data "hack" should be removed in the trunk during an alpha
887  if ($this->__isMainTree() || $this->table_tree == "lm_tree") {
888  $this->in_tree_cache[$row['child']] = true;
889  }
890  }
891  return $subtree ? $subtree : array();
892  }
893 
902  public function getSubTreeTypes($a_node, $a_filter = 0)
903  {
904  $a_filter = $a_filter ? $a_filter : array();
905 
906  foreach ($this->getSubtree($this->getNodeData($a_node)) as $node) {
907  if (in_array($node["type"], $a_filter)) {
908  continue;
909  }
910  $types["$node[type]"] = $node["type"];
911  }
912  return $types ? $types : array();
913  }
914 
922  public function deleteTree($a_node)
923  {
924  global $ilDB;
925 
926  $this->log->debug('Delete tree with node ' . $a_node);
927 
928  if (!is_array($a_node)) {
929  $this->log->logStack(ilLogLevel::ERROR);
930  throw new InvalidArgumentException(__METHOD__ . ': Wrong datatype for node data!');
931  }
932 
933  $this->log->debug($this->tree_pk);
934 
935  if ($this->__isMainTree()) {
936  // @todo normally this part is not executed, since the subtree is first
937  // moved to trash and then deleted.
938  if (!$this->__checkDelete($a_node)) {
939  $this->log->logStack(ilLogLevel::ERROR);
940  throw new ilInvalidTreeStructureException('Deletion canceled due to invalid tree structure.' . print_r($a_node, true));
941  }
942  }
943 
944  $this->getTreeImplementation()->deleteTree($a_node['child']);
945 
946  $this->resetInTreeCache();
947  }
948 
953  public function validateParentRelations()
954  {
955  return $this->getTreeImplementation()->validateParentRelations();
956  }
957 
968  public function getPathFull($a_endnode_id, $a_startnode_id = 0)
969  {
970  $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
971 
972  // We retrieve the full path in a single query to improve performance
973  global $ilDB;
974 
975  // Abort if no path ids were found
976  if (count($pathIds) == 0) {
977  return null;
978  }
979 
980  $inClause = 'child IN (';
981  for ($i=0; $i < count($pathIds); $i++) {
982  if ($i > 0) {
983  $inClause .= ',';
984  }
985  $inClause .= $ilDB->quote($pathIds[$i], 'integer');
986  }
987  $inClause .= ')';
988 
989  $q = 'SELECT * ' .
990  'FROM ' . $this->table_tree . ' ' .
991  $this->buildJoin() . ' ' .
992  'WHERE ' . $inClause . ' ' .
993  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer') . ' ' .
994  'ORDER BY depth';
995  $r = $ilDB->query($q);
996 
997  $pathFull = array();
998  while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
999  $pathFull[] = $this->fetchNodeData($row);
1000 
1001  // Update cache
1002  if ($this->__isMainTree()) {
1003  #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$row['child']);
1004  $this->in_tree_cache[$row['child']] = $row['tree'] == 1;
1005  }
1006  }
1007  return $pathFull;
1008  }
1009 
1010 
1017  public function preloadDepthParent($a_node_ids)
1018  {
1019  global $ilDB;
1020 
1021  if (!$this->__isMainTree() || !is_array($a_node_ids) || !$this->isCacheUsed()) {
1022  return;
1023  }
1024 
1025  $res = $ilDB->query('SELECT t.depth, t.parent, t.child ' .
1026  'FROM ' . $this->table_tree . ' t ' .
1027  'WHERE ' . $ilDB->in("child", $a_node_ids, false, "integer") .
1028  'AND ' . $this->tree_pk . ' = ' . $ilDB->quote($this->tree_id, "integer"));
1029  while ($row = $ilDB->fetchAssoc($res)) {
1030  $this->depth_cache[$row["child"]] = $row["depth"];
1031  $this->parent_cache[$row["child"]] = $row["parent"];
1032  }
1033  }
1034 
1044  public function getPathId($a_endnode_id, $a_startnode_id = 0)
1045  {
1046  if (!$a_endnode_id) {
1047  $this->log->logStack(ilLogLevel::ERROR);
1048  throw new InvalidArgumentException(__METHOD__ . ': No endnode given!');
1049  }
1050 
1051  // path id cache
1052  if ($this->isCacheUsed() && isset($this->path_id_cache[$a_endnode_id][$a_startnode_id])) {
1053  //echo "<br>getPathIdhit";
1054  return $this->path_id_cache[$a_endnode_id][$a_startnode_id];
1055  }
1056  //echo "<br>miss";
1057 
1058  $pathIds = $this->getTreeImplementation()->getPathIds($a_endnode_id, $a_startnode_id);
1059 
1060  if ($this->__isMainTree()) {
1061  $this->path_id_cache[$a_endnode_id][$a_startnode_id] = $pathIds;
1062  }
1063  return $pathIds;
1064  }
1065 
1066  // BEGIN WebDAV: getNodePathForTitlePath function added
1084  public function getNodePathForTitlePath($titlePath, $a_startnode_id = null)
1085  {
1086  global $ilDB, $log;
1087  //$log->write('getNodePathForTitlePath('.implode('/',$titlePath));
1088 
1089  // handle empty title path
1090  if ($titlePath == null || count($titlePath) == 0) {
1091  if ($a_startnode_id == 0) {
1092  return null;
1093  } else {
1094  return $this->getNodePath($a_startnode_id);
1095  }
1096  }
1097 
1098  // fetch the node path up to the startnode
1099  if ($a_startnode_id != null && $a_startnode_id != 0) {
1100  // Start using the node path to the root of the relative path
1101  $nodePath = $this->getNodePath($a_startnode_id);
1102  $parent = $a_startnode_id;
1103  } else {
1104  // Start using the root of the tree
1105  $nodePath = array();
1106  $parent = 0;
1107  }
1108 
1109 
1110  // Convert title path into Unicode Normal Form C
1111  // This is needed to ensure that we can compare title path strings with
1112  // strings from the database.
1113  require_once('include/Unicode/UtfNormal.php');
1114  include_once './Services/Utilities/classes/class.ilStr.php';
1115  $inClause = 'd.title IN (';
1116  for ($i=0; $i < count($titlePath); $i++) {
1117  $titlePath[$i] = ilStr::strToLower(UtfNormal::toNFC($titlePath[$i]));
1118  if ($i > 0) {
1119  $inClause .= ',';
1120  }
1121  $inClause .= $ilDB->quote($titlePath[$i], 'text');
1122  }
1123  $inClause .= ')';
1124 
1125  // Fetch all rows that are potential path elements
1126  if ($this->table_obj_reference) {
1127  $joinClause = 'JOIN ' . $this->table_obj_reference . ' r ON t.child = r.' . $this->ref_pk . ' ' .
1128  'JOIN ' . $this->table_obj_data . ' d ON r.' . $this->obj_pk . ' = d.' . $this->obj_pk;
1129  } else {
1130  $joinClause = 'JOIN ' . $this->table_obj_data . ' d ON t.child = d.' . $this->obj_pk;
1131  }
1132  // The ORDER BY clause in the following SQL statement ensures that,
1133  // in case of a multiple objects with the same title, always the Object
1134  // with the oldest ref_id is chosen.
1135  // This ensure, that, if a new object with the same title is added,
1136  // WebDAV clients can still work with the older object.
1137  $q = 'SELECT t.depth, t.parent, t.child, d.' . $this->obj_pk . ' obj_id, d.type, d.title ' .
1138  'FROM ' . $this->table_tree . ' t ' .
1139  $joinClause . ' ' .
1140  'WHERE ' . $inClause . ' ' .
1141  'AND t.depth <= ' . (count($titlePath)+count($nodePath)) . ' ' .
1142  'AND t.tree = 1 ' .
1143  'ORDER BY t.depth, t.child ASC';
1144  $r = $ilDB->query($q);
1145 
1146  $rows = array();
1147  while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1148  $row['title'] = UtfNormal::toNFC($row['title']);
1149  $row['ref_id'] = $row['child'];
1150  $rows[] = $row;
1151  }
1152 
1153  // Extract the path elements from the fetched rows
1154  for ($i = 0; $i < count($titlePath); $i++) {
1155  $pathElementFound = false;
1156  foreach ($rows as $row) {
1157  if ($row['parent'] == $parent &&
1158  ilStr::strToLower($row['title']) == $titlePath[$i]) {
1159  // FIXME - We should test here, if the user has
1160  // 'visible' permission for the object.
1161  $nodePath[] = $row;
1162  $parent = $row['child'];
1163  $pathElementFound = true;
1164  break;
1165  }
1166  }
1167  // Abort if we haven't found a path element for the current depth
1168  if (!$pathElementFound) {
1169  //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):null');
1170  return null;
1171  }
1172  }
1173  // Return the node path
1174  //$log->write('ilTree.getNodePathForTitlePath('.var_export($titlePath,true).','.$a_startnode_id.'):'.var_export($nodePath,true));
1175  return $nodePath;
1176  }
1177  // END WebDAV: getNodePathForTitlePath function added
1178  // END WebDAV: getNodePath function added
1195  public function getNodePath($a_endnode_id, $a_startnode_id = 0)
1196  {
1197  global $ilDB;
1198 
1199  $pathIds = $this->getPathId($a_endnode_id, $a_startnode_id);
1200 
1201  // Abort if no path ids were found
1202  if (count($pathIds) == 0) {
1203  return null;
1204  }
1205 
1206 
1207  $types = array();
1208  $data = array();
1209  for ($i = 0; $i < count($pathIds); $i++) {
1210  $types[] = 'integer';
1211  $data[] = $pathIds[$i];
1212  }
1213 
1214  $query = 'SELECT t.depth,t.parent,t.child,d.obj_id,d.type,d.title ' .
1215  'FROM ' . $this->table_tree . ' t ' .
1216  'JOIN ' . $this->table_obj_reference . ' r ON r.ref_id = t.child ' .
1217  'JOIN ' . $this->table_obj_data . ' d ON d.obj_id = r.obj_id ' .
1218  'WHERE ' . $ilDB->in('t.child', $data, false, 'integer') . ' ' .
1219  'ORDER BY t.depth ';
1220 
1221  $res = $ilDB->queryF($query, $types, $data);
1222 
1223  $titlePath = array();
1224  while ($row = $ilDB->fetchAssoc($res)) {
1225  $titlePath[] = $row;
1226  }
1227  return $titlePath;
1228  }
1229  // END WebDAV: getNodePath function added
1230 
1238  public function checkTree()
1239  {
1240  global $ilDB;
1241 
1242  $types = array('integer');
1243  $query = 'SELECT lft,rgt FROM ' . $this->table_tree . ' ' .
1244  'WHERE ' . $this->tree_pk . ' = %s ';
1245 
1246  $res = $ilDB->queryF($query, $types, array($this->tree_id));
1247  while ($row = $ilDB->fetchObject($res)) {
1248  $lft[] = $row->lft;
1249  $rgt[] = $row->rgt;
1250  }
1251 
1252  $all = array_merge($lft, $rgt);
1253  $uni = array_unique($all);
1254 
1255  if (count($all) != count($uni)) {
1256  $message = 'Tree is corrupted!';
1257 
1258  $this->log->error($message);
1260  }
1261 
1262  return true;
1263  }
1264 
1272  public function checkTreeChilds($a_no_zero_child = true)
1273  {
1274  global $ilDB;
1275 
1276  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1277  'WHERE ' . $this->tree_pk . ' = %s ' .
1278  'ORDER BY lft';
1279  $r1 = $ilDB->queryF($query, array('integer'), array($this->tree_id));
1280 
1281  while ($row = $ilDB->fetchAssoc($r1)) {
1282  //echo "tree:".$row[$this->tree_pk].":lft:".$row["lft"].":rgt:".$row["rgt"].":child:".$row["child"].":<br>";
1283  if (($row["child"] == 0) && $a_no_zero_child) {
1284  $message = "Tree contains child with ID 0!";
1285  $this->log->error($message);
1287  }
1288 
1289  if ($this->table_obj_reference) {
1290  // get object reference data
1291  $query = 'SELECT * FROM ' . $this->table_obj_reference . ' WHERE ' . $this->ref_pk . ' = %s ';
1292  $r2 = $ilDB->queryF($query, array('integer'), array($row['child']));
1293 
1294  //echo "num_childs:".$r2->numRows().":<br>";
1295  if ($r2->numRows() == 0) {
1296  $message = "No Object-to-Reference entry found for ID " . $row["child"] . "!";
1297  $this->log->error($message);
1299  }
1300  if ($r2->numRows() > 1) {
1301  $message = "More Object-to-Reference entries found for ID " . $row["child"] . "!";
1302  $this->log->error($message);
1304  }
1305 
1306  // get object data
1307  $obj_ref = $ilDB->fetchAssoc($r2);
1308 
1309  $query = 'SELECT * FROM ' . $this->table_obj_data . ' WHERE ' . $this->obj_pk . ' = %s';
1310  $r3 = $ilDB->queryF($query, array('integer'), array($obj_ref[$this->obj_pk]));
1311  if ($r3->numRows() == 0) {
1312  $message = " No child found for ID " . $obj_ref[$this->obj_pk] . "!";
1313  $this->log->error($message);
1315  }
1316  if ($r3->numRows() > 1) {
1317  $message = "More childs found for ID " . $obj_ref[$this->obj_pk] . "!";
1318  $this->log->error($message);
1320  }
1321  } else {
1322  // get only object data
1323  $query = 'SELECT * FROM ' . $this->table_obj_data . ' WHERE ' . $this->obj_pk . ' = %s';
1324  $r2 = $ilDB->queryF($query, array('integer'), array($row['child']));
1325  //echo "num_childs:".$r2->numRows().":<br>";
1326  if ($r2->numRows() == 0) {
1327  $message = "No child found for ID " . $row["child"] . "!";
1328  $this->log->error($message);
1330  }
1331  if ($r2->numRows() > 1) {
1332  $message = "More childs found for ID " . $row["child"] . "!";
1333  $this->log->error($message);
1335  }
1336  }
1337  }
1338 
1339  return true;
1340  }
1341 
1347  public function getMaximumDepth()
1348  {
1349  global $ilDB;
1350 
1351  $query = 'SELECT MAX(depth) depth FROM ' . $this->table_tree;
1352  $res = $ilDB->query($query);
1353 
1354  $row = $ilDB->fetchAssoc($res);
1355  return $row['depth'];
1356  }
1357 
1364  public function getDepth($a_node_id)
1365  {
1366  global $ilDB;
1367 
1368  if ($a_node_id) {
1369  $query = 'SELECT depth FROM ' . $this->table_tree . ' ' .
1370  'WHERE child = %s ' .
1371  'AND ' . $this->tree_pk . ' = %s ';
1372  $res = $ilDB->queryF($query, array('integer','integer'), array($a_node_id,$this->tree_id));
1373  $row = $ilDB->fetchObject($res);
1374 
1375  return $row->depth;
1376  } else {
1377  return 1;
1378  }
1379  }
1380 
1388  public function getNodeTreeData($a_node_id)
1389  {
1390  global $ilDB;
1391 
1392  if (!$a_node_id) {
1393  $this->log->logStack(ilLogLevel::ERROR);
1394  throw new InvalidArgumentException('Missing or empty parameter $a_node_id: ' . $a_node_id);
1395  }
1396 
1397  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1398  'WHERE child = ' . $ilDB->quote($a_node_id, 'integer');
1399  $res = $ilDB->query($query);
1400  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1401  return $row;
1402  }
1403  return array();
1404  }
1405 
1406 
1415  // BEGIN WebDAV: Pass tree id to this method
1416  //function getNodeData($a_node_id)
1417  public function getNodeData($a_node_id, $a_tree_pk = null)
1418  // END PATCH WebDAV: Pass tree id to this method
1419  {
1420  global $ilDB;
1421 
1422  if (!isset($a_node_id)) {
1423  $this->log->logStack(ilLogLevel::ERROR);
1424  throw new InvalidArgumentException("No node_id given!");
1425  }
1426  if ($this->__isMainTree()) {
1427  if ($a_node_id < 1) {
1428  $message = 'No valid parameter given! $a_node_id: %s' . $a_node_id;
1429 
1430  $this->log->error($message);
1432  }
1433  }
1434 
1435  // BEGIN WebDAV: Pass tree id to this method
1436  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1437  $this->buildJoin() .
1438  'WHERE ' . $this->table_tree . '.child = %s ' .
1439  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
1440  $res = $ilDB->queryF($query, array('integer','integer'), array(
1441  $a_node_id,
1442  $a_tree_pk === null ? $this->tree_id : $a_tree_pk));
1443  // END WebDAV: Pass tree id to this method
1444  $row = $ilDB->fetchAssoc($res);
1446 
1447  return $this->fetchNodeData($row);
1448  }
1449 
1457  public function fetchNodeData($a_row)
1458  {
1459  global $objDefinition, $lng, $ilBench,$ilDB;
1460 
1461  //$ilBench->start("Tree", "fetchNodeData_getRow");
1462  $data = $a_row;
1463  $data["desc"] = $a_row["description"]; // for compability
1464  //$ilBench->stop("Tree", "fetchNodeData_getRow");
1465 
1466  // multilingual support systemobjects (sys) & categories (db)
1467  //$ilBench->start("Tree", "fetchNodeData_readDefinition");
1468  if (is_object($objDefinition)) {
1469  $translation_type = $objDefinition->getTranslationType($data["type"]);
1470  }
1471  //$ilBench->stop("Tree", "fetchNodeData_readDefinition");
1472 
1473  if ($translation_type == "sys") {
1474  //$ilBench->start("Tree", "fetchNodeData_getLangData");
1475  if ($data["type"] == "rolf" and $data["obj_id"] != ROLE_FOLDER_ID) {
1476  $data["description"] = $lng->txt("obj_" . $data["type"] . "_local_desc") . $data["title"] . $data["desc"];
1477  $data["desc"] = $lng->txt("obj_" . $data["type"] . "_local_desc") . $data["title"] . $data["desc"];
1478  $data["title"] = $lng->txt("obj_" . $data["type"] . "_local");
1479  } else {
1480  $data["title"] = $lng->txt("obj_" . $data["type"]);
1481  $data["description"] = $lng->txt("obj_" . $data["type"] . "_desc");
1482  $data["desc"] = $lng->txt("obj_" . $data["type"] . "_desc");
1483  }
1484  //$ilBench->stop("Tree", "fetchNodeData_getLangData");
1485  } elseif ($translation_type == "db") {
1486 
1487  // Try to retrieve object translation from cache
1488  if ($this->isCacheUsed() &&
1489  array_key_exists($data["obj_id"] . '.' . $lang_code, $this->translation_cache)) {
1490  $key = $data["obj_id"] . '.' . $lang_code;
1491  $data["title"] = $this->translation_cache[$key]['title'];
1492  $data["description"] = $this->translation_cache[$key]['description'];
1493  $data["desc"] = $this->translation_cache[$key]['desc'];
1494  } else {
1495  // Object translation is not in cache, read it from database
1496  //$ilBench->start("Tree", "fetchNodeData_getTranslation");
1497  $query = 'SELECT title,description FROM object_translation ' .
1498  'WHERE obj_id = %s ' .
1499  'AND lang_code = %s ' .
1500  'AND NOT lang_default = %s';
1501 
1502  $res = $ilDB->queryF($query, array('integer','text','integer'), array(
1503  $data['obj_id'],
1504  $this->lang_code,
1505  1));
1506  $row = $ilDB->fetchObject($res);
1507 
1508  if ($row) {
1509  $data["title"] = $row->title;
1510  $data["description"] = ilUtil::shortenText($row->description, ilObject::DESC_LENGTH, true);
1511  $data["desc"] = $row->description;
1512  }
1513  //$ilBench->stop("Tree", "fetchNodeData_getTranslation");
1514 
1515  // Store up to 1000 object translations in cache
1516  if ($this->isCacheUsed() && count($this->translation_cache) < 1000) {
1517  $key = $data["obj_id"] . '.' . $lang_code;
1518  $this->translation_cache[$key] = array();
1519  $this->translation_cache[$key]['title'] = $data["title"] ;
1520  $this->translation_cache[$key]['description'] = $data["description"];
1521  $this->translation_cache[$key]['desc'] = $data["desc"];
1522  }
1523  }
1524  }
1525 
1526  // TODO: Handle this switch by module.xml definitions
1527  if ($data['type'] == 'crsr' or $data['type'] == 'catr' or $data['type'] == 'grpr') {
1528  include_once('./Services/ContainerReference/classes/class.ilContainerReference.php');
1529  $data['title'] = ilContainerReference::_lookupTitle($data['obj_id']);
1530  }
1531 
1532  return $data ? $data : array();
1533  }
1534 
1540  protected function fetchTranslationFromObjectDataCache($a_obj_ids)
1541  {
1542  global $ilObjDataCache;
1543 
1544  if ($this->isCacheUsed() && is_array($a_obj_ids) && is_object($ilObjDataCache)) {
1545  foreach ($a_obj_ids as $id) {
1546  $this->translation_cache[$id . '.']['title'] = $ilObjDataCache->lookupTitle($id);
1547  $this->translation_cache[$id . '.']['description'] = $ilObjDataCache->lookupDescription($id);
1548  ;
1549  $this->translation_cache[$id . '.']['desc'] =
1550  $this->translation_cache[$id . '.']['description'];
1551  }
1552  }
1553  }
1554 
1555 
1563  public function isInTree($a_node_id)
1564  {
1565  global $ilDB;
1566 
1567  if (!isset($a_node_id)) {
1568  return false;
1569  #$this->ilErr->raiseError(get_class($this)."::getNodeData(): No node_id given! ",$this->ilErr->WARNING);
1570  }
1571  // is in tree cache
1572  if ($this->isCacheUsed() && isset($this->in_tree_cache[$a_node_id])) {
1573  #$GLOBALS['ilLog']->write(__METHOD__.': Using in tree cache '.$a_node_id);
1574  //echo "<br>in_tree_hit";
1575  return $this->in_tree_cache[$a_node_id];
1576  }
1577 
1578  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1579  'WHERE ' . $this->table_tree . '.child = %s ' .
1580  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s';
1581 
1582  $res = $ilDB->queryF($query, array('integer','integer'), array(
1583  $a_node_id,
1584  $this->tree_id));
1585 
1586  if ($res->numRows() > 0) {
1587  if ($this->__isMainTree()) {
1588  #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = true');
1589  $this->in_tree_cache[$a_node_id] = true;
1590  }
1591  return true;
1592  } else {
1593  if ($this->__isMainTree()) {
1594  #$GLOBALS['ilLog']->write(__METHOD__.': Storing in tree cache '.$a_node_id.' = false');
1595  $this->in_tree_cache[$a_node_id] = false;
1596  }
1597  return false;
1598  }
1599  }
1600 
1608  public function getParentNodeData($a_node_id)
1609  {
1610  global $ilDB;
1611  global $ilLog;
1612 
1613  if (!isset($a_node_id)) {
1614  $ilLog->logStack();
1615  throw new InvalidArgumentException(__METHOD__ . ': No node_id given!');
1616  }
1617 
1618  if ($this->table_obj_reference) {
1619  // Use inner join instead of left join to improve performance
1620  $innerjoin = "JOIN " . $this->table_obj_reference . " ON v.child=" . $this->table_obj_reference . "." . $this->ref_pk . " " .
1621  "JOIN " . $this->table_obj_data . " ON " . $this->table_obj_reference . "." . $this->obj_pk . "=" . $this->table_obj_data . "." . $this->obj_pk . " ";
1622  } else {
1623  // Use inner join instead of left join to improve performance
1624  $innerjoin = "JOIN " . $this->table_obj_data . " ON v.child=" . $this->table_obj_data . "." . $this->obj_pk . " ";
1625  }
1626 
1627  $query = 'SELECT * FROM ' . $this->table_tree . ' s, ' . $this->table_tree . ' v ' .
1628  $innerjoin .
1629  'WHERE s.child = %s ' .
1630  'AND s.parent = v.child ' .
1631  'AND s.' . $this->tree_pk . ' = %s ' .
1632  'AND v.' . $this->tree_pk . ' = %s';
1633  $res = $ilDB->queryF($query, array('integer','integer','integer'), array(
1634  $a_node_id,
1635  $this->tree_id,
1636  $this->tree_id));
1637  $row = $ilDB->fetchAssoc($res);
1638  return $this->fetchNodeData($row);
1639  }
1640 
1648  public function isGrandChild($a_startnode_id, $a_querynode_id)
1649  {
1650  return $this->getRelation($a_startnode_id, $a_querynode_id) == self::RELATION_PARENT;
1651  }
1652 
1662  public function addTree($a_tree_id, $a_node_id = -1)
1663  {
1664  global $ilDB;
1665 
1666  // FOR SECURITY addTree() IS NOT ALLOWED ON MAIN TREE
1667  if ($this->__isMainTree()) {
1668  $message = sprintf(
1669  'Operation not allowed on main tree! $a_tree_if: %s $a_node_id: %s',
1670  $a_tree_id,
1671  $a_node_id
1672  );
1673  $this->log->error($message);
1675  }
1676 
1677  if (!isset($a_tree_id)) {
1678  $message = "No tree_id given!";
1679  $this->log->error($message);
1681  }
1682 
1683  if ($a_node_id <= 0) {
1684  $a_node_id = $a_tree_id;
1685  }
1686 
1687  $query = 'INSERT INTO ' . $this->table_tree . ' (' .
1688  $this->tree_pk . ', child,parent,lft,rgt,depth) ' .
1689  'VALUES ' .
1690  '(%s,%s,%s,%s,%s,%s)';
1691  $res = $ilDB->manipulateF($query, array('integer','integer','integer','integer','integer','integer'), array(
1692  $a_tree_id,
1693  $a_node_id,
1694  0,
1695  1,
1696  2,
1697  1));
1698 
1699  return true;
1700  }
1701 
1711  public function getNodeDataByType($a_type)
1712  {
1713  global $ilDB;
1714 
1715  if (!isset($a_type) or (!is_string($a_type))) {
1716  $this->log->logStack(ilLogLevel::ERROR);
1717  throw new InvalidArgumentException('Type not given or wrong datatype');
1718  }
1719 
1720  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1721  $this->buildJoin() .
1722  'WHERE ' . $this->table_obj_data . '.type = ' . $this->ilDB->quote($a_type, 'text') .
1723  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = ' . $this->ilDB->quote($this->tree_id, 'integer');
1724 
1725  $res = $ilDB->query($query);
1726  $data = array();
1727  while ($row = $ilDB->fetchAssoc($res)) {
1728  $data[] = $this->fetchNodeData($row);
1729  }
1730 
1731  return $data;
1732  }
1733 
1742  public function removeTree($a_tree_id)
1743  {
1744  global $ilDB;
1745 
1746  // OPERATION NOT ALLOWED ON MAIN TREE
1747  if ($this->__isMainTree()) {
1748  $this->log->logStack(ilLogLevel::ERROR);
1749  throw new InvalidArgumentException('Operation not allowed on main tree');
1750  }
1751  if (!$a_tree_id) {
1752  $this->log->logStack(ilLogLevel::ERROR);
1753  throw new InvalidArgumentException('Missing parameter tree id');
1754  }
1755 
1756  $query = 'DELETE FROM ' . $this->table_tree .
1757  ' WHERE ' . $this->tree_pk . ' = %s ';
1758  $ilDB->manipulateF($query, array('integer'), array($a_tree_id));
1759  return true;
1760  }
1761 
1769  public function moveToTrash($a_node_id, $a_set_deleted = false)
1770  {
1771  global $ilDB;
1772 
1773  if (!$a_node_id) {
1774  $this->log->logStack(ilLogLevel::ERROR);
1775  throw new InvalidArgumentException('No valid parameter given! $a_node_id: ' . $a_node_id);
1776  }
1777 
1778 
1779  $query = $this->getTreeImplementation()->getSubTreeQuery($this->getNodeTreeData($a_node_id), '', false);
1780  $res = $ilDB->query($query);
1781 
1782  $subnodes = array();
1783  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
1784  $subnodes[] = $row['child'];
1785  }
1786 
1787  if (!count($subnodes)) {
1788  // Possibly already deleted
1789  return false;
1790  }
1791 
1792  if ($a_set_deleted) {
1793  include_once './Services/Object/classes/class.ilObject.php';
1794  ilObject::setDeletedDates($subnodes);
1795  }
1796 
1797  // netsted set <=> mp
1798  $this->getTreeImplementation()->moveToTrash($a_node_id);
1799 
1800  return true;
1801  }
1802 
1813  public function saveSubTree($a_node_id, $a_set_deleted = false)
1814  {
1815  return $this->moveToTrash($a_node_id, $a_set_deleted);
1816  }
1817 
1822  public function isDeleted($a_node_id)
1823  {
1824  return $this->isSaved($a_node_id);
1825  }
1826 
1832  public function isSaved($a_node_id)
1833  {
1834  global $ilDB;
1835 
1836  // is saved cache
1837  if ($this->isCacheUsed() && isset($this->is_saved_cache[$a_node_id])) {
1838  //echo "<br>issavedhit";
1839  return $this->is_saved_cache[$a_node_id];
1840  }
1841 
1842  $query = 'SELECT ' . $this->tree_pk . ' FROM ' . $this->table_tree . ' ' .
1843  'WHERE child = %s ';
1844  $res = $ilDB->queryF($query, array('integer'), array($a_node_id));
1845  $row = $ilDB->fetchAssoc($res);
1846 
1847  if ($row[$this->tree_pk] < 0) {
1848  if ($this->__isMainTree()) {
1849  $this->is_saved_cache[$a_node_id] = true;
1850  }
1851  return true;
1852  } else {
1853  if ($this->__isMainTree()) {
1854  $this->is_saved_cache[$a_node_id] = false;
1855  }
1856  return false;
1857  }
1858  }
1859 
1866  public function preloadDeleted($a_node_ids)
1867  {
1868  global $ilDB;
1869 
1870  if (!is_array($a_node_ids) || !$this->isCacheUsed()) {
1871  return;
1872  }
1873 
1874  $query = 'SELECT ' . $this->tree_pk . ', child FROM ' . $this->table_tree . ' ' .
1875  'WHERE ' . $ilDB->in("child", $a_node_ids, false, "integer");
1876 
1877  $res = $ilDB->query($query);
1878  while ($row = $ilDB->fetchAssoc($res)) {
1879  if ($row[$this->tree_pk] < 0) {
1880  if ($this->__isMainTree()) {
1881  $this->is_saved_cache[$row["child"]] = true;
1882  }
1883  } else {
1884  if ($this->__isMainTree()) {
1885  $this->is_saved_cache[$row["child"]] = false;
1886  }
1887  }
1888  }
1889  }
1890 
1891 
1899  public function getSavedNodeData($a_parent_id)
1900  {
1901  global $ilDB;
1902 
1903  if (!isset($a_parent_id)) {
1904  $message = "No node_id given!";
1905  $this->log->error($message);
1907  }
1908 
1909  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
1910  $this->buildJoin() .
1911  'WHERE ' . $this->table_tree . '.' . $this->tree_pk . ' < %s ' .
1912  'AND ' . $this->table_tree . '.parent = %s';
1913  $res = $ilDB->queryF($query, array('integer','integer'), array(
1914  0,
1915  $a_parent_id));
1916 
1917  while ($row = $ilDB->fetchAssoc($res)) {
1918  $saved[] = $this->fetchNodeData($row);
1919  }
1920 
1921  return $saved ? $saved : array();
1922  }
1923 
1930  public function getSavedNodeObjIds(array $a_obj_ids)
1931  {
1932  global $ilDB;
1933 
1934  $query = 'SELECT ' . $this->table_obj_data . '.obj_id FROM ' . $this->table_tree . ' ' .
1935  $this->buildJoin() .
1936  'WHERE ' . $this->table_tree . '.' . $this->tree_pk . ' < ' . $ilDB->quote(0, 'integer') . ' ' .
1937  'AND ' . $ilDB->in($this->table_obj_data . '.obj_id', $a_obj_ids, '', 'integer');
1938  $res = $ilDB->query($query);
1939  while ($row = $ilDB->fetchAssoc($res)) {
1940  $saved[] = $row['obj_id'];
1941  }
1942 
1943  return $saved ? $saved : array();
1944  }
1945 
1953  public function getParentId($a_node_id)
1954  {
1955  global $ilDB;
1956 
1957  if (!isset($a_node_id)) {
1958  $message = "No node_id given!";
1959  $this->log->error($message);
1961  }
1962 
1963  $query = 'SELECT parent FROM ' . $this->table_tree . ' ' .
1964  'WHERE child = %s ' .
1965  'AND ' . $this->tree_pk . ' = %s ';
1966  $res = $ilDB->queryF($query, array('integer','integer'), array(
1967  $a_node_id,
1968  $this->tree_id));
1969 
1970  $row = $ilDB->fetchObject($res);
1971  return $row->parent;
1972  }
1973 
1981  public function getLeftValue($a_node_id)
1982  {
1983  global $ilDB;
1984 
1985  if (!isset($a_node_id)) {
1986  $message = "No node_id given!";
1987  $this->log->error($message);
1989  }
1990 
1991  $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
1992  'WHERE child = %s ' .
1993  'AND ' . $this->tree_pk . ' = %s ';
1994  $res = $ilDB->queryF($query, array('integer','integer'), array(
1995  $a_node_id,
1996  $this->tree_id));
1997  $row = $ilDB->fetchObject($res);
1998  return $row->lft;
1999  }
2000 
2008  public function getChildSequenceNumber($a_node, $type = "")
2009  {
2010  global $ilDB;
2011 
2012  if (!isset($a_node)) {
2013  $message = "No node_id given!";
2014  $this->log->error($message);
2016  }
2017 
2018  if ($type) {
2019  $query = 'SELECT count(*) cnt FROM ' . $this->table_tree . ' ' .
2020  $this->buildJoin() .
2021  'WHERE lft <= %s ' .
2022  'AND type = %s ' .
2023  'AND parent = %s ' .
2024  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2025 
2026  $res = $ilDB->queryF($query, array('integer','text','integer','integer'), array(
2027  $a_node['lft'],
2028  $type,
2029  $a_node['parent'],
2030  $this->tree_id));
2031  } else {
2032  $query = 'SELECT count(*) cnt FROM ' . $this->table_tree . ' ' .
2033  $this->buildJoin() .
2034  'WHERE lft <= %s ' .
2035  'AND parent = %s ' .
2036  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2037 
2038  $res = $ilDB->queryF($query, array('integer','integer','integer'), array(
2039  $a_node['lft'],
2040  $a_node['parent'],
2041  $this->tree_id));
2042  }
2043  $row = $ilDB->fetchAssoc($res);
2044  return $row["cnt"];
2045  }
2046 
2053  public function readRootId()
2054  {
2055  global $ilDB;
2056 
2057  $query = 'SELECT child FROM ' . $this->table_tree . ' ' .
2058  'WHERE parent = %s ' .
2059  'AND ' . $this->tree_pk . ' = %s ';
2060  $res = $ilDB->queryF($query, array('integer','integer'), array(
2061  0,
2062  $this->tree_id));
2063  $row = $ilDB->fetchObject($res);
2064  $this->root_id = $row->child;
2065  return $this->root_id;
2066  }
2067 
2073  public function getRootId()
2074  {
2075  return $this->root_id;
2076  }
2077  public function setRootId($a_root_id)
2078  {
2079  $this->root_id = $a_root_id;
2080  }
2081 
2087  public function getTreeId()
2088  {
2089  return $this->tree_id;
2090  }
2091 
2097  public function setTreeId($a_tree_id)
2098  {
2099  $this->tree_id = $a_tree_id;
2100  }
2101 
2110  public function fetchSuccessorNode($a_node_id, $a_type = "")
2111  {
2112  global $ilDB;
2113 
2114  if (!isset($a_node_id)) {
2115  $message = "No node_id given!";
2116  $this->log->error($message);
2118  }
2119 
2120  // get lft value for current node
2121  $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2122  'WHERE ' . $this->table_tree . '.child = %s ' .
2123  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2124  $res = $ilDB->queryF($query, array('integer','integer'), array(
2125  $a_node_id,
2126  $this->tree_id));
2127  $curr_node = $ilDB->fetchAssoc($res);
2128 
2129  if ($a_type) {
2130  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2131  $this->buildJoin() .
2132  'WHERE lft > %s ' .
2133  'AND ' . $this->table_obj_data . '.type = %s ' .
2134  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2135  'ORDER BY lft ';
2136  $ilDB->setLimit(1);
2137  $res = $ilDB->queryF($query, array('integer','text','integer'), array(
2138  $curr_node['lft'],
2139  $a_type,
2140  $this->tree_id));
2141  } else {
2142  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2143  $this->buildJoin() .
2144  'WHERE lft > %s ' .
2145  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2146  'ORDER BY lft ';
2147  $ilDB->setLimit(1);
2148  $res = $ilDB->queryF($query, array('integer','integer'), array(
2149  $curr_node['lft'],
2150  $this->tree_id));
2151  }
2152 
2153  if ($res->numRows() < 1) {
2154  return false;
2155  } else {
2156  $row = $ilDB->fetchAssoc($res);
2157  return $this->fetchNodeData($row);
2158  }
2159  }
2160 
2169  public function fetchPredecessorNode($a_node_id, $a_type = "")
2170  {
2171  global $ilDB;
2172 
2173  if (!isset($a_node_id)) {
2174  $message = "No node_id given!";
2175  $this->log->error($message);
2177  }
2178 
2179  // get lft value for current node
2180  $query = 'SELECT lft FROM ' . $this->table_tree . ' ' .
2181  'WHERE ' . $this->table_tree . '.child = %s ' .
2182  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ';
2183  $res = $ilDB->queryF($query, array('integer','integer'), array(
2184  $a_node_id,
2185  $this->tree_id));
2186 
2187  $curr_node = $ilDB->fetchAssoc($res);
2188 
2189  if ($a_type) {
2190  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2191  $this->buildJoin() .
2192  'WHERE lft < %s ' .
2193  'AND ' . $this->table_obj_data . '.type = %s ' .
2194  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2195  'ORDER BY lft DESC';
2196  $ilDB->setLimit(1);
2197  $res = $ilDB->queryF($query, array('integer','text','integer'), array(
2198  $curr_node['lft'],
2199  $a_type,
2200  $this->tree_id));
2201  } else {
2202  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2203  $this->buildJoin() .
2204  'WHERE lft < %s ' .
2205  'AND ' . $this->table_tree . '.' . $this->tree_pk . ' = %s ' .
2206  'ORDER BY lft DESC';
2207  $ilDB->setLimit(1);
2208  $res = $ilDB->queryF($query, array('integer','integer'), array(
2209  $curr_node['lft'],
2210  $this->tree_id));
2211  }
2212 
2213  if ($res->numRows() < 1) {
2214  return false;
2215  } else {
2216  $row = $ilDB->fetchAssoc($res);
2217  return $this->fetchNodeData($row);
2218  }
2219  }
2220 
2229  public function renumber($node_id = 1, $i = 1)
2230  {
2231  global $ilDB;
2232 
2233  $renumber_callable = function (ilDBInterface $ilDB) use ($node_id,$i,&$return) {
2234  $return = $this->__renumber($node_id, $i);
2235  };
2236 
2237  // LOCKED ###################################
2238  if ($this->__isMainTree()) {
2239  $ilAtomQuery = $ilDB->buildAtomQuery();
2240  $ilAtomQuery->addTableLock($this->table_tree);
2241 
2242  $ilAtomQuery->addQueryCallable($renumber_callable);
2243  $ilAtomQuery->run();
2244  } else {
2245  $renumber_callable($ilDB);
2246  }
2247  return $return;
2248  }
2249 
2250  // PRIVATE
2260  public function __renumber($node_id = 1, $i = 1)
2261  {
2262  global $ilDB;
2263 
2264  $query = 'UPDATE ' . $this->table_tree . ' SET lft = %s WHERE child = %s AND tree = %s';
2265  $res = $ilDB->manipulateF($query, array('integer','integer','integer'), array(
2266  $i,
2267  $node_id,
2268  $this->tree_id));
2269 
2270  // to much dependencies
2271  //$childs = $this->getChilds($node_id);
2272  $childs = $this->getChildIds($node_id);
2273 
2274  foreach ($childs as $child) {
2275  $i = $this->__renumber($child, $i+1);
2276  }
2277  $i++;
2278 
2279  // Insert a gap at the end of node, if the node has children
2280  if (count($childs) > 0) {
2281  $i += $this->gap * 2;
2282  }
2283 
2284 
2285  $query = 'UPDATE ' . $this->table_tree . ' SET rgt = %s WHERE child = %s AND tree = %s';
2286  $res = $ilDB->manipulateF($query, array('integer','integer', 'integer'), array(
2287  $i,
2288  $node_id,
2289  $this->tree_id));
2290  return $i;
2291  }
2292 
2293 
2304  public function checkForParentType($a_ref_id, $a_type, $a_exclude_source_check = false)
2305  {
2306  // #12577
2307  $cache_key = $a_ref_id . '.' . $a_type . '.' . ((int) $a_exclude_source_check);
2308 
2309  // Try to return a cached result
2310  if ($this->isCacheUsed() &&
2311  array_key_exists($cache_key, $this->parent_type_cache)) {
2312  return $this->parent_type_cache[$cache_key];
2313  }
2314 
2315  // Store up to 1000 results in cache
2316  $do_cache = ($this->__isMainTree() && count($this->parent_type_cache) < 1000);
2317 
2318  // ref_id is not in tree
2319  if (!$this->isInTree($a_ref_id)) {
2320  if ($do_cache) {
2321  $this->parent_type_cache[$cache_key] = false;
2322  }
2323  return false;
2324  }
2325 
2326  $path = array_reverse($this->getPathFull($a_ref_id));
2327 
2328  // remove first path entry as it is requested node
2329  if ($a_exclude_source_check) {
2330  array_shift($path);
2331  }
2332 
2333  foreach ($path as $node) {
2334  // found matching parent
2335  if ($node["type"] == $a_type) {
2336  if ($do_cache) {
2337  $this->parent_type_cache[$cache_key] = $node["child"];
2338  }
2339  return $node["child"];
2340  }
2341  }
2342 
2343  if ($do_cache) {
2344  $this->parent_type_cache[$cache_key] = false;
2345  }
2346  return 0;
2347  }
2348 
2359  public static function _removeEntry($a_tree, $a_child, $a_db_table = "tree")
2360  {
2361  global $ilDB;
2362 
2363  if ($a_db_table === 'tree') {
2364  if ($a_tree == 1 and $a_child == ROOT_FOLDER_ID) {
2365  $message = sprintf(
2366  'Tried to delete root node! $a_tree: %s $a_child: %s',
2367  $a_tree,
2368  $a_child
2369  );
2370  ilLoggerFactory::getLogger('tree')->error($message);
2372  }
2373  }
2374 
2375  $query = 'DELETE FROM ' . $a_db_table . ' ' .
2376  'WHERE tree = %s ' .
2377  'AND child = %s ';
2378  $res = $ilDB->manipulateF($query, array('integer','integer'), array(
2379  $a_tree,
2380  $a_child));
2381  }
2382 
2389  public function __isMainTree()
2390  {
2391  return $this->table_tree === 'tree';
2392  }
2393 
2405  public function __checkDelete($a_node)
2406  {
2407  global $ilDB;
2408 
2409 
2410  $query = $this->getTreeImplementation()->getSubTreeQuery($a_node, array(), false);
2411  $this->log->debug($query);
2412  $res = $ilDB->query($query);
2413 
2414  $counter = (int) $lft_childs = array();
2415  while ($row = $ilDB->fetchObject($res)) {
2416  $lft_childs[$row->child] = $row->parent;
2417  ++$counter;
2418  }
2419 
2420  // CHECK FOR DUPLICATE CHILD IDS
2421  if ($counter != count($lft_childs)) {
2422  $message = 'Duplicate entries for "child" in maintree! $a_node_id: ' . $a_node['child'];
2423 
2424  $this->log->error($message);
2426  }
2427 
2428  // GET SUBTREE BY PARENT RELATION
2429  $parent_childs = array();
2430  $this->__getSubTreeByParentRelation($a_node['child'], $parent_childs);
2431  $this->__validateSubtrees($lft_childs, $parent_childs);
2432 
2433  return true;
2434  }
2435 
2445  public function __getSubTreeByParentRelation($a_node_id, &$parent_childs)
2446  {
2447  global $ilDB;
2448 
2449  // GET PARENT ID
2450  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2451  'WHERE child = %s ' .
2452  'AND tree = %s ';
2453  $res = $ilDB->queryF($query, array('integer','integer'), array(
2454  $a_node_id,
2455  $this->tree_id));
2456 
2457  $counter = 0;
2458  while ($row = $ilDB->fetchObject($res)) {
2459  $parent_childs[$a_node_id] = $row->parent;
2460  ++$counter;
2461  }
2462  // MULTIPLE ENTRIES
2463  if ($counter > 1) {
2464  $message = 'Multiple entries in maintree! $a_node_id: ' . $a_node_id;
2465 
2466  $this->log->error($message);
2468  }
2469 
2470  // GET ALL CHILDS
2471  $query = 'SELECT * FROM ' . $this->table_tree . ' ' .
2472  'WHERE parent = %s ';
2473  $res = $ilDB->queryF($query, array('integer'), array($a_node_id));
2474 
2475  while ($row = $ilDB->fetchObject($res)) {
2476  // RECURSION
2477  $this->__getSubTreeByParentRelation($row->child, $parent_childs);
2478  }
2479  return true;
2480  }
2481 
2489  public function __validateSubtrees(&$lft_childs, $parent_childs)
2490  {
2491  // SORT BY KEY
2492  ksort($lft_childs);
2493  ksort($parent_childs);
2494 
2495  $this->log->debug('left childs ' . print_r($lft_childs, true));
2496  $this->log->debug('parent childs ' . print_r($parent_childs, true));
2497 
2498  if (count($lft_childs) != count($parent_childs)) {
2499  $message = '(COUNT) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2500  $this->log->error($message);
2502  }
2503 
2504 
2505  foreach ($lft_childs as $key => $value) {
2506  if ($parent_childs[$key] != $value) {
2507  $message = '(COMPARE) Tree is corrupted! Left/Right subtree does not comply with parent relation';
2508  $this->log->error($message);
2510  }
2511  if ($key == ROOT_FOLDER_ID) {
2512  $message = '(ROOT_FOLDER) Tree is corrupted! Tried to delete root folder';
2513  $this->log->error($message);
2515  }
2516  }
2517  return true;
2518  }
2519 
2529  public function moveTree($a_source_id, $a_target_id, $a_location = self::POS_LAST_NODE)
2530  {
2531  $old_parent_id = $this->getParentId($a_source_id);
2532  $this->getTreeImplementation()->moveTree($a_source_id, $a_target_id, $a_location);
2533  if (isset($GLOBALS["ilAppEventHandler"]) && $this->__isMainTree()) {
2534  $GLOBALS['ilAppEventHandler']->raise(
2535  "Services/Tree",
2536  "moveTree",
2537  array(
2538  'tree' => $this->table_tree,
2539  'source_id' => $a_source_id,
2540  'target_id' => $a_target_id,
2541  'old_parent_id' => $old_parent_id
2542  )
2543  );
2544  }
2545  return true;
2546  }
2547 
2548 
2549 
2550 
2558  public function getRbacSubtreeInfo($a_endnode_id)
2559  {
2560  return $this->getTreeImplementation()->getSubtreeInfo($a_endnode_id);
2561  }
2562 
2563 
2571  public function getSubTreeQuery($a_node_id, $a_fields = array(), $a_types = '', $a_force_join_reference = false)
2572  {
2573  return $this->getTreeImplementation()->getSubTreeQuery(
2574  $this->getNodeTreeData($a_node_id),
2575  $a_types,
2576  $a_force_join_reference,
2577  $a_fields
2578  );
2579  }
2580 
2581 
2590  public function getSubTreeFilteredByObjIds($a_node_id, array $a_obj_ids, array $a_fields = array())
2591  {
2592  global $ilDB;
2593 
2594  $node = $this->getNodeData($a_node_id);
2595  if (!sizeof($node)) {
2596  return;
2597  }
2598 
2599  $res = array();
2600 
2601  $query = $this->getTreeImplementation()->getSubTreeQuery($node, '', true, array($this->ref_pk));
2602 
2603  $fields = '*';
2604  if (count($a_fields)) {
2605  $fields = implode(',', $a_fields);
2606  }
2607 
2608  $query = "SELECT " . $fields .
2609  " FROM " . $this->getTreeTable() .
2610  " " . $this->buildJoin() .
2611  " WHERE " . $this->getTableReference() . "." . $this->ref_pk . " IN (" . $query . ")" .
2612  " AND " . $ilDB->in($this->getObjectDataTable() . "." . $this->obj_pk, $a_obj_ids, "", "integer");
2613  $set = $ilDB->query($query);
2614  while ($row = $ilDB->fetchAssoc($set)) {
2615  $res[] = $row;
2616  }
2617 
2618  return $res;
2619  }
2620 
2621  public function deleteNode($a_tree_id, $a_node_id)
2622  {
2623  global $ilDB, $ilAppEventHandler;
2624 
2625  $query = 'DELETE FROM tree where ' .
2626  'child = ' . $ilDB->quote($a_node_id, 'integer') . ' ' .
2627  'AND tree = ' . $ilDB->quote($a_tree_id, 'integer');
2628  $ilDB->manipulate($query);
2629 
2630  $ilAppEventHandler->raise(
2631  "Services/Tree",
2632  "deleteNode",
2633  array('tree' => $this->table_tree,
2634  'node_id' => $a_node_id,
2635  'tree_id' => $a_tree_id
2636  )
2637  );
2638  }
2639 
2645  public function lookupTrashedObjectTypes()
2646  {
2647  global $ilDB;
2648 
2649  $query = 'SELECT DISTINCT(o.type) ' . $ilDB->quoteIdentifier('type') . ' FROM tree t JOIN object_reference r ON child = r.ref_id ' .
2650  'JOIN object_data o on r.obj_id = o.obj_id ' .
2651  'WHERE tree < ' . $ilDB->quote(0, 'integer') . ' ' .
2652  'AND child = -tree ' .
2653  'GROUP BY o.type';
2654  $res = $ilDB->query($query);
2655 
2656  $types_deleted = array();
2657  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
2658  $types_deleted[] = $row->type;
2659  }
2660  return $types_deleted;
2661  }
2662 } // 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 type $ilDB.
static _resetDeletedDate($a_ref_id)
only called in ilObjectGUI::insertSavedNodes
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
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.
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.
$counter
__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
Interface ilDBInterface.
$a_type
Definition: workflow.php:92
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
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.
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.
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
$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
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.
$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.
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