ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilPCQuestion.php
Go to the documentation of this file.
1<?php
2
25{
26 protected ilLanguage $lng;
27 protected ilCtrl $ctrl;
28 protected ilObjUser $user;
29 protected static bool $initial_done = false;
30
34 public function init(): void
35 {
36 global $DIC;
37
38 $this->lng = $DIC->language();
39 $this->ctrl = $DIC->ctrl();
40 $this->user = $DIC->user();
41 $this->setType("pcqst");
42 }
43
44 public function setQuestionReference(string $a_questionreference): void
45 {
46 if (is_object($this->getChildNode())) {
47 $this->getChildNode()->setAttribute("QRef", $a_questionreference);
48 }
49 }
50
51 public function getQuestionReference(): ?string
52 {
53 if (is_object($this->getChildNode())) {
54 return $this->getChildNode()->getAttribute("QRef");
55 }
56 return null;
57 }
58
59 public function create(
60 ilPageObject $a_pg_obj,
61 string $a_hier_id,
62 string $a_pc_id = ""
63 ): void {
65 $a_hier_id,
66 $a_pc_id,
67 "Question",
68 ["QRef" => ""]
69 );
70 }
71
75 public function copyPoolQuestionIntoPage(
76 string $a_q_id,
77 string $a_hier_id
78 ): void {
79 $question = assQuestion::instantiateQuestion($a_q_id);
80 $duplicate_id = $question->copyObject(0, $question->getTitle());
81 $duplicate = assQuestion::instantiateQuestion($duplicate_id);
82 $duplicate->setObjId(0);
83
85
86 $this->getChildNode()->setAttribute("QRef", "il__qst_" . $duplicate_id);
87 }
88
89 public static function getLangVars(): array
90 {
91 return array("ed_insert_pcqst", "empty_question", "pc_qst");
92 }
93
97 public static function afterPageUpdate(
98 ilPageObject $a_page,
99 DOMDocument $a_domdoc,
100 string $a_xml,
101 bool $a_creation
102 ): void {
103 global $DIC;
104
105 $ilDB = $DIC->database();
106
107 $ilDB->manipulateF(
108 "DELETE FROM page_question WHERE page_parent_type = %s " .
109 " AND page_id = %s AND page_lang = %s",
110 array("text", "integer", "text"),
111 array($a_page->getParentType(), $a_page->getId(), $a_page->getLanguage())
112 );
113
114 $xpath = new DOMXPath($a_domdoc);
115 $nodes = $xpath->query('//Question');
116 $q_ids = array();
117 foreach ($nodes as $node) {
118 $q_ref = $node->getAttribute("QRef");
119
120 $inst_id = ilInternalLink::_extractInstOfTarget($q_ref);
121 if (!($inst_id > 0)) {
123 if ($q_id > 0) {
124 $q_ids[$q_id] = $q_id;
125 }
126 }
127 }
128 foreach ($q_ids as $qid) {
129 $ilDB->replace(
130 "page_question",
131 [
132 "page_parent_type" => ["text", $a_page->getParentType()],
133 "page_id" => ["integer", $a_page->getId()],
134 "page_lang" => ["text", $a_page->getLanguage()],
135 "question_id" => ["integer", $qid]
136 ],
137 []
138 );
139 }
140 }
141
142 public static function beforePageDelete(
143 ilPageObject $a_page
144 ): void {
145 global $DIC;
146
147 $ilDB = $DIC->database();
148
149 $ilDB->manipulateF(
150 "DELETE FROM page_question WHERE page_parent_type = %s " .
151 " AND page_id = %s AND page_lang = %s",
152 array("text", "integer", "text"),
153 array($a_page->getParentType(), $a_page->getId(), $a_page->getLanguage())
154 );
155 }
156
157 public static function _getQuestionIdsForPage(
158 string $a_parent_type,
159 int $a_page_id,
160 string $a_lang = "-"
161 ): array {
162 global $DIC;
163
164 $ilDB = $DIC->database();
165
166 $parent_type_array = explode(':', $a_parent_type);
167
168 $res = $ilDB->queryF(
169 "SELECT * FROM page_question WHERE page_parent_type = %s " .
170 " AND page_id = %s AND page_lang = %s",
171 array("text", "integer", "text"),
172 array($parent_type_array[0], $a_page_id, $a_lang)
173 );
174 $q_ids = array();
175 while ($rec = $ilDB->fetchAssoc($res)) {
176 $q_ids[] = $rec["question_id"];
177 }
178
179 return $q_ids;
180 }
181
182 public static function _getPageForQuestionId(
183 int $a_q_id,
184 string $a_parent_type = ""
185 ): ?array {
186 global $DIC;
187
188 $ilDB = $DIC->database();
189
190 $set = $ilDB->query(
191 "SELECT * FROM page_question " .
192 " WHERE question_id = " . $ilDB->quote($a_q_id, "integer")
193 );
194 while ($rec = $ilDB->fetchAssoc($set)) {
195 if ($a_parent_type == "" || $rec["page_parent_type"] == $a_parent_type) {
196 return array("page_id" => $rec["page_id"], "parent_type" => $rec["page_parent_type"]);
197 }
198 }
199 return null;
200 }
201
203 string $a_output,
204 string $a_mode,
205 bool $a_abstract_only = false
206 ): string {
207 $lng = $this->lng;
208
209 $qhtml = "";
210 if ($this->getPage()->getPageConfig()->getEnableSelfAssessment()) {
211 // #14154
212 $q_ids = $this->getQuestionIds();
213 if (count($q_ids)) {
214 foreach ($q_ids as $q_id) {
215 $q_gui = assQuestionGUI::_getQuestionGUI("", $q_id);
216 // object check due to #16557
217 if (!is_null($q_gui) && is_object($q_gui->getObject()) && !$q_gui->getObject()->isComplete()) {
218 $a_output = str_replace(
219 "{{{{{Question;il__qst_" . $q_id . "}}}}}",
220 "<i>" . $lng->txt("cont_empty_question") . "</i>",
221 $a_output
222 );
223 }
224 }
225
226 // this exports the questions which is needed below
227 $qhtml = $this->getQuestionJsOfPage($a_mode == "edit", $a_mode);
228
229 $a_output = "<script>" . ilQuestionExporter::questionsJS($q_ids) . "</script>" . $a_output;
230 if (!self::$initial_done) {
231 $a_output = "<script>var ScormApi=null; var questions = new Array();</script>" . $a_output;
232 self::$initial_done = true;
233 }
234 }
235 } else {
236 // set by T&A components
237 $qhtml = $this->getPage()->getPageConfig()->getQuestionHTML();
238
239 // address #19788
240 if (!is_array($qhtml) || count($qhtml) == 0) {
241 // #14154
242 $q_ids = $this->getQuestionIds();
243 if (count($q_ids)) {
244 foreach ($q_ids as $k) {
245 $a_output = str_replace("{{{{{Question;il__qst_$k" . "}}}}}", " " . $lng->txt("copg_questions_not_supported_here"), $a_output);
246 }
247 }
248 }
249 }
250
251 if (is_array($qhtml)) {
252 foreach ($qhtml as $k => $h) {
253 $a_output = str_replace("{{{{{Question;il__qst_$k" . "}}}}}", " " . $h, $a_output);
254 }
255 }
256
257 return $a_output;
258 }
259
263 public static function resetInitialState(): void
264 {
265 self::$initial_done = false;
266 }
267
268 public function getJavascriptFiles(string $a_mode): array
269 {
270 $js_files = array();
271
272 if ($this->getPage()->getPageConfig()->getEnableSelfAssessment()) {
273 $js_files[] = 'assets/js/pure_rendering.js';
274 $js_files[] = 'assets/js/question_handling.js';
275 $js_files[] = 'assets/js/matchinginput.js';
276 $js_files[] = 'assets/js/orderinghorizontal.js';
277 $js_files[] = 'assets/js/orderingvertical.js';
278 $js_files[] = 'assets/js/matching.js';
279
280 foreach ($this->getQuestionIds() as $qId) {
281 $qstGui = assQuestionGUI::_getQuestionGUI('', $qId);
282 if (!is_null($qstGui)) {
283 $js_files = array_merge($js_files, $qstGui->getPresentationJavascripts());
284 }
285 }
286 }
287
288 if (!$this->getPage()->getPageConfig()->getEnableSelfAssessmentScorm() && $a_mode != ilPageObjectGUI::PREVIEW
289 && $a_mode != "offline") {
290 $js_files[] = "./components/ILIAS/COPage/js/ilCOPageQuestionHandler.js";
291 }
292
293 return $js_files;
294 }
295
296 public function getCssFiles(string $a_mode): array
297 {
298 if ($this->getPage()->getPageConfig()->getEnableSelfAssessment()) {
299 return array("./components/ILIAS/TestQuestionPool/resources/js/dist/question_handling.css",
300 "components/ILIAS/TestQuestionPool/templates/default/test_javascript.css");
301 }
302 return array();
303 }
304
305 public function getOnloadCode(string $a_mode): array
306 {
307 $ilCtrl = $this->ctrl;
308 $ilUser = $this->user;
309
310 $code = array();
311
312 if ($this->getPage()->getPageConfig()->getEnableSelfAssessment()) {
313 if (!$this->getPage()->getPageConfig()->getEnableSelfAssessmentScorm() && $a_mode != ilPageObjectGUI::PREVIEW
314 && $a_mode != "offline" && $a_mode !== "edit") {
315 $ilCtrl->setParameterByClass(strtolower(get_class($this->getPage())) . "gui", "page_id", $this->getPage()->getId());
316 $url = $ilCtrl->getLinkTargetByClass(strtolower(get_class($this->getPage())) . "gui", "processAnswer", "", true, false);
317 $code[] = "ilCOPageQuestionHandler.initCallback('" . $url . "');";
318 }
319
320 if ($this->getPage()->getPageConfig()->getDisableDefaultQuestionFeedback()) {
321 $code[] = "ilias.questions.default_feedback = false;";
322 }
323
324 $code[] = self::getJSTextInitCode($this->getPage()->getPageConfig()->getLocalizationLanguage()) . ' il.COPagePres.updateQuestionOverviews();';
325 }
326
327 $q_ids = $this->getQuestionIds();
328
329 // call renderers
330 foreach ($q_ids as $q_id) {
331 $code[] = "if (typeof renderILQuestion$q_id === 'function') {renderILQuestion$q_id();}";
332 }
333
334 // init answer status
335 $get_stored_tries = $this->getPage()->getPageConfig()->getUseStoredQuestionTries();
336 if ($get_stored_tries) {
337 if (count($q_ids) > 0) {
338 foreach ($q_ids as $q_id) {
339 $status = ilPageQuestionProcessor::getAnswerStatus($q_id, $ilUser->getId());
340 $status = $status[$q_id] ?? $status;
341
342 $try = (int) ($status["try"] ?? 0);
343 $passed = isset($status["passed"]) && $status["passed"] ? "true" : "null";
344 $points = $status["points"] ?? "null";
345 $code[] = "ilias.questions.initAnswer(" . $q_id . ", " . $try . ", " . $passed . ", " . $points . ");";
346 }
347 }
348 }
349
350 if ($this->getPage()->getPageConfig()->getEnableSelfAssessment()) {
351 $code[] = "ilias.questions.refresh_lang();";
352 }
353 return $code;
354 }
355
359 public static function getJSTextInitCode(string $a_lang): string
360 {
361 global $DIC;
362
363 $lng = $DIC->language();
364 $ilUser = $DIC->user();
365
366 if ($a_lang == "") {
367 $a_lang = $ilUser->getLanguage();
368 }
369
370 return
371 '
372 ilias.questions.txt.wrong_answers = "' . $lng->txtlng("content", "cont_wrong_answers", $a_lang) . '";
373 ilias.questions.txt.wrong_answers_single = "' . $lng->txtlng("content", "cont_wrong_answers_single", $a_lang) . '";
374 ilias.questions.txt.tries_remaining = "' . $lng->txtlng("content", "cont_tries_remaining", $a_lang) . '";
375 ilias.questions.txt.please_try_again = "' . $lng->txtlng("content", "cont_please_try_again", $a_lang) . '";
376 ilias.questions.txt.all_answers_correct = "' . $lng->txtlng("content", "cont_all_answers_correct", $a_lang) . '";
377 ilias.questions.txt.enough_answers_correct = "' . $lng->txtlng("content", "cont_enough_answers_correct", $a_lang) . '";
378 ilias.questions.txt.nr_of_tries_exceeded = "' . $lng->txtlng("content", "cont_nr_of_tries_exceeded", $a_lang) . '";
379 ilias.questions.txt.correct_answers_separator = "' . $lng->txtlng("assessment", "or", $a_lang) . '";
380 ilias.questions.txt.correct_answers_shown = "' . $lng->txtlng("content", "cont_correct_answers_shown", $a_lang) . '";
381 ilias.questions.txt.correct_answers_also = "' . $lng->txtlng("content", "cont_correct_answers_also", $a_lang) . '";
382 ilias.questions.txt.correct_answer_also = "' . $lng->txtlng("content", "cont_correct_answer_also", $a_lang) . '";
383 ilias.questions.txt.ov_all_correct = "' . $lng->txtlng("content", "cont_ov_all_correct", $a_lang) . '";
384 ilias.questions.txt.ov_some_correct = "' . $lng->txtlng("content", "cont_ov_some_correct", $a_lang) . '";
385 ilias.questions.txt.ov_wrong_answered = "' . $lng->txtlng("content", "cont_ov_wrong_answered", $a_lang) . '";
386 ilias.questions.txt.please_select = "' . $lng->txtlng("content", "cont_please_select", $a_lang) . '";
387 ilias.questions.txt.ov_preview = "' . $lng->txtlng("content", "cont_ov_preview", $a_lang) . '";
388 ilias.questions.txt.submit_answers = "' . $lng->txtlng("content", "cont_submit_answers", $a_lang) . '";
389 ';
390 }
391
392 public function getQuestionJsOfPage(
393 bool $a_no_interaction,
394 string $a_mode
395 ): array {
396 $q_ids = $this->getQuestionIds();
397 $js = array();
398 if (count($q_ids) > 0) {
399 foreach ($q_ids as $q_id) {
400 $q_exporter = new ilQuestionExporter($a_no_interaction);
401 $image_path = "";
402 if ($a_mode == "offline") {
403 if ($this->getPage()->getParentType() == "sahs") {
404 $image_path = "./objects/";
405 }
406 if ($this->getPage()->getParentType() == "lm") {
407 $image_path = "./assessment/0/" . $q_id . "/images/";
408 }
409 }
410 $js[$q_id] = $q_exporter->exportQuestion($q_id, $image_path, $a_mode);
411 }
412 }
413 return $js;
414 }
415
416 protected function getQuestionIds(): array
417 {
418 $dom = $this->getPage()->getDomDoc();
419 $q_ids = [];
420 $nodes = $this->dom_util->path($dom, "//Question");
421 foreach ($nodes as $node) {
422 $qref = $node->getAttribute("QRef");
423 $inst_id = ilInternalLink::_extractInstOfTarget($qref);
425
426 if (!($inst_id > 0)) {
427 if ($obj_id > 0) {
428 $q_ids[] = $obj_id;
429 }
430 }
431 }
432 return $q_ids;
433 }
434
435 public static function handleCopiedContent(
436 DOMDocument $a_domdoc,
437 bool $a_self_ass = true,
438 bool $a_clone_mobs = false,
439 int $new_parent_id = 0,
440 int $obj_copy_id = 0
441 ): void {
442 global $DIC;
443
444 $dom_util = $DIC->copage()->internal()->domain()->domUtil();
445
446 // handle question elements
447 if ($a_self_ass) {
448 // copy questions
449 $path = "//Question";
450 $nodes = $dom_util->path($a_domdoc, $path);
451 foreach ($nodes as $node) {
452 $qref = $node->getAttribute("QRef");
453
454 $inst_id = ilInternalLink::_extractInstOfTarget($qref);
456
457 if (!($inst_id > 0)) {
458 if ($q_id > 0) {
459 $question = null;
460 try {
461 $question = assQuestion::instantiateQuestion($q_id);
462 } catch (Exception $e) {
463 }
464 // check due to #16557
465 if (is_object($question) && $question->isComplete()) {
466 // check if page for question exists
467 // due to a bug in early 4.2.x version this is possible
468 if (!ilPageObject::_exists("qpl", $q_id)) {
469 $question->createPageObject();
470 }
471
472 // now copy this question and change reference to
473 // new question id
474 $duplicate_id = $question->duplicate(false);
475 $node->setAttribute("QRef", "il__qst_" . $duplicate_id);
476 }
477 }
478 }
479 }
480 } else {
481 // remove question
482 $path = "//Question";
483 $nodes = $dom_util->path($a_domdoc, $path);
484 foreach ($nodes as $node) {
485 $parent = $node->parentNode;
486 $parent->parentNode->removeChild($parent);
487 }
488 }
489 }
490}
static _getQuestionGUI(string $question_type='', int $question_id=-1)
Creates a question gui representation and returns the alias to the question gui.
copyObject(int $target_parent_id, string $title='')
static instantiateQuestion(int $question_id)
Class ilCtrl provides processing control methods.
language handling
User class.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getPageForQuestionId(int $a_q_id, string $a_parent_type="")
modifyPageContentPostXsl(string $a_output, string $a_mode, bool $a_abstract_only=false)
Modify page content after xsl.
getQuestionJsOfPage(bool $a_no_interaction, string $a_mode)
init()
Init page content component.
static afterPageUpdate(ilPageObject $a_page, DOMDocument $a_domdoc, string $a_xml, bool $a_creation)
After page has been updated (or created)
getJavascriptFiles(string $a_mode)
static beforePageDelete(ilPageObject $a_page)
Before page is being deleted.
static getJSTextInitCode(string $a_lang)
Get js txt init code.
static handleCopiedContent(DOMDocument $a_domdoc, bool $a_self_ass=true, bool $a_clone_mobs=false, int $new_parent_id=0, int $obj_copy_id=0)
Handle copied content.
static resetInitialState()
Reset initial state (for exports)
static getLangVars()
Get lang vars needed for editing.
setQuestionReference(string $a_questionreference)
copyPoolQuestionIntoPage(string $a_q_id, string $a_hier_id)
Copy question from pool into page.
static _getQuestionIdsForPage(string $a_parent_type, int $a_page_id, string $a_lang="-")
static bool $initial_done
getOnloadCode(string $a_mode)
create(ilPageObject $a_pg_obj, string $a_hier_id, string $a_pc_id="")
getCssFiles(string $a_mode)
Content object of ilPageObject (see ILIAS DTD).
createInitialChildNode(string $hier_id, string $pc_id, string $child, array $child_attributes=[])
setType(string $a_type)
Set Type.
Class ilPageObject Handles PageObjects of ILIAS Learning Modules (see ILIAS DTD)
static _exists(string $a_parent_type, int $a_id, string $a_lang="", bool $a_no_cache=false)
Checks whether page exists.
static getAnswerStatus( $a_q_id, int $a_user_id=0)
Scorm 2004 Question Exporter.
static questionsJS(?array $a_qids=null)
$path
Definition: ltiservices.php:30
$res
Definition: ltiservices.php:69
global $lng
Definition: privfeed.php:31
global $DIC
Definition: shib_login.php:26
$url
Definition: shib_logout.php:70