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