ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.ilTestArchiver.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
16 {
17  #region Constants / Config
18 
19  const DIR_SEP = '/';
20 
21  const HTML_SUBMISSION_FILENAME = 'test_submission.html';
22  const PDF_SUBMISSION_FILENAME = 'test_submission.pdf';
23  const PASS_MATERIALS_PATH_COMPONENT = 'materials';
25 
26  const TEST_BEST_SOLUTION_PATH_COMPONENT = 'best_solution';
27  const HTML_BEST_SOLUTION_FILENAME = 'best_solution.html';
28  const PDF_BEST_SOLUTION_FILENAME = 'best_solution.pdf';
29  const TEST_MATERIALS_PATH_COMPONENT = 'materials';
30 
31  const TEST_RESULT_FILENAME = 'test_result_v';
32  const TEST_RESULT_POSTFIX = '.pdf';
33 
34  const TEST_OVERVIEW_PDF_FILENAME = 'results_overview_html_v';
35  const TEST_OVERVIEW_PDF_POSTFIX = '.pdf';
36 
37  const TEST_OVERVIEW_HTML_FILENAME = 'results_overview_pdf_v';
38  const TEST_OVERVIEW_HTML_POSTFIX = '.html';
39 
40  const LOG_DTSGROUP_FORMAT = 'D M j G:i:s T Y';
41  const LOG_ADDITION_STRING = ' Adding ';
42  const LOG_CREATION_STRING = ' Creating ';
43  const LOG_UPDATE_STRING = ' Updating ';
44  const LOG_DELETION_STRING = ' Deleting ';
45 
46  const TEST_LOG_FILENAME = 'test.log';
47  const DATA_INDEX_FILENAME = 'data_index.csv';
48  const ARCHIVE_LOG = 'archive.log';
49 
50  const EXPORT_DIRECTORY = 'archive_exports';
51 
52  #endregion
53 
54  /*
55  * Test-Archive Schema:
56  *
57  * <external directory>/<client>/tst_data/archive/tst_<obj_id>/
58  * - archive_data_index.dat
59  * - archive_log.log
60  *
61  * - test_log.log
62  *
63  * - test_results_v<n>.pdf
64  * - test_results_v<n>.csv
65  *
66  * -> best_solution/
67  * best_solution_v<n>.pdf
68  * -> /materials/q_<question_fi>/<n>_<filename>
69  *
70  * -> <year>/<month>/<day>/<ActiveFi>_<Pass>[_<Lastname>][_<Firstname>][_<Matriculation>]/
71  * -> test_submission.pdf
72  * -> test_submission.html
73  * -> test_submission.sig (et al)
74  * -> test_result_v<n>.pdf
75  * -> /materials_v<n>/<question_fi>/<n>_<filename>
76  */
77 
78  #region Properties
79 
81  protected $client_id;
82  protected $test_obj_id;
83  protected $archive_data_index;
85  protected $ilDB;
90  protected $participantData;
91 
92  #endregion
93 
99  public function __construct($test_obj_id)
100  {
102  global $ilias;
103  $this->external_directory_path = $ilias->ini_ilias->readVariable('clients','datadir');
104  $this->client_id = $ilias->client_id;
105  $this->test_obj_id = $test_obj_id;
106  $this->ilDB = $ilias->db;
107 
108  $this->archive_data_index = $this->readArchiveDataIndex();
109 
110  $this->participantData = null;
111  }
112 
116  public function getParticipantData()
117  {
118  return $this->participantData;
119  }
120 
125  {
126  $this->participantData = $participantData;
127  }
128 
129  #region API methods
130 
143  public function handInParticipantSubmission($active_fi, $pass, $pdf_path, $html_string)
144  {
146  $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
147 
148  $pdf_new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
149  . self::PDF_SUBMISSION_FILENAME;
150  copy( $pdf_path, $pdf_new_path );
151 # /home/mbecker/public_html/ilias/trunk-primary/extern/default/tst_data/archive/tst_350/2013/09/19/80_1_root_user_/test_submission.pdf
152  $html_new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
153  . self::HTML_SUBMISSION_FILENAME;
154  file_put_contents($html_new_path, $html_string);
155 
156  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $pdf_new_path );
157  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $html_new_path );
158  }
159 
169  public function handInParticipantQuestionMaterial($active_fi, $pass, $question_fi, $original_filename, $file_path)
170  {
172  $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
173 
174  $pass_question_directory = $this->getPassDataDirectory( $active_fi, $pass )
175  . self::DIR_SEP . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
176  if ( !is_dir( $pass_question_directory ) )
177  {
178  mkdir( $pass_question_directory , 0777, true );
179  }
180 
181  copy( $file_path, $pass_question_directory . self::DIR_SEP. $original_filename );
182 
183  $this->logArchivingProcess(
184  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
185  . $pass_question_directory . self::DIR_SEP. $original_filename
186  );
187  }
188 
199  public function handInParticipantMisc($active_fi, $pass, $original_filename, $file_path)
200  {
202  $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
203  $new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP . $original_filename;
204  copy( $file_path, $new_path );
205  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $new_path );
206  }
207 
214  public function handInTestBestSolution($html_string, $pdf_path)
215  {
217 
218  $best_solution_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
219  if ( !is_dir( $best_solution_path ) )
220  {
221  mkdir( $best_solution_path , 0777, true );
222  }
223 
224  file_put_contents( $best_solution_path . self::DIR_SEP . self::HTML_BEST_SOLUTION_FILENAME, $html_string );
225 
226  copy( $pdf_path, $best_solution_path . self::DIR_SEP . self::PDF_BEST_SOLUTION_FILENAME );
227 
228  $this->logArchivingProcess(
229  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
230  . $best_solution_path . self::DIR_SEP . self::HTML_BEST_SOLUTION_FILENAME);
231 
232  $this->logArchivingProcess(
233  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
234  . $best_solution_path . self::DIR_SEP . self::PDF_BEST_SOLUTION_FILENAME);
235  }
236 
244  public function handInBestSolutionQuestionMaterial($question_fi, $orginial_filename, $file_path)
245  {
247 
248  $best_solution_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
249  if ( !is_dir( $best_solution_path ) )
250  {
251  mkdir( $best_solution_path , 0777, true);
252  }
253 
254  $materials_path = $best_solution_path . self::DIR_SEP . self::TEST_MATERIALS_PATH_COMPONENT;
255  if ( !is_dir( $materials_path ) )
256  {
257  mkdir( $materials_path , 0777, true);
258  }
259 
260  $question_materials_path = $materials_path . self::DIR_SEP . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
261  if ( !is_dir( $question_materials_path ) )
262  {
263  mkdir( $question_materials_path , 0777, true);
264  }
265 
266  copy( $file_path, $question_materials_path . self::DIR_SEP . $orginial_filename );
267 
268  $this->logArchivingProcess(
269  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
270  . $question_materials_path . self::DIR_SEP . $orginial_filename);
271  }
272 
282  public function handInTestResult($active_fi, $pass, $pdf_path)
283  {
285  $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
286  $new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
287  . self::TEST_RESULT_FILENAME . ($this->countFilesInDirectory( $this->getPassDataDirectory($active_fi, $pass), self::TEST_RESULT_FILENAME ))
288  . self::TEST_RESULT_POSTFIX;
289  copy( $pdf_path, $new_path );
290  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $new_path );
291  }
292 
299  public function handInTestResultsOverview($html_string, $pdf_path)
300  {
302  $new_pdf_path = $this->getTestArchive() . self::DIR_SEP
303  . self::TEST_OVERVIEW_PDF_FILENAME
304  . $this->countFilesInDirectory($this->getTestArchive(), self::TEST_OVERVIEW_PDF_FILENAME) . self::TEST_OVERVIEW_PDF_POSTFIX;
305  copy( $pdf_path, $new_pdf_path );
306  $html_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_OVERVIEW_HTML_FILENAME
307  . $this->countFilesInDirectory($this->getTestArchive(), self::TEST_OVERVIEW_HTML_FILENAME) . self::TEST_OVERVIEW_HTML_POSTFIX;
308  file_put_contents($html_path, $html_string);
309 
310  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $new_pdf_path );
311  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $html_path );
312  }
313 
314  #endregion
315 
316  #region TestArchive
317  // The TestArchive lives here: <external directory>/<client>/tst_data/archive/tst_<obj_id>/
318 
324  protected function hasTestArchive()
325  {
326  return is_dir($this->getTestArchive());
327  }
328 
332  protected function createArchiveForTest()
333  {
335  //mkdir( $this->getTestArchive(), 0777, true );
336  }
337 
343  protected function getTestArchive()
344  {
345  $test_archive_directory = $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
346  . self::DIR_SEP . 'archive' . self::DIR_SEP . 'tst_'.$this->test_obj_id;
347  return $test_archive_directory;
348  }
349 
357  protected function ensureTestArchiveIsAvailable()
358  {
359  if (!$this->hasTestArchive())
360  {
361  $this->createArchiveForTest();
362  }
363  return;
364  }
365 
371  public function updateTestArchive()
372  {
373  $query = 'SELECT * FROM ass_log WHERE obj_fi = ' . $this->ilDB->quote($this->test_obj_id, 'integer');
374  $result = $this->ilDB->query($query);
375 
376  $outfile_lines = '';
378  while ($row = $this->ilDB->fetchAssoc($result))
379  {
380  $outfile_lines .= "\r\n" . implode("\t", $row);
381  }
382  file_put_contents($this->getTestArchive() . self::DIR_SEP . self::TEST_LOG_FILENAME, $outfile_lines);
383 
384  // Generate test pass overview
385  $test = new ilObjTest($this->test_obj_id, false);
386  $gui = new ilObjTestGUI();
387  $gui->object = $test;
388  $array_of_actives = array();
389  $participants = $test->getParticipants();
390 
391  foreach($participants as $key => $value)
392  {
393  $array_of_actives[] = $key;
394  }
395  $output_template = $gui->createUserResults(true, false, true, $array_of_actives);
396 
397  require_once 'class.ilTestPDFGenerator.php';
398  $filename = realpath($this->getTestArchive()) . self::DIR_SEP . 'participant_pass_overview.pdf';
400 
401  return;
402  }
403 
405  {
406  if (!$this->hasZipExportDirectory())
407  {
408  $this->createZipExportDirectory();
409  }
410  }
411 
417  public function hasZipExportDirectory()
418  {
419  return is_dir($this->getZipExportDirectory());
420  }
421 
422  protected function createZipExportDirectory()
423  {
424  mkdir($this->getZipExportDirectory(), 0777, true);
425  }
426 
432  public function getZipExportDirectory()
433  {
434  return $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
435  . self::DIR_SEP . self::EXPORT_DIRECTORY . self::DIR_SEP . 'tst_' . $this->test_obj_id;
436  }
437 
443  public function compressTestArchive()
444  {
445  $this->updateTestArchive();
447 
448  $zip_output_path = $this->getZipExportDirectory();
449  $zip_output_filename = 'test_archive_obj_'.$this->test_obj_id . '_' . time() . '_.zip';
450 
451  ilUtil::zip($this->getTestArchive(), $zip_output_path . self::DIR_SEP . $zip_output_filename, true);
452  return;
453  }
454 
455  #endregion
456 
457  #region PassDataDirectory
458  // The pass data directory contains all data relevant for a participants pass.
459  // In addition to the test-archive-directory, this directory lives here:
460  // .../<year>/<month>/<day>/<ActiveFi>_<Pass>[_<Lastname>][_<Firstname>][_<Matriculation>]/
461  // Lastname, Firstname and Matriculation are not mandatory in the directory name.
462 
471  protected function hasPassDataDirectory($active_fi, $pass)
472  {
473  $pass_data_dir = $this->getPassDataDirectory($active_fi, $pass);
474  return is_dir($this->getPassDataDirectory($active_fi, $pass));
475  }
476 
485  protected function createPassDataDirectory($active_fi, $pass)
486  {
487  mkdir($this->getPassDataDirectory($active_fi, $pass), 0777, true);
488  return;
489  }
490 
491  private function buildPassDataDirectory($active_fi, $pass)
492  {
493  foreach ($this->archive_data_index as $data_index_entry)
494  {
495  if ( $data_index_entry != null && $data_index_entry['identifier'] == $active_fi.'|'.$pass )
496  {
497  array_shift($data_index_entry);
498  return $this->getTestArchive() . self::DIR_SEP . implode(self::DIR_SEP, $data_index_entry);
499  }
500  }
501 
502  return null;
503  }
504 
513  protected function getPassDataDirectory($active_fi, $pass)
514  {
515  $passDataDir = $this->buildPassDataDirectory($active_fi, $pass);
516 
517  if( !$passDataDir )
518  {
519  if( $this->getParticipantData() )
520  {
521  $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
522  $user = new ilObjUser();
523  $user->setFirstname($usrData['firstname']);
524  $user->setLastname($usrData['lastname']);
525  $user->setMatriculation($usrData['matriculation']);
526  $user->setFirstname($usrData['firstname']);
527  }
528  else
529  {
530  global $ilUser;
531  $user = $ilUser;
532  }
533 
535  date(DATE_ISO8601), $active_fi, $pass,
536  $user->getFirstname(), $user->getLastname(), $user->getMatriculation()
537  );
538 
539  $passDataDir = $this->buildPassDataDirectory($active_fi, $pass);
540  }
541 
542  return $passDataDir;
543  }
544 
555  protected function ensurePassDataDirectoryIsAvailable($active_fi, $pass)
556  {
557  if (!$this->hasPassDataDirectory( $active_fi, $pass ))
558  {
559  $this->createPassDataDirectory( $active_fi, $pass );
560  }
561  return;
562  }
563 
564  #endregion
565 
566  #region PassMaterialsDirectory
567 
576  protected function hasPassMaterialsDirectory($active_fi, $pass)
577  {
579  if ( @is_dir( $this->getPassMaterialsDirectory($active_fi, $pass)) )
580  {
581  return true;
582  }
583  return false;
584  }
585 
594  protected function createPassMaterialsDirectory($active_fi, $pass)
595  {
596  // Data are taken from the current user as the implementation expects the first interaction of the pass
597  // takes place from the usage/behaviour of the current user.
598 
599  if( $this->getParticipantData() )
600  {
601  $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
602  $user = new ilObjUser();
603  $user->setFirstname($usrData['firstname']);
604  $user->setLastname($usrData['lastname']);
605  $user->setMatriculation($usrData['matriculation']);
606  $user->setFirstname($usrData['firstname']);
607  }
608  else
609  {
610  global $ilUser;
611  $user = $ilUser;
612  }
613 
615  date('Y'), $active_fi, $pass, $user->getFirstname(), $user->getLastname(), $user->getMatriculation()
616  );
617  mkdir($this->getPassMaterialsDirectory($active_fi, $pass) , 0777, true );
618  }
619 
628  protected function getPassMaterialsDirectory($active_fi, $pass)
629  {
630  $pass_data_directory = $this->getPassMaterialsDirectory($active_fi, $pass);
631  return $pass_data_directory . self::DIR_SEP . self::PASS_MATERIALS_PATH_COMPONENT;
632  }
633 
643  protected function ensurePassMaterialsDirectoryIsAvailable($active_fi, $pass)
644  {
645  if (!$this->hasPassMaterialsDirectory( $active_fi, $pass ))
646  {
647  $this->createPassMaterialsDirectory( $active_fi, $pass );
648  }
649  }
650 
651  #endregion
652 
658  protected function readArchiveDataIndex()
659  {
664  $data_index_file = $this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME;
665 
666  $contents = array();
667 
669  if ( @file_exists( $data_index_file ) )
670  {
671  $lines = explode( "\n", file_get_contents( $data_index_file ) );
672  foreach ($lines as $line)
673  {
674  $line_items = explode('|', $line);
675  $line_data['identifier'] = $line_items[0] . '|' . $line_items[1];
676  $line_data['yyyy'] = $line_items[2];
677  $line_data['mm'] = $line_items[3];
678  $line_data['dd'] = $line_items[4];
679  $line_data['directory'] = $line_items[5];
680  $contents[] = $line_data;
681  }
682  }
683  return $contents;
684  }
685 
698  protected function appendToArchiveDataIndex($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
699  {
700  $line = $this->determinePassDataPath( $date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation );
701 
702  $this->archive_data_index[] = $line;
703  $output_contents = '';
704 
705  foreach ($this->archive_data_index as $line_data)
706  {
707  if($line_data['identifier'] == "|")
708  {
709  continue;
710  }
711  $output_contents .= implode('|', $line_data) . "\n";
712  }
713 
714  file_put_contents($this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME, $output_contents);
715  $this->readArchiveDataIndex();
716  return;
717  }
718 
731  protected function determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
732  {
733  $date = date_create_from_format(DATE_ISO8601, $date);
734  $line = array(
735  'identifier' => $active_fi . '|' . $pass,
736  'yyyy' => date_format( $date, 'Y' ),
737  'mm' => date_format( $date, 'm' ),
738  'dd' => date_format( $date, 'd' ),
739  'directory' => $active_fi . '_' . $pass . '_' . $user_firstname . '_' . $user_lastname . '_' . $matriculation
740  );
741  return $line;
742  }
743 
751  protected function logArchivingProcess($message)
752  {
753  $archive = $this->getTestArchive() . self::DIR_SEP . self::ARCHIVE_LOG;
754  if( file_exists($archive) )
755  {
756  $content = file_get_contents($archive). "\n" . $message;
757  }
758  else
759  {
760  $content = $message;
761  }
762 
763  file_put_contents($archive, $content);
764  }
765 
774  protected function countFilesInDirectory($directory, $pattern = null)
775  {
776  $filecount = 0;
777 
779  if ($handle = opendir( $directory ))
780  {
781  while (($file = readdir( $handle )) !== false)
782  {
783  if (!in_array( $file, array( '.', '..' ) ) && !is_dir( $directory . $file ))
784  {
785  if ($pattern && strpos($file, $pattern) === 0 )
786  {
787  $filecount++;
788  }
789  }
790  }
791  }
792  return $filecount;
793  }
794 }
getPassDataDirectory($active_fi, $pass)
Returns the pass data directory.
static makeDirParents($a_dir)
Create a new directory and all parent directories.
determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
Determines the pass data path.
createArchiveForTest()
Creates the directory for the test archive.
Class ilObjTestGUI.
$result
handInTestResultsOverview($html_string, $pdf_path)
Hands in a test results overview.
query($sql, $a_handle_error=true)
Query.
createPassDataDirectory($active_fi, $pass)
Creates pass data directory.
countFilesInDirectory($directory, $pattern=null)
Returns the count of files in a directory, eventually matching the given, optional, pattern.
handInParticipantQuestionMaterial($active_fi, $pass, $question_fi, $original_filename, $file_path)
Hands in a particpants question material, such as an upload or other binary content.
getPassMaterialsDirectory($active_fi, $pass)
Returns the pass materials directory.
compressTestArchive()
Generate the test archive for download.
readArchiveDataIndex()
Reads the archive data index.
fetchAssoc($a_set)
Fetch row as associative array from result set.
quote($a_query, $a_type=null)
Wrapper for quote method.
const QUESTION_PATH_COMPONENT_PREFIX
getZipExportDirectory()
Return the export directory, where zips are placed.
buildPassDataDirectory($active_fi, $pass)
date( 'd-M-Y', $objPHPExcel->getProperties() ->getCreated())
appendToArchiveDataIndex($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
Appends a line to the archive data index.
handInParticipantSubmission($active_fi, $pass, $pdf_path, $html_string)
Hands in a participants test submission ("a completed test") for archiving.
ensurePassDataDirectoryIsAvailable($active_fi, $pass)
Ensures the availability of the participant data directory.
$ilUser
Definition: imgupload.php:18
handInBestSolutionQuestionMaterial($question_fi, $orginial_filename, $file_path)
Hands in a file related to a question in context of the best solution.
hasZipExportDirectory()
Returns if the export directory for zips exists.
static zip($a_dir, $a_file, $compress_content=false)
zips given directory/file into given zip.file
Create styles array
The data for the language used.
hasTestArchive()
Returns if the archive directory structure for the test the object is created for exists...
handInTestBestSolution($html_string, $pdf_path)
Hands in the best solution for a test.
Class ilTestArchiver.
getTestArchive()
Returns the (theoretical) path to the archive directory of the test, this object is created for...
Database Wrapper.
Definition: class.ilDB.php:29
hasPassDataDirectory($active_fi, $pass)
Checks if the directory for pass data is available.
hasPassMaterialsDirectory($active_fi, $pass)
Returns if the pass materials directory exists for a given pass.
createPassMaterialsDirectory($active_fi, $pass)
Creates pass materials directory.
static generatePDF($pdf_output, $output_mode, $filename=null)
const TEST_BEST_SOLUTION_PATH_COMPONENT
ensurePassMaterialsDirectoryIsAvailable($active_fi, $pass)
Ensures the availability of the pass materials directory.
ensureTestArchiveIsAvailable()
Ensures the availability of the test archive directory.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
handInParticipantMisc($active_fi, $pass, $original_filename, $file_path)
Hands in a participants file, which is relevant for archiving but an unspecified type.
setParticipantData($participantData)
logArchivingProcess($message)
Logs to the archive log.
$test
Definition: Utf8Test.php:84
updateTestArchive()
Replaces the test-log with the current one.
handInTestResult($active_fi, $pass, $pdf_path)
Hands in an individual test result for a pass.