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