ILIAS PHP Cross Reference Learning Management Systems

Source: /Modules/TestQuestionPool/classes/class.assFileUpload.php - 1097 lines - 31201 bytes - Summary - Text - Print

   1  <?php
   2  /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
   3  
   4  require_once  './Modules/TestQuestionPool/classes/class.assQuestion.php';
   5  require_once  './Modules/Test/classes/inc.AssessmentConstants.php';
   6  require_once  './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
   7  require_once  './Modules/TestQuestionPool/interfaces/interface.ilObjFileHandlingQuestionType.php';
   8  
   9  /**
  10   * Class for file upload questions
  11   * 
  12   * @author        Helmut Schottmüller <helmut.schottmueller@mac.com> 
  13   * @author        Björn Heyser <bheyser@databay.de> 
  14   * @author        Maximilian Becker <mbecker@databay.de> 
  15   * 
  16   * @version        $Id: class.assFileUpload.php 49561 2014-04-22 11:02:24Z bheyser $
  17   * 
  18   * @ingroup        ModulesTestQuestionPool
  19   */
  20  class assFileUpload extends assQuestion implements ilObjQuestionScoringAdjustable, ilObjFileHandlingQuestionType
  21  {
  22      protected $maxsize;
  23      
  24      protected $allowedextensions;
  25      
  26      /** @var boolean Indicates whether completion by submission is enabled or not */
  27      protected $completion_by_submission = false;
  28      
  29      /**
  30       * assFileUpload constructor
  31       *
  32       * The constructor takes possible arguments an creates an instance of the assFileUpload object.
  33       *
  34       * @param string     $title         A title string to describe the question
  35       * @param string     $comment     A comment string to describe the question
  36       * @param string     $author     A string containing the name of the questions author
  37       * @param integer     $owner         A numerical ID to identify the owner/creator
  38       * @param string     $question     The question string of the single choice question
  39       * 
  40       * @see assQuestion:__construct()
  41       */
  42  	function __construct(
  43          $title = "",
  44          $comment = "",
  45          $author = "",
  46          $owner = -1,
  47          $question = ""
  48      )
  49      {
  50          parent::__construct($title, $comment, $author, $owner, $question);
  51      }
  52      
  53      /**
  54       * Returns true, if the question is complete for use
  55       * 
  56       * @return boolean True, if the question is complete for use, otherwise false
  57       */
  58  	public function isComplete()
  59      {
  60          if (
  61              strlen($this->title) 
  62              && ($this->author) 
  63              && ($this->question) 
  64              && ($this->getMaximumPoints() >= 0) 
  65              && is_numeric($this->getMaximumPoints()))
  66          {
  67              return true;
  68          }
  69          return false;
  70      }
  71  
  72      /**
  73       * Saves a assFileUpload object to a database
  74       */
  75  	public function saveToDb($original_id = "")
  76      {
  77          $this->saveQuestionDataToDb($original_id);
  78          $this->saveAdditionalQuestionDataToDb();
  79          parent::saveToDb();
  80      }
  81  
  82  	public function saveAdditionalQuestionDataToDb()
  83      {
  84          global $ilDB;
  85          $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
  86                              array( "integer" ),
  87                              array( $this->getId() )
  88          );
  89          $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName(
  90                                                                                               ) . " (question_fi, maxsize, allowedextensions, compl_by_submission) VALUES (%s, %s, %s, %s)",
  91                              array( "integer", "float", "text", "integer" ),
  92                              array(
  93                                  $this->getId(),
  94                                  (strlen( $this->getMaxSize() )) ? $this->getMaxSize() : NULL,
  95                                  (strlen( $this->getAllowedExtensions() )) ? $this->getAllowedExtensions() : NULL,
  96                                  (int)$this->isCompletionBySubmissionEnabled()
  97                              )
  98          );
  99      }
 100  
 101      /**
 102       * Loads a assFileUpload object from a database
 103       *
 104       * @param integer $question_id A unique key which defines the question in the database
 105       */
 106  	public function loadFromDb($question_id)
 107      {
 108          global $ilDB;
 109          $result = $ilDB->queryF("SELECT qpl_questions.*, " . $this->getAdditionalTableName() . ".* FROM qpl_questions LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = qpl_questions.question_id WHERE qpl_questions.question_id = %s",
 110              array("integer"),
 111              array($question_id)
 112          );
 113          if ($result->numRows() == 1)
 114          {
 115              $data = $ilDB->fetchAssoc($result);
 116              $this->setId($question_id);
 117              $this->setTitle($data["title"]);
 118              $this->setComment($data["description"]);
 119              $this->setNrOfTries($data['nr_of_tries']);
 120              $this->setSuggestedSolution($data["solution_hint"]);
 121              $this->setOriginalId($data["original_id"]);
 122              $this->setObjId($data["obj_fi"]);
 123              $this->setAuthor($data["author"]);
 124              $this->setOwner($data["owner"]);
 125              $this->setPoints($data["points"]);            
 126  
 127              include_once ("./Services/RTE/classes/class.ilRTE.php");
 128              $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
 129              $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
 130              $this->setMaxSize($data["maxsize"]);
 131              $this->setAllowedExtensions($data["allowedextensions"]);
 132              $this->setCompletionBySubmission($data['compl_by_submission'] == 1 ? true : false);
 133              
 134              try
 135              {
 136                  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
 137              }
 138              catch(ilTestQuestionPoolException $e)
 139              {
 140              }
 141          }
 142          parent::loadFromDb($question_id);
 143      }
 144  
 145      /**
 146      * Duplicates an assFileUpload
 147      */
 148  	public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
 149      {
 150          if ($this->id <= 0)
 151          {
 152              // The question has not been saved. It cannot be duplicated
 153              return;
 154          }
 155          // duplicate the question in database
 156          $this_id = $this->getId();
 157          
 158          if( (int)$testObjId > 0 )
 159          {
 160              $thisObjId = $this->getObjId();
 161          }
 162          
 163          $clone = $this;
 164          include_once  ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
 165          $original_id = assQuestion::_getOriginalId($this->id);
 166          $clone->id = -1;
 167          
 168          if( (int)$testObjId > 0 )
 169          {
 170              $clone->setObjId($testObjId);
 171          }
 172          
 173          if ($title)
 174          {
 175              $clone->setTitle($title);
 176          }
 177  
 178          if ($author)
 179          {
 180              $clone->setAuthor($author);
 181          }
 182          if ($owner)
 183          {
 184              $clone->setOwner($owner);
 185          }
 186  
 187          if ($for_test)
 188          {
 189              $clone->saveToDb($original_id);
 190          }
 191          else
 192          {
 193              $clone->saveToDb();
 194          }
 195  
 196          // copy question page content
 197          $clone->copyPageOfQuestion($this_id);
 198          // copy XHTML media objects
 199          $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
 200  
 201          $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
 202          
 203          return $clone->id;
 204      }
 205  
 206      /**
 207      * Copies an assFileUpload object
 208      */
 209  	public function copyObject($target_questionpool_id, $title = "")
 210      {
 211          if ($this->id <= 0)
 212          {
 213              // The question has not been saved. It cannot be duplicated
 214              return;
 215          }
 216          // duplicate the question in database
 217          $clone = $this;
 218          include_once  ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
 219          $original_id = assQuestion::_getOriginalId($this->id);
 220          $clone->id = -1;
 221          $source_questionpool_id = $this->getObjId();
 222          $clone->setObjId($target_questionpool_id);
 223          if ($title)
 224          {
 225              $clone->setTitle($title);
 226          }
 227          $clone->saveToDb();
 228  
 229          // copy question page content
 230          $clone->copyPageOfQuestion($original_id);
 231          // copy XHTML media objects
 232          $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
 233  
 234          $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
 235  
 236          return $clone->id;
 237      }
 238  
 239  	public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
 240      {
 241          if ($this->id <= 0)
 242          {
 243              // The question has not been saved. It cannot be duplicated
 244              return;
 245          }
 246  
 247          include_once  ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
 248  
 249          $sourceQuestionId = $this->id;
 250          $sourceParentId = $this->getObjId();
 251  
 252          // duplicate the question in database
 253          $clone = $this;
 254          $clone->id = -1;
 255  
 256          $clone->setObjId($targetParentId);
 257  
 258          if ($targetQuestionTitle)
 259          {
 260              $clone->setTitle($targetQuestionTitle);
 261          }
 262  
 263          $clone->saveToDb();
 264          // copy question page content
 265          $clone->copyPageOfQuestion($sourceQuestionId);
 266          // copy XHTML media objects
 267          $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
 268  
 269          $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
 270  
 271          return $clone->id;
 272      }
 273  
 274      /**
 275      * Returns the maximum points, a learner can reach answering the question
 276      *
 277      * @see $points
 278      */
 279  	public function getMaximumPoints()
 280      {
 281          return $this->getPoints();
 282      }
 283  
 284      /**
 285       * Returns the points, a learner has reached answering the question.
 286       * The points are calculated from the given answers.
 287       * 
 288       * @access public
 289       * @param integer $active_id
 290       * @param integer $pass
 291       * @param boolean $returndetails (deprecated !!)
 292       * @return integer/array $points/$details (array $details is deprecated !!)
 293       */
 294  	public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
 295      {
 296          if( $returndetails )
 297          {
 298              throw new ilTestException('return details not implemented for '.__METHOD__);
 299          }
 300          
 301          global $ilDB;
 302          
 303          if (is_null($pass))
 304          {
 305              $pass = $this->getSolutionMaxPass($active_id);
 306          }
 307          $points = 0;
 308          return $points;
 309      }
 310      
 311      /**
 312      * Check file upload
 313      *
 314      * @return    boolean Input ok, true/false
 315      */    
 316  	function checkUpload()
 317      {
 318          $this->lng->loadLanguageModule("form");
 319          // remove trailing '/'
 320          while (substr($_FILES["upload"]["name"],-1) == '/')
 321          {
 322              $_FILES["upload"]["name"] = substr($_FILES["upload"]["name"],0,-1);
 323          }
 324  
 325          $filename = $_FILES["upload"]["name"];
 326          $filename_arr = pathinfo($_FILES["upload"]["name"]);
 327          $suffix = $filename_arr["extension"];
 328          $mimetype = $_FILES["upload"]["type"];
 329          $size_bytes = $_FILES["upload"]["size"];
 330          $temp_name = $_FILES["upload"]["tmp_name"];
 331          $error = $_FILES["upload"]["error"];
 332          
 333          if ($size_bytes > $this->getMaxFilesizeInBytes())
 334          {
 335              ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
 336              return false;
 337          }
 338  
 339          // error handling
 340          if ($error > 0)
 341          {
 342              switch ($error)
 343              {
 344                  case UPLOAD_ERR_INI_SIZE:
 345                      ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
 346                      return false;
 347                      break;
 348                       
 349                  case UPLOAD_ERR_FORM_SIZE:
 350                      ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
 351                      return false;
 352                      break;
 353      
 354                  case UPLOAD_ERR_PARTIAL:
 355                      ilUtil::sendFailure($this->lng->txt("form_msg_file_partially_uploaded"), true);
 356                      return false;
 357                      break;
 358      
 359                  case UPLOAD_ERR_NO_FILE:
 360                      ilUtil::sendFailure($this->lng->txt("form_msg_file_no_upload"), true);
 361                      return false;
 362                      break;
 363       
 364                  case UPLOAD_ERR_NO_TMP_DIR:
 365                      ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_tmp_dir"), true);
 366                      return false;
 367                      break;
 368                       
 369                  case UPLOAD_ERR_CANT_WRITE:
 370                      ilUtil::sendFailure($this->lng->txt("form_msg_file_cannot_write_to_disk"), true);
 371                      return false;
 372                      break;
 373       
 374                  case UPLOAD_ERR_EXTENSION:
 375                      ilUtil::sendFailure($this->lng->txt("form_msg_file_upload_stopped_ext"), true);
 376                      return false;
 377                      break;
 378              }
 379          }
 380          
 381          // check suffixes
 382          if (strlen($suffix) && count($this->getAllowedExtensionsArray()))
 383          {
 384              if (!in_array(strtolower($suffix), $this->getAllowedExtensionsArray()))
 385              {
 386                  ilUtil::sendFailure($this->lng->txt("form_msg_file_wrong_file_type"), true);
 387                  return false;
 388              }
 389          }
 390          
 391          // virus handling
 392          if (strlen($temp_name))
 393          {
 394              $vir = ilUtil::virusHandling($temp_name, $filename);
 395              if ($vir[0] == false)
 396              {
 397                  ilUtil::sendFailure($this->lng->txt("form_msg_file_virus_found")."<br />".$vir[1], true);
 398                  return false;
 399              }
 400          }
 401          return true;
 402      }
 403  
 404      /**
 405      * Returns the filesystem path for file uploads
 406      */
 407  	protected function getFileUploadPath($test_id, $active_id, $question_id = null)
 408      {
 409          if (is_null($question_id)) $question_id = $this->getId();
 410          return CLIENT_WEB_DIR . "/assessment/tst_$test_id/$active_id/$question_id/files/";
 411      }
 412  
 413      /**
 414      * Returns the file upload path for web accessible files of a question
 415      *
 416      * @access public
 417      */
 418  	function getFileUploadPathWeb($test_id, $active_id, $question_id = null)
 419      {
 420          if (is_null($question_id)) $question_id = $this->getId();
 421          include_once  "./Services/Utilities/classes/class.ilUtil.php";
 422          $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/tst_$test_id/$active_id/$question_id/files/";
 423          return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
 424      }
 425  
 426      /**
 427      * Returns the uploaded files for an active user in a given pass
 428      *
 429      * @return array Results
 430      */
 431  	public function getUploadedFiles($active_id, $pass = null)
 432      {
 433          global $ilDB;
 434          if (is_null($pass))
 435          {
 436              $pass = $this->getSolutionMaxPass($active_id);
 437          }
 438          $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s ORDER BY tstamp",
 439              array("integer", "integer", "integer"),
 440              array($active_id, $this->getId(), $pass)
 441          );
 442          $found = array();
 443          while ($data = $ilDB->fetchAssoc($result))
 444          {
 445              array_push($found, $data);
 446          }
 447          return $found;
 448      }
 449      
 450      /**
 451      * Returns the web accessible uploaded files for an active user in a given pass
 452      *
 453      * @return array Results
 454      */
 455  	public function getUploadedFilesForWeb($active_id, $pass)
 456      {
 457          global $ilDB;
 458          
 459          $found = $this->getUploadedFiles($active_id, $pass);
 460          $result = $ilDB->queryF("SELECT test_fi FROM tst_active WHERE active_id = %s",
 461              array('integer'),
 462              array($active_id)
 463          );
 464          if ($result->numRows() == 1)
 465          {
 466              $row = $ilDB->fetchAssoc($result);
 467              $test_id = $row["test_fi"];
 468              $path = $this->getFileUploadPathWeb($test_id, $active_id);
 469              foreach ($found as $idx => $data)
 470              {
 471                  $found[$idx]['webpath'] = $path;
 472              }
 473          }
 474          return $found;
 475      }
 476  
 477      /**
 478      * Delete uploaded files
 479      *
 480    * @param array Array with ID's of the file datasets
 481      */
 482  	protected function deleteUploadedFiles($files, $test_id, $active_id)
 483      {
 484          global $ilDB;
 485          
 486          $pass = null;
 487          $active_id = null;
 488          foreach ($files as $solution_id)
 489          {
 490              $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE solution_id = %s",
 491                  array("integer"),
 492                  array($solution_id)
 493              );
 494              if ($result->numRows() == 1)
 495              {
 496                  $data = $ilDB->fetchAssoc($result);
 497                  $pass = $data['pass'];
 498                  $active_id = $data['active_fi'];
 499                  @unlink($this->getFileUploadPath($test_id, $active_id) . $data['value1']);
 500              }
 501          }
 502          foreach ($files as $solution_id)
 503          {
 504              $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE solution_id = %s", 
 505                  array("integer"),
 506                  array($solution_id)
 507              );
 508          }
 509      }
 510      
 511      /**
 512      * Return the maximum allowed file size as string
 513      *
 514    * @return string The number of bytes of the maximum allowed file size
 515      */
 516  	public function getMaxFilesizeAsString()
 517      {
 518          $size = $this->getMaxFilesizeInBytes();
 519          if ($size < 1024)
 520          {
 521              $max_filesize = sprintf("%d Bytes",$size);
 522          }
 523          else if ($size < 1024*1024)
 524          {
 525              $max_filesize = sprintf("%.1f KB",$size/1024);
 526          }
 527          else
 528          {
 529              $max_filesize = sprintf("%.1f MB",$size/1024/1024);
 530          }
 531          
 532          return $max_filesize;
 533      }
 534  
 535      /**
 536      * Return the maximum allowed file size in bytes
 537      *
 538    * @return integer The number of bytes of the maximum allowed file size
 539      */
 540  	public function getMaxFilesizeInBytes()
 541      {
 542          if (strlen($this->getMaxSize()))
 543          {
 544              return $this->getMaxSize();
 545          }
 546          else
 547          {
 548              // get the value for the maximal uploadable filesize from the php.ini (if available)
 549              $umf = get_cfg_var("upload_max_filesize");
 550              // get the value for the maximal post data from the php.ini (if available)
 551              $pms = get_cfg_var("post_max_size");
 552  
 553              //convert from short-string representation to "real" bytes
 554              $multiplier_a=array("K"=>1024, "M"=>1024*1024, "G"=>1024*1024*1024);
 555  
 556              $umf_parts=preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
 557              $pms_parts=preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
 558  
 559              if (count($umf_parts) == 2) { $umf = $umf_parts[0]*$multiplier_a[$umf_parts[1]]; }
 560              if (count($pms_parts) == 2) { $pms = $pms_parts[0]*$multiplier_a[$pms_parts[1]]; }
 561  
 562              // use the smaller one as limit
 563              $max_filesize = min($umf, $pms);
 564  
 565              if (!$max_filesize) $max_filesize=max($umf, $pms);
 566              return $max_filesize;
 567          }
 568      }
 569  
 570      /**
 571       * Saves the learners input of the question to the database.
 572       * 
 573       * @access public
 574       * @param integer $active_id Active id of the user
 575       * @param integer $pass Test pass
 576       * @return boolean $status
 577       */
 578  	public function saveWorkingData($active_id, $pass = NULL)
 579      {
 580          global $ilDB;
 581          global $ilUser;
 582  
 583          if (is_null($pass))
 584          {
 585              include_once  "./Modules/Test/classes/class.ilObjTest.php";
 586              $pass = ilObjTest::_getPass($active_id);
 587          }
 588  
 589          $this->getProcessLocker()->requestUserSolutionUpdateLock();
 590  
 591          $result = $ilDB->queryF("SELECT test_fi FROM tst_active WHERE active_id = %s",
 592              array('integer'),
 593              array($active_id)
 594          );
 595          $test_id = 0;
 596          if ($result->numRows() == 1)
 597          {
 598              $row = $ilDB->fetchAssoc($result);
 599              $test_id = $row["test_fi"];
 600          }
 601  
 602          $entered_values = false;
 603          if (strcmp($_POST['cmd']['handleQuestionAction'], $this->lng->txt('delete')) == 0)
 604          {
 605              if (is_array($_POST['deletefiles']) && count($_POST['deletefiles']) > 0)
 606              {
 607                  $this->deleteUploadedFiles($_POST['deletefiles'], $test_id, $active_id);
 608              }
 609              else
 610              {
 611                  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
 612              }
 613          }
 614          else
 615          {
 616              if (strlen($_FILES["upload"]["tmp_name"]))
 617              {
 618                  if ($this->checkUpload())
 619                  {
 620                      if (!@file_exists($this->getFileUploadPath($test_id, $active_id))) ilUtil::makeDirParents($this->getFileUploadPath($test_id, $active_id));
 621                      $version = time();
 622                      $filename_arr = pathinfo($_FILES["upload"]["name"]);
 623                      $extension = $filename_arr["extension"];
 624                      $newfile = "file_" . $active_id . "_" . $pass . "_" . $version . "." . $extension;
 625                      ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getFileUploadPath($test_id, $active_id) . $newfile);
 626                      $next_id = $ilDB->nextId('tst_solutions');
 627                      $affectedRows = $ilDB->insert("tst_solutions", array(
 628                          "solution_id" => array("integer", $next_id),
 629                          "active_fi" => array("integer", $active_id),
 630                          "question_fi" => array("integer", $this->getId()),
 631                          "value1" => array("clob", $newfile),
 632                          "value2" => array("clob", $_FILES['upload']['name']),
 633                          "pass" => array("integer", $pass),
 634                          "tstamp" => array("integer", time())
 635                      ));
 636                      $entered_values = true;
 637                  }
 638              }
 639          }
 640          
 641          $this->getProcessLocker()->releaseUserSolutionUpdateLock();
 642  
 643          if ($entered_values)
 644          {
 645              include_once  ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
 646              if (ilObjAssessmentFolder::_enabledAssessmentLogging())
 647              {
 648                  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
 649              }
 650          }
 651          else
 652          {
 653              include_once  ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
 654              if (ilObjAssessmentFolder::_enabledAssessmentLogging())
 655              {
 656                  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
 657              }
 658          }
 659          
 660          return true;
 661      }
 662  
 663      /**
 664       * Reworks the allready saved working data if neccessary
 665       *
 666       * @access protected
 667       * @param integer $active_id
 668       * @param integer $pass
 669       * @param boolean $obligationsAnswered
 670       */
 671  	protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
 672      {
 673          $this->handleSubmission($active_id, $pass, $obligationsAnswered);
 674      }
 675      
 676      /**
 677       * This method is called after an user submitted one or more files.
 678       * It should handle the setting "Completion by Submission" and, if enabled, set the status of
 679       * the current user.
 680       *
 681       * @param    integer
 682       * @param    integer
 683       * @access    protected
 684       */
 685  	protected function handleSubmission($active_id, $pass, $obligationsAnswered)
 686      {
 687          global $ilObjDataCache;        
 688  
 689          if($this->isCompletionBySubmissionEnabled())
 690          {
 691              $maxpoints = assQuestion::_getMaximumPoints($this->getId());
 692      
 693              if($this->getUploadedFiles($active_id, $pass))
 694              {
 695                  $points = $maxpoints;    
 696              }
 697              else
 698              {
 699                  $points = 0;
 700              }
 701  
 702              assQuestion::_setReachedPoints($active_id, $this->getId(), $points, $maxpoints, $pass, 1, $obligationsAnswered);                    
 703              
 704              // update learning progress
 705              include_once  'Modules/Test/classes/class.ilObjTestAccess.php';
 706              include_once  'Services/Tracking/classes/class.ilLPStatusWrapper.php';
 707              ilLPStatusWrapper::_updateStatus(
 708                  ilObjTest::_getObjectIDFromActiveID((int)$active_id),
 709                  ilObjTestAccess::_getParticipantId((int) $active_id)
 710              );
 711          }
 712      }
 713  
 714      /**
 715      * Returns the question type of the question
 716      *
 717      * @return integer The question type of the question
 718      */
 719  	public function getQuestionType()
 720      {
 721          return "assFileUpload";
 722      }
 723      
 724      /**
 725      * Returns the name of the additional question data table in the database
 726      *
 727      * @return string The additional table name
 728      */
 729  	public function getAdditionalTableName()
 730      {
 731          return "qpl_qst_fileupload";
 732      }
 733      
 734      /**
 735      * Returns the name of the answer table in the database
 736      *
 737      * @return string The answer table name
 738      */
 739  	public function getAnswerTableName()
 740      {
 741          return "";
 742      }
 743      
 744      /**
 745      * Deletes datasets from answers tables
 746      *
 747      * @param integer $question_id The question id which should be deleted in the answers table
 748      */
 749  	public function deleteAnswers($question_id)
 750      {
 751      }
 752  
 753      /**
 754      * Collects all text in the question which could contain media objects
 755      * which were created with the Rich Text Editor
 756      */
 757  	public function getRTETextWithMediaObjects()
 758      {
 759          $text = parent::getRTETextWithMediaObjects();
 760          return $text;
 761      }
 762  
 763      /**
 764      * Creates an Excel worksheet for the detailed cumulated results of this question
 765      *
 766      * @param object $worksheet Reference to the parent excel worksheet
 767      * @param object $startrow Startrow of the output in the excel worksheet
 768      * @param object $active_id Active id of the participant
 769      * @param object $pass Test pass
 770      * @param object $format_title Excel title format
 771      * @param object $format_bold Excel bold format
 772      * @param array $eval_data Cumulated evaluation data
 773      */
 774  	public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
 775      {
 776          include_once  ("./Services/Excel/classes/class.ilExcelUtils.php");
 777          $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
 778          $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
 779          $i = 1;
 780          $solutions = $this->getSolutionValues($active_id, $pass);
 781          foreach ($solutions as $solution)
 782          {
 783              $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($this->lng->txt("result")), $format_bold);
 784              if (strlen($solution["value1"]))
 785              {
 786                  $worksheet->write($startrow + $i, 1, ilExcelUtils::_convert_text($solution["value1"]));
 787                  $worksheet->write($startrow + $i, 2, ilExcelUtils::_convert_text($solution["value2"]));
 788              }
 789              $i++;
 790          }
 791          return $startrow + $i + 1;
 792      }
 793      
 794      /**
 795      * Creates a question from a QTI file
 796      *
 797      * Receives parameters from a QTI parser and creates a valid ILIAS question object
 798      *
 799      * @param object $item The QTI item object
 800      * @param integer $questionpool_id The id of the parent questionpool
 801      * @param integer $tst_id The id of the parent test if the question is part of a test
 802      * @param object $tst_object A reference to the parent test object
 803      * @param integer $question_counter A reference to a question counter to count the questions of an imported question pool
 804      * @param array $import_mapping An array containing references to included ILIAS objects
 805      */
 806  	public function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
 807      {
 808          include_once  "./Modules/TestQuestionPool/classes/import/qti12/class.assFileUploadImport.php";
 809          $import = new assFileUploadImport($this);
 810          $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
 811      }
 812      
 813      /**
 814      * Returns a QTI xml representation of the question and sets the internal
 815      * domxml variable with the DOM XML representation of the QTI xml representation
 816      *
 817      * @return string The QTI xml representation of the question
 818      */
 819  	public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
 820      {
 821          include_once  "./Modules/TestQuestionPool/classes/export/qti12/class.assFileUploadExport.php";
 822          $export = new assFileUploadExport($this);
 823          return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
 824      }
 825  
 826      /**
 827      * Returns the best solution for a given pass of a participant
 828      *
 829      * @return array An associated array containing the best solution
 830      */
 831  	public function getBestSolution($active_id, $pass)
 832      {
 833          $user_solution = array();
 834          return $user_solution;
 835      }
 836      
 837      /**
 838      * Get max file size
 839      *
 840      * @return double Max file size
 841      */
 842  	public function getMaxSize()
 843      {
 844          return $this->maxsize;
 845      }
 846      
 847      /**
 848      * Set max file size
 849      *
 850      * @param double $a_value Max file size
 851      */
 852  	public function setMaxSize($a_value)
 853      {
 854          $this->maxsize = $a_value;
 855      }
 856      
 857      /**
 858      * Get allowed file extensions
 859      *
 860      * @return array Allowed file extensions
 861      */
 862  	public function getAllowedExtensionsArray()
 863      {
 864          if (strlen($this->allowedextensions))
 865          {
 866              return array_filter(array_map('trim', explode(",", $this->allowedextensions)));
 867          }
 868          return array();
 869      }
 870      
 871      /**
 872      * Get allowed file extensions
 873      *
 874      * @return string Allowed file extensions
 875      */
 876  	public function getAllowedExtensions()
 877      {
 878          return $this->allowedextensions;
 879      }
 880      
 881      /**
 882      * Set allowed file extensions
 883      *
 884      * @param string $a_value Allowed file extensions
 885      */
 886  	public function setAllowedExtensions($a_value)
 887      {
 888          $this->allowedextensions = strtolower(trim($a_value));
 889      }
 890      
 891      /**
 892      * Object getter
 893      */
 894  	public function __get($value)
 895      {
 896          switch ($value)
 897          {
 898              case "maxsize":
 899                  return $this->getMaxSize();
 900                  break;
 901              case "allowedextensions":
 902                  return $this->getAllowedExtensions();
 903                  break;
 904              case 'completion_by_submission':
 905                  return $this->isCompletionBySubmissionEnabled();
 906                  break;
 907              default:
 908                  return parent::__get($value);
 909                  break;
 910          }
 911      }
 912  
 913      /**
 914      * Object setter
 915      */
 916  	public function __set($key, $value)
 917      {
 918          switch ($key)
 919          {
 920              case "maxsize":
 921                  $this->setMaxSize($value);
 922                  break;
 923              case "allowedextensions":
 924                  $this->setAllowedExtensions($value);
 925                  break;
 926              case 'completion_by_submission':
 927                  $this->setCompletionBySubmission($value);
 928                  break;
 929              default:
 930                  parent::__set($key, $value);
 931                  break;
 932          }
 933      }
 934  
 935      /**
 936       * Checks if file uploads exist for a given test and the original id of the question
 937       *
 938       * @param int $test_id
 939       *
 940       * @return boolean TRUE if file uploads exist, FALSE otherwise
 941       */
 942  	public function hasFileUploads($test_id)
 943      {
 944          global $ilDB;
 945          $query  = "
 946          SELECT tst_solutions.solution_id 
 947          FROM tst_solutions, tst_active, qpl_questions 
 948          WHERE tst_solutions.active_fi = tst_active.active_id 
 949          AND tst_solutions.question_fi = qpl_questions.question_id 
 950          AND tst_solutions.question_fi = %s AND tst_active.test_fi = %s";
 951          $result = $ilDB->queryF( $query,
 952              array("integer", "integer"),
 953              array($this->getId(), $test_id)
 954          );
 955          if ($result->numRows() > 0)
 956          {
 957              return true;
 958          }
 959          else
 960          {
 961              return false;
 962          }
 963      }
 964  
 965      /**
 966       * Generates a ZIP file containing all file uploads for a given test and the original id of the question
 967       *
 968       * @param int $test_id
 969       */
 970  	public function getFileUploadZIPFile($test_id)
 971      {
 972          /** @var ilDB $ilDB */
 973          global $ilDB;
 974          $query  = "
 975          SELECT 
 976              tst_solutions.solution_id, tst_solutions.pass, tst_solutions.active_fi, tst_solutions.question_fi, 
 977              tst_solutions.value1, tst_solutions.value2, tst_solutions.tstamp 
 978          FROM tst_solutions, tst_active, qpl_questions 
 979          WHERE tst_solutions.active_fi = tst_active.active_id 
 980          AND tst_solutions.question_fi = qpl_questions.question_id 
 981          AND tst_solutions.question_fi = %s 
 982          AND tst_active.test_fi = %s 
 983          ORDER BY tst_solutions.active_fi, tst_solutions.tstamp";
 984          
 985          $result = $ilDB->queryF( $query,
 986              array("integer", "integer"),
 987              array($this->getId(), $test_id)
 988          );
 989          $zipfile = ilUtil::ilTempnam() . ".zip";
 990          $tempdir = ilUtil::ilTempnam();
 991          if ($result->numRows())
 992          {
 993              $userdata = array();
 994              $data .= "<html><head>";
 995              $data .= '<meta http-equiv="content-type" content="text/html; charset=UTF-8" />';
 996              $data .= '<style>
 997               table { border: 1px #333 solid; border-collapse:collapse;}    
 998               td, th { border: 1px #333 solid; padding: 0.25em;}    
 999               th { color: #fff; background-color: #666;}
1000              </style>
1001              ';
1002              $data .= "<title>" . $this->getTitle() . "</title></head><body>\n";
1003              $data .= "<h1>" . $this->getTitle() . "</h1>\n";
1004              $data .= "<table><thead>\n";
1005              $data .= "<tr><th>" . $this->lng->txt("name") . "</th><th>" . $this->lng->txt("filename") . "</th><th>" . $this->lng->txt("pass") . "</th><th>" . $this->lng->txt("location") . "</th><th>" . $this->lng->txt("date") . "</th></tr></thead><tbody>\n";
1006              while ($row = $ilDB->fetchAssoc($result))
1007              {
1008                  ilUtil::makeDirParents($tempdir . "/" . $row["active_fi"]."/".$row["question_fi"]);
1009                  @copy($this->getFileUploadPath($test_id, $row["active_fi"], $row["question_fi"]) . $row["value1"], $tempdir . "/" . $row["active_fi"]."/".$row["question_fi"] . "/" . $row["value1"]);
1010                  if (!array_key_exists($row["active_fi"], $userdata))
1011                  {
1012                      include_once  "./Modules/Test/classes/class.ilObjTestAccess.php";
1013                      $userdata[$row["active_fi"]] = ilObjTestAccess::_getParticipantData($row["active_fi"]);
1014                  }
1015                  $data .= "<tr><td>".$userdata[$row["active_fi"]]."</td><td><a href=\"".$row["active_fi"]."/".$row["question_fi"]."/".$row["value1"]."\" target=\"_blank\">".$row["value2"]."</a></td><td>".$row["pass"]."</td><td>".$row["active_fi"]."/".$row["question_fi"]."/".$row["value1"]."</td>";
1016                  $data .= "<td>" . ilFormat::fmtDateTime(ilFormat::unixtimestamp2datetime($row["tstamp"]), $this->lng->txt("lang_dateformat"), $this->lng->txt("lang_timeformat"), "datetime", FALSE) . "</td>";
1017                  $data .= "</tr>\n";
1018              }
1019              $data .= "</tbody></table>\n";
1020              $data .= "</body></html>\n";
1021  
1022              $indexfile = $tempdir . "/index.html";
1023              $fh = fopen($indexfile, 'w');
1024              fwrite($fh, $data);
1025              fclose($fh);
1026          }
1027          ilUtil::zip($tempdir, $zipfile);
1028          ilUtil::delDir($tempdir);
1029          ilUtil::deliverFile($zipfile, ilUtil::getASCIIFilename($this->getTitle().".zip"), "application/zip", false, true);
1030      }
1031      
1032      /**
1033       *
1034       * Checks whether completion by submission is enabled or not
1035       *
1036       * @return boolean
1037       * @access public
1038       *
1039       */
1040  	public function isCompletionBySubmissionEnabled()
1041      {
1042          return $this->completion_by_submission;
1043      }
1044      
1045      /**
1046       *
1047       * Enabled/Disable completion by submission
1048       *
1049       * @param boolean
1050       * @return assFileUpload
1051       * @access public
1052       *
1053       */
1054  	public function setCompletionBySubmission($bool)
1055      {
1056          $this->completion_by_submission = (bool)$bool;
1057          return $this;
1058      }
1059      
1060      /**
1061       * returns boolean wether the question
1062       * is answered during test pass or not
1063       * 
1064       * (overwrites method in class assQuestion)
1065       * 
1066       * @global ilDB $ilDB
1067       * @param integer $active_id
1068       * @param integer $pass
1069       * @return boolean $answered
1070       */
1071  	public function isAnswered($active_id, $pass)
1072      {
1073          $answered = assQuestion::doesSolutionRecordsExist($active_id, $pass, $this->getId());
1074          
1075          return $answered;
1076      }
1077      
1078      /**
1079       * returns boolean wether it is possible to set
1080       * this question type as obligatory or not
1081       * considering the current question configuration
1082       * 
1083       * (overwrites method in class assQuestion)
1084       * 
1085       * @param integer $questionId
1086       * @return boolean $obligationPossible
1087       */
1088  	public static function isObligationPossible($questionId)
1089      {
1090          return true;
1091      }
1092      
1093  	public function isAutosaveable()
1094      {
1095          return FALSE;
1096      }
1097  }

title

Description

title

Description

title

Description

title

title

Body