ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilValidator.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
12{
16 protected $obj_definition;
17
21 protected $db;
22
26 protected $lng;
27
31 protected $log;
32
36 protected $rbacadmin;
37
41 protected $tree;
42
46 protected $user;
47
48 protected $media_pool_ids = null;
49
54 public $rbac_object_types = null;
55
60 // i added folder due to bug #1860 (even if this will not completely fix it)
61 // and the fact, that media pool folders may find their way into
62 // the recovery folder (what results in broken pools, if the are deleted)
63 // Alex, 2006-07-21
64 // I removed file objects from this exclusion list, because file objects
65 // can be in the repository tree, and thus can suffer from data
66 // inconsistencies as well.
67 // Werner, 2007-04-16
68 public $object_types_exclude = array("adm","root","mail","usrf","objf","lngf",
69 "trac","taxf","auth","rolf","assf","svyf","extt","adve","fold");
70
75 public $mode = array(
76 "scan" => true, // gather information about corrupted entries
77 "dump_tree" => false, // dump tree
78 "clean" => false, // remove all unusable entries & renumber tree
79 "restore" => false, // restore objects with invalid parent to RecoveryFolder
80 "purge" => false, // delete all objects with invalid parent from system
81 "restore_trash" => false, // restore all objects in trash to RecoveryFolder
82 "purge_trash" => false // delete all objects in trash from system
83 );
84
89 public $invalid_references = array();
90
95 public $invalid_childs = array();
96
101 public $missing_objects = array();
102
107 public $unbound_objects = array();
108
113 public $deleted_objects = array();
114
121 public $invalid_rolefolders = array();
122
127 public $invalid_objects = array();
128
133 public $logging = false;
134
139 public $scan_log;
140
141 public $scan_log_file = "scanlog.log";
142
143 public $scan_log_separator = "<!-- scan log start -->";
144
151 public function __construct($a_log = false)
152 {
153 global $DIC;
154
155 $this->obj_definition = $DIC["objDefinition"];
156 $this->db = $DIC->database();
157 $this->lng = $DIC->language();
158 $this->log = $DIC["ilLog"];
159 $this->rbacadmin = $DIC->rbac()->admin();
160 $this->tree = $DIC->repositoryTree();
161 $this->user = $DIC->user();
162 $objDefinition = $DIC["objDefinition"];
163 $ilDB = $DIC->database();
164
165 $this->db = &$ilDB;
166 $this->rbac_object_types = "'" . implode("','", $objDefinition->getAllRBACObjects()) . "'";
167 $this->rbac_object_types = $objDefinition->getAllRBACObjects();
168
169 if ($a_log === true) {
170 $this->logging = true;
171
172 // should be available thru inc.header.php
173 // TODO: move log functionality to new class ilScanLog
174 include_once "./Services/Logging/classes/class.ilLog.php";
175
176 // Delete old scan log
177 $this->deleteScanLog();
178
179 // create scan log
180 include_once './Services/Logging/classes/class.ilLog.php';
181 $this->scan_log = new ilLog(CLIENT_DATA_DIR, "scanlog.log");
182 $this->scan_log->setLogFormat("");
183 $this->writeScanLogLine($this->scan_log_separator);
184 $this->writeScanLogLine("\n[Systemscan from " . date("y-m-d H:i]"));
185 }
186 }
187
193 public function getPossibleModes()
194 {
195 return array_keys($this->mode);
196 }
197
209 public function setMode($a_mode, $a_value)
210 {
211 if ((!in_array($a_mode, array_keys($this->mode)) and $a_mode != "all") or !is_bool($a_value)) {
212 $this->throwError(INVALID_PARAM, FATAL, DEBUG);
213 return false;
214 }
215
216 if ($a_mode == "all") {
217 foreach ($this->mode as $mode => $value) {
218 $this->mode[$mode] = $a_value;
219 }
220 } else {
221 $this->mode[$a_mode] = $a_value;
222 }
223
224 // consider mode dependencies
225 $this->setModeDependencies();
226
227 return true;
228 }
229
238 public function isModeEnabled($a_mode)
239 {
240 if (!in_array($a_mode, array_keys($this->mode))) {
241 $this->throwError(VALIDATER_UNKNOWN_MODE, WARNING, DEBUG);
242 return false;
243 }
244
245 return $this->mode[$a_mode];
246 }
247
248 public function isLogEnabled()
249 {
250 return $this->logging;
251 }
252
261 public function setModeDependencies()
262 {
263 // DO NOT change the order
264
265 if ($this->mode["restore"] === true) {
266 $this->mode["scan"] = true;
267 $this->mode["purge"] = false;
268 }
269
270 if ($this->mode["purge"] === true) {
271 $this->mode["scan"] = true;
272 $this->mode["restore"] = false;
273 }
274
275 if ($this->mode["restore_trash"] === true) {
276 $this->mode["scan"] = true;
277 $this->mode["purge_trash"] = false;
278 }
279
280 if ($this->mode["purge_trash"] === true) {
281 $this->mode["scan"] = true;
282 $this->mode["restore_trash"] = false;
283 }
284
285 if ($this->mode["clean"] === true) {
286 $this->mode["scan"] = true;
287 }
288 }
289
298 public function validate()
299 {
301
302 // The validation summary.
303 $summary = "";
304
305
306 // STEP 1: Scan
307 // -------------------
308 $summary .= $lng->txt("scanning_system");
309 if (!$this->isModeEnabled("scan")) {
310 $summary .= $lng->txt("disabled");
311 } else {
312 $summary .= "<br/>" . $lng->txt("searching_invalid_refs");
313 if ($this->findInvalidReferences()) {
314 $summary .= count($this->getInvalidReferences()) . " " . $lng->txt("found");
315 } else {
316 $summary .= $lng->txt("found_none");
317 }
318
319 $summary .= "<br/>" . $lng->txt("searching_invalid_childs");
320 if ($this->findInvalidChilds()) {
321 $summary .= count($this->getInvalidChilds()) . " " . $lng->txt("found");
322 } else {
323 $summary .= $lng->txt("found_none");
324 }
325
326 $summary .= "<br/>" . $lng->txt("searching_missing_objs");
327 if ($this->findMissingObjects()) {
328 $summary .= count($this->getMissingObjects()) . " " . $lng->txt("found");
329 } else {
330 $summary .= $lng->txt("found_none");
331 }
332
333 $summary .= "<br/>" . $lng->txt("searching_unbound_objs");
334 if ($this->findUnboundObjects()) {
335 $summary .= count($this->getUnboundObjects()) . " " . $lng->txt("found");
336 } else {
337 $summary .= $lng->txt("found_none");
338 }
339
340 $summary .= "<br/>" . $lng->txt("searching_deleted_objs");
341 if ($this->findDeletedObjects()) {
342 $summary .= count($this->getDeletedObjects()) . " " . $lng->txt("found");
343 } else {
344 $summary .= $lng->txt("found_none");
345 }
346
347 $summary .= "<br/>" . $lng->txt("searching_invalid_rolfs");
348 if ($this->findInvalidRolefolders()) {
349 $summary .= count($this->getInvalidRolefolders()) . " " . $lng->txt("found");
350 } else {
351 $summary .= $lng->txt("found_none");
352 }
353
354 $summary .= "<br/><br/>" . $lng->txt("analyzing_tree_structure");
355 if ($this->checkTreeStructure()) {
356 $summary .= $lng->txt("tree_corrupt");
357 } else {
358 $summary .= $lng->txt("done");
359 }
360 }
361
362 // STEP 2: Dump tree
363 // -------------------
364 $summary .= "<br /><br />" . $lng->txt("dumping_tree");
365 if (!$this->isModeEnabled("dump_tree")) {
366 $summary .= $lng->txt("disabled");
367 } else {
368 $error_count = $this->dumpTree();
369 if ($error_count > 0) {
370 $summary .= $lng->txt("tree_corrupt");
371 } else {
372 $summary .= $lng->txt("done");
373 }
374 }
375
376 // STEP 3: Clean Up
377 // -------------------
378 $summary .= "<br /><br />" . $lng->txt("cleaning");
379 if (!$this->isModeEnabled("clean")) {
380 $summary .= $lng->txt("disabled");
381 } else {
382 $summary .= "<br />" . $lng->txt("removing_invalid_refs");
383 if ($this->removeInvalidReferences()) {
384 $summary .= strtolower($lng->txt("done"));
385 } else {
386 $summary .= $lng->txt("nothing_to_remove") . $lng->txt("skipped");
387 }
388
389 $summary .= "<br />" . $lng->txt("removing_invalid_childs");
390 if ($this->removeInvalidChilds()) {
391 $summary .= strtolower($lng->txt("done"));
392 } else {
393 $summary .= $lng->txt("nothing_to_remove") . $lng->txt("skipped");
394 }
395
396 $summary .= "<br />" . $lng->txt("removing_invalid_rolfs");
397 if ($this->removeInvalidRolefolders()) {
398 $summary .= strtolower($lng->txt("done"));
399 } else {
400 $summary .= $lng->txt("nothing_to_remove") . $lng->txt("skipped");
401 }
402
403 // find unbound objects again AFTER cleaning process!
404 // This updates the array 'unboundobjects' required for the further steps
405 // There might be other objects unbounded now due to removal of object_data/reference entries.
406 $this->findUnboundObjects();
407 }
408
409 // STEP 4: Restore objects
410 $summary .= "<br /><br />" . $lng->txt("restoring");
411
412 if (!$this->isModeEnabled("restore")) {
413 $summary .= $lng->txt("disabled");
414 } else {
415 $summary .= "<br />" . $lng->txt("restoring_missing_objs");
416 if ($this->restoreMissingObjects()) {
417 $summary .= strtolower($lng->txt("done"));
418 } else {
419 $summary .= $lng->txt("nothing_to_restore") . $lng->txt("skipped");
420 }
421
422 $summary .= "<br />" . $lng->txt("restoring_unbound_objs");
423 if ($this->restoreUnboundObjects()) {
424 $summary .= strtolower($lng->txt("done"));
425 } else {
426 $summary .= $lng->txt("nothing_to_restore") . $lng->txt("skipped");
427 }
428 }
429
430 // STEP 5: Restoring Trash
431 $summary .= "<br /><br />" . $lng->txt("restoring_trash");
432
433 if (!$this->isModeEnabled("restore_trash")) {
434 $summary .= $lng->txt("disabled");
435 } else {
436 if ($this->restoreTrash()) {
437 $summary .= strtolower($lng->txt("done"));
438 } else {
439 $summary .= $lng->txt("nothing_to_restore") . $lng->txt("skipped");
440 }
441 }
442
443 // STEP 6: Purging...
444 $summary .= "<br /><br />" . $lng->txt("purging");
445
446 if (!$this->isModeEnabled("purge")) {
447 $summary .= $lng->txt("disabled");
448 } else {
449 $summary .= "<br />" . $lng->txt("purging_missing_objs");
450 if ($this->purgeMissingObjects()) {
451 $summary .= strtolower($lng->txt("done"));
452 } else {
453 $summary .= $lng->txt("nothing_to_purge") . $lng->txt("skipped");
454 }
455
456 $summary .= "<br />" . $lng->txt("purging_unbound_objs");
457 if ($this->purgeUnboundObjects()) {
458 $summary .= strtolower($lng->txt("done"));
459 } else {
460 $summary .= $lng->txt("nothing_to_purge") . $lng->txt("skipped");
461 }
462 }
463
464 // STEP 7: Purging trash...
465 $summary .= "<br /><br />" . $lng->txt("purging_trash");
466
467 if (!$this->isModeEnabled("purge_trash")) {
468 $summary .= $lng->txt("disabled");
469 } else {
470 if ($this->purgeTrash()) {
471 $summary .= strtolower($lng->txt("done"));
472 } else {
473 $summary .= $lng->txt("nothing_to_purge") . $lng->txt("skipped");
474 }
475 }
476
477 // STEP 8: Initialize gaps in tree
478 if ($this->isModeEnabled("clean")) {
479 $summary .= "<br /><br />" . $lng->txt("cleaning_final");
480 if ($this->initGapsInTree()) {
481 $summary .= "<br />" . $lng->txt("initializing_gaps") . " " . strtolower($lng->txt("done"));
482 }
483 }
484
485 // check RBAC starts here
486 // ...
487
488 // le fin
489 foreach ($this->mode as $mode => $value) {
490 $arr[] = $mode . "[" . (int) $value . "]";
491 }
492
493 return $summary;
494 }
495
496
506 public function findMissingObjects()
507 {
509
510 // check mode: analyze
511 if ($this->mode["scan"] !== true) {
512 return false;
513 }
514
515 // init
516 $this->missing_objects = array();
517
518 $this->writeScanLogLine("\nfindMissingObjects:");
519
520 // Repair missing objects.
521 // We only repair file objects which have an entry in table object_reference.
522 // XXX - We should check all references to file objects which don't
523 // have an object_reference. If we can't find any reference to such
524 // a file object, we should repair it too!
525 $q = "SELECT object_data.*, ref_id FROM object_data " .
526 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id " .
527 "LEFT JOIN tree ON object_reference.ref_id = tree.child " .
528 "WHERE tree.child IS NULL " .
529 "AND (object_reference.obj_id IS NOT NULL " .
530 " OR object_data.type <> 'file' AND " .
531 $ilDB->in('object_data.type', $this->rbac_object_types, false, 'text') .
532 ")";
533 $r = $this->db->query($q);
534
535 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
536 #if (!in_array($row->type,$this->object_types_exclude))
537 if (!$this->isExcludedFromRecovery($row->type, $row->obj_id)) {
538 $this->missing_objects[] = array(
539 "obj_id" => $row->obj_id,
540 "type" => $row->type,
541 "ref_id" => $row->ref_id,
542 "child" => $row->child,
543 "title" => $row->title,
544 "desc" => $row->description,
545 "owner" => $row->owner,
546 "create_date" => $row->create_date,
547 "last_update" => $row->last_update
548 );
549 }
550 }
551
552 $this->filterWorkspaceObjects($this->missing_objects);
553 if (count($this->missing_objects) > 0) {
554 $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
555 $this->writeScanLogArray($this->missing_objects);
556 return true;
557 }
558
559 $this->writeScanLogLine("none");
560 return false;
561 }
562
574 public function findInvalidRolefolders()
575 {
577
578 // check mode: analyze
579 if ($this->mode["scan"] !== true) {
580 return false;
581 }
582
583 // init
584 $this->invalid_rolefolders = array();
585
586 $this->writeScanLogLine("\nfindInvalidRolefolders:");
587
588 // find rolfs without reference/tree entry
589 $q = "SELECT object_data.*, ref_id FROM object_data " .
590 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id " .
591 "LEFT JOIN tree ON object_reference.ref_id = tree.child " .
592 "WHERE (object_reference.obj_id IS NULL OR tree.child IS NULL) " .
593 "AND object_data.type='rolf'";
594 $r = $this->db->query($q);
595
596 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
597 $this->invalid_rolefolders[] = array(
598 "obj_id" => $row->obj_id,
599 "type" => $row->type,
600 "ref_id" => $row->ref_id,
601 "child" => $row->child,
602 "title" => $row->title,
603 "desc" => $row->description,
604 "owner" => $row->owner,
605 "create_date" => $row->create_date,
606 "last_update" => $row->last_update
607 );
608 }
609
610 // find rolfs within RECOVERY FOLDER
611 $q = "SELECT object_data.*, ref_id FROM object_data " .
612 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id " .
613 "LEFT JOIN tree ON object_reference.ref_id = tree.child " .
614 "WHERE object_reference.ref_id = " . $ilDB->quote(RECOVERY_FOLDER_ID, 'integer') . " " .
615 "AND object_data.type='rolf'";
616 $r = $this->db->query($q);
617
618 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
619 $this->invalid_rolefolders[] = array(
620 "obj_id" => $row->obj_id,
621 "type" => $row->type,
622 "ref_id" => $row->ref_id,
623 "child" => $row->child,
624 "title" => $row->title,
625 "desc" => $row->description,
626 "owner" => $row->owner,
627 "create_date" => $row->create_date,
628 "last_update" => $row->last_update
629 );
630 }
631
632 $this->filterWorkspaceObjects($this->invalid_rolefolders);
633 if (count($this->invalid_rolefolders) > 0) {
634 $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
635 $this->writeScanLogArray($this->invalid_rolefolders);
636 return true;
637 }
638
639 $this->writeScanLogLine("none");
640 return false;
641 }
642
652 public function findInvalidRBACEntries()
653 {
655
656 // check mode: analyze
657 if ($this->mode["scan"] !== true) {
658 return false;
659 }
660
661 // init
662 $this->invalid_rbac_entries = array();
663
664 $this->writeScanLogLine("\nfindInvalidRBACEntries:");
665
666 $q = "SELECT object_data.*, ref_id FROM object_data " .
667 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id " .
668 "LEFT JOIN tree ON object_reference.ref_id = tree.child " .
669 "WHERE (object_reference.obj_id IS NULL OR tree.child IS NULL) " .
670 "AND object_data.type='rolf'";
671 $r = $this->db->query($q);
672
673 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
674 $this->invalid_rolefolders[] = array(
675 "obj_id" => $row->obj_id,
676 "type" => $row->type,
677 "ref_id" => $row->ref_id,
678 "child" => $row->child,
679 "title" => $row->title,
680 "desc" => $row->description,
681 "owner" => $row->owner,
682 "create_date" => $row->create_date,
683 "last_update" => $row->last_update
684 );
685 }
686
687 // find rolfs within RECOVERY FOLDER
688 $q = "SELECT object_data.*, ref_id FROM object_data " .
689 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id " .
690 "LEFT JOIN tree ON object_reference.ref_id = tree.child " .
691 "WHERE object_reference.ref_id =" . $ilDB->quote(RECOVERY_FOLDER_ID) . " " .
692 "AND object_data.type='rolf'";
693 $r = $this->db->query($q);
694
695 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
696 $this->invalid_rolefolders[] = array(
697 "obj_id" => $row->obj_id,
698 "type" => $row->type,
699 "ref_id" => $row->ref_id,
700 "child" => $row->child,
701 "title" => $row->title,
702 "desc" => $row->description,
703 "owner" => $row->owner,
704 "create_date" => $row->create_date,
705 "last_update" => $row->last_update
706 );
707 }
708
709 $this->filterWorkspaceObjects($this->invalid_rolefolders);
710 if (count($this->invalid_rolefolders) > 0) {
711 $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
712 $this->writeScanLogArray($this->invalid_rolefolders);
713 return true;
714 }
715
716 $this->writeScanLogLine("none");
717 return false;
718 }
719
733 public function getMissingObjects()
734 {
736 }
737
747 public function findInvalidReferences()
748 {
750
751 // check mode: analyze
752 if ($this->mode["scan"] !== true) {
753 return false;
754 }
755
756 // init
757 $this->invalid_references = array();
758
759 $this->writeScanLogLine("\nfindInvalidReferences:");
760 $q = "SELECT object_reference.* FROM object_reference " .
761 "LEFT JOIN object_data ON object_data.obj_id = object_reference.obj_id " .
762 "WHERE object_data.obj_id IS NULL " .
763 "OR " . $ilDB->in('object_data.type', $this->rbac_object_types, true, 'text');
764 $r = $this->db->query($q);
765
766 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
767 $this->invalid_references[] = array(
768 "ref_id" => $row->ref_id,
769 "obj_id" => $row->obj_id,
770 "msg" => "Object does not exist."
771 );
772 }
773
774 $this->filterWorkspaceObjects($this->invalid_references);
775 if (count($this->invalid_references) > 0) {
776 $this->writeScanLogLine("ref_id\t\tobj_id");
777 $this->writeScanLogArray($this->invalid_references);
778 return true;
779 }
780
781 $this->writeScanLogLine("none");
782 return false;
783 }
784
793 public function getInvalidReferences()
794 {
796 }
797
807 public function findInvalidChilds()
808 {
810
811 // check mode: analyze
812 if ($this->mode["scan"] !== true) {
813 return false;
814 }
815
816 // init
817 $this->invalid_childs = array();
818
819 $this->writeScanLogLine("\nfindInvalidChilds:");
820
821 $q = "SELECT tree.*,object_reference.ref_id FROM tree " .
822 "LEFT JOIN object_reference ON tree.child = object_reference.ref_id " .
823 "LEFT JOIN object_data ON object_reference.obj_id = object_data.obj_id " .
824 "WHERE object_reference.ref_id IS NULL or object_data.obj_id IS NULL";
825 $r = $this->db->query($q);
826 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
827 $this->invalid_childs[] = array(
828 "child" => $row->child,
829 "ref_id" => $row->ref_id,
830 "msg" => "No object found"
831 );
832 }
833
834 if (count($this->invalid_childs) > 0) {
835 $this->writeScanLogLine("child\t\tref_id");
836 $this->writeScanLogArray($this->invalid_childs);
837 return true;
838 }
839
840 $this->writeScanLogLine("none");
841 return false;
842 }
843
852 public function getInvalidChilds()
853 {
855 }
856
868 public function findUnboundObjects()
869 {
870 // check mode: analyze
871 if ($this->mode["scan"] !== true) {
872 return false;
873 }
874
875 // init
876 $this->unbound_objects = array();
877
878 $this->writeScanLogLine("\nfindUnboundObjects:");
879
880 $q = "SELECT T1.tree,T1.child,T1.parent," .
881 "T2.tree deleted,T2.parent grandparent " .
882 "FROM tree T1 " .
883 "LEFT JOIN tree T2 ON T2.child=T1.parent " .
884 "WHERE (T2.tree!=1 OR T2.tree IS NULL) AND T1.parent!=0";
885 $r = $this->db->query($q);
886
887 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
888 // exclude deleted nodes
889 if ($row->deleted === null) {
890 $this->unbound_objects[] = array(
891 "child" => $row->child,
892 "parent" => $row->parent,
893 "tree" => $row->tree,
894 "msg" => "No valid parent node found"
895 );
896 }
897 }
898
899 if (count($this->unbound_objects) > 0) {
900 $this->writeScanLogLine("child\t\tparent\ttree");
901 $this->writeScanLogArray($this->unbound_objects);
902 return true;
903 }
904
905 $this->writeScanLogLine("none");
906 return false;
907 }
908
920 public function findDeletedObjects()
921 {
922 // check mode: analyze
923 if ($this->mode["scan"] !== true) {
924 return false;
925 }
926
927 // init
928 $this->deleted_objects = array();
929
930 $this->writeScanLogLine("\nfindDeletedObjects:");
931
932 // Delete objects, start with the oldest objects first
933 $query = "SELECT object_data.*,tree.tree,tree.child,tree.parent,deleted " .
934 "FROM object_data " .
935 "LEFT JOIN object_reference ON object_data.obj_id=object_reference.obj_id " .
936 "LEFT JOIN tree ON tree.child=object_reference.ref_id " .
937 " WHERE tree != 1 " .
938 " ORDER BY deleted";
939 $r = $this->db->query($query);
940
941 include_once './Services/Calendar/classes/class.ilDateTime.php';
942 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
943 $tmp_date = new ilDateTime($row->deleted, IL_CAL_DATETIME);
944
945 $this->deleted_objects[] = array(
946 "child" => $row->child,
947 "parent" => $row->parent,
948 "tree" => $row->tree,
949 "type" => $row->type,
950 "title" => $row->title,
951 "desc" => $row->description,
952 "owner" => $row->owner,
953 "deleted" => $row->deleted,
954 "deleted_timestamp" => $tmp_date->get(IL_CAL_UNIX),
955 "create_date" => $row->create_date,
956 "last_update" => $row->last_update
957 );
958 }
959
960 if (count($this->deleted_objects) > 0) {
961 $this->writeScanLogArray(array(array_keys($this->deleted_objects[0])));
962 $this->writeScanLogArray($this->deleted_objects);
963 return true;
964 }
965 $this->writeScanLogLine("none");
966 return false;
967 }
968
969
983 public function getUnboundObjects()
984 {
986 }
987
994 public function getDeletedObjects()
995 {
997 }
998
1007 public function getInvalidRolefolders()
1008 {
1010 }
1011
1021 public function removeInvalidReferences($a_invalid_refs = null)
1022 {
1024 $ilDB = $this->db;
1025
1026 // check mode: clean
1027 if ($this->mode["clean"] !== true) {
1028 return false;
1029 }
1030
1031 $this->writeScanLogLine("\nremoveInvalidReferences:");
1032
1033 if ($a_invalid_refs === null and isset($this->invalid_references)) {
1034 $a_invalid_refs = &$this->invalid_references;
1035 }
1036
1037 // handle wrong input
1038 if (!is_array($a_invalid_refs)) {
1039 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1040 return false;
1041 }
1042 // no unbound references found. do nothing
1043 if (count($a_invalid_refs) == 0) {
1044 $this->writeScanLogLine("none");
1045 return false;
1046 }
1047
1048 /*******************
1049 removal starts here
1050 ********************/
1051
1052 $message = sprintf(
1053 '%s::removeInvalidReferences(): Started...',
1054 get_class($this)
1055 );
1056 $ilLog->write($message, $ilLog->WARNING);
1057
1058 // to make sure
1059 $this->filterWorkspaceObjects($a_invalid_refs);
1060
1061 foreach ($a_invalid_refs as $entry) {
1062 $query = "DELETE FROM object_reference WHERE ref_id= " . $this->db->quote($entry["ref_id"], 'integer') .
1063 " AND obj_id = " . $this->db->quote($entry["obj_id"], 'integer') . " ";
1064 $res = $ilDB->manipulate($query);
1065
1066 $message = sprintf(
1067 '%s::removeInvalidReferences(): Reference %s removed',
1068 get_class($this),
1069 $entry["ref_id"]
1070 );
1071 $ilLog->write($message, $ilLog->WARNING);
1072
1073 $this->writeScanLogLine("Entry " . $entry["ref_id"] . " removed");
1074 }
1075
1076 return true;
1077 }
1078
1088 public function removeInvalidChilds($a_invalid_childs = null)
1089 {
1091
1092 // check mode: clean
1093 if ($this->mode["clean"] !== true) {
1094 return false;
1095 }
1096
1097 $this->writeScanLogLine("\nremoveInvalidChilds:");
1098
1099 if ($a_invalid_childs === null and isset($this->invalid_childs)) {
1100 $a_invalid_childs = &$this->invalid_childs;
1101 }
1102
1103 // handle wrong input
1104 if (!is_array($a_invalid_childs)) {
1105 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1106 return false;
1107 }
1108
1109 // no unbound childs found. do nothing
1110 if (count($a_invalid_childs) == 0) {
1111 $this->writeScanLogLine("none");
1112 return false;
1113 }
1114
1115 /*******************
1116 removal starts here
1117 ********************/
1118
1119 $message = sprintf(
1120 '%s::removeInvalidChilds(): Started...',
1121 get_class($this)
1122 );
1123 $ilLog->write($message, $ilLog->WARNING);
1124
1125 foreach ($a_invalid_childs as $entry) {
1126 $q = "DELETE FROM tree WHERE child='" . $entry["child"] . "'";
1127 $this->db->query($q);
1128
1129 $message = sprintf(
1130 '%s::removeInvalidChilds(): Entry child=%s removed',
1131 get_class($this),
1132 $entry["child"]
1133 );
1134 $ilLog->write($message, $ilLog->WARNING);
1135
1136 $this->writeScanLogLine("Entry " . $entry["child"] . " removed");
1137 }
1138
1139 return true;
1140 }
1141
1152 public function removeInvalidRolefolders($a_invalid_rolefolders = null)
1153 {
1155
1156 // check mode: clean
1157 if ($this->mode["clean"] !== true) {
1158 return false;
1159 }
1160
1161 $this->writeScanLogLine("\nremoveInvalidRolefolders:");
1162
1163 if ($a_invalid_rolefolders === null and isset($this->invalid_rolefolders)) {
1164 $a_invalid_rolefolders = $this->invalid_rolefolders;
1165 }
1166
1167 // handle wrong input
1168 if (!is_array($a_invalid_rolefolders)) {
1169 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1170 return false;
1171 }
1172
1173 // no invalid rolefolders found. do nothing
1174 if (count($a_invalid_rolefolders) == 0) {
1175 $this->writeScanLogLine("none");
1176 return false;
1177 }
1178
1179 /*******************
1180 removal starts here
1181 ********************/
1182
1183 $removed = false;
1184
1185 $message = sprintf(
1186 '%s::removeInvalidRolefolders(): Started...',
1187 get_class($this)
1188 );
1189 $ilLog->write($message, $ilLog->WARNING);
1190
1191 // to make sure
1192 $this->filterWorkspaceObjects($a_invalid_rolefolders);
1193
1194 foreach ($a_invalid_rolefolders as $rolf) {
1195 // restore ref_id in case of missing
1196 if ($rolf["ref_id"] === null) {
1197 $rolf["ref_id"] = $this->restoreReference($rolf["obj_id"]);
1198
1199 $this->writeScanLogLine("Created missing reference '" . $rolf["ref_id"] . "' for rolefolder object '" . $rolf["obj_id"] . "'");
1200 }
1201
1202 // now delete rolefolder
1203 $obj_data = ilObjectFactory::getInstanceByRefId($rolf["ref_id"]);
1204 $obj_data->delete();
1205 unset($obj_data);
1206 $removed = true;
1207 $this->writeScanLogLine("Removed invalid rolefolder '" . $rolf["title"] . "' (id=" . $rolf["obj_id"] . ",ref=" . $rolf["ref_id"] . ") from system");
1208 }
1209
1210 return $removed;
1211 }
1212
1223 public function restoreMissingObjects($a_missing_objects = null)
1224 {
1227
1228 // check mode: restore
1229 if ($this->mode["restore"] !== true) {
1230 return false;
1231 }
1232
1233 $this->writeScanLogLine("\nrestoreMissingObjects:");
1234
1235 if ($a_missing_objects === null and isset($this->missing_objects)) {
1236 $a_missing_objects = $this->missing_objects;
1237 }
1238
1239 // handle wrong input
1240 if (!is_array($a_missing_objects)) {
1241 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1242 return false;
1243 }
1244
1245 // no missing objects found. do nothing
1246 if (count($a_missing_objects) == 0) {
1247 $this->writeScanLogLine("none");
1248 return false;
1249 }
1250
1251 /*******************
1252 restore starts here
1253 ********************/
1254
1255 $restored = false;
1256
1257 $message = sprintf(
1258 '%s::restoreMissingObjects(): Started...',
1259 get_class($this)
1260 );
1261 $ilLog->write($message, $ilLog->WARNING);
1262
1263 // to make sure
1264 $this->filterWorkspaceObjects($a_missing_objects);
1265
1266 foreach ($a_missing_objects as $missing_obj) {
1267 // restore ref_id in case of missing
1268 if ($missing_obj["ref_id"] === null) {
1269 $missing_obj["ref_id"] = $this->restoreReference($missing_obj["obj_id"]);
1270
1271 $this->writeScanLogLine("Created missing reference '" . $missing_obj["ref_id"] . "' for object '" . $missing_obj["obj_id"] . "'");
1272 }
1273
1274 // put in tree under RecoveryFolder if not on exclude list
1275 #if (!in_array($missing_obj["type"],$this->object_types_exclude))
1276 if (!$this->isExcludedFromRecovery($missing_obj['type'], $missing_obj['obj_id'])) {
1277 $rbacadmin->revokePermission($missing_obj["ref_id"]);
1278 $obj_data = ilObjectFactory::getInstanceByRefId($missing_obj["ref_id"]);
1279 $obj_data->putInTree(RECOVERY_FOLDER_ID);
1280 $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1281 unset($obj_data);
1282 //$tree->insertNode($missing_obj["ref_id"],RECOVERY_FOLDER_ID);
1283 $restored = true;
1284 $this->writeScanLogLine("Restored object '" . $missing_obj["title"] . "' (id=" . $missing_obj["obj_id"] . ",ref=" . $missing_obj["ref_id"] . ") in 'Restored objects folder'");
1285 }
1286
1287 // TODO: process rolefolders
1288 }
1289
1290 return $restored;
1291 }
1292
1302 public function restoreReference($a_obj_id)
1303 {
1305 $ilDB = $this->db;
1306
1307 if (empty($a_obj_id)) {
1308 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1309 return false;
1310 }
1311
1312 $query = "INSERT INTO object_reference (ref_id,obj_id) " .
1313 "VALUES (" . $next_id = $ilDB->nextId('object_reference') . "," . $this->db->quote($a_obj_id, 'integer') . " )";
1314 $res = $ilDB->manipulate($query);
1315
1316 $message = sprintf(
1317 '%s::restoreReference(): new reference %s for obj_id %s created',
1318 get_class($this),
1319 $next_id,
1320 $a_obj_id
1321 );
1322 $ilLog->write($message, $ilLog->WARNING);
1323
1324 return $next_id;
1325 }
1326
1337 public function restoreUnboundObjects($a_unbound_objects = null)
1338 {
1340
1341 // check mode: restore
1342 if ($this->mode["restore"] !== true) {
1343 return false;
1344 }
1345
1346 $this->writeScanLogLine("\nrestoreUnboundObjects:");
1347
1348 if ($a_unbound_objects === null and isset($this->unbound_objects)) {
1349 $a_unbound_objects = $this->unbound_objects;
1350 }
1351
1352 // handle wrong input
1353 if (!is_array($a_unbound_objects)) {
1354 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1355 return false;
1356 }
1357
1358 $message = sprintf(
1359 '%s::restoreUnboundObjects(): Started...',
1360 get_class($this)
1361 );
1362 $ilLog->write($message, $ilLog->WARNING);
1363
1364 // start restore process
1365 return $this->restoreSubTrees($a_unbound_objects);
1366 }
1367
1377 public function restoreTrash($a_deleted_objects = null)
1378 {
1380
1381 // check mode: restore
1382 if ($this->mode["restore_trash"] !== true) {
1383 return false;
1384 }
1385
1386 $this->writeScanLogLine("\nrestoreTrash:");
1387
1388 if ($a_deleted_objects === null and isset($this->deleted_objects)) {
1389 $a_deleted_objects = $this->deleted_objects;
1390 }
1391
1392 // handle wrong input
1393 if (!is_array($a_deleted_objects)) {
1394 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1395 return false;
1396 }
1397
1398 $message = sprintf(
1399 '%s::restoreTrash(): Started...',
1400 get_class($this)
1401 );
1402 $ilLog->write($message, $ilLog->WARNING);
1403
1404 // start restore process
1405 $restored = $this->restoreDeletedObjects($a_deleted_objects);
1406
1407 if ($restored) {
1408 $q = "DELETE FROM tree WHERE tree!=1";
1409 $this->db->query($q);
1410
1411 $message = sprintf(
1412 '%s::restoreTrash(): Removed all trees with tree id <> 1',
1413 get_class($this)
1414 );
1415 $ilLog->write($message, $ilLog->WARNING);
1416
1417 $this->writeScanLogLine("Old tree entries removed");
1418 }
1419
1420 return $restored;
1421 }
1422
1431 public function restoreDeletedObjects($a_nodes)
1432 {
1436 //vd($a_nodes);exit;
1437 // handle wrong input
1438 if (!is_array($a_nodes)) {
1439 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1440 return false;
1441 }
1442
1443 // no invalid parents found. do nothing
1444 if (count($a_nodes) == 0) {
1445 $this->writeScanLogLine("none");
1446 return false;
1447 }
1448
1449 $message = sprintf(
1450 '%s::restoreDeletedObjects()): Started...',
1451 get_class($this)
1452 );
1453 $ilLog->write($message, $ilLog->WARNING);
1454
1455 // first delete all rolefolders
1456 // don't save rolefolders, remove them
1457 // TODO process ROLE_FOLDER_ID
1458 foreach ($a_nodes as $key => $node) {
1459 if ($node["type"] == "rolf") {
1460 // delete old tree entries
1461 $tree->deleteTree($node);
1462
1463 $obj_data = ilObjectFactory::getInstanceByRefId($node["child"]);
1464 $obj_data->delete();
1465 unset($a_nodes[$key]);
1466 }
1467 }
1468
1469 // process move
1470 foreach ($a_nodes as $node) {
1471 // delete old tree entries
1472 $tree->deleteTree($node);
1473
1474 $rbacadmin->revokePermission($node["child"]);
1475 $obj_data = ilObjectFactory::getInstanceByRefId($node["child"]);
1476 $obj_data->putInTree(RECOVERY_FOLDER_ID);
1477 $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1478 }
1479
1480 return true;
1481 }
1482
1491 public function restoreSubTrees($a_nodes)
1492 {
1496
1497 // handle wrong input
1498 if (!is_array($a_nodes)) {
1499 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1500 return false;
1501 }
1502
1503 // no invalid parents found. do nothing
1504 if (count($a_nodes) == 0) {
1505 $this->writeScanLogLine("none");
1506 return false;
1507 }
1508
1509 /*******************
1510 restore starts here
1511 ********************/
1512
1513 $subnodes = array();
1514 $topnode = array();
1515
1516 $message = sprintf(
1517 '%s::restoreSubTrees(): Started...',
1518 get_class($this)
1519 );
1520 $ilLog->write($message, $ilLog->WARNING);
1521
1522 // process move subtree
1523 foreach ($a_nodes as $node) {
1524 // get node data
1525 $topnode = $tree->getNodeData($node["child"], $node['tree']);
1526
1527 // don't save rolefolders, remove them
1528 // TODO process ROLE_FOLDER_ID
1529 if ($topnode["type"] == "rolf") {
1530 $rolfObj = ilObjectFactory::getInstanceByRefId($topnode["child"]);
1531 $rolfObj->delete();
1532 unset($top_node);
1533 unset($rolfObj);
1534 continue;
1535 }
1536
1537 // get subnodes of top nodes
1538 $subnodes[$node["child"]] = $tree->getSubtree($topnode);
1539
1540 // delete old tree entries
1541 $tree->deleteTree($topnode);
1542 }
1543
1544 // now move all subtrees to new location
1545 // TODO: this whole put in place again stuff needs revision. Permission settings get lost.
1546 foreach ($subnodes as $key => $subnode) {
1547
1548 // first paste top_node ...
1549 $rbacadmin->revokePermission($key);
1551 $obj_data->putInTree(RECOVERY_FOLDER_ID);
1552 $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1553
1554 $this->writeScanLogLine("Object '" . $obj_data->getId() . "' restored.");
1555
1556 // ... remove top_node from list ...
1557 array_shift($subnode);
1558
1559 // ... insert subtree of top_node if any subnodes exist
1560 if (count($subnode) > 0) {
1561 foreach ($subnode as $node) {
1562 $rbacadmin->revokePermission($node["child"]);
1563 $obj_data = ilObjectFactory::getInstanceByRefId($node["child"]);
1564 $obj_data->putInTree($node["parent"]);
1565 $obj_data->setPermissions($node["parent"]);
1566
1567 $this->writeScanLogLine("Object '" . $obj_data->getId() . "' restored.");
1568 }
1569 }
1570 }
1571
1572 // final clean up
1573 $this->findInvalidChilds();
1574 $this->removeInvalidChilds();
1575
1576 return true;
1577 }
1578
1588 public function purgeTrash($a_nodes = null)
1589 {
1591
1592 // check mode: purge_trash
1593 if ($this->mode["purge_trash"] !== true) {
1594 return false;
1595 }
1596
1597 $this->writeScanLogLine("\npurgeTrash:");
1598
1599 if ($a_nodes === null and isset($this->deleted_objects)) {
1600 $a_nodes = $this->deleted_objects;
1601 }
1602 $message = sprintf(
1603 '%s::purgeTrash(): Started...',
1604 get_class($this)
1605 );
1606 $ilLog->write($message, $ilLog->WARNING);
1607
1608 // start purge process
1609 return $this->purgeObjects($a_nodes);
1610 }
1611
1621 public function purgeUnboundObjects($a_nodes = null)
1622 {
1624
1625 // check mode: purge
1626 if ($this->mode["purge"] !== true) {
1627 return false;
1628 }
1629
1630 $this->writeScanLogLine("\npurgeUnboundObjects:");
1631
1632 if ($a_nodes === null and isset($this->unbound_objects)) {
1633 $a_nodes = $this->unbound_objects;
1634 }
1635
1636 $message = sprintf(
1637 '%s::purgeUnboundObjects(): Started...',
1638 get_class($this)
1639 );
1640 $ilLog->write($message, $ilLog->WARNING);
1641
1642 // start purge process
1643 return $this->purgeObjects($a_nodes);
1644 }
1645
1655 public function purgeMissingObjects($a_nodes = null)
1656 {
1658
1659 // check mode: purge
1660 if ($this->mode["purge"] !== true) {
1661 return false;
1662 }
1663
1664 $this->writeScanLogLine("\npurgeMissingObjects:");
1665
1666 if ($a_nodes === null and isset($this->missing_objects)) {
1667 $a_nodes = $this->missing_objects;
1668 }
1669
1670 $message = sprintf(
1671 '%s::purgeMissingObjects(): Started...',
1672 get_class($this)
1673 );
1674 $ilLog->write($message, $ilLog->WARNING);
1675
1676 // start purge process
1677 return $this->purgeObjects($a_nodes);
1678 }
1679
1687 public function purgeObjects($a_nodes)
1688 {
1691
1692 // Get purge limits
1693 $count_limit = $ilUser->getPref("systemcheck_count_limit");
1694 if (!is_numeric($count_limit) || $count_limit < 0) {
1695 $count_limit = count($a_nodes);
1696 }
1697 $timestamp_limit = time();
1698 $age_limit = $ilUser->getPref("systemcheck_age_limit");
1699 if (is_numeric($age_limit) && $age_limit > 0) {
1700 $timestamp_limit -= $age_limit * 60 * 60 * 24;
1701 }
1702 $type_limit = $ilUser->getPref("systemcheck_type_limit");
1703 if ($type_limit) {
1704 $type_limit = trim($type_limit);
1705 if (strlen($type_limit) == 0) {
1706 $type_limit = null;
1707 }
1708 }
1709
1710 // handle wrong input
1711 if (!is_array($a_nodes)) {
1712 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1713 return false;
1714 }
1715
1716 // start delete process
1717 $this->writeScanLogLine("action\tref_id\tobj_id\ttype\telapsed\ttitle");
1718 $count = 0;
1719 foreach ($a_nodes as $node) {
1720 if ($type_limit && $node['type'] != $type_limit) {
1721 $this->writeScanLogLine(
1722 "skip\t" .
1723 $node['child'] . "\t\t" . $node['type'] . "\t\t" . $node['title']
1724 );
1725 continue;
1726 }
1727
1728
1729 $count++;
1730 if ($count > $count_limit) {
1731 $this->writeScanLogLine("Stopped purging after " . ($count - 1) . " objects, because count limit was reached: " . $count_limit);
1732 break;
1733 }
1734 if ($node["deleted_timestamp"] > $timestamp_limit) {
1735 $this->writeScanLogLine("Stopped purging after " . ($count - 1) . " objects, because timestamp limit was reached: " . date("c", $timestamp_limit));
1736 continue;
1737 }
1738
1739 $ref_id = ($node["child"]) ? $node["child"] : $node["ref_id"];
1740 $node_obj = ilObjectFactory::getInstanceByRefId($ref_id, false);
1741
1742 if ($node_obj === false) {
1743 $this->invalid_objects[] = $node;
1744 continue;
1745 }
1746
1747 $message = sprintf(
1748 '%s::purgeObjects(): Removing object (id:%s ref:%s)',
1749 get_class($this),
1750 $ref_id,
1751 $node_obj->getId()
1752 );
1753 $ilLog->write($message, $ilLog->WARNING);
1754
1755 $startTime = microtime(true);
1756 $node_obj->delete();
1757 ilTree::_removeEntry($node["tree"], $ref_id);
1758 $endTime = microtime(true);
1759
1760 $this->writeScanLogLine("purged\t" . $ref_id . "\t" . $node_obj->getId() .
1761 "\t" . $node['type'] . "\t" . round($endTime - $startTime, 1) . "\t" . $node['title']);
1762 }
1763
1764 $this->findInvalidChilds();
1765 $this->removeInvalidChilds();
1766
1767 return true;
1768 }
1769
1783 public function initGapsInTree()
1784 {
1787
1788 $message = sprintf(
1789 '%s::initGapsInTree(): Started...',
1790 get_class($this)
1791 );
1792 $ilLog->write($message, $ilLog->WARNING);
1793
1794 // check mode: clean
1795 if ($this->mode["clean"] !== true) {
1796 return false;
1797 }
1798 $this->writeScanLogLine("\nrenumberTree:");
1799
1800 $tree->renumber(ROOT_FOLDER_ID);
1801
1802 $this->writeScanLogLine("done");
1803
1804 return true;
1805 }
1806
1816 public function handleErr($error)
1817 {
1818 $call_loc = $error->backtrace[count($error->backtrace) - 1];
1819 $num_args = count($call_loc["args"]);
1820
1821 if ($num_args > 0) {
1822 foreach ($call_loc["args"] as $arg) {
1823 $type = gettype($arg);
1824
1825 switch ($type) {
1826 case "string":
1827 $value = strlen($arg);
1828 break;
1829
1830 case "array":
1831 $value = count($arg);
1832 break;
1833
1834 case "object":
1835 $value = get_class($arg);
1836 break;
1837
1838 case "boolean":
1839 $value = ($arg) ? "true" : "false";
1840 break;
1841
1842 default:
1843 $value = $arg;
1844 break;
1845 }
1846
1847 $arg_list[] = array(
1848 "type" => $type,
1849 "value" => "(" . $value . ")"
1850 );
1851 }
1852
1853 foreach ($arg_list as $arg) {
1854 $arg_str .= implode("", $arg) . " ";
1855 }
1856 }
1857
1858 $err_msg = "<br/><b>" . $error->getCode() . ":</b> " . $error->getMessage() . " in " . $call_loc["class"] . $call_loc["type"] . $call_loc["function"] . "()" .
1859 "<br/>Called from: " . basename($call_loc["file"]) . " , line " . $call_loc["line"] .
1860 "<br/>Passed parameters: [" . $num_args . "] " . $arg_str . "<br/>";
1861 printf($err_msg);
1862
1863 if ($error->getUserInfo()) {
1864 printf("<br/>Parameter details:");
1865 echo "<pre>";
1866 var_dump($call_loc["args"]);
1867 echo "</pre>";
1868 }
1869
1870 if ($error->getCode() == FATAL) {
1871 exit();
1872 }
1873 }
1874
1875 public function writeScanLogArray($a_arr)
1876 {
1877 if (!$this->isLogEnabled()) {
1878 return false;
1879 }
1880
1881 foreach ($a_arr as $entry) {
1882 $this->scan_log->write(implode("\t", $entry));
1883 }
1884 }
1885
1886 public function writeScanLogLine($a_msg)
1887 {
1888 if (!$this->isLogEnabled()) {
1889 return false;
1890 }
1891
1892 $this->scan_log->write($a_msg);
1893 }
1894
1898 public function hasScanLog()
1899 {
1900 // file check
1901 return is_file(CLIENT_DATA_DIR . "/" . $this->scan_log_file);
1902 }
1903
1907 public function deleteScanLog()
1908 {
1909 @unlink(CLIENT_DATA_DIR . "/" . $this->scan_log_file);
1910 }
1911
1912 public function readScanLog()
1913 {
1914 // file check
1915 if (!$this->hasScanLog()) {
1916 return false;
1917 }
1918
1919 $scanfile = &file(CLIENT_DATA_DIR . "/" . $this->scan_log_file);
1920 if (!$scan_log = &$this->get_last_scan($scanfile)) {
1921 return false;
1922 }
1923 // Ensure that memory is freed
1924 unset($scanfile);
1925
1926 return $scan_log;
1927 }
1928
1929 public function get_last_scan($a_scan_log)
1930 {
1931 $logs = array_keys($a_scan_log, $this->scan_log_separator . "\n");
1932
1933 if (count($logs) > 0) {
1934 return array_slice($a_scan_log, array_pop($logs) + 2);
1935 }
1936
1937 return false;
1938 }
1939
1940 public function checkTreeStructure($a_startnode = null)
1941 {
1943
1944 $this->writeScanLogLine("\nchecking tree structure is disabled");
1945
1946 return false;
1947 }
1948
1955 public function dumpTree()
1956 {
1957 $ilDB = $this->db;
1958
1959 $this->writeScanLogLine("BEGIN dumpTree:");
1960
1961 // collect nodes with duplicate child Id's
1962 // (We use this, to mark these nodes later in the output as being
1963 // erroneous.).
1964 $q = 'SELECT child FROM tree GROUP BY child HAVING COUNT(*) > 1';
1965 $r = $this->db->query($q);
1966 $duplicateNodes = array();
1967 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
1968 $duplicateNodes[] = $row->child;
1969 }
1970
1971 // dump tree
1972 $q = "SELECT tree.*,ref.ref_id,dat.obj_id objobj_id,ref.obj_id refobj_id,ref.deleted,dat.* "
1973 . "FROM tree "
1974 . "RIGHT JOIN object_reference ref ON tree.child = ref.ref_id "
1975 . "RIGHT JOIN object_data dat ON ref.obj_id = dat.obj_id "
1976// ."LEFT JOIN usr_data usr ON usr.usr_id = dat.owner "
1977 . "ORDER BY tree, lft, type, dat.title";
1978 $r = $this->db->query($q);
1979
1980 $this->writeScanLogLine(
1981 '<table><tr>'
1982 . '<td>tree, child, parent, lft, rgt, depth</td>'
1983 . '<td>ref_id, ref.obj_id, deleted</td>'
1984 . '<td>obj_id, type, owner, title</td>'
1985 . '</tr>'
1986 );
1987
1988 // We use a stack to represent the path to the current node.
1989 // This allows us to do analyze the tree structure without having
1990 // to implement a recursive algorithm.
1991 $stack = array();
1992 $error_count = 0;
1993 $repository_tree_count = 0;
1994 $trash_trees_count = 0;
1995 $other_trees_count = 0;
1996 $not_in_tree_count = 0;
1997
1998 // The previous number is used for gap checking
1999 $previousNumber = 0;
2000
2001 $this->initWorkspaceObjects();
2002
2003 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
2004 // workspace objects are not to be processed
2005 if ($this->workspace_object_ids &&
2006 in_array($row->objobj_id, $this->workspace_object_ids)) {
2007 continue;
2008 }
2009
2010 // If there is no entry in table tree for the object, we display it here
2011 if (is_null($row->child)) {
2012 switch ($row->type) {
2013 case 'crsg':
2014 case 'usr':
2015 case 'typ':
2016 case 'lng':
2017 case 'rolt':
2018 case 'role':
2019 case 'mob':
2020 case 'sty':
2021 case 'tax': // #13798
2022 // We are not interested in dumping these object types.
2023 continue 2;
2024 //break; NOT REACHED
2025 case 'file':
2026 if (is_null($row->ref_id)) {
2027 // File objects can be part of a learning module.
2028 // In this case, they do not have a row in table object_reference.
2029 // We are not interested in dumping these file objects.
2030 continue 2;
2031 } else {
2032 // File objects which have a row in table object_reference, but
2033 // none in table tree are an error.
2034 $error_count++;
2035 $isRowOkay = false;
2036 $isParentOkay = false;
2037 $isLftOkay = false;
2038 $isRgtOkay = false;
2039 $isDepthOkay = false;
2040 }
2041 break;
2042
2043
2044 case 'fold':
2045 // ignore folders on media pools
2046 if ($this->isMediaFolder($row->obj_id)) {
2047 continue 2;
2048 }
2049 // no break
2050 default:
2051 $error_count++;
2052 $isRowOkay = false;
2053 $isParentOkay = false;
2054 $isLftOkay = false;
2055 $isRgtOkay = false;
2056 $isDepthOkay = false;
2057 break;
2058 }
2059
2060 // moved here (below continues in switch)
2061 $not_in_tree_count++;
2062
2063 $this->writeScanLogLine(
2064 '<tr>'
2065 . '<td>'
2066 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2067 . $row->tree . ', '
2068 . $row->child . ', '
2069 . (($isParentOkay) ? '' : 'parent:<b>')
2070 . $row->parent
2071 . (($isParentOkay) ? '' : '</b>')
2072 . ', '
2073 . (($isLftOkay) ? '' : 'lft:<b>')
2074 . $row->lft
2075 . (($isLftOkay) ? '' : '</b>')
2076 . ', '
2077 . (($isRgtOkay) ? '' : 'rgt:<b>')
2078 . $row->rgt
2079 . (($isRgtOkay) ? '' : '</b>')
2080 . ', '
2081 . (($isDepthOkay) ? '' : 'depth:<b>')
2082 . $row->depth
2083 . (($isDepthOkay) ? '' : '</b>')
2084 . (($isRowOkay) ? '' : '</font>')
2085 . '</td><td>'
2086 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2087 . (($isRefRefOkay && $isChildOkay) ? '' : 'ref.ref_id:<b>')
2088 . $row->ref_id
2089 . (($isRefRefOkay && $isChildOkay) ? '' : '</b>')
2090 . ', '
2091 . (($isRefObjOkay) ? '' : 'ref.obj_id:<b>')
2092 . $row->refobj_id
2093 . (($isRefObjOkay) ? '' : '</b>')
2094 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2095 . (($row->deleted != null) ? ', ' . $row->deleted : '')
2096 . '</td><td>'
2097 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2098 . $indent
2099 . $row->obj_id . ', '
2100 . $row->type . ', '
2101 . $row->login . ', '
2102 . $row->title
2103 . (($isRowOkay) ? '' : ' <b>*ERROR*</b><font color=#ff0000>')
2104 . '</td>'
2105 . '</tr>'
2106 );
2107 continue;
2108 }
2109
2110 // Update stack
2111 // -------------------
2112 $indent = "";
2113 for ($i = 1; $i < $row->depth; $i++) {
2114 $indent .= ". ";
2115 }
2116
2117 // Initialize the stack and the previous number if we are in a new tree
2118 if (count($stack) == 0 || $stack[0]->tree != $row->tree) {
2119 $stack = array();
2120 $previousNumber = $row->lft - 1;
2121 $this->writeScanLogLine('<tr><td>&nbsp;</td></tr>');
2122 }
2123 // Pop old stack entries
2124
2125
2126 while (count($stack) > 0 && $stack[count($stack) - 1]->rgt < $row->lft) {
2127 $popped = array_pop($stack);
2128
2129 // check for gap
2130 $gap = $popped->rgt - $previousNumber - 1;
2131 if ($gap > 0) {
2132 $poppedIndent = "";
2133 for ($i = 1; $i < $popped->depth; $i++) {
2134 $poppedIndent .= ". ";
2135 }
2136 $this->writeScanLogLine(
2137 '<tr>'
2138 . '<td colspan=2><div align="right">'
2139 . '<font color=#00cc00>*gap* for ' . ($gap / 2) . ' nodes at end of&nbsp;</font>'
2140 . '</div></td>'
2141 . '<td>'
2142 . '<font color=#00cc00>'
2143 . $poppedIndent
2144 . $popped->obj_id . ', '
2145 . $popped->type . ', '
2146 . $popped->login . ', '
2147 . $popped->title
2148 . '</font>'
2149 . '</td>'
2150 . '</tr>'
2151 );
2152 }
2153 $previousNumber = $popped->rgt;
2154 unset($popped);
2155 }
2156
2157 // Check row integrity
2158 // -------------------
2159 $isRowOkay = true;
2160
2161 // Check tree structure
2162 $isChildOkay = true;
2163 $isParentOkay = true;
2164 $isLftOkay = true;
2165 $isRgtOkay = true;
2166 $isDepthOkay = true;
2167 $isGap = false;
2168
2169 if (count($stack) > 0) {
2170 $parent = $stack[count($stack) - 1];
2171 if ($parent->depth + 1 != $row->depth) {
2172 $isDepthOkay = false;
2173 $isRowOkay = false;
2174 }
2175 if ($parent->child != $row->parent) {
2176 $isParentOkay = false;
2177 $isRowOkay = false;
2178 }
2179 if ($GLOBALS['ilSetting']->get('main_tree_impl', 'ns') == 'ns') {
2180 if ($parent->lft >= $row->lft) {
2181 $isLftOkay = false;
2182 $isRowOkay = false;
2183 }
2184 if ($parent->rgt <= $row->rgt) {
2185 $isRgtOkay = false;
2186 $isRowOkay = false;
2187 }
2188 }
2189 }
2190
2191 // Check lft rgt
2192 if ($GLOBALS['ilSetting']->get('main_tree_impl', 'ns') == 'ns') {
2193 if ($row->lft >= $row->rgt) {
2194 $isLftOkay = false;
2195 $isRgtOkay = false;
2196 $isRowOkay = false;
2197 }
2198 }
2199 if (in_array($row->child, $duplicateNodes)) {
2200 $isChildOkay = false;
2201 $isRowOkay = false;
2202 }
2203
2204 // Check object reference
2205 $isRefRefOkay = true;
2206 $isRefObjOkay = true;
2207 if ($row->ref_id == null) {
2208 $isRefRefOkay = false;
2209 $isRowOkay = false;
2210 }
2211 if ($row->obj_id == null) {
2212 $isRefObjOkay = false;
2213 $isRowOkay = false;
2214 }
2215
2216 if (!$isRowOkay) {
2217 $error_count++;
2218 }
2219
2220 // Check for gap between siblings,
2221 // and eventually write a log line
2222 if ($GLOBALS['ilSetting']->get('main_tree_impl', 'ns') == 'ns') {
2223 $gap = $row->lft - $previousNumber - 1;
2224 $previousNumber = $row->lft;
2225 if ($gap > 0) {
2226 $this->writeScanLogLine(
2227 '<tr>'
2228 . '<td colspan=2><div align="right">'
2229 . '<font color=#00cc00>*gap* for ' . ($gap / 2) . ' nodes between&nbsp;</font>'
2230 . '</div></td>'
2231 . '<td>'
2232 . '<font color=#00cc00>siblings</font>'
2233 . '</td>'
2234 . '</tr>'
2235 );
2236 }
2237 }
2238
2239 // Write log line
2240 // -------------------
2241 $this->writeScanLogLine(
2242 '<tr>'
2243 . '<td>'
2244 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2245 . $row->tree . ', '
2246 . $row->child . ', '
2247 . (($isParentOkay) ? '' : 'parent:<b>')
2248 . $row->parent
2249 . (($isParentOkay) ? '' : '</b>')
2250 . ', '
2251 . (($isLftOkay) ? '' : 'lft:<b>')
2252 . $row->lft
2253 . (($isLftOkay) ? '' : '</b>')
2254 . ', '
2255 . (($isRgtOkay) ? '' : 'rgt:<b>')
2256 . $row->rgt
2257 . (($isRgtOkay) ? '' : '</b>')
2258 . ', '
2259 . (($isDepthOkay) ? '' : 'depth:<b>')
2260 . $row->depth
2261 . (($isDepthOkay) ? '' : '</b>')
2262 . (($isRowOkay) ? '' : '</font>')
2263 . '</td><td>'
2264 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2265 . (($isRefRefOkay && $isChildOkay) ? '' : 'ref.ref_id:<b>')
2266 . $row->ref_id
2267 . (($isRefRefOkay && $isChildOkay) ? '' : '</b>')
2268 . ', '
2269 . (($isRefObjOkay) ? '' : 'ref.obj_id:<b>')
2270 . $row->refobj_id
2271 . (($isRefObjOkay) ? '' : '</b>')
2272 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2273 . (($row->tree < 0) ? ', ' . $row->deleted : '')
2274 . '</td><td>'
2275 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2276 . $indent
2277 . $row->obj_id . ', '
2278 . $row->type . ', '
2279 . $row->login . ', '
2280 . $row->title
2281 . (($isRowOkay) ? '' : ' <b>*ERROR*</b><font color=#ff0000>')
2282 . '</td>'
2283 . '</tr>'
2284 );
2285
2286 // Update stack
2287 // -------------------
2288 // Push node on stack
2289 $stack[] = $row;
2290
2291 // Count nodes
2292 // -----------------
2293 if ($row->tree == 1) {
2294 $repository_tree_count++;
2295 } elseif ($row->tree < 0) {
2296 $trash_trees_count++;
2297 } else {
2298 $other_trees_count++;
2299 }
2300 }
2301 //
2302 // Pop remaining stack entries
2303
2304 while (count($stack) > 0) {
2305 $popped = array_pop($stack);
2306
2307 // check for gap
2308 $gap = $popped->rgt - $previousNumber - 1;
2309 if ($gap > 0) {
2310 $poppedIndent = "";
2311 for ($i = 1; $i < $popped->depth; $i++) {
2312 $poppedIndent .= ". ";
2313 }
2314 $this->writeScanLogLine(
2315 '<tr>'
2316 . '<td colspan=2><div align="right">'
2317 . '<font color=#00cc00>*gap* for ' . ($gap / 2) . ' nodes at end of&nbsp;</font>'
2318 . '</div></td>'
2319 . '<td>'
2320 . '<font color=#00cc00>'
2321 . $poppedIndent
2322 . $popped->obj_id . ', '
2323 . $popped->type . ', '
2324 . $popped->login . ', '
2325 . $popped->title
2326 . '</font>'
2327 . '</td>'
2328 . '</tr>'
2329 );
2330 }
2331 $previousNumber = $popped->rgt;
2332 unset($popped);
2333 }
2334
2335 //
2336 $this->writeScanLogLine("</table>");
2337
2338 if ($error_count > 0) {
2339 $this->writeScanLogLine('<font color=#ff0000>' . $error_count . ' errors found while dumping tree.</font>');
2340 } else {
2341 $this->writeScanLogLine('No errors found while dumping tree.');
2342 }
2343 $this->writeScanLogLine("$repository_tree_count nodes in repository tree");
2344 $this->writeScanLogLine("$trash_trees_count nodes in trash trees");
2345 $this->writeScanLogLine("$other_trees_count nodes in other trees");
2346 $this->writeScanLogLine("$not_in_tree_count nodes are not in a tree");
2347 $this->writeScanLogLine("END dumpTree");
2348
2349 return $error_count;
2350 }
2351
2352 protected function isMediaFolder($a_obj_id)
2353 {
2354 $ilDB = $this->db;
2355
2356 if (!is_array($this->media_pool_ids)) {
2357 $this->media_pool_ids = array();
2358 $query = "SELECT child FROM mep_tree ";
2359 $res = $ilDB->query($query);
2360 while ($row = $ilDB->fetchObject($res)) {
2361 $this->media_pool_ids[] = $row->child;
2362 }
2363 }
2364
2365 return in_array($a_obj_id, $this->media_pool_ids) ? true : false;
2366 }
2367
2374 protected function isExcludedFromRecovery($a_type, $a_obj_id)
2375 {
2376 switch ($a_type) {
2377 case 'fold':
2378 if (!$this->isMediaFolder($a_obj_id)) {
2379 return false;
2380 }
2381 }
2382 return in_array($a_type, $this->object_types_exclude);
2383 }
2384
2385 protected function initWorkspaceObjects()
2386 {
2387 $ilDB = $this->db;
2388
2389 if ($this->workspace_object_ids === null) {
2390 $this->workspace_object_ids = array();
2391
2392 // workspace objects
2393 $set = $ilDB->query("SELECT DISTINCT(obj_id) FROM object_reference_ws");
2394 while ($row = $ilDB->fetchAssoc($set)) {
2395 $this->workspace_object_ids[] = $row["obj_id"];
2396 }
2397
2398 // portfolios
2399 $set = $ilDB->query("SELECT id FROM usr_portfolio");
2400 while ($row = $ilDB->fetchAssoc($set)) {
2401 $this->workspace_object_ids[] = $row["id"];
2402 }
2403 }
2404 }
2405
2406 protected function filterWorkspaceObjects(array &$a_data, $a_index = "obj_id")
2407 {
2408 if (sizeof($a_data)) {
2409 $this->initWorkspaceObjects();
2410
2411 // remove workspace objects from result objects
2412 if (is_array($this->workspace_object_ids)) {
2413 foreach ($a_data as $idx => $item) {
2414 if (in_array($item[$a_index], $this->workspace_object_ids)) {
2415 unset($a_data[$idx]);
2416 }
2417 }
2418 }
2419 }
2420 }
2421} // END class.ilValidator
user()
Definition: user.php:4
exit
Definition: backend.php:16
An exception for terminatinating execution or to throw for unit testing.
const IL_CAL_UNIX
const IL_CAL_DATETIME
@classDescription Date and time handling
logging
Definition: class.ilLog.php:19
static getInstanceByRefId($a_ref_id, $stop_on_error=true)
get an instance of an Ilias object by reference id
static _removeEntry($a_tree, $a_child, $a_db_table="tree")
STATIC METHOD Removes a single entry from a tree.
ILIAS Data Validator & Recovery Tool.
restoreUnboundObjects($a_unbound_objects=null)
Restore objects (and their subobjects) to RecoveryFolder that are valid but not linked correctly in t...
removeInvalidRolefolders($a_invalid_rolefolders=null)
Removes invalid rolefolders.
removeInvalidChilds($a_invalid_childs=null)
Removes all tree entries without any link to a valid object.
deleteScanLog()
Delete scan log.
findUnboundObjects()
Search database for all tree entries having no valid parent (=> no valid path to root node) and store...
__construct($a_log=false)
Constructor.
handleErr($error)
Callback function handles PEAR_error and outputs detailed infos about error TODO: implement that in g...
isExcludedFromRecovery($a_type, $a_obj_id)
Check if type is excluded from recovery.
restoreReference($a_obj_id)
restore a reference for an object Creates a new reference entry in DB table object_reference for $a_o...
purgeMissingObjects($a_nodes=null)
Removes all missing objects from system.
getInvalidChilds()
Gets all tree entries without any link to a valid object.
getDeletedObjects()
Gets all object in trash.
getInvalidReferences()
Gets all reference entries that are not linked with a valid object id.
writeScanLogLine($a_msg)
writeScanLogArray($a_arr)
filterWorkspaceObjects(array &$a_data, $a_index="obj_id")
getInvalidRolefolders()
Gets invalid rolefolders (same as missing objects)
purgeObjects($a_nodes)
removes objects from system
removeInvalidReferences($a_invalid_refs=null)
Removes all reference entries that are linked with invalid object IDs.
checkTreeStructure($a_startnode=null)
restoreSubTrees($a_nodes)
Restore objects (and their subobjects) to RecoveryFolder.
initGapsInTree()
Initializes gaps in lft/rgt values of a tree.
setModeDependencies()
Sets modes by considering mode dependencies; some modes require other modes to be activated.
getUnboundObjects()
Gets all tree entries having no valid parent (=> no valid path to root node) Returns an array with ch...
restoreDeletedObjects($a_nodes)
Restore deleted objects (and their subobjects) to RecoveryFolder.
isModeEnabled($a_mode)
Is a particular mode enabled?
get_last_scan($a_scan_log)
getPossibleModes()
get possible ilValidator modes @access public
restoreTrash($a_deleted_objects=null)
Restore all objects in trash to RecoveryFolder NOTE: All objects will be restored to top of RecoveryF...
restoreMissingObjects($a_missing_objects=null)
Restores missing reference and/or tree entry for all objects found by this::getMissingObjects() Resto...
findDeletedObjects()
Search database for all tree entries having no valid parent (=> no valid path to root node) and store...
hasScanLog()
Quickly determine if there is a scan log.
dumpTree()
Dumps the Tree structure into the scan log.
findInvalidChilds()
Search database for all tree entries without any link to a valid object and stores result in $this->i...
setMode($a_mode, $a_value)
set mode of ilValidator Usage: setMode("restore",true) => enable object restorey setMode("all",...
findMissingObjects()
Search database for all object entries with missing reference and/or tree entry and stores result in ...
findInvalidRBACEntries()
Search database for all role entries that are linked to invalid ref_ids.
purgeTrash($a_nodes=null)
Removes all objects in trash from system.
isMediaFolder($a_obj_id)
findInvalidReferences()
Search database for all reference entries that are not linked with a valid object id and stores resul...
findInvalidRolefolders()
Search database for all rolefolder object entries with missing reference entry.
validate()
Performs the validation for each enabled mode.
purgeUnboundObjects($a_nodes=null)
Removes all invalid objects from system.
getMissingObjects()
Gets all object entries with missing reference and/or tree entry.
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
$r
Definition: example_031.php:79
const DEBUG
$summary
Definition: cron.php:24
catch(Exception $e) $message
$row
$GLOBALS['JPEG_Segment_Names']
Global Variable: XMP_tag_captions.
$query
$type
global $DIC
Definition: saml.php:7
foreach($_POST as $key=> $value) $res
global $ilDB
$ilUser
Definition: imgupload.php:18
$a_type
Definition: workflow.php:92