ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilRepUtil.php
Go to the documentation of this file.
1 <?php
2 
24 class ilRepUtil
25 {
26  protected ilDBInterface $db;
27  protected ilTree $tree;
28  protected ilSetting $settings;
29 
30  public function __construct()
31  {
32  global $DIC;
33 
34  $this->db = $DIC->database();
35  $this->tree = $DIC->repositoryTree();
36  $this->settings = $DIC->settings();
37  }
38 
45  public static function deleteObjects(
46  int $a_cur_ref_id,
47  array $a_ids,
48  bool $throw_error_on_already_deleted = true
49  ): void {
50  global $DIC;
51 
52  $ilAppEventHandler = $DIC["ilAppEventHandler"];
53  $rbacsystem = $DIC->rbac()->system();
54  $rbacadmin = $DIC->rbac()->admin();
55  $ilLog = $DIC["ilLog"];
56  $tree = $DIC->repositoryTree();
57  $lng = $DIC->language();
58  $ilSetting = $DIC->settings();
59  $user = $DIC->user();
60 
61  // deactivating the cache to make functions like
62  // _hasUntrashedReference work properly (e.g. when event listener react)
63  $tree->useCache(false);
64 
65  $log = $ilLog;
66 
67  // Remove duplicate ids from array
68  $a_ids = array_unique($a_ids);
69 
70  // FOR ALL SELECTED OBJECTS
71  $not_deletable = [];
72  $all_node_data = [];
73  foreach ($a_ids as $id) {
74  if ($tree->isDeleted($id)) {
75  if (!$throw_error_on_already_deleted) {
76  continue;
77  }
78  $log->write(__METHOD__ . ': Object with ref_id: ' . $id . ' already deleted.');
79  throw new ilRepositoryException($lng->txt("msg_obj_already_deleted"));
80  }
81 
82  // GET COMPLETE NODE_DATA OF ALL SUBTREE NODES
83  $node_data = $tree->getNodeData($id);
84  $subtree_nodes = $tree->getSubTree($node_data);
85 
86  $all_node_data[] = $node_data;
87  $all_subtree_nodes[] = $subtree_nodes;
88 
89  // CHECK DELETE PERMISSION OF ALL OBJECTS
90  foreach ($subtree_nodes as $node) {
91  if ($node['type'] === 'rolf') {
92  continue;
93  }
94  if (!$rbacsystem->checkAccess('delete', $node["child"])) {
95  $not_deletable[] = $node["child"];
96  $perform_delete = false;
97  }
98  }
99  }
100 
101  // IF THERE IS ANY OBJECT WITH NO PERMISSION TO DELETE
102  if (is_array($not_deletable) && count($not_deletable) > 0) {
103  $not_deletable_titles = [];
104  foreach ($not_deletable as $key => $ref_id) {
105  $obj_id = ilObject::_lookupObjId($ref_id);
106  $not_deletable_titles[] = ilObject::_lookupTitle($obj_id);
107  }
108 
109  ilSession::clear("saved_post");
110  throw new ilRepositoryException(
111  $lng->txt("msg_no_perm_delete") . " " . implode(', ', $not_deletable_titles) . "<br/>" . $lng->txt("msg_cancel")
112  );
113  }
114 
115  // DELETE THEM
116  if (!$all_node_data[0]["type"]) {
117  // alex: this branch looks suspicious to me... I deactivate it for
118  // now. Objects that aren't in the tree should overwrite this method.
119  throw new ilRepositoryException($lng->txt("ilRepUtil::deleteObjects: Type information missing."));
120  }
121 
122  // SAVE SUBTREE AND DELETE SUBTREE FROM TREE
123  $affected_ids = [];
124  $affected_parents = [];
125  foreach ($a_ids as $id) {
126  if ($tree->isDeleted($id)) {
127  if (!$throw_error_on_already_deleted) {
128  continue;
129  }
130  $log->write(__METHOD__ . ': Object with ref_id: ' . $id . ' already deleted.');
131  throw new ilRepositoryException($lng->txt("msg_obj_already_deleted"));
132  }
133 
134  // DELETE OLD PERMISSION ENTRIES
135  $subnodes = $tree->getSubtree($tree->getNodeData($id));
136 
137  foreach ($subnodes as $subnode) {
138  $rbacadmin->revokePermission((int) $subnode["child"]);
139 
140  $affected_ids[$subnode["child"]] = $subnode["child"];
141  $affected_parents[$subnode["child"]] = $subnode["parent"];
142  }
143 
144  // TODO: needs other handling
145  // This class shouldn't have to know anything about ECS
147  if (!$tree->moveToTrash($id, true, $user->getId())) {
148  $log->write(__METHOD__ . ': Object with ref_id: ' . $id . ' already deleted.');
149  throw new ilRepositoryException($lng->txt("msg_obj_already_deleted"));
150  }
151 
152  // write log entry
153  $log->write("ilObjectGUI::confirmedDeleteObject(), moved ref_id " . $id .
154  " to trash");
155 
156  $affected_ids[$id] = $id;
157  }
158 
159  // send global events
160  foreach ($affected_ids as $aid) {
161  $ilAppEventHandler->raise(
162  "Services/Object",
163  "toTrash",
164  [
165  "obj_id" => ilObject::_lookupObjId($aid),
166  "ref_id" => $aid,
167  "old_parent_ref_id" => $affected_parents[$aid]
168  ]
169  );
170  }
171 
172  if (!$ilSetting->get('enable_trash')) {
173  self::removeObjectsFromSystem($a_ids);
174  }
175  }
176 
186  public static function removeObjectsFromSystem(
187  array $a_ref_ids,
188  bool $a_from_recovery_folder = false
189  ): void {
190  global $DIC;
191 
192  $ilLog = $DIC["ilLog"];
193  $ilAppEventHandler = $DIC["ilAppEventHandler"];
194  $tree = $DIC->repositoryTree();
195  $logger = $DIC->logger()->rep();
196 
197  $log = $ilLog;
198 
199  $affected_ids = [];
200 
201  // DELETE THEM
202  $a_ref_ids = array_map('intval', $a_ref_ids);
203  foreach ($a_ref_ids as $id) {
204  $saved_tree = null;
205  // GET COMPLETE NODE_DATA OF ALL SUBTREE NODES
206  if (!$a_from_recovery_folder) {
207  $trees = ilTree::lookupTreesForNode($id);
208  $tree_id = end($trees);
209 
210  if ($tree_id) {
211  $saved_tree = new ilTree($tree_id);
212  $node_data = $saved_tree->getNodeData($id);
213  $subtree_nodes = $saved_tree->getSubTree($node_data);
214  } else {
215  throw new ilRepositoryException('No valid tree id found for node id: ' . $id);
216  }
217  } else {
218  $node_data = $tree->getNodeData($id);
219  $subtree_nodes = $tree->getSubTree($node_data);
220  }
221 
222  global $DIC;
223 
224  $ilUser = $DIC->user();
225  $tree = $DIC->repositoryTree();
226 
228  $node_data['obj_id'],
229  $ilUser->getId(),
230  'purge',
231  null
232  );
233  // END ChangeEvent: Record remove from system.
234 
235  // remember already checked deleted node_ids
236  if (!$a_from_recovery_folder) {
237  $checked[] = -$id;
238  } else {
239  $checked[] = $id;
240  }
241 
242  // dive in recursive manner in each already deleted subtrees and remove these objects too
243  self::removeDeletedNodes($id, $checked, true, $affected_ids);
244 
245  foreach ($subtree_nodes as $node) {
246  if (!$node_obj = ilObjectFactory::getInstanceByRefId($node["ref_id"], false)) {
247  continue;
248  }
249 
250  $logger->info(
251  'delete obj_id: ' . $node_obj->getId() .
252  ', ref_id: ' . $node_obj->getRefId() .
253  ', type: ' . $node_obj->getType() .
254  ', title: ' . $node_obj->getTitle()
255  );
256  $affected_ids[$node["ref_id"]] = [
257  "ref_id" => $node["ref_id"],
258  "obj_id" => $node_obj->getId(),
259  "type" => $node_obj->getType(),
260  "old_parent_ref_id" => $node["parent"]
261  ];
262 
263  // this is due to bug #1860 (even if this will not completely fix it)
264  // and the fact, that media pool folders may find their way into
265  // the recovery folder (what results in broken pools, if the are deleted)
266  // Alex, 2006-07-21
267  if (!$a_from_recovery_folder || $node_obj->getType() !== "fold") {
268  $node_obj->delete();
269  }
270  }
271 
272  // Use the saved tree object here (negative tree_id)
273  if (!$a_from_recovery_folder) {
274  if ($saved_tree) {
275  $saved_tree->deleteTree($node_data);
276  }
277  } else {
278  $tree->deleteTree($node_data);
279  }
280 
281  $logger->info(
282  'deleted tree, tree_id: ' . $node_data['tree'] .
283  ', child: ' . $node_data['child']
284  );
285  }
286 
287  // send global events
288  foreach ($affected_ids as $aid) {
289  $ilAppEventHandler->raise(
290  "Services/Object",
291  "delete",
292  [
293  "obj_id" => $aid["obj_id"],
294  "ref_id" => $aid["ref_id"],
295  "type" => $aid["type"],
296  "old_parent_ref_id" => $aid["old_parent_ref_id"]
297  ]
298  );
299  }
300  }
301 
305  private static function removeDeletedNodes(
306  int $a_node_id,
307  array $a_checked,
308  bool $a_delete_objects,
309  array &$a_affected_ids
310  ): void {
311  global $DIC;
312 
313  $ilLog = $DIC["ilLog"];
314  $ilDB = $DIC->database();
315  $tree = $DIC->repositoryTree();
316  $logger = $DIC->logger()->rep();
317 
318  $log = $ilLog;
319 
320  // this queries for trash items in the trash of deleted nodes
321  $q = 'SELECT tree FROM tree WHERE parent = ' . $ilDB->quote($a_node_id, ilDBConstants::T_INTEGER) .
322  ' AND tree < 0 ' .
323  ' AND tree = -1 * child' ;
324 
325  $r = $ilDB->query($q);
326 
327  while ($row = $ilDB->fetchObject($r)) {
328  // only continue recursion if fetched node wasn't touched already!
329  if (!in_array($row->tree, $a_checked)) {
330  $deleted_tree = new ilTree($row->tree);
331  $a_checked[] = $row->tree;
332 
333  $row->tree *= (-1);
334  $del_node_data = $deleted_tree->getNodeData($row->tree);
335  $del_subtree_nodes = $deleted_tree->getSubTree($del_node_data);
336  // delete trash in the trash of trash...
337  self::removeDeletedNodes($row->tree, $a_checked, $a_delete_objects, $a_affected_ids);
338 
339  if ($a_delete_objects) {
340  foreach ($del_subtree_nodes as $node) {
341  $node_obj = ilObjectFactory::getInstanceByRefId($node["ref_id"]);
342  $logger->info(
343  'removeDeletedNodes: delete obj_id: ' . $node_obj->getId() .
344  ', ref_id: ' . $node_obj->getRefId() .
345  ', type: ' . $node_obj->getType() .
346  ', title: ' . $node_obj->getTitle()
347  );
348  $a_affected_ids[$node["ref_id"]] = [
349  "ref_id" => $node["ref_id"],
350  "obj_id" => $node_obj->getId(),
351  "type" => $node_obj->getType(),
352  "old_parent_ref_id" => $node["parent"]
353  ];
354  $node_obj->delete();
355  }
356  }
357  // tree instance with -child tree id
358  $trash_tree = new ilTree($del_node_data['tree']);
359  $trash_tree->deleteTree($del_node_data);
360  $logger->info(
361  'removeDeltedNodes: deleted tree, tree_id: ' . $del_node_data['tree'] .
362  ', child: ' . $del_node_data['child']
363  );
364  }
365  }
366  }
367 
376  public static function restoreObjects(
377  int $a_cur_ref_id,
378  array $a_ref_ids
379  ): void {
380  global $DIC;
381 
382  $rbacsystem = $DIC->rbac()->system();
383  $ilAppEventHandler = $DIC["ilAppEventHandler"];
384  $lng = $DIC->language();
385  $tree = $DIC->repositoryTree();
386 
387  $cur_obj_id = ilObject::_lookupObjId($a_cur_ref_id);
388 
389  $no_create = [];
390 
391  foreach ($a_ref_ids as $id) {
392  $obj_data = ilObjectFactory::getInstanceByRefId($id);
393 
394  if (!$rbacsystem->checkAccess('create', $a_cur_ref_id, $obj_data->getType())) {
395  $no_create[] = ilObject::_lookupTitle(ilObject::_lookupObjId($id));
396  }
397  }
398 
399  if (count($no_create)) {
400  throw new ilRepositoryException($lng->txt("msg_no_perm_paste") . " " . implode(',', $no_create));
401  }
402 
403  $affected_ids = [];
404 
405  foreach ($a_ref_ids as $id) {
406  $affected_ids[$id] = $id;
407 
408  // INSERT AND SET PERMISSIONS
409  try {
410  $tree_ids = ilTree::lookupTreesForNode($id);
411  $tree_id = $tree_ids[0];
412  self::insertSavedNodes($id, $a_cur_ref_id, $tree_id, $affected_ids);
413  } catch (Exception $e) {
414  throw new ilRepositoryException('Restore from trash failed with message: ' . $e->getMessage());
415  }
416 
417 
418  // BEGIN ChangeEvent: Record undelete.
419  global $DIC;
420 
421  $ilUser = $DIC->user();
422 
423 
426  $ilUser->getId(),
427  'undelete',
429  );
431  $cur_obj_id,
432  $ilUser->getId()
433  );
434  // END PATCH ChangeEvent: Record undelete.
435  }
436 
437  // send events
438  foreach ($affected_ids as $id) {
439  // send global event
440  $ilAppEventHandler->raise(
441  "Services/Object",
442  "undelete",
443  ["obj_id" => ilObject::_lookupObjId($id), "ref_id" => $id]
444  );
445  }
446  }
447 
451  private static function insertSavedNodes(
452  int $a_source_id,
453  int $a_dest_id,
454  int $a_tree_id,
455  array &$a_affected_ids
456  ): void {
457  global $DIC;
458 
459  $tree = $DIC->repositoryTree();
460 
461  ilLoggerFactory::getLogger('rep')->debug('Restoring from trash: source_id: ' . $a_source_id . ', dest_id: ' . $a_dest_id . ', tree_id:' . $a_tree_id);
462  ilLoggerFactory::getLogger('rep')->info('Restoring ref_id ' . $a_source_id . ' from trash.');
463 
464  // read child of node
465  $saved_tree = new ilTree($a_tree_id);
466  $childs = $saved_tree->getChilds($a_source_id);
467 
468  // then delete node and put in tree
469  try {
470  $tree->insertNodeFromTrash($a_source_id, $a_dest_id, $a_tree_id, ilTree::POS_LAST_NODE, true);
471  } catch (Exception $e) {
472  ilLoggerFactory::getLogger('rep')->error('Restore from trash failed with message: ' . $e->getMessage());
473  throw $e;
474  }
475 
476  $ref_obj = ilObjectFactory::getInstanceByRefId($a_source_id, false);
477  if ($ref_obj instanceof ilObject) {
478  $lroles = $GLOBALS['rbacreview']->getRolesOfRoleFolder($a_source_id, true);
479  foreach ($lroles as $role_id) {
480  $role = new ilObjRole($role_id);
481  $role->setParent($a_source_id);
482  $role->delete();
483  }
484  if ($a_dest_id) {
485  $ref_obj->setPermissions($a_dest_id);
486  }
487  }
488  foreach ($childs as $child) {
489  self::insertSavedNodes($child["child"], $a_source_id, $a_tree_id, $a_affected_ids);
490  }
491  }
492 
493 
494 
495  //
496  // OBJECT TYPE HANDLING / REMOVAL
497  //
498 
499  protected function findTypeInTrash(
500  string $a_type
501  ): array {
502  $ilDB = $this->db;
503 
504  $res = [];
505 
506  $set = $ilDB->query("SELECT child" .
507  " FROM tree" .
508  " JOIN object_reference ref ON (tree.child = ref.ref_id)" .
509  " JOIN object_data od ON (od.obj_id = ref.obj_id)" .
510  " WHERE tree.tree < " . $ilDB->quote(0, "integer") .
511  " AND od.type = " . $ilDB->quote($a_type, "text"));
512  while ($row = $ilDB->fetchAssoc($set)) {
513  $res[] = $row["child"];
514  }
515 
516  return $res;
517  }
518 
519  protected function getObjectTypeId(
520  string $a_type
521  ): int {
522  $ilDB = $this->db;
523 
524  $set = $ilDB->query("SELECT obj_id" .
525  " FROM object_data " .
526  " WHERE type = " . $ilDB->quote("typ", "text") .
527  " AND title = " . $ilDB->quote($a_type, "text"));
528  $row = $ilDB->fetchAssoc($set);
529  return (int) $row["obj_id"];
530  }
531 
532  public function deleteObjectType(
533  string $a_type
534  ): void {
535  $ilDB = $this->db;
536  $tree = $this->tree;
538 
539  // delete object instances (repository/trash)
540 
541  $ref_ids_in_tree = $tree->getSubTree($tree->getNodeData(ROOT_FOLDER_ID), false, [$a_type]);
542  if ($ref_ids_in_tree) {
543  self::deleteObjects(0, $ref_ids_in_tree);
544  }
545 
546  if ($ilSetting->get('enable_trash')) {
547  $ref_ids_in_trash = $this->findTypeInTrash($a_type);
548  if ($ref_ids_in_trash) {
549  self::removeObjectsFromSystem($ref_ids_in_trash);
550  }
551  }
552 
553  // delete "component"
554  $type_id = $this->getObjectTypeId($a_type);
555  if ($type_id) {
556  // see ilRepositoryObjectPlugin::beforeActivation()
557 
558  $ilDB->manipulate("DELETE FROM object_data" .
559  " WHERE obj_id = " . $ilDB->quote($type_id, "integer"));
560 
561  // RBAC
562 
563  // basic operations
564  $ilDB->manipulate("DELETE FROM rbac_ta" .
565  " WHERE typ_id = " . $ilDB->quote($type_id, "integer") /*.
566  " AND ".$ilDB->in("ops_id", array(1, 2, 3, 4, 6), "", "integer") */);
567 
568  // creation operation
569  $set = $ilDB->query("SELECT ops_id" .
570  " FROM rbac_operations " .
571  " WHERE class = " . $ilDB->quote("create", "text") .
572  " AND operation = " . $ilDB->quote("create_" . $a_type, "text"));
573  $row = $ilDB->fetchAssoc($set);
574  $create_ops_id = $row["ops_id"];
575  if ($create_ops_id) {
576  $ilDB->manipulate("DELETE FROM rbac_operations" .
577  " WHERE ops_id = " . $ilDB->quote($create_ops_id, "integer"));
578 
579  $ilDB->manipulate("DELETE FROM rbac_templates" .
580  " WHERE ops_id = " . $ilDB->quote($create_ops_id, "integer"));
581 
582  // container create
583  foreach (["root", "cat", "crs", "grp", "fold"] as $parent_type) {
584  $parent_type_id = $this->getObjectTypeId($parent_type);
585  if ($parent_type_id) {
586  $ilDB->manipulate("DELETE FROM rbac_ta" .
587  " WHERE typ_id = " . $ilDB->quote($parent_type_id, "integer") .
588  " AND ops_id = " . $ilDB->quote($create_ops_id, "integer"));
589  }
590  }
591  }
592  }
593 
594  // delete new item settings
596  }
597 }
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static deleteObjects(int $a_cur_ref_id, array $a_ids, bool $throw_error_on_already_deleted=true)
Delete objects.
Class ilObjRole.
$res
Definition: ltiservices.php:69
getNodeData(int $a_node_id, ?int $a_tree_pk=null)
get all information of a node.
static getLogger(string $a_component_id)
Get component logger.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const ROOT_FOLDER_ID
Definition: constants.php:32
$lng
static _handleDelete(array $a_subbtree_nodes)
handle delete Objects that are moved to the trash call ECS-Remove
ilSetting $settings
isDeleted(int $a_node_id)
This is a wrapper for isSaved() with a more useful name.
useCache(bool $a_use=true)
Use Cache (usually activated)
static lookupTreesForNode(int $node_id)
deleteTree(array $a_node)
delete node and the whole subtree under this node
static _lookupObjId(int $ref_id)
global $DIC
Definition: feed.php:28
deleteObjectType(string $a_type)
$ref_id
Definition: ltiauth.php:67
static _lookupTitle(int $obj_id)
$log
Definition: result.php:33
static restoreObjects(int $a_cur_ref_id, array $a_ref_ids)
Move objects from trash back to repository.
static getInstanceByRefId(int $ref_id, bool $stop_on_error=true)
get an instance of an Ilias object by reference id
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
string $key
Consumer key/client ID value.
Definition: System.php:193
static _recordWriteEvent(int $obj_id, int $usr_id, string $action, ?int $parent_obj_id=null)
Records a write event.
getParentId(int $a_node_id)
get parent id of given node
static removeObjectsFromSystem(array $a_ref_ids, bool $a_from_recovery_folder=false)
remove objects from trash bin and all entries therefore every object needs a specific deleteObject() ...
getObjectTypeId(string $a_type)
insertNodeFromTrash(int $a_source_id, int $a_target_id, int $a_tree_id, int $a_pos=self::POS_LAST_NODE, bool $a_reset_deleted_date=false)
Insert node from trash deletes trash entry.
findTypeInTrash(string $a_type)
const POS_LAST_NODE
static removeDeletedNodes(int $a_node_id, array $a_checked, bool $a_delete_objects, array &$a_affected_ids)
Remove already deleted objects within the objects in trash.
global $ilSetting
Definition: privfeed.php:17
$ilUser
Definition: imgupload.php:34
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
getSubTree(array $a_node, bool $a_with_data=true, array $a_type=[])
get all nodes in the subtree under specified node
ilDBInterface $db
static _catchupWriteEvents(int $obj_id, int $usr_id, ?string $timestamp=null)
Catches up with all write events which occured before the specified timestamp.
static clear(string $a_var)
static insertSavedNodes(int $a_source_id, int $a_dest_id, int $a_tree_id, array &$a_affected_ids)
Recursive method to insert all saved nodes of the clipboard.
moveToTrash(int $a_node_id, bool $a_set_deleted=false, int $a_deleted_by=0)
Move node to trash bin.