ILIAS  eassessment Revision 61809
 All Data Structures Namespaces Files Functions Variables Groups Pages
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  $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  if (count($this->missing_objects) > 0)
605  {
606  $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
607  $this->writeScanLogArray($this->missing_objects);
608  return true;
609  }
610 
611  $this->writeScanLogLine("none");
612  return false;
613  }
614 
627  {
628  global $ilDB;
629 
630  // check mode: analyze
631  if ($this->mode["scan"] !== true)
632  {
633  return false;
634  }
635 
636  // init
637  $this->invalid_rolefolders = array();
638 
639  $this->writeScanLogLine("\nfindInvalidRolefolders:");
640 
641  // find rolfs without reference/tree entry
642  $q = "SELECT object_data.*, ref_id FROM object_data ".
643  "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
644  "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
645  "WHERE (object_reference.obj_id IS NULL OR tree.child IS NULL) ".
646  "AND object_data.type='rolf'";
647  $r = $this->db->query($q);
648 
649  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
650  {
651  $this->invalid_rolefolders[] = array(
652  "obj_id" => $row->obj_id,
653  "type" => $row->type,
654  "ref_id" => $row->ref_id,
655  "child" => $row->child,
656  "title" => $row->title,
657  "desc" => $row->description,
658  "owner" => $row->owner,
659  "create_date" => $row->create_date,
660  "last_update" => $row->last_update
661  );
662  }
663 
664  // find rolfs within RECOVERY FOLDER
665  $q = "SELECT object_data.*, ref_id FROM object_data ".
666  "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
667  "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
668  "WHERE object_reference.ref_id = ".$ilDB->quote(RECOVERY_FOLDER_ID,'integer')." ".
669  "AND object_data.type='rolf'";
670  $r = $this->db->query($q);
671 
672  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
673  {
674  $this->invalid_rolefolders[] = array(
675  "obj_id" => $row->obj_id,
676  "type" => $row->type,
677  "ref_id" => $row->ref_id,
678  "child" => $row->child,
679  "title" => $row->title,
680  "desc" => $row->description,
681  "owner" => $row->owner,
682  "create_date" => $row->create_date,
683  "last_update" => $row->last_update
684  );
685  }
686 
687  if (count($this->invalid_rolefolders) > 0)
688  {
689  $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
690  $this->writeScanLogArray($this->invalid_rolefolders);
691  return true;
692  }
693 
694  $this->writeScanLogLine("none");
695  return false;
696  }
697 
708  {
709  global $ilDB;
710 
711  // check mode: analyze
712  if ($this->mode["scan"] !== true)
713  {
714  return false;
715  }
716 
717  // init
718  $this->invalid_rbac_entries = array();
719 
720  $this->writeScanLogLine("\nfindInvalidRBACEntries:");
721 
722  $q = "SELECT object_data.*, ref_id FROM object_data ".
723  "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
724  "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
725  "WHERE (object_reference.obj_id IS NULL OR tree.child IS NULL) ".
726  "AND object_data.type='rolf'";
727  $r = $this->db->query($q);
728 
729  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
730  {
731  $this->invalid_rolefolders[] = array(
732  "obj_id" => $row->obj_id,
733  "type" => $row->type,
734  "ref_id" => $row->ref_id,
735  "child" => $row->child,
736  "title" => $row->title,
737  "desc" => $row->description,
738  "owner" => $row->owner,
739  "create_date" => $row->create_date,
740  "last_update" => $row->last_update
741  );
742  }
743 
744  // find rolfs within RECOVERY FOLDER
745  $q = "SELECT object_data.*, ref_id FROM object_data ".
746  "LEFT JOIN object_reference ON object_data.obj_id = object_reference.obj_id ".
747  "LEFT JOIN tree ON object_reference.ref_id = tree.child ".
748  "WHERE object_reference.ref_id =".$ilDB->quote(RECOVERY_FOLDER_ID)." ".
749  "AND object_data.type='rolf'";
750  $r = $this->db->query($q);
751 
752  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
753  {
754  $this->invalid_rolefolders[] = array(
755  "obj_id" => $row->obj_id,
756  "type" => $row->type,
757  "ref_id" => $row->ref_id,
758  "child" => $row->child,
759  "title" => $row->title,
760  "desc" => $row->description,
761  "owner" => $row->owner,
762  "create_date" => $row->create_date,
763  "last_update" => $row->last_update
764  );
765  }
766 
767  if (count($this->invalid_rolefolders) > 0)
768  {
769  $this->writeScanLogLine("obj_id\ttype\tref_id\tchild\ttitle\tdesc\towner\tcreate_date\tlast_update");
770  $this->writeScanLogArray($this->invalid_rolefolders);
771  return true;
772  }
773 
774  $this->writeScanLogLine("none");
775  return false;
776  }
777 
791  function getMissingObjects()
792  {
793  return $this->missing_objects;
794  }
795 
806  {
807  global $ilDB;
808 
809  // check mode: analyze
810  if ($this->mode["scan"] !== true)
811  {
812  return false;
813  }
814 
815  // init
816  $this->invalid_references = array();
817 
818  $this->writeScanLogLine("\nfindInvalidReferences:");
819  $q = "SELECT object_reference.* FROM object_reference ".
820  "LEFT JOIN object_data ON object_data.obj_id = object_reference.obj_id ".
821  "WHERE object_data.obj_id IS NULL ".
822  "OR ".$ilDB->in('object_data.type',$this->rbac_object_types,true,'text');
823  $r = $this->db->query($q);
824 
825  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
826  {
827  $this->invalid_references[] = array(
828  "ref_id" => $row->ref_id,
829  "obj_id" => $row->obj_id,
830  "msg" => "Object does not exist."
831  );
832  }
833 
834  if (count($this->invalid_references) > 0)
835  {
836  $this->writeScanLogLine("ref_id\t\tobj_id");
837  $this->writeScanLogArray($this->invalid_references);
838  return true;
839  }
840 
841  $this->writeScanLogLine("none");
842  return false;
843  }
844 
854  {
856  }
857 
867  function findInvalidChilds()
868  {
869  global $ilDB;
870 
871  // check mode: analyze
872  if ($this->mode["scan"] !== true)
873  {
874  return false;
875  }
876 
877  // init
878  $this->invalid_childs = array();
879 
880  $this->writeScanLogLine("\nfindInvalidChilds:");
881 
882  $q = "SELECT tree.*,object_reference.ref_id FROM tree ".
883  "LEFT JOIN object_reference ON tree.child = object_reference.ref_id ".
884  "LEFT JOIN object_data ON object_reference.obj_id = object_data.obj_id ".
885  "WHERE object_reference.ref_id IS NULL or object_data.obj_id IS NULL";
886  $r = $this->db->query($q);
887 
888  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
889  {
890  $this->invalid_childs[] = array(
891  "child" => $row->child,
892  "ref_id" => $row->ref_id,
893  "msg" => "No object found"
894  );
895  }
896 
897  if (count($this->invalid_childs) > 0)
898  {
899  $this->writeScanLogLine("child\t\tref_id");
900  $this->writeScanLogArray($this->invalid_childs);
901  return true;
902  }
903 
904  $this->writeScanLogLine("none");
905  return false;
906  }
907 
916  function getInvalidChilds()
917  {
918  return $this->invalid_childs;
919  }
920 
933  {
934  // check mode: analyze
935  if ($this->mode["scan"] !== true)
936  {
937  return false;
938  }
939 
940  // init
941  $this->unbound_objects = array();
942 
943  $this->writeScanLogLine("\nfindUnboundObjects:");
944 
945  $q = "SELECT T1.tree,T1.child,T1.parent,".
946  "T2.tree deleted,T2.parent grandparent ".
947  "FROM tree T1 ".
948  "LEFT JOIN tree T2 ON T2.child=T1.parent ".
949  "WHERE (T2.tree!=1 OR T2.tree IS NULL) AND T1.parent!=0";
950  $r = $this->db->query($q);
951 
952  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
953  {
954  // exclude deleted nodes
955  if ($row->deleted === NULL)
956  {
957  $this->unbound_objects[] = array(
958  "child" => $row->child,
959  "parent" => $row->parent,
960  "tree" => $row->tree,
961  "msg" => "No valid parent node found"
962  );
963  }
964  }
965 
966  if (count($this->unbound_objects) > 0)
967  {
968  $this->writeScanLogLine("child\t\tparent\ttree");
969  $this->writeScanLogArray($this->unbound_objects);
970  return true;
971  }
972 
973  $this->writeScanLogLine("none");
974  return false;
975  }
976 
989  {
990  // check mode: analyze
991  if ($this->mode["scan"] !== true)
992  {
993  return false;
994  }
995 
996  // init
997  $this->deleted_objects = array();
998 
999  $this->writeScanLogLine("\nfindDeletedObjects:");
1000 
1001  // Delete objects, start with the oldest objects first
1002  $query = "SELECT object_data.*,tree.tree,tree.child,tree.parent,deleted ".
1003  "FROM object_data ".
1004  "LEFT JOIN object_reference ON object_data.obj_id=object_reference.obj_id ".
1005  "LEFT JOIN tree ON tree.child=object_reference.ref_id ".
1006  " WHERE tree != 1 ".
1007  " ORDER BY deleted";
1008  $r = $this->db->query($query);
1009 
1010  include_once './Services/Calendar/classes/class.ilDateTime.php';
1011  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
1012  {
1013  $tmp_date = new ilDateTime($row->deleted,IL_CAL_DATETIME);
1014 
1015  $this->deleted_objects[] = array(
1016  "child" => $row->child,
1017  "parent" => $row->parent,
1018  "tree" => $row->tree,
1019  "type" => $row->type,
1020  "title" => $row->title,
1021  "desc" => $row->description,
1022  "owner" => $row->owner,
1023  "deleted" => $row->deleted,
1024  "deleted_timestamp" => $tmp_date->get(IL_CAL_UNIX),
1025  "create_date" => $row->create_date,
1026  "last_update" => $row->last_update
1027  );
1028  }
1029 
1030  if (count($this->deleted_objects) > 0)
1031  {
1032  $this->writeScanLogArray(array(array_keys($this->deleted_objects[0])));
1033  $this->writeScanLogArray($this->deleted_objects);
1034  return true;
1035  }
1036 
1037  $this->writeScanLogLine("none");
1038  return false;
1039  }
1040 
1041 
1056  {
1057  return $this->unbound_objects;
1058  }
1059 
1067  {
1068  return $this->deleted_objects;
1069  }
1070 
1080  {
1082  }
1083 
1093  function removeInvalidReferences($a_invalid_refs = NULL)
1094  {
1095  global $ilLog;
1096  global $ilDB;
1097 
1098  // check mode: clean
1099  if ($this->mode["clean"] !== true)
1100  {
1101  return false;
1102  }
1103 
1104  $this->writeScanLogLine("\nremoveInvalidReferences:");
1105 
1106  if ($a_invalid_refs === NULL and isset($this->invalid_references))
1107  {
1108  $a_invalid_refs =& $this->invalid_references;
1109  }
1110 
1111  // handle wrong input
1112  if (!is_array($a_invalid_refs))
1113  {
1114  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1115  return false;
1116  }
1117  // no unbound references found. do nothing
1118  if (count($a_invalid_refs) == 0)
1119  {
1120  $this->writeScanLogLine("none");
1121  return false;
1122  }
1123 
1124 /*******************
1125 removal starts here
1126 ********************/
1127 
1128  $message = sprintf('%s::removeInvalidReferences(): Started...',
1129  get_class($this));
1130  $ilLog->write($message,$ilLog->WARNING);
1131 
1132  foreach ($a_invalid_refs as $entry)
1133  {
1134  $query = "DELETE FROM object_reference WHERE ref_id= ".$this->db->quote($entry["ref_id"],'integer').
1135  " AND obj_id = ".$this->db->quote($entry["obj_id"],'integer')." ";
1136  $res = $ilDB->manipulate($query);
1137 
1138  $message = sprintf('%s::removeInvalidReferences(): Reference %s removed',
1139  get_class($this),
1140  $entry["ref_id"]);
1141  $ilLog->write($message,$ilLog->WARNING);
1142 
1143  $this->writeScanLogLine("Entry ".$entry["ref_id"]." removed");
1144  }
1145 
1146  return true;
1147  }
1148 
1158  function removeInvalidChilds($a_invalid_childs = NULL)
1159  {
1160  global $ilLog;
1161 
1162  // check mode: clean
1163  if ($this->mode["clean"] !== true)
1164  {
1165  return false;
1166  }
1167 
1168  $this->writeScanLogLine("\nremoveInvalidChilds:");
1169 
1170  if ($a_invalid_childs === NULL and isset($this->invalid_childs))
1171  {
1172  $a_invalid_childs =& $this->invalid_childs;
1173  }
1174 
1175  // handle wrong input
1176  if (!is_array($a_invalid_childs))
1177  {
1178  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1179  return false;
1180  }
1181 
1182  // no unbound childs found. do nothing
1183  if (count($a_invalid_childs) == 0)
1184  {
1185  $this->writeScanLogLine("none");
1186  return false;
1187  }
1188 
1189 /*******************
1190 removal starts here
1191 ********************/
1192 
1193  $message = sprintf('%s::removeInvalidChilds(): Started...',
1194  get_class($this));
1195  $ilLog->write($message,$ilLog->WARNING);
1196 
1197  foreach ($a_invalid_childs as $entry)
1198  {
1199  $q = "DELETE FROM tree WHERE child='".$entry["child"]."'";
1200  $this->db->query($q);
1201 
1202  $message = sprintf('%s::removeInvalidChilds(): Entry child=%s removed',
1203  get_class($this),
1204  $entry["child"]);
1205  $ilLog->write($message,$ilLog->WARNING);
1206 
1207  $this->writeScanLogLine("Entry ".$entry["child"]." removed");
1208  }
1209 
1210  return true;
1211  }
1212 
1223  function removeInvalidRolefolders($a_invalid_rolefolders = NULL)
1224  {
1225  global $ilias,$ilLog;
1226 
1227  // check mode: clean
1228  if ($this->mode["clean"] !== true)
1229  {
1230  return false;
1231  }
1232 
1233  $this->writeScanLogLine("\nremoveInvalidRolefolders:");
1234 
1235  if ($a_invalid_rolefolders === NULL and isset($this->invalid_rolefolders))
1236  {
1237  $a_invalid_rolefolders = $this->invalid_rolefolders;
1238  }
1239 
1240  // handle wrong input
1241  if (!is_array($a_invalid_rolefolders))
1242  {
1243  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1244  return false;
1245  }
1246 
1247  // no invalid rolefolders found. do nothing
1248  if (count($a_invalid_rolefolders) == 0)
1249  {
1250  $this->writeScanLogLine("none");
1251  return false;
1252  }
1253 
1254 /*******************
1255 removal starts here
1256 ********************/
1257 
1258  $removed = false;
1259 
1260  $message = sprintf('%s::removeInvalidRolefolders(): Started...',
1261  get_class($this));
1262  $ilLog->write($message,$ilLog->WARNING);
1263 
1264  foreach ($a_invalid_rolefolders as $rolf)
1265  {
1266  // restore ref_id in case of missing
1267  if ($rolf["ref_id"] === NULL)
1268  {
1269  $rolf["ref_id"] = $this->restoreReference($rolf["obj_id"]);
1270 
1271  $this->writeScanLogLine("Created missing reference '".$rolf["ref_id"]."' for rolefolder object '".$rolf["obj_id"]."'");
1272  }
1273 
1274  // now delete rolefolder
1275  $obj_data =& $ilias->obj_factory->getInstanceByRefId($rolf["ref_id"]);
1276  $obj_data->delete();
1277  unset($obj_data);
1278  $removed = true;
1279  $this->writeScanLogLine("Removed invalid rolefolder '".$rolf["title"]."' (id=".$rolf["obj_id"].",ref=".$rolf["ref_id"].") from system");
1280  }
1281 
1282  return $removed;
1283  }
1284 
1295  function restoreMissingObjects($a_missing_objects = NULL)
1296  {
1297  global $ilias,$rbacadmin,$ilLog;
1298 
1299  // check mode: restore
1300  if ($this->mode["restore"] !== true)
1301  {
1302  return false;
1303  }
1304 
1305  $this->writeScanLogLine("\nrestoreMissingObjects:");
1306 
1307  if ($a_missing_objects === NULL and isset($this->missing_objects))
1308  {
1309  $a_missing_objects = $this->missing_objects;
1310  }
1311 
1312  // handle wrong input
1313  if (!is_array($a_missing_objects))
1314  {
1315  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1316  return false;
1317  }
1318 
1319  // no missing objects found. do nothing
1320  if (count($a_missing_objects) == 0)
1321  {
1322  $this->writeScanLogLine("none");
1323  return false;
1324  }
1325 
1326 /*******************
1327 restore starts here
1328 ********************/
1329 
1330  $restored = false;
1331 
1332  $message = sprintf('%s::restoreMissingObjects(): Started...',
1333  get_class($this));
1334  $ilLog->write($message,$ilLog->WARNING);
1335 
1336  foreach ($a_missing_objects as $missing_obj)
1337  {
1338  // restore ref_id in case of missing
1339  if ($missing_obj["ref_id"] === NULL)
1340  {
1341  $missing_obj["ref_id"] = $this->restoreReference($missing_obj["obj_id"]);
1342 
1343  $this->writeScanLogLine("Created missing reference '".$missing_obj["ref_id"]."' for object '".$missing_obj["obj_id"]."'");
1344  }
1345 
1346  // put in tree under RecoveryFolder if not on exclude list
1347  #if (!in_array($missing_obj["type"],$this->object_types_exclude))
1348  if(!$this->isExcludedFromRecovery($missing_obj['type'],$missing_obj['obj_id']))
1349  {
1350  $rbacadmin->revokePermission($missing_obj["ref_id"]);
1351  $obj_data =& $ilias->obj_factory->getInstanceByRefId($missing_obj["ref_id"]);
1352  $obj_data->putInTree(RECOVERY_FOLDER_ID);
1353  $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1354  unset($obj_data);
1355  //$tree->insertNode($missing_obj["ref_id"],RECOVERY_FOLDER_ID);
1356  $restored = true;
1357  $this->writeScanLogLine("Restored object '".$missing_obj["title"]."' (id=".$missing_obj["obj_id"].",ref=".$missing_obj["ref_id"].") in 'Restored objects folder'");
1358  }
1359 
1360  // TODO: process rolefolders
1361  }
1362 
1363  return $restored;
1364  }
1365 
1375  function restoreReference($a_obj_id)
1376  {
1377  global $ilLog;
1378  global $ilDB;
1379 
1380  if (empty($a_obj_id))
1381  {
1382  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1383  return false;
1384  }
1385 
1386  $query = "INSERT INTO object_reference (ref_id,obj_id) ".
1387  "VALUES (".$next_id = $ilDB->nextId('object_reference').",".$this->db->quote($a_obj_id,'integer')." )";
1388  $res = $ilDB->manipulate($query);
1389 
1390  $message = sprintf('%s::restoreReference(): new reference %s for obj_id %s created',
1391  get_class($this),
1392  $next_id,
1393  $a_obj_id);
1394  $ilLog->write($message,$ilLog->WARNING);
1395 
1396  return $next_id;
1397  }
1398 
1409  function restoreUnboundObjects($a_unbound_objects = NULL)
1410  {
1411  global $ilLog;
1412 
1413  // check mode: restore
1414  if ($this->mode["restore"] !== true)
1415  {
1416  return false;
1417  }
1418 
1419  $this->writeScanLogLine("\nrestoreUnboundObjects:");
1420 
1421  if ($a_unbound_objects === NULL and isset($this->unbound_objects))
1422  {
1423  $a_unbound_objects = $this->unbound_objects;
1424  }
1425 
1426  // handle wrong input
1427  if (!is_array($a_unbound_objects))
1428  {
1429  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1430  return false;
1431  }
1432 
1433  $message = sprintf('%s::restoreUnboundObjects(): Started...',
1434  get_class($this));
1435  $ilLog->write($message,$ilLog->WARNING);
1436 
1437  // start restore process
1438  return $this->restoreSubTrees($a_unbound_objects);
1439  }
1440 
1450  function restoreTrash($a_deleted_objects = NULL)
1451  {
1452  global $ilLog;
1453 
1454  // check mode: restore
1455  if ($this->mode["restore_trash"] !== true)
1456  {
1457  return false;
1458  }
1459 
1460  $this->writeScanLogLine("\nrestoreTrash:");
1461 
1462  if ($a_deleted_objects === NULL and isset($this->deleted_objects))
1463  {
1464  $a_deleted_objects = $this->deleted_objects;
1465  }
1466 
1467  // handle wrong input
1468  if (!is_array($a_deleted_objects))
1469  {
1470  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1471  return false;
1472  }
1473 
1474  $message = sprintf('%s::restoreTrash(): Started...',
1475  get_class($this));
1476  $ilLog->write($message,$ilLog->WARNING);
1477 
1478  // start restore process
1479  $restored = $this->restoreDeletedObjects($a_deleted_objects);
1480 
1481  if ($restored)
1482  {
1483  $q = "DELETE FROM tree WHERE tree!=1";
1484  $this->db->query($q);
1485 
1486  $message = sprintf('%s::restoreTrash(): Removed all trees with tree id <> 1',
1487  get_class($this));
1488  $ilLog->write($message,$ilLog->WARNING);
1489 
1490  $this->writeScanLogLine("Old tree entries removed");
1491  }
1492 
1493  return $restored;
1494  }
1495 
1504  function restoreDeletedObjects($a_nodes)
1505  {
1506  global $tree,$rbacadmin,$ilias,$ilLog;
1507 //vd($a_nodes);exit;
1508  // handle wrong input
1509  if (!is_array($a_nodes))
1510  {
1511  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1512  return false;
1513  }
1514 
1515  // no invalid parents found. do nothing
1516  if (count($a_nodes) == 0)
1517  {
1518  $this->writeScanLogLine("none");
1519  return false;
1520  }
1521 
1522  $message = sprintf('%s::restoreDeletedObjects()): Started...',
1523  get_class($this));
1524  $ilLog->write($message,$ilLog->WARNING);
1525 
1526  // first delete all rolefolders
1527  // don't save rolefolders, remove them
1528  // TODO process ROLE_FOLDER_ID
1529  foreach ($a_nodes as $key => $node)
1530  {
1531  if ($node["type"] == "rolf")
1532  {
1533  // delete old tree entries
1534  $tree->deleteTree($node);
1535 
1536  $obj_data =& $ilias->obj_factory->getInstanceByRefId($node["child"]);
1537  $obj_data->delete();
1538  unset($a_nodes[$key]);
1539  }
1540  }
1541 
1542  // process move
1543  foreach ($a_nodes as $node)
1544  {
1545  // delete old tree entries
1546  $tree->deleteTree($node);
1547 
1548  $rbacadmin->revokePermission($node["child"]);
1549  $obj_data =& $ilias->obj_factory->getInstanceByRefId($node["child"]);
1550  $obj_data->putInTree(RECOVERY_FOLDER_ID);
1551  $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1552  }
1553 
1554  return true;
1555  }
1556 
1565  function restoreSubTrees ($a_nodes)
1566  {
1567  global $tree,$rbacadmin,$ilias,$ilLog;
1568 
1569  // handle wrong input
1570  if (!is_array($a_nodes))
1571  {
1572  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1573  return false;
1574  }
1575 
1576  // no invalid parents found. do nothing
1577  if (count($a_nodes) == 0)
1578  {
1579  $this->writeScanLogLine("none");
1580  return false;
1581  }
1582 
1583 /*******************
1584 restore starts here
1585 ********************/
1586 
1587  $subnodes = array();
1588  $topnode = array();
1589 
1590  $message = sprintf('%s::restoreSubTrees(): Started...',
1591  get_class($this));
1592  $ilLog->write($message,$ilLog->WARNING);
1593 
1594  // process move subtree
1595  foreach ($a_nodes as $node)
1596  {
1597  // get node data
1598  $topnode = $tree->getNodeData($node["child"], $node['tree']);
1599 
1600  // don't save rolefolders, remove them
1601  // TODO process ROLE_FOLDER_ID
1602  if ($topnode["type"] == "rolf")
1603  {
1604  $rolfObj = $ilias->obj_factory->getInstanceByRefId($topnode["child"]);
1605  $rolfObj->delete();
1606  unset($top_node);
1607  unset($rolfObj);
1608  continue;
1609  }
1610 
1611  // get subnodes of top nodes
1612  $subnodes[$node["child"]] = $tree->getSubtree($topnode);
1613 
1614  // delete old tree entries
1615  $tree->deleteTree($topnode);
1616  }
1617 
1618  // now move all subtrees to new location
1619  // TODO: this whole put in place again stuff needs revision. Permission settings get lost.
1620  foreach ($subnodes as $key => $subnode)
1621  {
1622 
1623  // first paste top_node ...
1624  $rbacadmin->revokePermission($key);
1625  $obj_data =& $ilias->obj_factory->getInstanceByRefId($key);
1626  $obj_data->putInTree(RECOVERY_FOLDER_ID);
1627  $obj_data->setPermissions(RECOVERY_FOLDER_ID);
1628 
1629  $this->writeScanLogLine("Object '".$obj_data->getId()."' restored.");
1630 
1631  // ... remove top_node from list ...
1632  array_shift($subnode);
1633 
1634  // ... insert subtree of top_node if any subnodes exist
1635  if (count($subnode) > 0)
1636  {
1637  foreach ($subnode as $node)
1638  {
1639  $rbacadmin->revokePermission($node["child"]);
1640  $obj_data =& $ilias->obj_factory->getInstanceByRefId($node["child"]);
1641  $obj_data->putInTree($node["parent"]);
1642  $obj_data->setPermissions($node["parent"]);
1643 
1644  $this->writeScanLogLine("Object '".$obj_data->getId()."' restored.");
1645  }
1646  }
1647  }
1648 
1649  // final clean up
1650  $this->findInvalidChilds();
1651  $this->removeInvalidChilds();
1652 
1653  return true;
1654  }
1655 
1665  function purgeTrash($a_nodes = NULL)
1666  {
1667  global $ilLog;
1668 
1669  // check mode: purge_trash
1670  if ($this->mode["purge_trash"] !== true)
1671  {
1672  return false;
1673  }
1674 
1675  $this->writeScanLogLine("\npurgeTrash:");
1676 
1677  if ($a_nodes === NULL and isset($this->deleted_objects))
1678  {
1679  $a_nodes = $this->deleted_objects;
1680  }
1681 
1682  $message = sprintf('%s::purgeTrash(): Started...',
1683  get_class($this));
1684  $ilLog->write($message,$ilLog->WARNING);
1685 
1686  // start purge process
1687  return $this->purgeObjects($a_nodes);
1688  }
1689 
1699  function purgeUnboundObjects($a_nodes = NULL)
1700  {
1701  global $ilLog;
1702 
1703  // check mode: purge
1704  if ($this->mode["purge"] !== true)
1705  {
1706  return false;
1707  }
1708 
1709  $this->writeScanLogLine("\npurgeUnboundObjects:");
1710 
1711  if ($a_nodes === NULL and isset($this->unbound_objects))
1712  {
1713  $a_nodes = $this->unbound_objects;
1714  }
1715 
1716  $message = sprintf('%s::purgeUnboundObjects(): Started...',
1717  get_class($this));
1718  $ilLog->write($message,$ilLog->WARNING);
1719 
1720  // start purge process
1721  return $this->purgeObjects($a_nodes);
1722  }
1723 
1733  function purgeMissingObjects($a_nodes = NULL)
1734  {
1735  global $ilLog;
1736 
1737  // check mode: purge
1738  if ($this->mode["purge"] !== true)
1739  {
1740  return false;
1741  }
1742 
1743  $this->writeScanLogLine("\npurgeMissingObjects:");
1744 
1745  if ($a_nodes === NULL and isset($this->missing_objects))
1746  {
1747  $a_nodes = $this->missing_objects;
1748  }
1749 
1750  $message = sprintf('%s::purgeMissingObjects(): Started...',
1751  get_class($this));
1752  $ilLog->write($message,$ilLog->WARNING);
1753 
1754  // start purge process
1755  return $this->purgeObjects($a_nodes);
1756  }
1757 
1765  function purgeObjects($a_nodes)
1766  {
1767  global $ilias,$ilLog;
1768 
1769  // Get purge limits
1770  $count_limit = $ilias->account->getPref("systemcheck_count_limit");
1771  if (! is_numeric($count_limit) || $count_limit < 0)
1772  {
1773  $count_limit = count($a_nodes);
1774  }
1775  $timestamp_limit = time();
1776  $age_limit = $ilias->account->getPref("systemcheck_age_limit");
1777  if (is_numeric($age_limit) && $age_limit > 0)
1778  {
1779  $timestamp_limit -= $age_limit * 60 * 60 * 24;
1780  }
1781  else
1782  {
1783  $timestamp_limit = 0;
1784  }
1785  $type_limit = $ilias->account->getPref("systemcheck_type_limit");
1786  if ($type_limit)
1787  {
1788  $type_limit = trim($type_limit);
1789  if (strlen($type_limit) == 0)
1790  {
1791  $type_limit = null;
1792  }
1793  }
1794 
1795  // handle wrong input
1796  if (!is_array($a_nodes))
1797  {
1798  $this->throwError(INVALID_PARAM, WARNING, DEBUG);
1799  return false;
1800  }
1801 
1802  // start delete process
1803  $this->writeScanLogLine("action\tref_id\tobj_id\ttype\telapsed\ttitle");
1804  $count = 0;
1805  foreach ($a_nodes as $node)
1806  {
1807  if ($type_limit && $node['type'] != $type_limit)
1808  {
1809  $this->writeScanLogLine("skip\t".
1810  $node['child']."\t\t".$node['type']."\t\t".$node['title']
1811  );
1812  continue;
1813  }
1814 
1815 
1816  $count++;
1817  if ($count > $count_limit)
1818  {
1819  $this->writeScanLogLine("Stopped purging after ".($count - 1)." objects, because count limit was reached: ".$count_limit);
1820  break;
1821  }
1822  if ($node["deleted_timestamp"] > $timestamp_limit)
1823  {
1824  $this->writeScanLogLine("Stopped purging after ".($count - 1)." objects, because timestamp limit was reached: ".date("c", $timestamp_limit));
1825  break;
1826  }
1827 
1828  $ref_id = ($node["child"]) ? $node["child"] : $node["ref_id"];
1829  $node_obj =& $ilias->obj_factory->getInstanceByRefId($ref_id,false);
1830 
1831  if ($node_obj === false)
1832  {
1833  $this->invalid_objects[] = $node;
1834  continue;
1835  }
1836 
1837  $message = sprintf('%s::purgeObjects(): Removing object (id:%s ref:%s)',
1838  get_class($this),
1839  $ref_id,
1840  $node_obj->getId());
1841  $ilLog->write($message,$ilLog->WARNING);
1842 
1843  $startTime = microtime(true);
1844  $node_obj->delete();
1845  ilTree::_removeEntry($node["tree"],$ref_id);
1846  $endTime = microtime(true);
1847 
1848  $this->writeScanLogLine("purged\t".$ref_id."\t".$node_obj->getId().
1849  "\t".$node['type']."\t".round($endTime-$startTime,1)."\t".$node['title']);
1850  }
1851 
1852  $this->findInvalidChilds();
1853  $this->removeInvalidChilds();
1854 
1855  return true;
1856  }
1857 
1871  function initGapsInTree()
1872  {
1873  global $tree,$ilLog;
1874 
1875  $message = sprintf('%s::initGapsInTree(): Started...',
1876  get_class($this));
1877  $ilLog->write($message,$ilLog->WARNING);
1878 
1879  // check mode: clean
1880  if ($this->mode["clean"] !== true)
1881  {
1882  return false;
1883  }
1884  $this->writeScanLogLine("\nrenumberTree:");
1885 
1886  $tree->renumber(ROOT_FOLDER_ID);
1887 
1888  $this->writeScanLogLine("done");
1889 
1890  return true;
1891  }
1892 
1902  function handleErr($error)
1903  {
1904  $call_loc = $error->backtrace[count($error->backtrace)-1];
1905  $num_args = count($call_loc["args"]);
1906 
1907  if ($num_args > 0)
1908  {
1909  foreach ($call_loc["args"] as $arg)
1910  {
1911  $type = gettype($arg);
1912 
1913  switch ($type)
1914  {
1915  case "string":
1916  $value = strlen($arg);
1917  break;
1918 
1919  case "array":
1920  $value = count($arg);
1921  break;
1922 
1923  case "object":
1924  $value = get_class($arg);
1925  break;
1926 
1927  case "boolean":
1928  $value = ($arg) ? "true" : "false";
1929  break;
1930 
1931  default:
1932  $value = $arg;
1933  break;
1934  }
1935 
1936  $arg_list[] = array(
1937  "type" => $type,
1938  "value" => "(".$value.")"
1939  );
1940  }
1941 
1942  foreach ($arg_list as $arg)
1943  {
1944  $arg_str .= implode("",$arg)." ";
1945  }
1946  }
1947 
1948  $err_msg = "<br/><b>".$error->getCode().":</b> ".$error->getMessage()." in ".$call_loc["class"].$call_loc["type"].$call_loc["function"]."()".
1949  "<br/>Called from: ".basename($call_loc["file"])." , line ".$call_loc["line"].
1950  "<br/>Passed parameters: [".$num_args."] ".$arg_str."<br/>";
1951  printf($err_msg);
1952 
1953  if ($error->getUserInfo())
1954  {
1955  printf("<br/>Parameter details:");
1956  echo "<pre>";
1957  var_dump($call_loc["args"]);
1958  echo "</pre>";
1959  }
1960 
1961  if ($error->getCode() == FATAL)
1962  {
1963  exit();
1964  }
1965  }
1966 
1967  function writeScanLogArray($a_arr)
1968  {
1969  if (!$this->isLogEnabled())
1970  {
1971  return false;
1972  }
1973 
1974  foreach ($a_arr as $entry)
1975  {
1976  $this->scan_log->write(implode("\t",$entry));
1977  }
1978  }
1979 
1980  function writeScanLogLine($a_msg)
1981  {
1982  if (!$this->isLogEnabled())
1983  {
1984  return false;
1985  }
1986 
1987  $this->scan_log->write($a_msg);
1988  }
1989 
1993  function hasScanLog()
1994  {
1995  // file check
1996  return is_file(CLIENT_DATA_DIR."/".$this->scan_log_file);
1997  }
1998 
2002  function deleteScanLog()
2003  {
2004  @unlink(CLIENT_DATA_DIR."/".$this->scan_log_file);
2005  }
2006 
2007  function readScanLog()
2008  {
2009  // file check
2010  if (! $this->hasScanLog())
2011  {
2012  return false;
2013  }
2014 
2015  $scanfile =& file(CLIENT_DATA_DIR."/".$this->scan_log_file);
2016  if (!$scan_log =& $this->get_last_scan($scanfile))
2017  {
2018  return false;
2019  }
2020  // Ensure that memory is freed
2021  unset($scanfile);
2022 
2023  return $scan_log;
2024  }
2025 
2026  function get_last_scan($a_scan_log)
2027  {
2028  $logs = array_keys($a_scan_log,$this->scan_log_separator."\n");
2029 
2030  if (count($logs) > 0)
2031  {
2032  return array_slice($a_scan_log,array_pop($logs)+2);
2033  }
2034 
2035  return false;
2036  }
2037 
2038  function checkTreeStructure($a_startnode = null)
2039  {
2040  global $tree;
2041 
2042  $this->writeScanLogLine("\nchecking tree structure is disabled");
2043 
2044  return false;
2045  }
2046 
2053  function dumpTree()
2054  {
2055  global $ilDB;
2056 
2057  $this->writeScanLogLine("BEGIN dumpTree:");
2058 
2059  // collect nodes with duplicate child Id's
2060  // (We use this, to mark these nodes later in the output as being
2061  // erroneous.).
2062  $q = 'SELECT child FROM tree GROUP BY child HAVING COUNT(*) > 1';
2063  $r = $this->db->query($q);
2064  $duplicateNodes = array();
2065  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
2066  {
2067  $duplicateNodes[] = $row->child;
2068  }
2069 
2070  // dump tree
2071  $q = "SELECT tree.*,ref.ref_id,ref.obj_id refobj_id,ref.deleted,dat.*,usr.login "
2072  ."FROM tree "
2073  ."RIGHT JOIN object_reference ref ON tree.child = ref.ref_id "
2074  ."RIGHT JOIN object_data dat ON ref.obj_id = dat.obj_id "
2075  ."LEFT JOIN usr_data usr ON usr.usr_id = dat.owner "
2076  ."ORDER BY tree, lft, type, dat.title";
2077  $r = $this->db->query($q);
2078 
2079  $this->writeScanLogLine(
2080  '<table><tr>'
2081  .'<td>tree, child, parent, lft, rgt, depth</td>'
2082  .'<td>ref_id, ref.obj_id, deleted</td>'
2083  .'<td>obj_id, type, owner, title</td>'
2084  .'</tr>'
2085  );
2086 
2087  // We use a stack to represent the path to the current node.
2088  // This allows us to do analyze the tree structure without having
2089  // to implement a recursive algorithm.
2090  $stack = array();
2091  $error_count = 0;
2092  $repository_tree_count = 0;
2093  $trash_trees_count = 0;
2094  $other_trees_count = 0;
2095  $not_in_tree_count = 0;
2096 
2097  // The previous number is used for gap checking
2098  $previousNumber = 0;
2099 
2100  while ($row = $r->fetchRow(DB_FETCHMODE_OBJECT))
2101  {
2102  // If there is no entry in table tree for the object, we display it here
2103  if (is_null($row->child))
2104  {
2105  $not_in_tree_count++;
2106  switch ($row->type) {
2107  case 'crsg' :
2108  case 'usr' :
2109  case 'typ' :
2110  case 'lng' :
2111  case 'rolt' :
2112  case 'role' :
2113  case 'mob' :
2114  case 'sty' :
2115  // We are not interested in dumping these object types.
2116  continue 2;
2117  //break; NOT REACHED
2118  case 'file' :
2119  if (is_null($row->ref_id)) {
2120  // File objects can be part of a learning module.
2121  // In this case, they do not have a row in table object_reference.
2122  // We are not interested in dumping these file objects.
2123  continue 2;
2124  } else {
2125  // File objects which have a row in table object_reference, but
2126  // none in table tree are an error.
2127  $error_count++;
2128  $isRowOkay = false;
2129  $isParentOkay = false;
2130  $isLftOkay = false;
2131  $isRgtOkay = false;
2132  $isDepthOkay = false;
2133  }
2134  break;
2135 
2136 
2137  case 'fold':
2138  // ignore folders on media pools
2139  if($this->isMediaFolder($row->obj_id))
2140  {
2141  continue 2;
2142  }
2143  default :
2144  $error_count++;
2145  $isRowOkay = false;
2146  $isParentOkay = false;
2147  $isLftOkay = false;
2148  $isRgtOkay = false;
2149  $isDepthOkay = false;
2150  break;
2151  }
2152 
2153  $this->writeScanLogLine(
2154  '<tr>'
2155  .'<td>'
2156  .(($isRowOkay) ? '' : '<font color=#ff0000>')
2157  .$row->tree.', '
2158  .$row->child.', '
2159  .(($isParentOkay) ? '' : 'parent:<b>')
2160  .$row->parent
2161  .(($isParentOkay) ? '' : '</b>')
2162  .', '
2163  .(($isLftOkay) ? '' : 'lft:<b>')
2164  .$row->lft
2165  .(($isLftOkay) ? '' : '</b>')
2166  .', '
2167  .(($isRgtOkay) ? '' : 'rgt:<b>')
2168  .$row->rgt
2169  .(($isRgtOkay) ? '' : '</b>')
2170  .', '
2171  .(($isDepthOkay) ? '' : 'depth:<b>')
2172  .$row->depth
2173  .(($isDepthOkay) ? '' : '</b>')
2174  .(($isRowOkay) ? '' : '</font>')
2175  .'</td><td>'
2176  .(($isRowOkay) ? '' : '<font color=#ff0000>')
2177  .(($isRefRefOkay && $isChildOkay) ? '' : 'ref.ref_id:<b>')
2178  .$row->ref_id
2179  .(($isRefRefOkay && $isChildOkay) ? '' : '</b>')
2180  .', '
2181  .(($isRefObjOkay) ? '' : 'ref.obj_id:<b>')
2182  .$row->refobj_id
2183  .(($isRefObjOkay) ? '' : '</b>')
2184  .(($isRowOkay) ? '' : '<font color=#ff0000>')
2185  .(($row->deleted != null) ? ', '.$row->deleted : '')
2186  .'</td><td>'
2187  .(($isRowOkay) ? '' : '<font color=#ff0000>')
2188  .$indent
2189  .$row->obj_id.', '
2190  .$row->type.', '
2191  .$row->login.', '
2192  .$row->title
2193  .(($isRowOkay) ? '' : ' <b>*ERROR*</b><font color=#ff0000>')
2194  .'</td>'
2195  .'</tr>'
2196  );
2197  continue;
2198  }
2199 
2200  // Update stack
2201  // -------------------
2202  $indent = "";
2203  for ($i = 1; $i < $row->depth; $i++)
2204  {
2205  $indent .= ". ";
2206  }
2207 
2208  // Initialize the stack and the previous number if we are in a new tree
2209  if (count($stack) == 0 || $stack[0]->tree != $row->tree)
2210  {
2211  $stack = array();
2212  $previousNumber = $row->lft - 1;
2213  $this->writeScanLogLine('<tr><td>&nbsp;</td></tr>');
2214  }
2215  // Pop old stack entries
2216  while (count($stack) > 0 && $stack[count($stack) - 1]->rgt < $row->lft)
2217  {
2218  $popped = array_pop($stack);
2219 
2220  // check for gap
2221  $gap = $popped->rgt - $previousNumber - 1;
2222  if ($gap > 0)
2223  {
2224  $poppedIndent = "";
2225  for ($i = 1; $i < $popped->depth; $i++)
2226  {
2227  $poppedIndent .= ". ";
2228  }
2229  $this->writeScanLogLine(
2230  '<tr>'
2231  .'<td colspan=2><div align="right">'
2232  .'<font color=#00cc00>*gap* for '.($gap/2).' nodes at end of&nbsp;</font>'
2233  .'</div></td>'
2234  .'<td>'
2235  .'<font color=#00cc00>'
2236  .$poppedIndent
2237  .$popped->obj_id.', '
2238  .$popped->type.', '
2239  .$popped->login.', '
2240  .$popped->title
2241  .'</font>'
2242  .'</td>'
2243  .'</tr>'
2244  );
2245  }
2246  $previousNumber = $popped->rgt;
2247  unset($popped);
2248  }
2249 
2250  // Check row integrity
2251  // -------------------
2252  $isRowOkay = true;
2253 
2254  // Check tree structure
2255  $isChildOkay = true;
2256  $isParentOkay = true;
2257  $isLftOkay = true;
2258  $isRgtOkay = true;
2259  $isDepthOkay = true;
2260  $isGap = false;
2261 
2262  if (count($stack) > 0)
2263  {
2264  $parent = $stack[count($stack) - 1];
2265  if ($parent->depth + 1 != $row->depth)
2266  {
2267  $isDepthOkay = false;
2268  $isRowOkay = false;
2269  }
2270  if ($parent->child != $row->parent)
2271  {
2272  $isParentOkay = false;
2273  $isRowOkay = false;
2274  }
2275  if ($parent->lft >= $row->lft)
2276  {
2277  $isLftOkay = false;
2278  $isRowOkay = false;
2279  }
2280  if ($parent->rgt <= $row->rgt)
2281  {
2282  $isRgtOkay = false;
2283  $isRowOkay = false;
2284  }
2285  }
2286 
2287  // Check lft rgt
2288  if ($row->lft >= $row->rgt)
2289  {
2290  $isLftOkay = false;
2291  $isRgtOkay = false;
2292  $isRowOkay = false;
2293  }
2294  if (in_array($row->child, $duplicateNodes))
2295  {
2296  $isChildOkay = false;
2297  $isRowOkay = false;
2298  }
2299 
2300  // Check object reference
2301  $isRefRefOkay = true;
2302  $isRefObjOkay = true;
2303  if ($row->ref_id == null)
2304  {
2305  $isRefRefOkay = false;
2306  $isRowOkay = false;
2307  }
2308  if ($row->obj_id == null)
2309  {
2310  $isRefObjOkay = false;
2311  $isRowOkay = false;
2312  }
2313 
2314  if (! $isRowOkay)
2315  {
2316  $error_count++;
2317  }
2318 
2319  // Check for gap between siblings,
2320  // and eventually write a log line
2321  $gap = $row->lft - $previousNumber - 1;
2322  $previousNumber = $row->lft;
2323  if ($gap > 0)
2324  {
2325  $this->writeScanLogLine(
2326  '<tr>'
2327  .'<td colspan=2><div align="right">'
2328  .'<font color=#00cc00>*gap* for '.($gap/2).' nodes between&nbsp;</font>'
2329  .'</div></td>'
2330  .'<td>'
2331  .'<font color=#00cc00>siblings</font>'
2332  .'</td>'
2333  .'</tr>'
2334  );
2335  }
2336 
2337  // Write log line
2338  // -------------------
2339  $this->writeScanLogLine(
2340  '<tr>'
2341  .'<td>'
2342  .(($isRowOkay) ? '' : '<font color=#ff0000>')
2343  .$row->tree.', '
2344  .$row->child.', '
2345  .(($isParentOkay) ? '' : 'parent:<b>')
2346  .$row->parent
2347  .(($isParentOkay) ? '' : '</b>')
2348  .', '
2349  .(($isLftOkay) ? '' : 'lft:<b>')
2350  .$row->lft
2351  .(($isLftOkay) ? '' : '</b>')
2352  .', '
2353  .(($isRgtOkay) ? '' : 'rgt:<b>')
2354  .$row->rgt
2355  .(($isRgtOkay) ? '' : '</b>')
2356  .', '
2357  .(($isDepthOkay) ? '' : 'depth:<b>')
2358  .$row->depth
2359  .(($isDepthOkay) ? '' : '</b>')
2360  .(($isRowOkay) ? '' : '</font>')
2361  .'</td><td>'
2362  .(($isRowOkay) ? '' : '<font color=#ff0000>')
2363  .(($isRefRefOkay && $isChildOkay) ? '' : 'ref.ref_id:<b>')
2364  .$row->ref_id
2365  .(($isRefRefOkay && $isChildOkay) ? '' : '</b>')
2366  .', '
2367  .(($isRefObjOkay) ? '' : 'ref.obj_id:<b>')
2368  .$row->refobj_id
2369  .(($isRefObjOkay) ? '' : '</b>')
2370  .(($isRowOkay) ? '' : '<font color=#ff0000>')
2371  .(($row->tree < 0) ? ', '.$row->deleted : '')
2372  .'</td><td>'
2373  .(($isRowOkay) ? '' : '<font color=#ff0000>')
2374  .$indent
2375  .$row->obj_id.', '
2376  .$row->type.', '
2377  .$row->login.', '
2378  .$row->title
2379  .(($isRowOkay) ? '' : ' <b>*ERROR*</b><font color=#ff0000>')
2380  .'</td>'
2381  .'</tr>'
2382  );
2383 
2384  // Update stack
2385  // -------------------
2386  // Push node on stack
2387  $stack[] = $row;
2388 
2389  // Count nodes
2390  // -----------------
2391  if ($row->tree == 1)
2392  {
2393  $repository_tree_count++;
2394  }
2395  else if ($row->tree < 0)
2396  {
2397  $trash_trees_count++;
2398  }
2399  else
2400  {
2401  $other_trees_count++;
2402  }
2403 
2404  }
2405  //
2406  // Pop remaining stack entries
2407  while (count($stack) > 0)
2408  {
2409  $popped = array_pop($stack);
2410 
2411  // check for gap
2412  $gap = $popped->rgt - $previousNumber - 1;
2413  if ($gap > 0)
2414  {
2415  $poppedIndent = "";
2416  for ($i = 1; $i < $popped->depth; $i++)
2417  {
2418  $poppedIndent .= ". ";
2419  }
2420  $this->writeScanLogLine(
2421  '<tr>'
2422  .'<td colspan=2><div align="right">'
2423  .'<font color=#00cc00>*gap* for '.($gap/2).' nodes at end of&nbsp;</font>'
2424  .'</div></td>'
2425  .'<td>'
2426  .'<font color=#00cc00>'
2427  .$poppedIndent
2428  .$popped->obj_id.', '
2429  .$popped->type.', '
2430  .$popped->login.', '
2431  .$popped->title
2432  .'</font>'
2433  .'</td>'
2434  .'</tr>'
2435  );
2436  }
2437  $previousNumber = $popped->rgt;
2438  unset($popped);
2439  }
2440 
2441  //
2442  $this->writeScanLogLine("</table>");
2443 
2444  if ($error_count > 0)
2445  {
2446  $this->writeScanLogLine('<font color=#ff0000>'.$error_count.' errors found while dumping tree.</font>');
2447  }
2448  else
2449  {
2450  $this->writeScanLogLine('No errors found while dumping tree.');
2451  }
2452  $this->writeScanLogLine("$repository_tree_count nodes in repository tree");
2453  $this->writeScanLogLine("$trash_trees_count nodes in trash trees");
2454  $this->writeScanLogLine("$other_trees_count nodes in other trees");
2455  $this->writeScanLogLine("$not_in_tree_count nodes are not in a tree");
2456  $this->writeScanLogLine("END dumpTree");
2457 
2458  return $error_count;
2459  }
2460 
2461  protected function isMediaFolder($a_obj_id)
2462  {
2463  global $ilDB;
2464 
2465  if(!is_array($this->media_pool_ids))
2466  {
2467  $this->media_pool_ids = array();
2468  $query = "SELECT child FROM mep_tree ";
2469  $res = $ilDB->query($query);
2470  while($row = $ilDB->fetchObject($res))
2471  {
2472  $this->media_pool_ids[] = $row->child;
2473  }
2474  }
2475 
2476  return in_array($a_obj_id,$this->media_pool_ids) ? true : false;
2477  }
2478 
2485  protected function isExcludedFromRecovery($a_type,$a_obj_id)
2486  {
2487  switch($a_type)
2488  {
2489  case 'fold':
2490  if(!$this->isMediaFolder($a_obj_id))
2491  {
2492  return false;
2493  }
2494  }
2495  return in_array($a_type,$this->object_types_exclude);
2496  }
2497 } // END class.ilValidator
2498 ?>