ILIAS  release_8 Revision v8.24
class.ilSCTreeTasks.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
5
11{
12 protected ilTree $tree;
13 protected ilDBInterface $db;
14 private ilSCTask $task;
15
16 public function __construct(ilSCTask $task)
17 {
18 global $DIC;
19 $this->db = $DIC->database();
20 $this->tree = $DIC->repositoryTree();
21 $this->task = $task;
22 }
23
24 public static function findDeepestDuplicate(): int
25 {
26 global $DIC;
27
28 $ilDB = $DIC->database();
29
30 $query = 'SELECT child FROM tree first ' .
31 'WHERE EXISTS ( ' .
32 'SELECT child FROM tree second WHERE first.child = second.child ' .
33 'GROUP BY child HAVING COUNT(child) > 1 ) ' .
34 'ORDER BY depth DESC';
35
36 $res = $ilDB->query($query);
37 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
38 return (int) $row->child;
39 }
40 return 0;
41 }
42
43 public static function repairPK(): void
44 {
45 global $DIC;
46
47 $ilDB = $DIC->database();
48
49 $ilDB->addPrimaryKey('tree', array('child'));
50 }
51
52 public static function getNodeInfo(int $a_tree_id, int $a_child): array
53 {
54 global $DIC;
55
56 $ilDB = $DIC->database();
57
58 $query = 'SELECT * FROM tree WHERE child = ' . $ilDB->quote(
59 $a_child,
61 ) . ' AND tree = ' . $ilDB->quote($a_tree_id, ilDBConstants::T_INTEGER);
62 $res = $ilDB->query($query);
63
64 $node = array();
65 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
66 $node['child'] = (int) $row->child;
67 $node['tree'] = (int) $row->tree;
68 $node['depth'] = (int) $row->depth;
69
70 // read obj_id
71 $query = 'SELECT obj_id FROM object_reference WHERE ref_id = ' . $ilDB->quote(
72 $a_child,
74 );
75 $ref_res = $ilDB->query($query);
76 while ($ref_row = $ref_res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
77 $node['obj_id'] = (int) $ref_row->obj_id;
78
79 // read object info
80 $query = 'SELECT title, description, type FROM object_data ' .
81 'WHERE obj_id = ' . $ilDB->quote($ref_row->obj_id, ilDBConstants::T_INTEGER);
82 $obj_res = $ilDB->query($query);
83 while ($obj_row = $obj_res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
84 $node['title'] = (string) $obj_row->title;
85 $node['description'] = (string) $obj_row->description;
86 $node['type'] = (string) $obj_row->type;
87 }
88 }
89 }
90 return $node;
91 }
92
93 public static function getChilds(int $a_tree_id, int $a_childs): array
94 {
95 global $DIC;
96
97 $ilDB = $DIC->database();
98
99 $query = 'SELECT * FROM tree WHERE tree = ' . $ilDB->quote(
100 $a_tree_id,
102 ) . ' ' . 'AND child = ' . $ilDB->quote($a_childs, ilDBConstants::T_INTEGER);
103 $res = $ilDB->query($query);
104
105 $childs = array();
106 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
107 $childs[] = (int) $row->child;
108 }
109 return $childs;
110 }
111
112 public static function findDuplicates(int $a_duplicate_id): array
113 {
114 global $DIC;
115
116 $ilDB = $DIC->database();
117
118 $query = 'SELECT * FROM tree first ' .
119 'WHERE EXISTS ( ' .
120 'SELECT child FROM tree second WHERE first.child = second.child ' .
121 'GROUP BY child HAVING COUNT(child) > 1 ) ' .
122 'AND child = ' . $ilDB->quote($a_duplicate_id, ilDBConstants::T_INTEGER) . ' ' .
123 'ORDER BY depth DESC';
124 $res = $ilDB->query($query);
125
126 $nodes = array();
127 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
128 $node = array();
129 $node['tree'] = (int) $row->tree;
130 $node['child'] = (int) $row->child;
131 $node['depth'] = (int) $row->depth;
132
133 $nodes[] = $node;
134 }
135
136 return $nodes;
137 }
138
139 public static function hasDuplicate(int $a_child): int
140 {
141 global $DIC;
142
143 $ilDB = $DIC->database();
144
145 return count(self::findDuplicates($a_child));
146 }
147
148 public static function deleteDuplicateFromTree(int $a_duplicate_id, bool $a_delete_trash): bool
149 {
150 $dups = self::findDuplicates($a_duplicate_id);
151 foreach ($dups as $dup) {
152 if ($a_delete_trash && $dup['tree'] < 1) {
153 self::deleteDuplicate($dup['tree'], $dup['child']);
154 }
155 if (!$a_delete_trash && $dup['tree'] == 1) {
156 self::deleteDuplicate($dup['tree'], $dup['child']);
157 }
158 }
159 return true;
160 }
161
162 protected static function deleteDuplicate(int $tree_id, int $dup_id): void
163 {
164 global $DIC;
165
166 $ilDB = $DIC->database();
167
168 $query = 'SELECT child FROM tree ' .
169 'WHERE parent = ' . $ilDB->quote($dup_id, ilDBConstants::T_INTEGER) . ' ' .
170 'AND tree = ' . $ilDB->quote($tree_id, ilDBConstants::T_INTEGER);
171 $res = $ilDB->query($query);
172 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
173 // start recursion
174 self::deleteDuplicate($tree_id, (int) $row->child);
175 }
176 // now delete node
177 if (self::hasDuplicate($dup_id)) {
178 $query = 'DELETE FROM tree ' .
179 'WHERE child = ' . $ilDB->quote($dup_id, ilDBConstants::T_INTEGER) . ' ' .
180 'AND tree = ' . $ilDB->quote($tree_id, ilDBConstants::T_INTEGER);
181 $ilDB->manipulate($query);
182 }
183 }
184
185 protected function getDB(): ilDBInterface
186 {
187 return $this->db;
188 }
189
190 public function getTask(): ilSCTask
191 {
192 return $this->task;
193 }
194
195 public function validateStructure(): int
196 {
197 $failures = $this->checkStructure();
198
199 if (count($failures)) {
200 $this->getTask()->setStatus(ilSCTask::STATUS_FAILED);
201 } else {
202 $this->getTask()->setStatus(ilSCTask::STATUS_COMPLETED);
203 }
204 $this->getTask()->setLastUpdate(new ilDateTime(time(), IL_CAL_UNIX));
205 $this->getTask()->update();
206 return count($failures);
207 }
208
209 public function checkStructure(): array
210 {
211 return $this->tree->validateParentRelations();
212 }
213
214 public function validateDuplicates(): int
215 {
216 $failures = $this->checkDuplicates();
217
218 if (count($failures)) {
219 $this->getTask()->setStatus(ilSCTask::STATUS_FAILED);
220 } else {
221 $this->getTask()->setStatus(ilSCTask::STATUS_COMPLETED);
222 }
223 $this->getTask()->setLastUpdate(new ilDateTime(time(), IL_CAL_UNIX));
224 $this->getTask()->update();
225 return count($failures);
226 }
227
228 public function checkDuplicates(): array
229 {
230 $query = 'SELECT child, count(child) num FROM tree ' .
231 'GROUP BY child ' .
232 'HAVING count(child) > 1';
233 $res = $this->db->query($query);
234
235 $failures = array();
236 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
237 $failures[] = (int) $row->child;
238 }
239 return $failures;
240 }
241
242 public function findMissingTreeEntries(): int
243 {
244 $failures = $this->readMissingTreeEntries();
245
246 if (count($failures)) {
247 $this->getTask()->setStatus(ilSCTask::STATUS_FAILED);
248 } else {
249 $this->getTask()->setStatus(ilSCTask::STATUS_COMPLETED);
250 }
251
252 $this->getTask()->setLastUpdate(new ilDateTime(time(), IL_CAL_UNIX));
253 $this->getTask()->update();
254 return count($failures);
255 }
256
257 public function findMissing(): int
258 {
259 $failures = $this->readMissing();
260
261 if (count($failures)) {
262 $this->getTask()->setStatus(ilSCTask::STATUS_FAILED);
263 } else {
264 $this->getTask()->setStatus(ilSCTask::STATUS_COMPLETED);
265 }
266
267 $this->getTask()->setLastUpdate(new ilDateTime(time(), IL_CAL_UNIX));
268 $this->getTask()->update();
269 return count($failures);
270 }
271
272 public function repairMissing(): void
273 {
274 $failures = $this->readMissing();
275 $recf_ref_id = $this->createRecoveryContainer();
276 foreach ($failures as $ref_id) {
277 $this->repairMissingObject($recf_ref_id, $ref_id);
278 }
279 }
280
281 protected function repairMissingObject(int $a_parent_ref, int $a_ref_id): void
282 {
283
284 // check if object entry exist
285 $query = 'SELECT obj_id FROM object_reference ' .
286 'WHERE ref_id = ' . $this->db->quote($a_ref_id, ilDBConstants::T_INTEGER);
287
288 $res = $this->db->query($query);
289 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
290 $query = 'SELECT type, title FROM object_data ' .
291 'WHERE obj_id = ' . $this->db->quote($row->obj_id, ilDBConstants::T_INTEGER);
292 $ores = $this->db->query($query);
293
294 $done = false;
295 while ($orow = $ores->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
296 $done = true;
297
299 $ref_obj = $factory->getInstanceByRefId($a_ref_id, false);
300
301 if ($ref_obj instanceof ilObjRoleFolder) {
302 $ref_obj->delete();
303 } elseif ($ref_obj instanceof ilObject) {
304 $ref_obj->putInTree($a_parent_ref);
305 $ref_obj->setPermissions($a_parent_ref);
306
307 break;
308 }
309 }
310 if (!$done) {
311 // delete reference value
312 $query = 'DELETE FROM object_reference WHERE ref_id = ' . $this->db->quote(
313 $a_ref_id,
315 );
316 $this->db->manipulate($query);
317 }
318 }
319 }
320
324 protected function readMissing(): array
325 {
326 $query = 'SELECT ref_id FROM object_reference ' .
327 'LEFT JOIN tree ON ref_id = child ' .
328 'WHERE child IS NULL';
329 $res = $this->db->query($query);
330
331 $failures = array();
332 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
333 $failures[] = (int) $row->ref_id;
334 }
335 return $failures;
336 }
337
338 public function repairMissingTreeEntries(): void
339 {
340 $missing = $this->readMissingTreeEntries();
341 foreach ($missing as $ref_id) {
342 // check for duplicates
343 $query = 'SELECT tree, child FROM tree ' .
344 'WHERE child = ' . $this->db->quote($ref_id, ilDBConstants::T_INTEGER);
345 $res = $this->db->query($query);
346 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
347 $this->deleteMissingTreeEntry((int) $row->tree, $ref_id);
348 }
349 }
350 }
351
352 protected function deleteMissingTreeEntry(int $a_tree_id, int $a_ref_id): void
353 {
354 $query = 'SELECT child FROM tree ' .
355 'WHERE parent = ' . $this->db->quote($a_ref_id, ilDBConstants::T_INTEGER) . ' ' .
356 'AND tree = ' . $this->db->quote($a_tree_id, ilDBConstants::T_INTEGER);
357
358 $res = $this->db->query($query);
359 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
360 // check for duplicates
361 $query = 'SELECT tree, child FROM tree ' .
362 'WHERE child = ' . $this->db->quote($row->child, ilDBConstants::T_INTEGER);
363 $resd = $this->db->query($query);
364 while ($rowd = $resd->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
365 $this->deleteMissingTreeEntry((int) $rowd->tree, (int) $rowd->child);
366 }
367 }
368
369 // finally delete
370
372 $ref_obj = $factory->getInstanceByRefId($a_ref_id, false);
373
374 if (($ref_obj instanceof ilObject) && $ref_obj->getType()) {
375 $ref_obj->delete();
376 }
377
378 $query = 'DELETE from tree ' .
379 'WHERE tree = ' . $this->db->quote($a_tree_id, ilDBConstants::T_INTEGER) . ' ' .
380 'AND child = ' . $this->db->quote($a_ref_id, ilDBConstants::T_INTEGER);
381 $this->db->manipulate($query);
382 }
383
388 protected function readMissingTreeEntries(): array
389 {
390 $query = 'SELECT child FROM tree ' .
391 'LEFT JOIN object_reference ON child = ref_id ' .
392 'WHERE ref_id IS NULL';
393
394 $res = $this->db->query($query);
395
396 $failures = array();
397 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
398 $failures[] = (int) $row->child;
399 }
400 return $failures;
401 }
402
403 protected function createRecoveryContainer(): int
404 {
405 $now = new ilDateTime(time(), IL_CAL_UNIX);
406
407 $folder = new ilObjFolder();
408 $folder->setTitle('__System check recovery: ' . $now->get(IL_CAL_DATETIME));
409 $folder->create();
410 $folder->createReference();
411 $folder->putInTree(RECOVERY_FOLDER_ID);
412
413 return $folder->getRefId();
414 }
415}
const IL_CAL_UNIX
const IL_CAL_DATETIME
@classDescription Date and time handling
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const STATUS_COMPLETED
const STATUS_FAILED
Defines a system check task.
static findDeepestDuplicate()
static findDuplicates(int $a_duplicate_id)
static hasDuplicate(int $a_child)
ilDBInterface $db
static deleteDuplicateFromTree(int $a_duplicate_id, bool $a_delete_trash)
repairMissingObject(int $a_parent_ref, int $a_ref_id)
__construct(ilSCTask $task)
static getNodeInfo(int $a_tree_id, int $a_child)
static deleteDuplicate(int $tree_id, int $dup_id)
static getChilds(int $a_tree_id, int $a_childs)
deleteMissingTreeEntry(int $a_tree_id, int $a_ref_id)
readMissingTreeEntries()
Read missing tree entries for referenced objects Entry in tree but no entry in object reference.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const RECOVERY_FOLDER_ID
Definition: constants.php:37
global $DIC
Definition: feed.php:28
Interface ilDBInterface.
$ref_id
Definition: ltiauth.php:67
$res
Definition: ltiservices.php:69
$factory
Definition: metadata.php:75
$query