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