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