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