ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
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  protected const DEFAULT_TEXT_SIZE = 100;
36 
37  protected $ordertext;
38  protected $textsize;
39  protected $separator = "::";
40  protected $answer_separator = '{::}';
41 
54  public function __construct(
55  $title = "",
56  $comment = "",
57  $author = "",
58  $owner = -1,
59  $question = ""
60  ) {
62  $this->ordertext = "";
63  }
64 
70  public function isComplete(): bool
71  {
72  if (strlen($this->title) and ($this->author) and ($this->question) and ($this->getMaximumPoints() > 0)) {
73  return true;
74  } else {
75  return false;
76  }
77  }
78 
83  public function saveToDb($original_id = ""): void
84  {
85  if ($original_id == "") {
86  $this->saveQuestionDataToDb();
87  } else {
89  }
90 
92  parent::saveToDb();
93  }
94 
98  public function getAnswerSeparator(): string
99  {
101  }
102 
103 
110  public function loadFromDb($question_id): void
111  {
112  global $DIC;
113  $ilDB = $DIC['ilDB'];
114 
115  $result = $ilDB->queryF(
116  "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",
117  array("integer"),
118  array($question_id)
119  );
120  if ($result->numRows() == 1) {
121  $data = $ilDB->fetchAssoc($result);
122  $this->setId($question_id);
123  $this->setObjId($data["obj_fi"]);
124  $this->setTitle((string) $data["title"]);
125  $this->setComment((string) $data["description"]);
126  $this->setOriginalId($data["original_id"]);
127  $this->setNrOfTries($data['nr_of_tries']);
128  $this->setAuthor($data["author"]);
129  $this->setPoints($data["points"]);
130  $this->setOwner($data["owner"]);
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 = "", int $owner = -1, $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 
165  $original_id = $this->questioninfo->getOriginalId($this->id);
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 
210  $original_id = $this->questioninfo->getOriginalId($this->id);
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  $sourceQuestionId = $this->id;
235  $sourceParentId = $this->getObjId();
236 
237  // duplicate the question in database
238  $clone = $this;
239  $clone->id = -1;
240 
241  $clone->setObjId($targetParentId);
242 
243  if ($targetQuestionTitle) {
244  $clone->setTitle($targetQuestionTitle);
245  }
246 
247  $clone->saveToDb();
248  // copy question page content
249  $clone->copyPageOfQuestion($sourceQuestionId);
250  // copy XHTML media objects
251  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
252 
253  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
254 
255  return $clone->id;
256  }
257 
263  public function getMaximumPoints(): float
264  {
265  return $this->getPoints();
266  }
267 
278  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false): float
279  {
280  if ($returndetails) {
281  throw new ilTestException('return details not implemented for ' . __METHOD__);
282  }
283 
284  global $DIC;
285  $ilDB = $DIC['ilDB'];
286 
287  $found_values = [];
288  if (is_null($pass)) {
289  $pass = $this->getSolutionMaxPass($active_id);
290  }
291  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
292  $points = 0;
293 
294  if ($ilDB->numRows($result) > 0) {
295  $data = $ilDB->fetchAssoc($result);
296  $points = $this->calculateReachedPointsForSolution($data['value1']);
297  }
298 
299  return $points;
300  }
301 
311  public function splitAndTrimOrderElementText(string $in_string, string $separator): array
312  {
313  $result = [];
314 
315  if (ilStr::strPos($in_string, $separator, 0) === false) {
316  $result = preg_split("/\\s+/", $in_string);
317  } else {
318  $result = explode($separator, $in_string);
319  }
320 
321  foreach ($result as $key => $value) {
322  $result[$key] = trim($value);
323  }
324 
325  return $result;
326  }
327 
328  public function getSolutionSubmit()
329  {
330  return $_POST["orderresult"];
331  }
332 
341  public function saveWorkingData($active_id, $pass = null, $authorized = true): bool
342  {
343  if ($this->dic->testQuestionPool()->internal()->request()->raw('test_answer_changed') === null) {
344  return true;
345  }
346 
347  global $DIC;
348  $ilDB = $DIC['ilDB'];
349  $ilUser = $DIC['ilUser'];
350 
351  if (is_null($pass)) {
352  $pass = ilObjTest::_getPass($active_id);
353  }
354 
355  $entered_values = false;
356 
357  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
358  $this->removeCurrentSolution($active_id, $pass, $authorized);
359 
360  $solutionSubmit = $this->getSolutionSubmit();
361 
362  $entered_values = false;
363  if (strlen($solutionSubmit)) {
364  $this->saveCurrentSolution($active_id, $pass, $_POST['orderresult'], null, $authorized);
365  $entered_values = true;
366  }
367  });
368 
369  if ($entered_values) {
371  assQuestion::logAction($this->lng->txtlng(
372  "assessment",
373  "log_user_entered_values",
375  ), $active_id, $this->getId());
376  }
377  } else {
379  assQuestion::logAction($this->lng->txtlng(
380  "assessment",
381  "log_user_not_entered_values",
383  ), $active_id, $this->getId());
384  }
385  }
386 
387  return true;
388  }
389 
391  {
392  global $DIC;
393  $ilDB = $DIC['ilDB'];
394 
395  // save additional data
396  $ilDB->manipulateF(
397  "DELETE FROM " . $this->getAdditionalTableName()
398  . " WHERE question_fi = %s",
399  array( "integer" ),
400  array( $this->getId() )
401  );
402 
403  $ilDB->manipulateF(
404  "INSERT INTO " . $this->getAdditionalTableName()
405  . " (question_fi, ordertext, textsize) VALUES (%s, %s, %s)",
406  array( "integer", "text", "float" ),
407  array(
408  $this->getId(),
409  $this->getOrderText(),
410  ($this->getTextSize() < 10) ? null : (float) $this->getTextSize()
411  )
412  );
413  }
414 
420  public function getQuestionType(): string
421  {
422  return "assOrderingHorizontal";
423  }
424 
430  public function getAdditionalTableName(): string
431  {
432  return "qpl_qst_horder";
433  }
434 
440  public function getAnswerTableName(): string
441  {
442  return "";
443  }
444 
450  public function deleteAnswers($question_id): void
451  {
452  }
453 
458  public function getRTETextWithMediaObjects(): string
459  {
460  $text = parent::getRTETextWithMediaObjects();
461  return $text;
462  }
463 
467  public function setExportDetailsXLSX(ilAssExcelFormatHelper $worksheet, int $startrow, int $col, int $active_id, int $pass): int
468  {
469  parent::setExportDetailsXLSX($worksheet, $startrow, $col, $active_id, $pass);
470 
471  $solutionvalue = "";
472  $solutions = $this->getSolutionValues($active_id, $pass);
473  if (array_key_exists(0, $solutions)) {
474  $solutionvalue = str_replace("{::}", " ", $solutions[0]["value1"]);
475  }
476  $i = 1;
477  $worksheet->setCell($startrow + $i, $col + 2, $solutionvalue);
478  $i++;
479 
480  return $startrow + $i + 1;
481  }
482 
495  public function fromXML($item, int $questionpool_id, ?int $tst_id, &$tst_object, int &$question_counter, array $import_mapping, array &$solutionhints = []): array
496  {
497  $import = new assOrderingHorizontalImport($this);
498  return $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
499  }
500 
507  public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false): string
508  {
509  $export = new assOrderingHorizontalExport($this);
510  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
511  }
512 
518  public function getBestSolution($active_id, $pass): array
519  {
520  $user_solution = [];
521  return $user_solution;
522  }
523 
529  public function getOrderingElements(): array
530  {
531  return $this->splitAndTrimOrderElementText($this->getOrderText() ?? "", $this->separator);
532  }
533 
539  public function getRandomOrderingElements(): array
540  {
541  $elements = $this->getOrderingElements();
542  $elements = $this->getShuffler()->transform($elements);
543  return $elements;
544  }
545 
551  public function getOrderText()
552  {
553  return $this->ordertext;
554  }
555 
561  public function setOrderText($a_value): void
562  {
563  $this->ordertext = $a_value;
564  }
565 
571  public function getTextSize()
572  {
573  return $this->textsize;
574  }
575 
581  public function setTextSize(?float $textsize): void
582  {
583  if ($textsize === null || $textsize === 0.0) {
584  $textsize = self::DEFAULT_TEXT_SIZE;
585  }
586  $this->textsize = $textsize;
587  }
588 
594  public function getSeparator(): string
595  {
596  return $this->separator;
597  }
598 
604  public function setSeparator($a_value): void
605  {
606  $this->separator = $a_value;
607  }
608 
612  public function toJSON(): string
613  {
614  $result = [];
615  $result['id'] = $this->getId();
616  $result['type'] = (string) $this->getQuestionType();
617  $result['title'] = $this->getTitleForHTMLOutput();
618  $result['question'] = $this->formatSAQuestion($this->getQuestion());
619  $result['nr_of_tries'] = $this->getNrOfTries();
620  $result['shuffle'] = true;
621  $result['points'] = (bool) $this->getPoints();
622  $result['textsize'] = ((int) $this->getTextSize()) // #10923
623  ? (int) $this->getTextSize()
624  : self::DEFAULT_TEXT_SIZE;
625  $result['feedback'] = array(
626  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
627  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
628  );
629 
630  $arr = [];
631  foreach ($this->getOrderingElements() as $order => $answer) {
632  array_push($arr, array(
633  "answertext" => (string) $answer,
634  "order" => (int) $order + 1
635  ));
636  }
637  $result['answers'] = $arr;
638 
639  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
640  $result['mobs'] = $mobs;
641 
642  return json_encode($result);
643  }
644 
653  public function getOperators($expression): array
654  {
656  }
657 
662  public function getExpressionTypes(): array
663  {
664  return array(
669  );
670  }
671 
680  public function getUserQuestionResult($active_id, $pass): ilUserQuestionResult
681  {
683  global $DIC;
684  $ilDB = $DIC['ilDB'];
685  $result = new ilUserQuestionResult($this, $active_id, $pass);
686 
687  $maxStep = $this->lookupMaxStep($active_id, $pass);
688 
689  if ($maxStep > 0) {
690  $data = $ilDB->queryF(
691  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
692  array("integer", "integer", "integer","integer"),
693  array($active_id, $pass, $this->getId(), $maxStep)
694  );
695  } else {
696  $data = $ilDB->queryF(
697  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
698  array("integer", "integer", "integer"),
699  array($active_id, $pass, $this->getId())
700  );
701  }
702  $row = $ilDB->fetchAssoc($data);
703 
704  $answer_elements = $this->splitAndTrimOrderElementText($row["value1"] ?? "", $this->answer_separator);
705  $elements = $this->getOrderingElements();
706  $solutions = [];
707 
708  foreach ($answer_elements as $answer) {
709  foreach ($elements as $key => $element) {
710  if ($element == $answer) {
711  $result->addKeyValue($key + 1, $answer);
712  }
713  }
714  }
715 
716  $glue = " ";
717  if ($this->answer_separator = '{::}') {
718  $glue = "";
719  }
720  $result->addKeyValue(null, join($glue, $answer_elements));
721 
722  $points = $this->calculateReachedPoints($active_id, $pass);
723  $max_points = $this->getMaximumPoints();
724 
725  $result->setReachedPercentage(($points / $max_points) * 100);
726 
727  return $result;
728  }
729 
738  public function getAvailableAnswerOptions($index = null)
739  {
740  $elements = $this->getOrderingElements();
741  if ($index !== null) {
742  if (array_key_exists($index, $elements)) {
743  return $elements[$index];
744  }
745  return null;
746  } else {
747  return $elements;
748  }
749  }
750 
755  protected function calculateReachedPointsForSolution($value): float
756  {
757  $value = $this->splitAndTrimOrderElementText($value ?? "", $this->answer_separator);
758  $value = join($this->answer_separator, $value);
759  if (strcmp($value, join($this->answer_separator, $this->getOrderingElements())) == 0) {
760  $points = $this->getPoints();
761  return $points;
762  }
763  return 0;
764  }
765 
766  // fau: testNav - new function getTestQuestionConfig()
771  // hey: refactored identifiers
773  // hey.
774  {
775  // hey: refactored identifiers
776  return parent::buildTestPresentationConfig()
777  // hey.
779  ->setUseUnchangedAnswerLabel($this->lng->txt('tst_unchanged_order_is_correct'));
780  }
781  // fau.
782 }
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.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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.
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...
setExportDetailsXLSX(ilAssExcelFormatHelper $worksheet, int $startrow, int $col, int $active_id, int $pass)
{}
setOwner(int $owner=-1)
static strPos(string $a_haystack, string $a_needle, int $a_offset=0)
Definition: class.ilStr.php:42
duplicate(bool $for_test=true, string $title="", string $author="", int $owner=-1, $testObjId=null)
Duplicates an assOrderingHorizontal.
getSeparator()
Get order text separator.
copyObject($target_questionpool_id, $title="")
Copies an assOrderingHorizontal object.
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.
Base Exception for all Exceptions relating to Modules/Test.
global $DIC
Definition: feed.php:28
toJSON()
Returns a JSON representation of the question.
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
__construct(VocabulariesInterface $vocabularies)
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...
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.
setTextSize(?float $textsize)
Set text size.
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.
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)
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.
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.
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.