ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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{
13 protected $media_pool_ids = null;
14
20
25 // i added folder due to bug #1860 (even if this will not completely fix it)
26 // and the fact, that media pool folders may find their way into
27 // the recovery folder (what results in broken pools, if the are deleted)
28 // Alex, 2006-07-21
29 // I removed file objects from this exclusion list, because file objects
30 // can be in the repository tree, and thus can suffer from data
31 // inconsistencies as well.
32 // Werner, 2007-04-16
33 var $object_types_exclude = array("adm","root","mail","usrf","objf","lngf",
34 "trac","taxf","auth","rolf","assf","svyf","extt","adve","fold");
35
40 var $mode = array(
41 "scan" => true, // gather information about corrupted entries
42 "dump_tree" => false, // dump tree
43 "clean" => false, // remove all unusable entries & renumber tree
44 "restore" => false, // restore objects with invalid parent to RecoveryFolder
45 "purge" => false, // delete all objects with invalid parent from system
46 "restore_trash" => false, // restore all objects in trash to RecoveryFolder
47 "purge_trash" => false // delete all objects in trash from system
48 );
49
54 var $invalid_references = array();
55
60 var $invalid_childs = array();
61
66 var $missing_objects = array();
67
72 var $unbound_objects = array();
73
78 var $deleted_objects = array();
79
86 var $invalid_rolefolders = array();
87
92 var $invalid_objects = array();
93
98 var $logging = false;
99
105
106 var $scan_log_file = "scanlog.log";
107
108 var $scan_log_separator = "<!-- scan log start -->";
109
116 function __construct($a_log = false)
117 {
118 global $objDefinition, $ilDB;
119
120 $this->db =& $ilDB;
121 $this->rbac_object_types = "'".implode("','",$objDefinition->getAllRBACObjects())."'";
122 $this->rbac_object_types = $objDefinition->getAllRBACObjects();
123
124 if ($a_log === true)
125 {
126 $this->logging = true;
127
128 // should be available thru inc.header.php
129 // TODO: move log functionality to new class ilScanLog
130 include_once "./Services/Logging/classes/class.ilLog.php";
131
132 // Delete old scan log
133 $this->deleteScanLog();
134
135 // create scan log
136 include_once './Services/Logging/classes/class.ilLog.php';
137 $this->scan_log = new ilLog(CLIENT_DATA_DIR,"scanlog.log");
138 $this->scan_log->setLogFormat("");
139 $this->writeScanLogLine($this->scan_log_separator);
140 $this->writeScanLogLine("\n[Systemscan from ".date("y-m-d H:i]"));
141 }
142 }
143
149 public function getPossibleModes()
150 {
151 return array_keys($this->mode);
152 }
153
165 function setMode($a_mode,$a_value)
166 {
167 if ((!in_array($a_mode,array_keys($this->mode)) and $a_mode != "all") or !is_bool($a_value))
168 {
169 $this->throwError(INVALID_PARAM, FATAL, DEBUG);
170 return false;
171 }
172
173 if ($a_mode == "all")
174 {
175 foreach ($this->mode as $mode => $value)
176 {
177 $this->mode[$mode] = $a_value;
178 }
179 }
180 else
181 {
182 $this->mode[$a_mode] = $a_value;
183 }
184
185 // consider mode dependencies
186 $this->setModeDependencies();
187
188 return true;
189 }
190
199 function isModeEnabled($a_mode)
200 {
201 if (!in_array($a_mode,array_keys($this->mode)))
202 {
203 $this->throwError(VALIDATER_UNKNOWN_MODE, WARNING, DEBUG);
204 return false;
205 }
206
207 return $this->mode[$a_mode];
208 }
209
210 function isLogEnabled()
211 {
212 return $this->logging;
213 }
214
224 {
225 // DO NOT change the order
226
227 if ($this->mode["restore"] === true)
228 {
229 $this->mode["scan"] = true;
230 $this->mode["purge"] = false;
231 }
232
233 if ($this->mode["purge"] === true)
234 {
235 $this->mode["scan"] = true;
236 $this->mode["restore"] = false;
237 }
238
239 if ($this->mode["restore_trash"] === true)
240 {
241 $this->mode["scan"] = true;
242 $this->mode["purge_trash"] = false;
243 }
244
245 if ($this->mode["purge_trash"] === true)
246 {
247 $this->mode["scan"] = true;
248 $this->mode["restore_trash"] = false;
249 }
250
251 if ($this->mode["clean"] === true)
252 {
253 $this->mode["scan"] = true;
254 }
255 }
256
265 function validate()
266 {
267 global $lng;
268
269 // The validation summary.
270 $summary = "";
271
272
273 // STEP 1: Scan
274 // -------------------
275 $summary .= $lng->txt("scanning_system");
276 if (!$this->isModeEnabled("scan"))
277 {
278 $summary .= $lng->txt("disabled");
279 }
280 else
281 {
282 $summary .= "<br/>".$lng->txt("searching_invalid_refs");
283 if ($this->findInvalidReferences())
284 {
285 $summary .= count($this->getInvalidReferences())." ".$lng->txt("found");
286 }
287 else
288 {
289 $summary .= $lng->txt("found_none");
290 }
291
292 $summary .= "<br/>".$lng->txt("searching_invalid_childs");
293 if ($this->findInvalidChilds())
294 {
295 $summary .= count($this->getInvalidChilds())." ".$lng->txt("found");
296
297 }
298 else
299 {
300 $summary .= $lng->txt("found_none");
301 }
302
303 $summary .= "<br/>".$lng->txt("searching_missing_objs");
304 if ($this->findMissingObjects())
305 {
306 $summary .= count($this->getMissingObjects())." ".$lng->txt("found");
307
308 }
309 else
310 {
311 $summary .= $lng->txt("found_none");
312 }
313
314 $summary .= "<br/>".$lng->txt("searching_unbound_objs");
315 if ($this->findUnboundObjects())
316 {
317 $summary .= count($this->getUnboundObjects())." ".$lng->txt("found");
318
319 }
320 else
321 {
322 $summary .= $lng->txt("found_none");
323 }
324
325 $summary .= "<br/>".$lng->txt("searching_deleted_objs");
326 if ($this->findDeletedObjects())
327 {
328 $summary .= count($this->getDeletedObjects())." ".$lng->txt("found");
329
330 }
331 else
332 {
333 $summary .= $lng->txt("found_none");
334 }
335
336 $summary .= "<br/>".$lng->txt("searching_invalid_rolfs");
337 if ($this->findInvalidRolefolders())
338 {
339 $summary .= count($this->getInvalidRolefolders())." ".$lng->txt("found");
340
341 }
342 else
343 {
344 $summary .= $lng->txt("found_none");
345 }
346
347 $summary .= "<br/><br/>".$lng->txt("analyzing_tree_structure");
348 if ($this->checkTreeStructure())
349 {
350 $summary .= $lng->txt("tree_corrupt");
351 }
352 else
353 {
354 $summary .= $lng->txt("done");
355 }
356 }
357
358 // STEP 2: Dump tree
359 // -------------------
360 $summary .= "<br /><br />".$lng->txt("dumping_tree");
361 if (!$this->isModeEnabled("dump_tree"))
362 {
363 $summary .= $lng->txt("disabled");
364 }
365 else
366 {
367 $error_count = $this->dumpTree();
368 if ($error_count > 0)
369 {
370 $summary .= $lng->txt("tree_corrupt");
371 }
372 else
373 {
374 $summary .= $lng->txt("done");
375 }
376 }
377
378 // STEP 3: Clean Up
379 // -------------------
380 $summary .= "<br /><br />".$lng->txt("cleaning");
381 if (!$this->isModeEnabled("clean"))
382 {
383 $summary .= $lng->txt("disabled");
384 }
385 else
386 {
387 $summary .= "<br />".$lng->txt("removing_invalid_refs");
388 if ($this->removeInvalidReferences())
389 {
390 $summary .= strtolower($lng->txt("done"));
391 }
392 else
393 {
394 $summary .= $lng->txt("nothing_to_remove").$lng->txt("skipped");
395 }
396
397 $summary .= "<br />".$lng->txt("removing_invalid_childs");
398 if ($this->removeInvalidChilds())
399 {
400 $summary .= strtolower($lng->txt("done"));
401 }
402 else
403 {
404 $summary .= $lng->txt("nothing_to_remove").$lng->txt("skipped");
405 }
406
407 $summary .= "<br />".$lng->txt("removing_invalid_rolfs");
408 if ($this->removeInvalidRolefolders())
409 {
410 $summary .= strtolower($lng->txt("done"));
411 }
412 else
413 {
414 $summary .= $lng->txt("nothing_to_remove").$lng->txt("skipped");
415 }
416
417 // find unbound objects again AFTER cleaning process!
418 // This updates the array 'unboundobjects' required for the further steps
419 // There might be other objects unbounded now due to removal of object_data/reference entries.
420 $this->findUnboundObjects();
421 }
422
423 // STEP 4: Restore objects
424 $summary .= "<br /><br />".$lng->txt("restoring");
425
426 if (!$this->isModeEnabled("restore"))
427 {
428 $summary .= $lng->txt("disabled");
429 }
430 else
431 {
432 $summary .= "<br />".$lng->txt("restoring_missing_objs");
433 if ($this->restoreMissingObjects())
434 {
435 $summary .= strtolower($lng->txt("done"));
436 }
437 else
438 {
439 $summary .= $lng->txt("nothing_to_restore").$lng->txt("skipped");
440 }
441
442 $summary .= "<br />".$lng->txt("restoring_unbound_objs");
443 if ($this->restoreUnboundObjects())
444 {
445 $summary .= strtolower($lng->txt("done"));
446 }
447 else
448 {
449 $summary .= $lng->txt("nothing_to_restore").$lng->txt("skipped");
450 }
451 }
452
453 // STEP 5: Restoring Trash
454 $summary .= "<br /><br />".$lng->txt("restoring_trash");
455
456 if (!$this->isModeEnabled("restore_trash"))
457 {
458 $summary .= $lng->txt("disabled");
459 }
460 else
461 {
462 if ($this->restoreTrash())
463 {
464 $summary .= strtolower($lng->txt("done"));
465 }
466 else
467 {
468 $summary .= $lng->txt("nothing_to_restore").$lng->txt("skipped");
469 }
470 }
471
472 // STEP 6: Purging...
473 $summary .= "<br /><br />".$lng->txt("purging");
474
475 if (!$this->isModeEnabled("purge"))
476 {
477 $summary .= $lng->txt("disabled");
478 }
479 else
480 {
481 $summary .= "<br />".$lng->txt("purging_missing_objs");
482 if ($this->purgeMissingObjects())
483 {
484 $summary .= strtolower($lng->txt("done"));
485 }
486 else
487 {
488 $summary .= $lng->txt("nothing_to_purge").$lng->txt("skipped");
489 }
490
491 $summary .= "<br />".$lng->txt("purging_unbound_objs");
492 if ($this->purgeUnboundObjects())
493 {
494 $summary .= strtolower($lng->txt("done"));
495 }
496 else
497 {
498 $summary .= $lng->txt("nothing_to_purge").$lng->txt("skipped");
499 }
500 }
501
502 // STEP 7: Purging trash...
503 $summary .= "<br /><br />".$lng->txt("purging_trash");
504
505 if (!$this->isModeEnabled("purge_trash"))
506 {
507 $summary .= $lng->txt("disabled");
508 }
509 else
510 {
511 if ($this->purgeTrash())
512 {
513 $summary .= strtolower($lng->txt("done"));
514 }
515 else
516 {
517 $summary .= $lng->txt("nothing_to_purge").$lng->txt("skipped");
518 }
519 }
520
521 // STEP 8: Initialize gaps in tree
522 if ($this->isModeEnabled("clean"))
523 {
524 $summary .= "<br /><br />".$lng->txt("cleaning_final");
525 if ($this->initGapsInTree())
526 {
527 $summary .= "<br />".$lng->txt("initializing_gaps")." ".strtolower($lng->txt("done"));
528 }
529 }
530
531 // check RBAC starts here
532 // ...
533
534 // le fin
535 foreach ($this->mode as $mode => $value)
536 {
537 $arr[] = $mode."[".(int)$value."]";
538 }
539
540 return $summary;
541 }
542
543
554 {
555 global $ilDB;
556
557 // check mode: analyze
558 if ($this->mode["scan"] !== true)
559 {
560 return false;
561 }
562
563 // init
564 $this->missing_objects = array();
565
566 $this->writeScanLogLine("\nfindMissingObjects:");
567
568 // Repair missing objects.
569 // We only repair file objects which have an entry in table object_reference.
570 // XXX - We should check all references to file objects which don't
571 // have an object_reference. If we can't find any reference to such
572 // a file object, we should repair it too!
573 $q = "SELECT object_data.*, ref_id FROM object_data ".
574 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
575 "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
576 "WHERE tree.child IS NULL ".
577 "AND (object_reference.obj_id IS NOT NULL ".
578 " OR object_data.type <> 'file' AND ".
579 $ilDB->in('object_data.type',$this->rbac_object_types,false,'text').
580 ")";
581 $r = $this->db->query($q);
582
583 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
584 {
585 #if (!in_array($row->type,$this->object_types_exclude))
586 if(!$this->isExcludedFromRecovery($row->type,$row->obj_id))
587 {
588 $this->missing_objects[] = array(
589 "obj_id" => $row->obj_id,
590 "type" => $row->type,
591 "ref_id" => $row->ref_id,
592 "child" => $row->child,
593 "title" => $row->title,
594 "desc" => $row->description,
595 "owner" => $row->owner,
596 "create_date" => $row->create_date,
597 "last_update" => $row->last_update
598 );
599 }
600 }
601
602 $this->filterWorkspaceObjects($this->missing_objects);
603 if (count($this->missing_objects) > 0)
604 {
605 $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
606 $this->writeScanLogArray($this->missing_objects);
607 return true;
608 }
609
610 $this->writeScanLogLine("none");
611 return false;
612 }
613
626 {
627 global $ilDB;
628
629 // check mode: analyze
630 if ($this->mode["scan"] !== true)
631 {
632 return false;
633 }
634
635 // init
636 $this->invalid_rolefolders = array();
637
638 $this->writeScanLogLine("\nfindInvalidRolefolders:");
639
640 // find rolfs without reference/tree entry
641 $q = "SELECT object_data.*, ref_id FROM object_data ".
642 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
643 "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
644 "WHERE (object_reference.obj_id IS NULL OR tree.child IS NULL) ".
645 "AND object_data.type='rolf'";
646 $r = $this->db->query($q);
647
648 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
649 {
650 $this->invalid_rolefolders[] = array(
651 "obj_id" => $row->obj_id,
652 "type" => $row->type,
653 "ref_id" => $row->ref_id,
654 "child" => $row->child,
655 "title" => $row->title,
656 "desc" => $row->description,
657 "owner" => $row->owner,
658 "create_date" => $row->create_date,
659 "last_update" => $row->last_update
660 );
661 }
662
663 // find rolfs within RECOVERY FOLDER
664 $q = "SELECT object_data.*, ref_id FROM object_data ".
665 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
666 "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
667 "WHERE object_reference.ref_id = ".$ilDB->quote(RECOVERY_FOLDER_ID,'integer')." ".
668 "AND object_data.type='rolf'";
669 $r = $this->db->query($q);
670
671 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
672 {
673 $this->invalid_rolefolders[] = array(
674 "obj_id" => $row->obj_id,
675 "type" => $row->type,
676 "ref_id" => $row->ref_id,
677 "child" => $row->child,
678 "title" => $row->title,
679 "desc" => $row->description,
680 "owner" => $row->owner,
681 "create_date" => $row->create_date,
682 "last_update" => $row->last_update
683 );
684 }
685
686 $this->filterWorkspaceObjects($this->invalid_rolefolders);
687 if (count($this->invalid_rolefolders) > 0)
688 {
689 $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
690 $this->writeScanLogArray($this->invalid_rolefolders);
691 return true;
692 }
693
694 $this->writeScanLogLine("none");
695 return false;
696 }
697
708 {
709 global $ilDB;
710
711 // check mode: analyze
712 if ($this->mode["scan"] !== true)
713 {
714 return false;
715 }
716
717 // init
718 $this->invalid_rbac_entries = array();
719
720 $this->writeScanLogLine("\nfindInvalidRBACEntries:");
721
722 $q = "SELECT object_data.*, ref_id FROM object_data ".
723 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
724 "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
725 "WHERE (object_reference.obj_id IS NULL OR tree.child IS NULL) ".
726 "AND object_data.type='rolf'";
727 $r = $this->db->query($q);
728
729 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
730 {
731 $this->invalid_rolefolders[] = array(
732 "obj_id" => $row->obj_id,
733 "type" => $row->type,
734 "ref_id" => $row->ref_id,
735 "child" => $row->child,
736 "title" => $row->title,
737 "desc" => $row->description,
738 "owner" => $row->owner,
739 "create_date" => $row->create_date,
740 "last_update" => $row->last_update
741 );
742 }
743
744 // find rolfs within RECOVERY FOLDER
745 $q = "SELECT object_data.*, ref_id FROM object_data ".
746 "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
747 "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
748 "WHERE object_reference.ref_id =".$ilDB->quote(RECOVERY_FOLDER_ID)." ".
749 "AND object_data.type='rolf'";
750 $r = $this->db->query($q);
751
752 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
753 {
754 $this->invalid_rolefolders[] = array(
755 "obj_id" => $row->obj_id,
756 "type" => $row->type,
757 "ref_id" => $row->ref_id,
758 "child" => $row->child,
759 "title" => $row->title,
760 "desc" => $row->description,
761 "owner" => $row->owner,
762 "create_date" => $row->create_date,
763 "last_update" => $row->last_update
764 );
765 }
766
767 $this->filterWorkspaceObjects($this->invalid_rolefolders);
768 if (count($this->invalid_rolefolders) > 0)
769 {
770 $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
771 $this->writeScanLogArray($this->invalid_rolefolders);
772 return true;
773 }
774
775 $this->writeScanLogLine("none");
776 return false;
777 }
778
793 {
795 }
796
807 {
808 global $ilDB;
809
810 // check mode: analyze
811 if ($this->mode["scan"] !== true)
812 {
813 return false;
814 }
815
816 // init
817 $this->invalid_references = array();
818
819 $this->writeScanLogLine("\nfindInvalidReferences:");
820 $q = "SELECT object_reference.* FROM object_reference ".
821 "LEFT JOIN object_data ON object_data.obj_id = object_reference.obj_id ".
822 "WHERE object_data.obj_id IS NULL ".
823 "OR ".$ilDB->in('object_data.type',$this->rbac_object_types,true,'text');
824 $r = $this->db->query($q);
825
826 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
827 {
828 $this->invalid_references[] = array(
829 "ref_id" => $row->ref_id,
830 "obj_id" => $row->obj_id,
831 "msg" => "Object does not exist."
832 );
833 }
834
835 $this->filterWorkspaceObjects($this->invalid_references);
836 if (count($this->invalid_references) > 0)
837 {
838 $this->writeScanLogLine("ref_id\t\tobj_id");
839 $this->writeScanLogArray($this->invalid_references);
840 return true;
841 }
842
843 $this->writeScanLogLine("none");
844 return false;
845 }
846
856 {
858 }
859
870 {
871 global $ilDB;
872
873 // check mode: analyze
874 if ($this->mode["scan"] !== true)
875 {
876 return false;
877 }
878
879 // init
880 $this->invalid_childs = array();
881
882 $this->writeScanLogLine("\nfindInvalidChilds:");
883
884 $q = "SELECT tree.*,object_reference.ref_id FROM tree ".
885 "LEFT JOIN object_reference ON tree.child = object_reference.ref_id ".
886 "LEFT JOIN object_data ON object_reference.obj_id = object_data.obj_id ".
887 "WHERE object_reference.ref_id IS NULL or object_data.obj_id IS NULL";
888 $r = $this->db->query($q);
889 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
890 {
891 $this->invalid_childs[] = array(
892 "child" => $row->child,
893 "ref_id" => $row->ref_id,
894 "msg" => "No object found"
895 );
896 }
897
898 if (count($this->invalid_childs) > 0)
899 {
900 $this->writeScanLogLine("child\t\tref_id");
901 $this->writeScanLogArray($this->invalid_childs);
902 return true;
903 }
904
905 $this->writeScanLogLine("none");
906 return false;
907 }
908
918 {
920 }
921
934 {
935 // check mode: analyze
936 if ($this->mode["scan"] !== true)
937 {
938 return false;
939 }
940
941 // init
942 $this->unbound_objects = array();
943
944 $this->writeScanLogLine("\nfindUnboundObjects:");
945
946 $q = "SELECT T1.tree,T1.child,T1.parent,".
947 "T2.tree deleted,T2.parent grandparent ".
948 "FROM tree T1 ".
949 "LEFT JOIN tree T2 ON T2.child=T1.parent ".
950 "WHERE (T2.tree!=1 OR T2.tree IS NULL) AND T1.parent!=0";
951 $r = $this->db->query($q);
952
953 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
954 {
955 // exclude deleted nodes
956 if ($row->deleted === NULL)
957 {
958 $this->unbound_objects[] = array(
959 "child" => $row->child,
960 "parent" => $row->parent,
961 "tree" => $row->tree,
962 "msg" => "No valid parent node found"
963 );
964 }
965 }
966
967 if (count($this->unbound_objects) > 0)
968 {
969 $this->writeScanLogLine("child\t\tparent\ttree");
970 $this->writeScanLogArray($this->unbound_objects);
971 return true;
972 }
973
974 $this->writeScanLogLine("none");
975 return false;
976 }
977
990 {
991 // check mode: analyze
992 if ($this->mode["scan"] !== true)
993 {
994 return false;
995 }
996
997 // init
998 $this->deleted_objects = array();
999
1000 $this->writeScanLogLine("\nfindDeletedObjects:");
1001
1002 // Delete objects, start with the oldest objects first
1003 $query = "SELECT object_data.*,tree.tree,tree.child,tree.parent,deleted ".
1004 "FROM object_data ".
1005 "LEFT JOIN object_reference ON object_data.obj_id=object_reference.obj_id ".
1006 "LEFT JOIN tree ON tree.child=object_reference.ref_id ".
1007 " WHERE tree != 1 ".
1008 " ORDER BY deleted";
1009 $r = $this->db->query($query);
1010
1011 include_once './Services/Calendar/classes/class.ilDateTime.php';
1012 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
1013 {
1014 $tmp_date = new ilDateTime($row->deleted,IL_CAL_DATETIME);
1015
1016 $this->deleted_objects[] = array(
1017 "child" => $row->child,
1018 "parent" => $row->parent,
1019 "tree" => $row->tree,
1020 "type" => $row->type,
1021 "title" => $row->title,
1022 "desc" => $row->description,
1023 "owner" => $row->owner,
1024 "deleted" => $row->deleted,
1025 "deleted_timestamp" => $tmp_date->get(IL_CAL_UNIX),
1026 "create_date" => $row->create_date,
1027 "last_update" => $row->last_update
1028 );
1029 }
1030
1031 if (count($this->deleted_objects) > 0)
1032 {
1033 $this->writeScanLogArray(array(array_keys($this->deleted_objects[0])));
1034 $this->writeScanLogArray($this->deleted_objects);
1035 return true;
1036 }
1037 $this->writeScanLogLine("none");
1038 return false;
1039 }
1040
1041
1056 {
1058 }
1059
1067 {
1069 }
1070
1080 {
1082 }
1083
1093 function removeInvalidReferences($a_invalid_refs = NULL)
1094 {
1095 global $ilLog;
1096 global $ilDB;
1097
1098 // check mode: clean
1099 if ($this->mode["clean"] !== true)
1100 {
1101 return false;
1102 }
1103
1104 $this->writeScanLogLine("\nremoveInvalidReferences:");
1105
1106 if ($a_invalid_refs === NULL and isset($this->invalid_references))
1107 {
1108 $a_invalid_refs =& $this->invalid_references;
1109 }
1110
1111 // handle wrong input
1112 if (!is_array($a_invalid_refs))
1113 {
1114 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1115 return false;
1116 }
1117 // no unbound references found. do nothing
1118 if (count($a_invalid_refs) == 0)
1119 {
1120 $this->writeScanLogLine("none");
1121 return false;
1122 }
1123
1124/*******************
1125removal starts here
1126********************/
1127
1128 $message = sprintf('%s::removeInvalidReferences(): Started...',
1129 get_class($this));
1130 $ilLog->write($message,$ilLog->WARNING);
1131
1132 // to make sure
1133 $this->filterWorkspaceObjects($a_invalid_refs);
1134
1135 foreach ($a_invalid_refs as $entry)
1136 {
1137 $query = "DELETE FROM object_reference WHERE ref_id= ".$this->db->quote($entry["ref_id"],'integer').
1138 " AND obj_id = ".$this->db->quote($entry["obj_id"],'integer')." ";
1139 $res = $ilDB->manipulate($query);
1140
1141 $message = sprintf('%s::removeInvalidReferences(): Reference %s removed',
1142 get_class($this),
1143 $entry["ref_id"]);
1144 $ilLog->write($message,$ilLog->WARNING);
1145
1146 $this->writeScanLogLine("Entry ".$entry["ref_id"]." removed");
1147 }
1148
1149 return true;
1150 }
1151
1161 function removeInvalidChilds($a_invalid_childs = NULL)
1162 {
1163 global $ilLog;
1164
1165 // check mode: clean
1166 if ($this->mode["clean"] !== true)
1167 {
1168 return false;
1169 }
1170
1171 $this->writeScanLogLine("\nremoveInvalidChilds:");
1172
1173 if ($a_invalid_childs === NULL and isset($this->invalid_childs))
1174 {
1175 $a_invalid_childs =& $this->invalid_childs;
1176 }
1177
1178 // handle wrong input
1179 if (!is_array($a_invalid_childs))
1180 {
1181 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1182 return false;
1183 }
1184
1185 // no unbound childs found. do nothing
1186 if (count($a_invalid_childs) == 0)
1187 {
1188 $this->writeScanLogLine("none");
1189 return false;
1190 }
1191
1192/*******************
1193removal starts here
1194********************/
1195
1196 $message = sprintf('%s::removeInvalidChilds(): Started...',
1197 get_class($this));
1198 $ilLog->write($message,$ilLog->WARNING);
1199
1200 foreach ($a_invalid_childs as $entry)
1201 {
1202 $q = "DELETE FROM tree WHERE child='".$entry["child"]."'";
1203 $this->db->query($q);
1204
1205 $message = sprintf('%s::removeInvalidChilds(): Entry child=%s removed',
1206 get_class($this),
1207 $entry["child"]);
1208 $ilLog->write($message,$ilLog->WARNING);
1209
1210 $this->writeScanLogLine("Entry ".$entry["child"]." removed");
1211 }
1212
1213 return true;
1214 }
1215
1226 function removeInvalidRolefolders($a_invalid_rolefolders = NULL)
1227 {
1228 global $ilias,$ilLog;
1229
1230 // check mode: clean
1231 if ($this->mode["clean"] !== true)
1232 {
1233 return false;
1234 }
1235
1236 $this->writeScanLogLine("\nremoveInvalidRolefolders:");
1237
1238 if ($a_invalid_rolefolders === NULL and isset($this->invalid_rolefolders))
1239 {
1240 $a_invalid_rolefolders = $this->invalid_rolefolders;
1241 }
1242
1243 // handle wrong input
1244 if (!is_array($a_invalid_rolefolders))
1245 {
1246 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1247 return false;
1248 }
1249
1250 // no invalid rolefolders found. do nothing
1251 if (count($a_invalid_rolefolders) == 0)
1252 {
1253 $this->writeScanLogLine("none");
1254 return false;
1255 }
1256
1257/*******************
1258removal starts here
1259********************/
1260
1261 $removed = false;
1262
1263 $message = sprintf('%s::removeInvalidRolefolders(): Started...',
1264 get_class($this));
1265 $ilLog->write($message,$ilLog->WARNING);
1266
1267 // to make sure
1268 $this->filterWorkspaceObjects($a_invalid_rolefolders);
1269
1270 foreach ($a_invalid_rolefolders as $rolf)
1271 {
1272 // restore ref_id in case of missing
1273 if ($rolf["ref_id"] === NULL)
1274 {
1275 $rolf["ref_id"] = $this->restoreReference($rolf["obj_id"]);
1276
1277 $this->writeScanLogLine("Created missing reference '".$rolf["ref_id"]."' for rolefolder object '".$rolf["obj_id"]."'");
1278 }
1279
1280 // now delete rolefolder
1281 $obj_data =& $ilias->obj_factory->getInstanceByRefId($rolf["ref_id"]);
1282 $obj_data->delete();
1283 unset($obj_data);
1284 $removed = true;
1285 $this->writeScanLogLine("Removed invalid rolefolder '".$rolf["title"]."' (id=".$rolf["obj_id"].",ref=".$rolf["ref_id"].") from system");
1286 }
1287
1288 return $removed;
1289 }
1290
1301 function restoreMissingObjects($a_missing_objects = NULL)
1302 {
1303 global $ilias,$rbacadmin,$ilLog;
1304
1305 // check mode: restore
1306 if ($this->mode["restore"] !== true)
1307 {
1308 return false;
1309 }
1310
1311 $this->writeScanLogLine("\nrestoreMissingObjects:");
1312
1313 if ($a_missing_objects === NULL and isset($this->missing_objects))
1314 {
1315 $a_missing_objects = $this->missing_objects;
1316 }
1317
1318 // handle wrong input
1319 if (!is_array($a_missing_objects))
1320 {
1321 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1322 return false;
1323 }
1324
1325 // no missing objects found. do nothing
1326 if (count($a_missing_objects) == 0)
1327 {
1328 $this->writeScanLogLine("none");
1329 return false;
1330 }
1331
1332/*******************
1333restore starts here
1334********************/
1335
1336 $restored = false;
1337
1338 $message = sprintf('%s::restoreMissingObjects(): Started...',
1339 get_class($this));
1340 $ilLog->write($message,$ilLog->WARNING);
1341
1342 // to make sure
1343 $this->filterWorkspaceObjects($a_missing_objects);
1344
1345 foreach ($a_missing_objects as $missing_obj)
1346 {
1347 // restore ref_id in case of missing
1348 if ($missing_obj["ref_id"] === NULL)
1349 {
1350 $missing_obj["ref_id"] = $this->restoreReference($missing_obj["obj_id"]);
1351
1352 $this->writeScanLogLine("Created missing reference '".$missing_obj["ref_id"]."' for object '".$missing_obj["obj_id"]."'");
1353 }
1354
1355 // put in tree under RecoveryFolder if not on exclude list
1356 #if (!in_array($missing_obj["type"],$this->object_types_exclude))
1357 if(!$this->isExcludedFromRecovery($missing_obj['type'],$missing_obj['obj_id']))
1358 {
1359 $rbacadmin->revokePermission($missing_obj["ref_id"]);
1360 $obj_data =& $ilias->obj_factory->getInstanceByRefId($missing_obj["ref_id"]);
1361 $obj_data->putInTree(RECOVERY_FOLDER_ID);
1362 $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1363 unset($obj_data);
1364 //$tree->insertNode($missing_obj["ref_id"],RECOVERY_FOLDER_ID);
1365 $restored = true;
1366 $this->writeScanLogLine("Restored object '".$missing_obj["title"]."' (id=".$missing_obj["obj_id"].",ref=".$missing_obj["ref_id"].") in 'Restored objects folder'");
1367 }
1368
1369 // TODO: process rolefolders
1370 }
1371
1372 return $restored;
1373 }
1374
1384 function restoreReference($a_obj_id)
1385 {
1386 global $ilLog;
1387 global $ilDB;
1388
1389 if (empty($a_obj_id))
1390 {
1391 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1392 return false;
1393 }
1394
1395 $query = "INSERT INTO object_reference (ref_id,obj_id) ".
1396 "VALUES (".$next_id = $ilDB->nextId('object_reference').",".$this->db->quote($a_obj_id,'integer')." )";
1397 $res = $ilDB->manipulate($query);
1398
1399 $message = sprintf('%s::restoreReference(): new reference %s for obj_id %s created',
1400 get_class($this),
1401 $next_id,
1402 $a_obj_id);
1403 $ilLog->write($message,$ilLog->WARNING);
1404
1405 return $next_id;
1406 }
1407
1418 function restoreUnboundObjects($a_unbound_objects = NULL)
1419 {
1420 global $ilLog;
1421
1422 // check mode: restore
1423 if ($this->mode["restore"] !== true)
1424 {
1425 return false;
1426 }
1427
1428 $this->writeScanLogLine("\nrestoreUnboundObjects:");
1429
1430 if ($a_unbound_objects === NULL and isset($this->unbound_objects))
1431 {
1432 $a_unbound_objects = $this->unbound_objects;
1433 }
1434
1435 // handle wrong input
1436 if (!is_array($a_unbound_objects))
1437 {
1438 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1439 return false;
1440 }
1441
1442 $message = sprintf('%s::restoreUnboundObjects(): Started...',
1443 get_class($this));
1444 $ilLog->write($message,$ilLog->WARNING);
1445
1446 // start restore process
1447 return $this->restoreSubTrees($a_unbound_objects);
1448 }
1449
1459 function restoreTrash($a_deleted_objects = NULL)
1460 {
1461 global $ilLog;
1462
1463 // check mode: restore
1464 if ($this->mode["restore_trash"] !== true)
1465 {
1466 return false;
1467 }
1468
1469 $this->writeScanLogLine("\nrestoreTrash:");
1470
1471 if ($a_deleted_objects === NULL and isset($this->deleted_objects))
1472 {
1473 $a_deleted_objects = $this->deleted_objects;
1474 }
1475
1476 // handle wrong input
1477 if (!is_array($a_deleted_objects))
1478 {
1479 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1480 return false;
1481 }
1482
1483 $message = sprintf('%s::restoreTrash(): Started...',
1484 get_class($this));
1485 $ilLog->write($message,$ilLog->WARNING);
1486
1487 // start restore process
1488 $restored = $this->restoreDeletedObjects($a_deleted_objects);
1489
1490 if ($restored)
1491 {
1492 $q = "DELETE FROM tree WHERE tree!=1";
1493 $this->db->query($q);
1494
1495 $message = sprintf('%s::restoreTrash(): Removed all trees with tree id <> 1',
1496 get_class($this));
1497 $ilLog->write($message,$ilLog->WARNING);
1498
1499 $this->writeScanLogLine("Old tree entries removed");
1500 }
1501
1502 return $restored;
1503 }
1504
1513 function restoreDeletedObjects($a_nodes)
1514 {
1515 global $tree,$rbacadmin,$ilias,$ilLog;
1516//vd($a_nodes);exit;
1517 // handle wrong input
1518 if (!is_array($a_nodes))
1519 {
1520 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1521 return false;
1522 }
1523
1524 // no invalid parents found. do nothing
1525 if (count($a_nodes) == 0)
1526 {
1527 $this->writeScanLogLine("none");
1528 return false;
1529 }
1530
1531 $message = sprintf('%s::restoreDeletedObjects()): Started...',
1532 get_class($this));
1533 $ilLog->write($message,$ilLog->WARNING);
1534
1535 // first delete all rolefolders
1536 // don't save rolefolders, remove them
1537 // TODO process ROLE_FOLDER_ID
1538 foreach ($a_nodes as $key => $node)
1539 {
1540 if ($node["type"] == "rolf")
1541 {
1542 // delete old tree entries
1543 $tree->deleteTree($node);
1544
1545 $obj_data =& $ilias->obj_factory->getInstanceByRefId($node["child"]);
1546 $obj_data->delete();
1547 unset($a_nodes[$key]);
1548 }
1549 }
1550
1551 // process move
1552 foreach ($a_nodes as $node)
1553 {
1554 // delete old tree entries
1555 $tree->deleteTree($node);
1556
1557 $rbacadmin->revokePermission($node["child"]);
1558 $obj_data =& $ilias->obj_factory->getInstanceByRefId($node["child"]);
1559 $obj_data->putInTree(RECOVERY_FOLDER_ID);
1560 $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1561 }
1562
1563 return true;
1564 }
1565
1574 function restoreSubTrees ($a_nodes)
1575 {
1576 global $tree,$rbacadmin,$ilias,$ilLog;
1577
1578 // handle wrong input
1579 if (!is_array($a_nodes))
1580 {
1581 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1582 return false;
1583 }
1584
1585 // no invalid parents found. do nothing
1586 if (count($a_nodes) == 0)
1587 {
1588 $this->writeScanLogLine("none");
1589 return false;
1590 }
1591
1592/*******************
1593restore starts here
1594********************/
1595
1596 $subnodes = array();
1597 $topnode = array();
1598
1599 $message = sprintf('%s::restoreSubTrees(): Started...',
1600 get_class($this));
1601 $ilLog->write($message,$ilLog->WARNING);
1602
1603 // process move subtree
1604 foreach ($a_nodes as $node)
1605 {
1606 // get node data
1607 $topnode = $tree->getNodeData($node["child"], $node['tree']);
1608
1609 // don't save rolefolders, remove them
1610 // TODO process ROLE_FOLDER_ID
1611 if ($topnode["type"] == "rolf")
1612 {
1613 $rolfObj = $ilias->obj_factory->getInstanceByRefId($topnode["child"]);
1614 $rolfObj->delete();
1615 unset($top_node);
1616 unset($rolfObj);
1617 continue;
1618 }
1619
1620 // get subnodes of top nodes
1621 $subnodes[$node["child"]] = $tree->getSubtree($topnode);
1622
1623 // delete old tree entries
1624 $tree->deleteTree($topnode);
1625 }
1626
1627 // now move all subtrees to new location
1628 // TODO: this whole put in place again stuff needs revision. Permission settings get lost.
1629 foreach ($subnodes as $key => $subnode)
1630 {
1631
1632 // first paste top_node ...
1633 $rbacadmin->revokePermission($key);
1634 $obj_data =& $ilias->obj_factory->getInstanceByRefId($key);
1635 $obj_data->putInTree(RECOVERY_FOLDER_ID);
1636 $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1637
1638 $this->writeScanLogLine("Object '".$obj_data->getId()."' restored.");
1639
1640 // ... remove top_node from list ...
1641 array_shift($subnode);
1642
1643 // ... insert subtree of top_node if any subnodes exist
1644 if (count($subnode) > 0)
1645 {
1646 foreach ($subnode as $node)
1647 {
1648 $rbacadmin->revokePermission($node["child"]);
1649 $obj_data =& $ilias->obj_factory->getInstanceByRefId($node["child"]);
1650 $obj_data->putInTree($node["parent"]);
1651 $obj_data->setPermissions($node["parent"]);
1652
1653 $this->writeScanLogLine("Object '".$obj_data->getId()."' restored.");
1654 }
1655 }
1656 }
1657
1658 // final clean up
1659 $this->findInvalidChilds();
1660 $this->removeInvalidChilds();
1661
1662 return true;
1663 }
1664
1674 function purgeTrash($a_nodes = NULL)
1675 {
1676 global $ilLog;
1677
1678 // check mode: purge_trash
1679 if ($this->mode["purge_trash"] !== true)
1680 {
1681 return false;
1682 }
1683
1684 $this->writeScanLogLine("\npurgeTrash:");
1685
1686 if ($a_nodes === NULL and isset($this->deleted_objects))
1687 {
1688
1689
1690 $a_nodes = $this->deleted_objects;
1691 }
1692 $message = sprintf('%s::purgeTrash(): Started...',
1693 get_class($this));
1694 $ilLog->write($message,$ilLog->WARNING);
1695
1696 // start purge process
1697 return $this->purgeObjects($a_nodes);
1698 }
1699
1709 function purgeUnboundObjects($a_nodes = NULL)
1710 {
1711 global $ilLog;
1712
1713 // check mode: purge
1714 if ($this->mode["purge"] !== true)
1715 {
1716 return false;
1717 }
1718
1719 $this->writeScanLogLine("\npurgeUnboundObjects:");
1720
1721 if ($a_nodes === NULL and isset($this->unbound_objects))
1722 {
1723 $a_nodes = $this->unbound_objects;
1724 }
1725
1726 $message = sprintf('%s::purgeUnboundObjects(): Started...',
1727 get_class($this));
1728 $ilLog->write($message,$ilLog->WARNING);
1729
1730 // start purge process
1731 return $this->purgeObjects($a_nodes);
1732 }
1733
1743 function purgeMissingObjects($a_nodes = NULL)
1744 {
1745 global $ilLog;
1746
1747 // check mode: purge
1748 if ($this->mode["purge"] !== true)
1749 {
1750 return false;
1751 }
1752
1753 $this->writeScanLogLine("\npurgeMissingObjects:");
1754
1755 if ($a_nodes === NULL and isset($this->missing_objects))
1756 {
1757 $a_nodes = $this->missing_objects;
1758 }
1759
1760 $message = sprintf('%s::purgeMissingObjects(): Started...',
1761 get_class($this));
1762 $ilLog->write($message,$ilLog->WARNING);
1763
1764 // start purge process
1765 return $this->purgeObjects($a_nodes);
1766 }
1767
1775 function purgeObjects($a_nodes)
1776 {
1777 global $ilias,$ilLog;
1778
1779 // Get purge limits
1780 $count_limit = $ilias->account->getPref("systemcheck_count_limit");
1781 if (! is_numeric($count_limit) || $count_limit < 0)
1782 {
1783 $count_limit = count($a_nodes);
1784 }
1785 $timestamp_limit = time();
1786 $age_limit = $ilias->account->getPref("systemcheck_age_limit");
1787 if (is_numeric($age_limit) && $age_limit > 0)
1788 {
1789 $timestamp_limit -= $age_limit * 60 * 60 * 24;
1790 }
1791 $type_limit = $ilias->account->getPref("systemcheck_type_limit");
1792 if ($type_limit)
1793 {
1794 $type_limit = trim($type_limit);
1795 if (strlen($type_limit) == 0)
1796 {
1797 $type_limit = null;
1798 }
1799 }
1800
1801 // handle wrong input
1802 if (!is_array($a_nodes))
1803 {
1804 $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1805 return false;
1806 }
1807
1808 // start delete process
1809 $this->writeScanLogLine("action\tref_id\tobj_id\ttype\telapsed\ttitle");
1810 $count = 0;
1811 foreach ($a_nodes as $node)
1812 {
1813 if ($type_limit && $node['type'] != $type_limit)
1814 {
1815 $this->writeScanLogLine("skip\t".
1816 $node['child']."\t\t".$node['type']."\t\t".$node['title']
1817 );
1818 continue;
1819 }
1820
1821
1822 $count++;
1823 if ($count > $count_limit)
1824 {
1825 $this->writeScanLogLine("Stopped purging after ".($count - 1)." objects, because count limit was reached: ".$count_limit);
1826 break;
1827 }
1828 if ($node["deleted_timestamp"] > $timestamp_limit)
1829 {
1830 $this->writeScanLogLine("Stopped purging after ".($count - 1)." objects, because timestamp limit was reached: ".date("c", $timestamp_limit));
1831 continue;
1832 }
1833
1834 $ref_id = ($node["child"]) ? $node["child"] : $node["ref_id"];
1835 $node_obj =& $ilias->obj_factory->getInstanceByRefId($ref_id,false);
1836
1837 if ($node_obj === false)
1838 {
1839 $this->invalid_objects[] = $node;
1840 continue;
1841 }
1842
1843 $message = sprintf('%s::purgeObjects(): Removing object (id:%s ref:%s)',
1844 get_class($this),
1845 $ref_id,
1846 $node_obj->getId());
1847 $ilLog->write($message,$ilLog->WARNING);
1848
1849 $startTime = microtime(true);
1850 $node_obj->delete();
1851 ilTree::_removeEntry($node["tree"],$ref_id);
1852 $endTime = microtime(true);
1853
1854 $this->writeScanLogLine("purged\t".$ref_id."\t".$node_obj->getId().
1855 "\t".$node['type']."\t".round($endTime-$startTime,1)."\t".$node['title']);
1856 }
1857
1858 $this->findInvalidChilds();
1859 $this->removeInvalidChilds();
1860
1861 return true;
1862 }
1863
1878 {
1879 global $tree,$ilLog;
1880
1881 $message = sprintf('%s::initGapsInTree(): Started...',
1882 get_class($this));
1883 $ilLog->write($message,$ilLog->WARNING);
1884
1885 // check mode: clean
1886 if ($this->mode["clean"] !== true)
1887 {
1888 return false;
1889 }
1890 $this->writeScanLogLine("\nrenumberTree:");
1891
1892 $tree->renumber(ROOT_FOLDER_ID);
1893
1894 $this->writeScanLogLine("done");
1895
1896 return true;
1897 }
1898
1909 {
1910 $call_loc = $error->backtrace[count($error->backtrace)-1];
1911 $num_args = count($call_loc["args"]);
1912
1913 if ($num_args > 0)
1914 {
1915 foreach ($call_loc["args"] as $arg)
1916 {
1917 $type = gettype($arg);
1918
1919 switch ($type)
1920 {
1921 case "string":
1922 $value = strlen($arg);
1923 break;
1924
1925 case "array":
1926 $value = count($arg);
1927 break;
1928
1929 case "object":
1930 $value = get_class($arg);
1931 break;
1932
1933 case "boolean":
1934 $value = ($arg) ? "true" : "false";
1935 break;
1936
1937 default:
1938 $value = $arg;
1939 break;
1940 }
1941
1942 $arg_list[] = array(
1943 "type" => $type,
1944 "value" => "(".$value.")"
1945 );
1946 }
1947
1948 foreach ($arg_list as $arg)
1949 {
1950 $arg_str .= implode("",$arg)." ";
1951 }
1952 }
1953
1954 $err_msg = "<br/><b>".$error->getCode().":</b> ".$error->getMessage()." in ".$call_loc["class"].$call_loc["type"].$call_loc["function"]."()".
1955 "<br/>Called from: ".basename($call_loc["file"])." , line ".$call_loc["line"].
1956 "<br/>Passed parameters: [".$num_args."] ".$arg_str."<br/>";
1957 printf($err_msg);
1958
1959 if ($error->getUserInfo())
1960 {
1961 printf("<br/>Parameter details:");
1962 echo "<pre>";
1963 var_dump($call_loc["args"]);
1964 echo "</pre>";
1965 }
1966
1967 if ($error->getCode() == FATAL)
1968 {
1969 exit();
1970 }
1971 }
1972
1973 function writeScanLogArray($a_arr)
1974 {
1975 if (!$this->isLogEnabled())
1976 {
1977 return false;
1978 }
1979
1980 foreach ($a_arr as $entry)
1981 {
1982 $this->scan_log->write(implode("\t",$entry));
1983 }
1984 }
1985
1986 function writeScanLogLine($a_msg)
1987 {
1988 if (!$this->isLogEnabled())
1989 {
1990 return false;
1991 }
1992
1993 $this->scan_log->write($a_msg);
1994 }
1995
1999 function hasScanLog()
2000 {
2001 // file check
2002 return is_file(CLIENT_DATA_DIR."/".$this->scan_log_file);
2003 }
2004
2008 function deleteScanLog()
2009 {
2010 @unlink(CLIENT_DATA_DIR."/".$this->scan_log_file);
2011 }
2012
2013 function readScanLog()
2014 {
2015 // file check
2016 if (! $this->hasScanLog())
2017 {
2018 return false;
2019 }
2020
2021 $scanfile =& file(CLIENT_DATA_DIR."/".$this->scan_log_file);
2022 if (!$scan_log =& $this->get_last_scan($scanfile))
2023 {
2024 return false;
2025 }
2026 // Ensure that memory is freed
2027 unset($scanfile);
2028
2029 return $scan_log;
2030 }
2031
2032 function get_last_scan($a_scan_log)
2033 {
2034 $logs = array_keys($a_scan_log,$this->scan_log_separator."\n");
2035
2036 if (count($logs) > 0)
2037 {
2038 return array_slice($a_scan_log,array_pop($logs)+2);
2039 }
2040
2041 return false;
2042 }
2043
2044 function checkTreeStructure($a_startnode = null)
2045 {
2046 global $tree;
2047
2048 $this->writeScanLogLine("\nchecking tree structure is disabled");
2049
2050 return false;
2051 }
2052
2059 function dumpTree()
2060 {
2061 global $ilDB;
2062
2063 $this->writeScanLogLine("BEGIN dumpTree:");
2064
2065 // collect nodes with duplicate child Id's
2066 // (We use this, to mark these nodes later in the output as being
2067 // erroneous.).
2068 $q = 'SELECT child FROM tree GROUP BY child HAVING COUNT(*) > 1';
2069 $r = $this->db->query($q);
2070 $duplicateNodes = array();
2071 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
2072 {
2073 $duplicateNodes[] = $row->child;
2074 }
2075
2076 // dump tree
2077 $q = "SELECT tree.*,ref.ref_id,dat.obj_id objobj_id,ref.obj_id refobj_id,ref.deleted,dat.* "
2078 ."FROM tree "
2079 ."RIGHT JOIN object_reference ref ON tree.child = ref.ref_id "
2080 ."RIGHT JOIN object_data dat ON ref.obj_id = dat.obj_id "
2081// ."LEFT JOIN usr_data usr ON usr.usr_id = dat.owner "
2082 ."ORDER BY tree, lft, type, dat.title";
2083 $r = $this->db->query($q);
2084
2085 $this->writeScanLogLine(
2086 '<table><tr>'
2087 .'<td>tree, child, parent, lft, rgt, depth</td>'
2088 .'<td>ref_id, ref.obj_id, deleted</td>'
2089 .'<td>obj_id, type, owner, title</td>'
2090 .'</tr>'
2091 );
2092
2093 // We use a stack to represent the path to the current node.
2094 // This allows us to do analyze the tree structure without having
2095 // to implement a recursive algorithm.
2096 $stack = array();
2097 $error_count = 0;
2098 $repository_tree_count = 0;
2099 $trash_trees_count = 0;
2100 $other_trees_count = 0;
2101 $not_in_tree_count = 0;
2102
2103 // The previous number is used for gap checking
2104 $previousNumber = 0;
2105
2106 $this->initWorkspaceObjects();
2107
2108 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
2109 {
2110 // workspace objects are not to be processed
2111 if($this->workspace_object_ids &&
2112 in_array($row->objobj_id, $this->workspace_object_ids))
2113 {
2114 continue;
2115 }
2116
2117 // If there is no entry in table tree for the object, we display it here
2118 if (is_null($row->child))
2119 {
2120 switch ($row->type) {
2121 case 'crsg' :
2122 case 'usr' :
2123 case 'typ' :
2124 case 'lng' :
2125 case 'rolt' :
2126 case 'role' :
2127 case 'mob' :
2128 case 'sty' :
2129 case 'tax' : // #13798
2130 // We are not interested in dumping these object types.
2131 continue 2;
2132 //break; NOT REACHED
2133 case 'file' :
2134 if (is_null($row->ref_id)) {
2135 // File objects can be part of a learning module.
2136 // In this case, they do not have a row in table object_reference.
2137 // We are not interested in dumping these file objects.
2138 continue 2;
2139 } else {
2140 // File objects which have a row in table object_reference, but
2141 // none in table tree are an error.
2142 $error_count++;
2143 $isRowOkay = false;
2144 $isParentOkay = false;
2145 $isLftOkay = false;
2146 $isRgtOkay = false;
2147 $isDepthOkay = false;
2148 }
2149 break;
2150
2151
2152 case 'fold':
2153 // ignore folders on media pools
2154 if($this->isMediaFolder($row->obj_id))
2155 {
2156 continue 2;
2157 }
2158 default :
2159 $error_count++;
2160 $isRowOkay = false;
2161 $isParentOkay = false;
2162 $isLftOkay = false;
2163 $isRgtOkay = false;
2164 $isDepthOkay = false;
2165 break;
2166 }
2167
2168 // moved here (below continues in switch)
2169 $not_in_tree_count++;
2170
2171 $this->writeScanLogLine(
2172 '<tr>'
2173 .'<td>'
2174 .(($isRowOkay) ? '' : '<font color=#ff0000>')
2175 .$row->tree.', '
2176 .$row->child.', '
2177 .(($isParentOkay) ? '' : 'parent:<b>')
2178 .$row->parent
2179 .(($isParentOkay) ? '' : '</b>')
2180 .', '
2181 .(($isLftOkay) ? '' : 'lft:<b>')
2182 .$row->lft
2183 .(($isLftOkay) ? '' : '</b>')
2184 .', '
2185 .(($isRgtOkay) ? '' : 'rgt:<b>')
2186 .$row->rgt
2187 .(($isRgtOkay) ? '' : '</b>')
2188 .', '
2189 .(($isDepthOkay) ? '' : 'depth:<b>')
2190 .$row->depth
2191 .(($isDepthOkay) ? '' : '</b>')
2192 .(($isRowOkay) ? '' : '</font>')
2193 .'</td><td>'
2194 .(($isRowOkay) ? '' : '<font color=#ff0000>')
2195 .(($isRefRefOkay && $isChildOkay) ? '' : 'ref.ref_id:<b>')
2196 .$row->ref_id
2197 .(($isRefRefOkay && $isChildOkay) ? '' : '</b>')
2198 .', '
2199 .(($isRefObjOkay) ? '' : 'ref.obj_id:<b>')
2200 .$row->refobj_id
2201 .(($isRefObjOkay) ? '' : '</b>')
2202 .(($isRowOkay) ? '' : '<font color=#ff0000>')
2203 .(($row->deleted != null) ? ', '.$row->deleted : '')
2204 .'</td><td>'
2205 .(($isRowOkay) ? '' : '<font color=#ff0000>')
2206 .$indent
2207 .$row->obj_id.', '
2208 .$row->type.', '
2209 .$row->login.', '
2210 .$row->title
2211 .(($isRowOkay) ? '' : ' <b>*ERROR*</b><font color=#ff0000>')
2212 .'</td>'
2213 .'</tr>'
2214 );
2215 continue;
2216 }
2217
2218 // Update stack
2219 // -------------------
2220 $indent = "";
2221 for ($i = 1; $i < $row->depth; $i++)
2222 {
2223 $indent .= ". ";
2224 }
2225
2226 // Initialize the stack and the previous number if we are in a new tree
2227 if (count($stack) == 0 || $stack[0]->tree != $row->tree)
2228 {
2229 $stack = array();
2230 $previousNumber = $row->lft - 1;
2231 $this->writeScanLogLine('<tr><td>&nbsp;</td></tr>');
2232 }
2233 // Pop old stack entries
2234
2235
2236 while (count($stack) > 0 && $stack[count($stack) - 1]->rgt < $row->lft)
2237 {
2238 $popped = array_pop($stack);
2239
2240 // check for gap
2241 $gap = $popped->rgt - $previousNumber - 1;
2242 if ($gap > 0)
2243 {
2244 $poppedIndent = "";
2245 for ($i = 1; $i < $popped->depth; $i++)
2246 {
2247 $poppedIndent .= ". ";
2248 }
2249 $this->writeScanLogLine(
2250 '<tr>'
2251 .'<td colspan=2><div align="right">'
2252 .'<font color=#00cc00>*gap* for '.($gap/2).' nodes at end of&nbsp;</font>'
2253 .'</div></td>'
2254 .'<td>'
2255 .'<font color=#00cc00>'
2256 .$poppedIndent
2257 .$popped->obj_id.', '
2258 .$popped->type.', '
2259 .$popped->login.', '
2260 .$popped->title
2261 .'</font>'
2262 .'</td>'
2263 .'</tr>'
2264 );
2265 }
2266 $previousNumber = $popped->rgt;
2267 unset($popped);
2268 }
2269
2270 // Check row integrity
2271 // -------------------
2272 $isRowOkay = true;
2273
2274 // Check tree structure
2275 $isChildOkay = true;
2276 $isParentOkay = true;
2277 $isLftOkay = true;
2278 $isRgtOkay = true;
2279 $isDepthOkay = true;
2280 $isGap = false;
2281
2282 if (count($stack) > 0)
2283 {
2284 $parent = $stack[count($stack) - 1];
2285 if ($parent->depth + 1 != $row->depth)
2286 {
2287 $isDepthOkay = false;
2288 $isRowOkay = false;
2289 }
2290 if ($parent->child != $row->parent)
2291 {
2292 $isParentOkay = false;
2293 $isRowOkay = false;
2294 }
2295 if($GLOBALS['ilSetting']->get('main_tree_impl','ns') == 'ns')
2296 {
2297 if ($parent->lft >= $row->lft)
2298 {
2299 $isLftOkay = false;
2300 $isRowOkay = false;
2301 }
2302 if ($parent->rgt <= $row->rgt)
2303 {
2304 $isRgtOkay = false;
2305 $isRowOkay = false;
2306 }
2307 }
2308 }
2309
2310 // Check lft rgt
2311 if($GLOBALS['ilSetting']->get('main_tree_impl','ns') == 'ns')
2312 {
2313 if ($row->lft >= $row->rgt)
2314 {
2315 $isLftOkay = false;
2316 $isRgtOkay = false;
2317 $isRowOkay = false;
2318 }
2319 }
2320 if (in_array($row->child, $duplicateNodes))
2321 {
2322 $isChildOkay = false;
2323 $isRowOkay = false;
2324 }
2325
2326 // Check object reference
2327 $isRefRefOkay = true;
2328 $isRefObjOkay = true;
2329 if ($row->ref_id == null)
2330 {
2331 $isRefRefOkay = false;
2332 $isRowOkay = false;
2333 }
2334 if ($row->obj_id == null)
2335 {
2336 $isRefObjOkay = false;
2337 $isRowOkay = false;
2338 }
2339
2340 if (! $isRowOkay)
2341 {
2342 $error_count++;
2343 }
2344
2345 // Check for gap between siblings,
2346 // and eventually write a log line
2347 if($GLOBALS['ilSetting']->get('main_tree_impl','ns') == 'ns')
2348 {
2349 $gap = $row->lft - $previousNumber - 1;
2350 $previousNumber = $row->lft;
2351 if ($gap > 0)
2352 {
2353 $this->writeScanLogLine(
2354 '<tr>'
2355 .'<td colspan=2><div align="right">'
2356 .'<font color=#00cc00>*gap* for '.($gap/2).' nodes between&nbsp;</font>'
2357 .'</div></td>'
2358 .'<td>'
2359 .'<font color=#00cc00>siblings</font>'
2360 .'</td>'
2361 .'</tr>'
2362 );
2363 }
2364 }
2365
2366 // Write log line
2367 // -------------------
2368 $this->writeScanLogLine(
2369 '<tr>'
2370 . '<td>'
2371 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2372 . $row->tree . ', '
2373 . $row->child . ', '
2374 . (($isParentOkay) ? '' : 'parent:<b>')
2375 . $row->parent
2376 . (($isParentOkay) ? '' : '</b>')
2377 . ', '
2378 . (($isLftOkay) ? '' : 'lft:<b>')
2379 . $row->lft
2380 . (($isLftOkay) ? '' : '</b>')
2381 . ', '
2382 . (($isRgtOkay) ? '' : 'rgt:<b>')
2383 . $row->rgt
2384 . (($isRgtOkay) ? '' : '</b>')
2385 . ', '
2386 . (($isDepthOkay) ? '' : 'depth:<b>')
2387 . $row->depth
2388 . (($isDepthOkay) ? '' : '</b>')
2389 . (($isRowOkay) ? '' : '</font>')
2390 . '</td><td>'
2391 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2392 . (($isRefRefOkay && $isChildOkay) ? '' : 'ref.ref_id:<b>')
2393 . $row->ref_id
2394 . (($isRefRefOkay && $isChildOkay) ? '' : '</b>')
2395 . ', '
2396 . (($isRefObjOkay) ? '' : 'ref.obj_id:<b>')
2397 . $row->refobj_id
2398 . (($isRefObjOkay) ? '' : '</b>')
2399 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2400 . (($row->tree < 0) ? ', ' . $row->deleted : '')
2401 . '</td><td>'
2402 . (($isRowOkay) ? '' : '<font color=#ff0000>')
2403 . $indent
2404 . $row->obj_id . ', '
2405 . $row->type . ', '
2406 . $row->login . ', '
2407 . $row->title
2408 . (($isRowOkay) ? '' : ' <b>*ERROR*</b><font color=#ff0000>')
2409 . '</td>'
2410 . '</tr>'
2411 );
2412
2413 // Update stack
2414 // -------------------
2415 // Push node on stack
2416 $stack[] = $row;
2417
2418 // Count nodes
2419 // -----------------
2420 if ($row->tree == 1)
2421 {
2422 $repository_tree_count++;
2423 }
2424 else if ($row->tree < 0)
2425 {
2426 $trash_trees_count++;
2427 }
2428 else
2429 {
2430 $other_trees_count++;
2431 }
2432 }
2433 //
2434 // Pop remaining stack entries
2435
2436 while (count($stack) > 0)
2437 {
2438 $popped = array_pop($stack);
2439
2440 // check for gap
2441 $gap = $popped->rgt - $previousNumber - 1;
2442 if ($gap > 0)
2443 {
2444 $poppedIndent = "";
2445 for ($i = 1; $i < $popped->depth; $i++)
2446 {
2447 $poppedIndent .= ". ";
2448 }
2449 $this->writeScanLogLine(
2450 '<tr>'
2451 .'<td colspan=2><div align="right">'
2452 .'<font color=#00cc00>*gap* for '.($gap/2).' nodes at end of&nbsp;</font>'
2453 .'</div></td>'
2454 .'<td>'
2455 .'<font color=#00cc00>'
2456 .$poppedIndent
2457 .$popped->obj_id.', '
2458 .$popped->type.', '
2459 .$popped->login.', '
2460 .$popped->title
2461 .'</font>'
2462 .'</td>'
2463 .'</tr>'
2464 );
2465 }
2466 $previousNumber = $popped->rgt;
2467 unset($popped);
2468 }
2469
2470 //
2471 $this->writeScanLogLine("</table>");
2472
2473 if ($error_count > 0)
2474 {
2475 $this->writeScanLogLine('<font color=#ff0000>'.$error_count.' errors found while dumping tree.</font>');
2476 }
2477 else
2478 {
2479 $this->writeScanLogLine('No errors found while dumping tree.');
2480 }
2481 $this->writeScanLogLine("$repository_tree_count nodes in repository tree");
2482 $this->writeScanLogLine("$trash_trees_count nodes in trash trees");
2483 $this->writeScanLogLine("$other_trees_count nodes in other trees");
2484 $this->writeScanLogLine("$not_in_tree_count nodes are not in a tree");
2485 $this->writeScanLogLine("END dumpTree");
2486
2487 return $error_count;
2488 }
2489
2490 protected function isMediaFolder($a_obj_id)
2491 {
2492 global $ilDB;
2493
2494 if(!is_array($this->media_pool_ids))
2495 {
2496 $this->media_pool_ids = array();
2497 $query = "SELECT child FROM mep_tree ";
2498 $res = $ilDB->query($query);
2499 while($row = $ilDB->fetchObject($res))
2500 {
2501 $this->media_pool_ids[] = $row->child;
2502 }
2503 }
2504
2505 return in_array($a_obj_id,$this->media_pool_ids) ? true : false;
2506 }
2507
2514 protected function isExcludedFromRecovery($a_type,$a_obj_id)
2515 {
2516 switch($a_type)
2517 {
2518 case 'fold':
2519 if(!$this->isMediaFolder($a_obj_id))
2520 {
2521 return false;
2522 }
2523 }
2524 return in_array($a_type,$this->object_types_exclude);
2525 }
2526
2527 protected function initWorkspaceObjects()
2528 {
2529 global $ilDB;
2530
2531 if($this->workspace_object_ids === null)
2532 {
2533 $this->workspace_object_ids = array();
2534
2535 // workspace objects
2536 $set = $ilDB->query("SELECT DISTINCT(obj_id) FROM object_reference_ws");
2537 while($row = $ilDB->fetchAssoc($set))
2538 {
2539 $this->workspace_object_ids[] = $row["obj_id"];
2540 }
2541
2542 // portfolios
2543 $set = $ilDB->query("SELECT id FROM usr_portfolio");
2544 while($row = $ilDB->fetchAssoc($set))
2545 {
2546 $this->workspace_object_ids[] = $row["id"];
2547 }
2548 }
2549 }
2550
2551 protected function filterWorkspaceObjects(array &$a_data, $a_index = "obj_id")
2552 {
2553 if(sizeof($a_data))
2554 {
2555 $this->initWorkspaceObjects();
2556
2557 // remove workspace objects from result objects
2558 if(is_array($this->workspace_object_ids))
2559 {
2560 foreach($a_data as $idx => $item)
2561 {
2562 if(in_array($item[$a_index], $this->workspace_object_ids))
2563 {
2564 unset($a_data[$idx]);
2565 }
2566 }
2567 }
2568 }
2569 }
2570} // END class.ilValidator
2571?>
sprintf('%.4f', $callTime)
date( 'd-M-Y', $objPHPExcel->getProperties() ->getCreated())
$error
Definition: Error.php:17
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 _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...
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...
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.
purgeTrash($a_nodes=NULL)
Removes all objects in trash from system.
writeScanLogLine($a_msg)
writeScanLogArray($a_arr)
filterWorkspaceObjects(array &$a_data, $a_index="obj_id")
getInvalidRolefolders()
Gets invalid rolefolders (same as missing objects)
removeInvalidRolefolders($a_invalid_rolefolders=NULL)
Removes invalid rolefolders.
restoreTrash($a_deleted_objects=NULL)
Restore all objects in trash to RecoveryFolder NOTE: All objects will be restored to top of RecoveryF...
purgeObjects($a_nodes)
removes objects from system
removeInvalidChilds($a_invalid_childs=NULL)
Removes all tree entries without any link to a valid object.
checkTreeStructure($a_startnode=null)
restoreSubTrees($a_nodes)
Restore objects (and their subobjects) to RecoveryFolder.
purgeUnboundObjects($a_nodes=NULL)
Removes all invalid objects from system.
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
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...
restoreMissingObjects($a_missing_objects=NULL)
Restores missing reference and/or tree entry for all objects found by this::getMissingObjects() Resto...
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.
purgeMissingObjects($a_nodes=NULL)
Removes all missing objects from system.
isMediaFolder($a_obj_id)
removeInvalidReferences($a_invalid_refs=NULL)
Removes all reference entries that are linked with invalid object IDs.
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.
getMissingObjects()
Gets all object entries with missing reference and/or tree entry.
$r
Definition: example_031.php:79
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
const DEBUG
global $lng
Definition: privfeed.php:17
$ref_id
Definition: sahs_server.php:39
global $ilDB
$a_type
Definition: workflow.php:93