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