ILIAS  release_8 Revision v8.23
class.assOrderingHorizontal.php
Go to the documentation of this file.
1 <?php
2 
19 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
20 
33 {
34  protected const HAS_SPECIFIC_FEEDBACK = false;
35 
36  protected $ordertext;
37  protected $textsize;
38  protected $separator = "::";
39  protected $answer_separator = '{::}';
40 
53  public function __construct(
54  $title = "",
55  $comment = "",
56  $author = "",
57  $owner = -1,
58  $question = ""
59  ) {
61  $this->ordertext = "";
62  }
63 
69  public function isComplete(): bool
70  {
71  if (strlen($this->title) and ($this->author) and ($this->question) and ($this->getMaximumPoints() > 0)) {
72  return true;
73  } else {
74  return false;
75  }
76  }
77 
82  public function saveToDb($original_id = ""): void
83  {
84  if ($original_id == "") {
85  $this->saveQuestionDataToDb();
86  } else {
88  }
89 
91  parent::saveToDb();
92  }
93 
97  public function getAnswerSeparator(): string
98  {
100  }
101 
102 
109  public function loadFromDb($question_id): void
110  {
111  global $DIC;
112  $ilDB = $DIC['ilDB'];
113 
114  $result = $ilDB->queryF(
115  "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",
116  array("integer"),
117  array($question_id)
118  );
119  if ($result->numRows() == 1) {
120  $data = $ilDB->fetchAssoc($result);
121  $this->setId($question_id);
122  $this->setObjId($data["obj_fi"]);
123  $this->setTitle((string) $data["title"]);
124  $this->setComment((string) $data["description"]);
125  $this->setOriginalId($data["original_id"]);
126  $this->setNrOfTries($data['nr_of_tries']);
127  $this->setAuthor($data["author"]);
128  $this->setPoints($data["points"]);
129  $this->setOwner($data["owner"]);
130  include_once("./Services/RTE/classes/class.ilRTE.php");
131  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc((string) $data["question_text"], 1));
132  $this->setOrderText($data["ordertext"]);
133  $this->setTextSize($data["textsize"]);
134 
135  try {
136  $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
139  }
140 
141  try {
142  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
143  } catch (ilTestQuestionPoolException $e) {
144  }
145  }
146 
147  parent::loadFromDb($question_id);
148  }
149 
153  public function duplicate(bool $for_test = true, string $title = "", string $author = "", string $owner = "", $testObjId = null): int
154  {
155  if ($this->id <= 0) {
156  // The question has not been saved. It cannot be duplicated
157  return -1;
158  }
159  // duplicate the question in database
160  $this_id = $this->getId();
161  $thisObjId = $this->getObjId();
162 
163  $clone = $this;
164  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
166  $clone->id = -1;
167 
168  if ((int) $testObjId > 0) {
169  $clone->setObjId($testObjId);
170  }
171 
172  if ($title) {
173  $clone->setTitle($title);
174  }
175 
176  if ($author) {
177  $clone->setAuthor($author);
178  }
179  if ($owner) {
180  $clone->setOwner($owner);
181  }
182 
183  if ($for_test) {
184  $clone->saveToDb($original_id);
185  } else {
186  $clone->saveToDb();
187  }
188 
189  // copy question page content
190  $clone->copyPageOfQuestion($this_id);
191  // copy XHTML media objects
192  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
193 
194  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
195 
196  return $clone->id;
197  }
198 
202  public function copyObject($target_questionpool_id, $title = ""): int
203  {
204  if ($this->getId() <= 0) {
205  throw new RuntimeException('The question has not been saved. It cannot be duplicated');
206  }
207  // duplicate the question in database
208  $clone = $this;
209  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
211  $clone->id = -1;
212  $source_questionpool_id = $this->getObjId();
213  $clone->setObjId($target_questionpool_id);
214  if ($title) {
215  $clone->setTitle($title);
216  }
217  $clone->saveToDb();
218  // copy question page content
219  $clone->copyPageOfQuestion($original_id);
220  // copy XHTML media objects
221  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
222 
223  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
224 
225  return $clone->id;
226  }
227 
228  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = ""): int
229  {
230  if ($this->getId() <= 0) {
231  throw new RuntimeException('The question has not been saved. It cannot be duplicated');
232  }
233 
234  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
235 
236  $sourceQuestionId = $this->id;
237  $sourceParentId = $this->getObjId();
238 
239  // duplicate the question in database
240  $clone = $this;
241  $clone->id = -1;
242 
243  $clone->setObjId($targetParentId);
244 
245  if ($targetQuestionTitle) {
246  $clone->setTitle($targetQuestionTitle);
247  }
248 
249  $clone->saveToDb();
250  // copy question page content
251  $clone->copyPageOfQuestion($sourceQuestionId);
252  // copy XHTML media objects
253  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
254 
255  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
256 
257  return $clone->id;
258  }
259 
265  public function getMaximumPoints(): float
266  {
267  return $this->getPoints();
268  }
269 
280  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
281  {
282  if ($returndetails) {
283  throw new ilTestException('return details not implemented for ' . __METHOD__);
284  }
285 
286  global $DIC;
287  $ilDB = $DIC['ilDB'];
288 
289  $found_values = array();
290  if (is_null($pass)) {
291  $pass = $this->getSolutionMaxPass($active_id);
292  }
293  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
294  $points = 0;
295 
296  if ($ilDB->numRows($result) > 0) {
297  $data = $ilDB->fetchAssoc($result);
298  $points = $this->calculateReachedPointsForSolution($data['value1']);
299  }
300 
301  return $points;
302  }
303 
313  public function splitAndTrimOrderElementText(string $in_string, string $separator): array
314  {
315  $result = array();
316  include_once "./Services/Utilities/classes/class.ilStr.php";
317 
318  if (ilStr::strPos($in_string, $separator) === false) {
319  $result = preg_split("/\\s+/", $in_string);
320  } else {
321  $result = explode($separator, $in_string);
322  }
323 
324  foreach ($result as $key => $value) {
325  $result[$key] = trim($value);
326  }
327 
328  return $result;
329  }
330 
331  public function getSolutionSubmit()
332  {
333  return $_POST["orderresult"];
334  }
335 
344  public function saveWorkingData($active_id, $pass = null, $authorized = true): bool
345  {
346  global $DIC;
347  if ($DIC->testQuestionPool()->internal()->request()->raw('test_answer_changed') === null) {
348  return true;
349  }
350 
351  global $DIC;
352  $ilDB = $DIC['ilDB'];
353  $ilUser = $DIC['ilUser'];
354 
355  if (is_null($pass)) {
356  include_once "./Modules/Test/classes/class.ilObjTest.php";
357  $pass = ilObjTest::_getPass($active_id);
358  }
359 
360  $entered_values = false;
361 
362  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
363  $this->removeCurrentSolution($active_id, $pass, $authorized);
364 
365  $solutionSubmit = $this->getSolutionSubmit();
366 
367  $entered_values = false;
368  if (strlen($solutionSubmit)) {
369  $this->saveCurrentSolution($active_id, $pass, $_POST['orderresult'], null, $authorized);
370  $entered_values = true;
371  }
372  });
373 
374  if ($entered_values) {
375  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
377  assQuestion::logAction($this->lng->txtlng(
378  "assessment",
379  "log_user_entered_values",
381  ), $active_id, $this->getId());
382  }
383  } else {
384  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
386  assQuestion::logAction($this->lng->txtlng(
387  "assessment",
388  "log_user_not_entered_values",
390  ), $active_id, $this->getId());
391  }
392  }
393 
394  return true;
395  }
396 
398  {
399  global $DIC;
400  $ilDB = $DIC['ilDB'];
401 
402  // save additional data
403  $ilDB->manipulateF(
404  "DELETE FROM " . $this->getAdditionalTableName()
405  . " WHERE question_fi = %s",
406  array( "integer" ),
407  array( $this->getId() )
408  );
409 
410  $ilDB->manipulateF(
411  "INSERT INTO " . $this->getAdditionalTableName()
412  . " (question_fi, ordertext, textsize) VALUES (%s, %s, %s)",
413  array( "integer", "text", "float" ),
414  array(
415  $this->getId(),
416  $this->getOrderText(),
417  ($this->getTextSize() < 10) ? null : (float) $this->getTextSize()
418  )
419  );
420  }
421 
427  public function getQuestionType(): string
428  {
429  return "assOrderingHorizontal";
430  }
431 
437  public function getAdditionalTableName(): string
438  {
439  return "qpl_qst_horder";
440  }
441 
447  public function getAnswerTableName(): string
448  {
449  return "";
450  }
451 
457  public function deleteAnswers($question_id): void
458  {
459  }
460 
465  public function getRTETextWithMediaObjects(): string
466  {
467  $text = parent::getRTETextWithMediaObjects();
468  return $text;
469  }
470 
474  public function setExportDetailsXLS(ilAssExcelFormatHelper $worksheet, int $startrow, int $active_id, int $pass): int
475  {
476  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
477 
478  $solutionvalue = "";
479  $solutions = $this->getSolutionValues($active_id, $pass);
480  if (array_key_exists(0, $solutions)) {
481  $solutionvalue = str_replace("{::}", " ", $solutions[0]["value1"]);
482  }
483  $i = 1;
484  $worksheet->setCell($startrow + $i, 2, $solutionvalue);
485  $i++;
486 
487  return $startrow + $i + 1;
488  }
489 
502  public function fromXML($item, int $questionpool_id, ?int $tst_id, &$tst_object, int &$question_counter, array $import_mapping, array &$solutionhints = []): array
503  {
504  include_once "./Modules/TestQuestionPool/classes/import/qti12/class.assOrderingHorizontalImport.php";
505  $import = new assOrderingHorizontalImport($this);
506  return $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
507  }
508 
515  public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false): string
516  {
517  include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assOrderingHorizontalExport.php";
518  $export = new assOrderingHorizontalExport($this);
519  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
520  }
521 
527  public function getBestSolution($active_id, $pass): array
528  {
529  $user_solution = array();
530  return $user_solution;
531  }
532 
538  public function getOrderingElements(): array
539  {
540  return $this->splitAndTrimOrderElementText($this->getOrderText() ?? "", $this->separator);
541  }
542 
548  public function getRandomOrderingElements(): array
549  {
550  $elements = $this->getOrderingElements();
551  $elements = $this->getShuffler()->transform($elements);
552  return $elements;
553  }
554 
560  public function getOrderText()
561  {
562  return $this->ordertext;
563  }
564 
570  public function setOrderText($a_value): void
571  {
572  $this->ordertext = $a_value;
573  }
574 
580  public function getTextSize()
581  {
582  return $this->textsize;
583  }
584 
590  public function setTextSize($a_value): void
591  {
592  if ($a_value >= 10) {
593  $this->textsize = $a_value;
594  }
595  }
596 
602  public function getSeparator(): string
603  {
604  return $this->separator;
605  }
606 
612  public function setSeparator($a_value): void
613  {
614  $this->separator = $a_value;
615  }
616 
617  public function supportsJavascriptOutput(): bool
618  {
619  return true;
620  }
621 
622  public function supportsNonJsOutput(): bool
623  {
624  return false;
625  }
626 
630  public function toJSON(): string
631  {
632  include_once("./Services/RTE/classes/class.ilRTE.php");
633  $result = array();
634  $result['id'] = $this->getId();
635  $result['type'] = (string) $this->getQuestionType();
636  $result['title'] = $this->getTitleForHTMLOutput();
637  $result['question'] = $this->formatSAQuestion($this->getQuestion());
638  $result['nr_of_tries'] = $this->getNrOfTries();
639  $result['shuffle'] = true;
640  $result['points'] = (bool) $this->getPoints();
641  $result['textsize'] = ((int) $this->getTextSize()) // #10923
642  ? (int) $this->getTextSize()
643  : 100;
644  $result['feedback'] = array(
645  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
646  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
647  );
648 
649  $arr = array();
650  foreach ($this->getOrderingElements() as $order => $answer) {
651  array_push($arr, array(
652  "answertext" => (string) $answer,
653  "order" => (int) $order + 1
654  ));
655  }
656  $result['answers'] = $arr;
657 
658  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
659  $result['mobs'] = $mobs;
660 
661  return json_encode($result);
662  }
663 
672  public function getOperators($expression): array
673  {
675  }
676 
681  public function getExpressionTypes(): array
682  {
683  return array(
688  );
689  }
690 
699  public function getUserQuestionResult($active_id, $pass): ilUserQuestionResult
700  {
702  global $DIC;
703  $ilDB = $DIC['ilDB'];
704  $result = new ilUserQuestionResult($this, $active_id, $pass);
705 
706  $maxStep = $this->lookupMaxStep($active_id, $pass);
707 
708  if ($maxStep !== null) {
709  $data = $ilDB->queryF(
710  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
711  array("integer", "integer", "integer","integer"),
712  array($active_id, $pass, $this->getId(), $maxStep)
713  );
714  } else {
715  $data = $ilDB->queryF(
716  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
717  array("integer", "integer", "integer"),
718  array($active_id, $pass, $this->getId())
719  );
720  }
721  $row = $ilDB->fetchAssoc($data);
722 
723  $answer_elements = $this->splitAndTrimOrderElementText($row["value1"] ?? "", $this->answer_separator);
724  $elements = $this->getOrderingElements();
725  $solutions = array();
726 
727  foreach ($answer_elements as $answer) {
728  foreach ($elements as $key => $element) {
729  if ($element == $answer) {
730  $result->addKeyValue($key + 1, $answer);
731  }
732  }
733  }
734 
735  $glue = " ";
736  if ($this->answer_separator = '{::}') {
737  $glue = "";
738  }
739  $result->addKeyValue(null, join($glue, $answer_elements));
740 
741  $points = $this->calculateReachedPoints($active_id, $pass);
742  $max_points = $this->getMaximumPoints();
743 
744  $result->setReachedPercentage(($points / $max_points) * 100);
745 
746  return $result;
747  }
748 
757  public function getAvailableAnswerOptions($index = null)
758  {
759  $elements = $this->getOrderingElements();
760  if ($index !== null) {
761  if (array_key_exists($index, $elements)) {
762  return $elements[$index];
763  }
764  return null;
765  } else {
766  return $elements;
767  }
768  }
769 
774  protected function calculateReachedPointsForSolution($value): float
775  {
776  $value = $this->splitAndTrimOrderElementText($value ?? "", $this->answer_separator);
777  $value = join($this->answer_separator, $value);
778  if (strcmp($value, join($this->answer_separator, $this->getOrderingElements())) == 0) {
779  $points = $this->getPoints();
780  return $points;
781  }
782  return 0;
783  }
784 
785  // fau: testNav - new function getTestQuestionConfig()
790  // hey: refactored identifiers
792  // hey.
793  {
794  // hey: refactored identifiers
795  return parent::buildTestPresentationConfig()
796  // hey.
798  ->setUseUnchangedAnswerLabel($this->lng->txt('tst_unchanged_order_is_correct'));
799  }
800  // fau.
801 }
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
getSolutionValues($active_id, $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
setNrOfTries(int $a_nr_of_tries)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
fromXML($item, int $questionpool_id, ?int $tst_id, &$tst_object, int &$question_counter, array $import_mapping, array &$solutionhints=[])
Creates a question from a QTI file.
$mobs
Definition: imgupload.php:70
setIsUnchangedAnswerPossible($isUnchangedAnswerPossible)
Set if the saving of an unchanged answer is supported with an additional checkbox.
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
Abstract basic class which is to be extended by the concrete assessment question type classes...
setOwner(int $owner=-1)
getSeparator()
Get order text separator.
copyObject($target_questionpool_id, $title="")
Copies an assOrderingHorizontal object.
static strPos(string $a_haystack, string $a_needle, ?int $a_offset=null)
Definition: class.ilStr.php:42
static _getOriginalId(int $question_id)
setOrderText($a_value)
Set order text.
setCell($a_row, $a_col, $a_value, $datatype=null)
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
setComment(string $comment="")
getBestSolution($active_id, $pass)
Returns the best solution for a given pass of a participant.
float $points
The maximum available points for the question.
loadFromDb($question_id)
Loads a assOrderingHorizontal object from a database.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$index
Definition: metadata.php:145
global $DIC
Definition: feed.php:28
duplicate(bool $for_test=true, string $title="", string $author="", string $owner="", $testObjId=null)
Duplicates an assOrderingHorizontal.
toJSON()
Returns a JSON representation of the question.
setExportDetailsXLS(ilAssExcelFormatHelper $worksheet, int $startrow, int $active_id, int $pass)
{}
getAdditionalTableName()
Returns the name of the additional question data table in the database.
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assOrderingHorizontal constructor
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
isComplete()
Returns true, if a single choice question is complete for use.
static logAction(string $logtext, int $active_id, int $question_id)
string $key
Consumer key/client ID value.
Definition: System.php:193
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
buildTestPresentationConfig()
Get the test question configuration.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setPoints(float $points)
setObjId(int $obj_id=0)
string $question
The question text.
splitAndTrimOrderElementText(string $in_string, string $separator)
Splits the answer string either by space(s) or the separator (eg.
setTextSize($a_value)
Set text size.
static _getMobsOfObject(string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
toXML($a_include_header=true, $a_include_binary=true, $a_shuffle=false, $test_output=false, $force_image_references=false)
Returns a QTI xml representation of the question and sets the internal domxml variable with the DOM X...
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
getRandomOrderingElements()
Get ordering elements from order text in random sequence.
setSeparator($a_value)
Set order text separator.
deleteAnswers($question_id)
Deletes datasets from answers tables.
saveQuestionDataToDb(int $original_id=-1)
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
getSolutionMaxPass(int $active_id)
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setId(int $id=-1)
__construct(Container $dic, ilPlugin $plugin)
setOriginalId(?int $original_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getQuestionType()
Returns the question type of the question.
$ilUser
Definition: imgupload.php:34
setTitle(string $title="")
setLifecycle(ilAssQuestionLifecycle $lifecycle)
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
getExpressionTypes()
Get all available expression types for a specific question.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
saveToDb($original_id="")
Saves a assOrderingHorizontal object to a database.
lookupMaxStep(int $active_id, int $pass)
setAuthor(string $author="")
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
$i
Definition: metadata.php:41
getAnswerTableName()
Returns the name of the answer table in the database.
setQuestion(string $question="")
getOrderingElements()
Get ordering elements from order text.
getOperators($expression)
Get all available operations for a specific question.