MDL-45336 questionbank: use core strings instead of quiz
[moodle.git] / mod / lesson / format.php
CommitLineData
472e5662 1<?php
0a4abb73
SH
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
5491947a 18/**
86342d63 19 * format.php - Default format class for file imports/exports. Doesn't do
5491947a 20 * everything on it's own -- it needs to be extended.
21 *
0a4abb73
SH
22 * Included by import.ph
23 *
9b24f68b 24 * @package mod_lesson
cc3dbaaa
PS
25 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
0a4abb73
SH
27 **/
28
1e7f8ea2
PS
29defined('MOODLE_INTERNAL') || die();
30
0a4abb73
SH
31/**
32 * Given some question info and some data about the the answers
33 * this function parses, organises and saves the question
34 *
35 * This is only used when IMPORTING questions and is only called
36 * from format.php
37 * Lifted from mod/quiz/lib.php -
38 * 1. all reference to oldanswers removed
39 * 2. all reference to quiz_multichoice table removed
e231a3ff
TH
40 * 3. In shortanswer questions usecase is store in the qoption field
41 * 4. In numeric questions store the range as two answers
42 * 5. truefalse options are ignored
43 * 6. For multichoice questions with more than one answer the qoption field is true
0a4abb73
SH
44 *
45 * @param opject $question Contains question data like question, type and answers.
46 * @return object Returns $result->error or $result->notice.
5491947a 47 **/
0a4abb73
SH
48function lesson_save_question_options($question, $lesson) {
49 global $DB;
50
51 // These lines are required to ensure that all page types have
52 // been loaded for the following switch
53 if (!($lesson instanceof lesson)) {
54 $lesson = new lesson($lesson);
55 }
56 $manager = lesson_page_type_manager::get($lesson);
2f67a9b3 57
0a4abb73 58 $timenow = time();
39790bd8 59 $result = new stdClass();
0a4abb73
SH
60 switch ($question->qtype) {
61 case LESSON_PAGE_SHORTANSWER:
62
63 $answers = array();
64 $maxfraction = -1;
65
66 // Insert all the new answers
67 foreach ($question->answer as $key => $dataanswer) {
68 if ($dataanswer != "") {
69 $answer = new stdClass;
70 $answer->lessonid = $question->lessonid;
71 $answer->pageid = $question->id;
72 if ($question->fraction[$key] >=0.5) {
73 $answer->jumpto = LESSON_NEXTPAGE;
74 }
75 $answer->timecreated = $timenow;
76 $answer->grade = $question->fraction[$key] * 100;
77 $answer->answer = $dataanswer;
912ea4bc
RT
78 $answer->response = $question->feedback[$key]['text'];
79 $answer->responseformat = $question->feedback[$key]['format'];
0a4abb73
SH
80 $answer->id = $DB->insert_record("lesson_answers", $answer);
81 $answers[] = $answer->id;
82 if ($question->fraction[$key] > $maxfraction) {
83 $maxfraction = $question->fraction[$key];
84 }
85 }
86 }
87
88
89 /// Perform sanity checks on fractional grades
90 if ($maxfraction != 1) {
91 $maxfraction = $maxfraction * 100;
92 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
93 return $result;
94 }
95 break;
96
e231a3ff 97 case LESSON_PAGE_NUMERICAL: // Note similarities to shortanswer.
0a4abb73
SH
98
99 $answers = array();
100 $maxfraction = -1;
101
102
103 // for each answer store the pair of min and max values even if they are the same
104 foreach ($question->answer as $key => $dataanswer) {
105 if ($dataanswer != "") {
106 $answer = new stdClass;
107 $answer->lessonid = $question->lessonid;
108 $answer->pageid = $question->id;
109 $answer->jumpto = LESSON_NEXTPAGE;
110 $answer->timecreated = $timenow;
111 $answer->grade = $question->fraction[$key] * 100;
112 $min = $question->answer[$key] - $question->tolerance[$key];
113 $max = $question->answer[$key] + $question->tolerance[$key];
114 $answer->answer = $min.":".$max;
115 // $answer->answer = $question->min[$key].":".$question->max[$key]; original line for min/max
912ea4bc
RT
116 $answer->response = $question->feedback[$key]['text'];
117 $answer->responseformat = $question->feedback[$key]['format'];
0a4abb73
SH
118 $answer->id = $DB->insert_record("lesson_answers", $answer);
119
120 $answers[] = $answer->id;
121 if ($question->fraction[$key] > $maxfraction) {
122 $maxfraction = $question->fraction[$key];
123 }
124 }
125 }
126
127 /// Perform sanity checks on fractional grades
128 if ($maxfraction != 1) {
129 $maxfraction = $maxfraction * 100;
130 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
131 return $result;
132 }
133 break;
134
135
136 case LESSON_PAGE_TRUEFALSE:
137
138 // the truth
92701024 139 $answer = new stdClass();
0a4abb73
SH
140 $answer->lessonid = $question->lessonid;
141 $answer->pageid = $question->id;
142 $answer->timecreated = $timenow;
143 $answer->answer = get_string("true", "quiz");
912ea4bc 144 $answer->grade = $question->correctanswer * 100;
0a4abb73
SH
145 if ($answer->grade > 50 ) {
146 $answer->jumpto = LESSON_NEXTPAGE;
147 }
148 if (isset($question->feedbacktrue)) {
912ea4bc
RT
149 $answer->response = $question->feedbacktrue['text'];
150 $answer->responseformat = $question->feedbacktrue['format'];
0a4abb73 151 }
64a3ce8c 152 $DB->insert_record("lesson_answers", $answer);
0a4abb73
SH
153
154 // the lie
155 $answer = new stdClass;
156 $answer->lessonid = $question->lessonid;
157 $answer->pageid = $question->id;
158 $answer->timecreated = $timenow;
159 $answer->answer = get_string("false", "quiz");
912ea4bc 160 $answer->grade = (1 - (int)$question->correctanswer) * 100;
0a4abb73
SH
161 if ($answer->grade > 50 ) {
162 $answer->jumpto = LESSON_NEXTPAGE;
163 }
164 if (isset($question->feedbackfalse)) {
912ea4bc
RT
165 $answer->response = $question->feedbackfalse['text'];
166 $answer->responseformat = $question->feedbackfalse['format'];
0a4abb73 167 }
64a3ce8c 168 $DB->insert_record("lesson_answers", $answer);
0a4abb73
SH
169
170 break;
171
172 case LESSON_PAGE_MULTICHOICE:
173
174 $totalfraction = 0;
175 $maxfraction = -1;
176
177 $answers = array();
178
179 // Insert all the new answers
180 foreach ($question->answer as $key => $dataanswer) {
181 if ($dataanswer != "") {
182 $answer = new stdClass;
183 $answer->lessonid = $question->lessonid;
184 $answer->pageid = $question->id;
185 $answer->timecreated = $timenow;
186 $answer->grade = $question->fraction[$key] * 100;
187 // changed some defaults
188 /* Original Code
189 if ($answer->grade > 50 ) {
190 $answer->jumpto = LESSON_NEXTPAGE;
191 }
192 Replaced with: */
193 if ($answer->grade > 50 ) {
194 $answer->jumpto = LESSON_NEXTPAGE;
195 $answer->score = 1;
196 }
197 // end Replace
912ea4bc
RT
198 $answer->answer = $dataanswer['text'];
199 $answer->answerformat = $dataanswer['format'];
200 $answer->response = $question->feedback[$key]['text'];
201 $answer->responseformat = $question->feedback[$key]['format'];
0a4abb73
SH
202 $answer->id = $DB->insert_record("lesson_answers", $answer);
203 // for Sanity checks
204 if ($question->fraction[$key] > 0) {
205 $totalfraction += $question->fraction[$key];
206 }
207 if ($question->fraction[$key] > $maxfraction) {
208 $maxfraction = $question->fraction[$key];
209 }
210 }
211 }
212
213 /// Perform sanity checks on fractional grades
214 if ($question->single) {
215 if ($maxfraction != 1) {
216 $maxfraction = $maxfraction * 100;
217 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
218 return $result;
219 }
220 } else {
221 $totalfraction = round($totalfraction,2);
222 if ($totalfraction != 1) {
223 $totalfraction = $totalfraction * 100;
38cde336 224 $result->notice = get_string("fractionsaddwrong", "qtype_multichoice", $totalfraction);
0a4abb73
SH
225 return $result;
226 }
227 }
228 break;
229
230 case LESSON_PAGE_MATCHING:
231
232 $subquestions = array();
233
234 $defaultanswer = new stdClass;
235 $defaultanswer->lessonid = $question->lessonid;
236 $defaultanswer->pageid = $question->id;
237 $defaultanswer->timecreated = $timenow;
238 $defaultanswer->grade = 0;
239
240 // The first answer should always be the correct answer
241 $correctanswer = clone($defaultanswer);
242 $correctanswer->answer = get_string('thatsthecorrectanswer', 'lesson');
b09ac20b 243 $correctanswer->jumpto = LESSON_NEXTPAGE;
0a4abb73
SH
244 $DB->insert_record("lesson_answers", $correctanswer);
245
246 // The second answer should always be the wrong answer
247 $wronganswer = clone($defaultanswer);
248 $wronganswer->answer = get_string('thatsthewronganswer', 'lesson');
249 $DB->insert_record("lesson_answers", $wronganswer);
250
251 $i = 0;
252 // Insert all the new question+answer pairs
253 foreach ($question->subquestions as $key => $questiontext) {
254 $answertext = $question->subanswers[$key];
255 if (!empty($questiontext) and !empty($answertext)) {
256 $answer = clone($defaultanswer);
912ea4bc
RT
257 $answer->answer = $questiontext['text'];
258 $answer->answerformat = $questiontext['format'];
0a4abb73
SH
259 $answer->response = $answertext;
260 if ($i == 0) {
261 // first answer contains the correct answer jump
262 $answer->jumpto = LESSON_NEXTPAGE;
263 }
64a3ce8c 264 $subquestions[] = $DB->insert_record("lesson_answers", $answer);
0a4abb73
SH
265 $i++;
266 }
267 }
268
269 if (count($subquestions) < 3) {
270 $result->notice = get_string("notenoughsubquestions", "quiz");
271 return $result;
272 }
273 break;
274 default:
275 $result->error = "Unsupported question type ($question->qtype)!";
276 return $result;
277 }
278 return true;
279}
394c97c8 280
394c97c8 281
90455bb3 282class qformat_default {
394c97c8 283
284 var $displayerrors = true;
ecea65ca 285 var $category = null;
394c97c8 286 var $questionids = array();
efbe07b0 287 protected $importcontext = null;
e231a3ff
TH
288 var $qtypeconvert = array('numerical' => LESSON_PAGE_NUMERICAL,
289 'multichoice' => LESSON_PAGE_MULTICHOICE,
290 'truefalse' => LESSON_PAGE_TRUEFALSE,
291 'shortanswer' => LESSON_PAGE_SHORTANSWER,
292 'match' => LESSON_PAGE_MATCHING
90455bb3 293 );
394c97c8 294
0a4abb73
SH
295 // Importing functions
296 function provide_import() {
297 return false;
298 }
394c97c8 299
efbe07b0
JMV
300 function set_importcontext($context) {
301 $this->importcontext = $context;
302 }
303
f88d75e5
JMV
304 /**
305 * Handle parsing error
306 *
307 * @param string $message information about error
308 * @param string $text imported text that triggered the error
309 * @param string $questionname imported question name
310 */
311 protected function error($message, $text='', $questionname='') {
312 $importerrorquestion = get_string('importerrorquestion', 'question');
313
314 echo "<div class=\"importerror\">\n";
315 echo "<strong>$importerrorquestion $questionname</strong>";
316 if (!empty($text)) {
317 $text = s($text);
318 echo "<blockquote>$text</blockquote>\n";
319 }
320 echo "<strong>$message</strong>\n";
321 echo "</div>";
322 }
323
394c97c8 324 function importpreprocess() {
0a4abb73 325 // Does any pre-processing that may be desired
394c97c8 326 return true;
327 }
328
329 function importprocess($filename, $lesson, $pageid) {
d68ccdba 330 global $DB, $OUTPUT;
86342d63 331
394c97c8 332 /// Processes a given file. There's probably little need to change this
333 $timenow = time();
334
335 if (! $lines = $this->readdata($filename)) {
d68ccdba 336 echo $OUTPUT->notification("File could not be read, or was empty");
394c97c8 337 return false;
338 }
339
340 if (! $questions = $this->readquestions($lines)) { // Extract all the questions
d68ccdba 341 echo $OUTPUT->notification("There are no questions in this file!");
394c97c8 342 return false;
343 }
86342d63 344
912ea4bc
RT
345 //Avoid category as question type
346 echo $OUTPUT->notification(get_string('importcount', 'lesson',
347 $this->count_questions($questions)), 'notifysuccess');
394c97c8 348
349 $count = 0;
eebc821c
RW
350 $addquestionontop = false;
351 if ($pageid == 0) {
352 $addquestionontop = true;
353 $updatelessonpage = $DB->get_record('lesson_pages', array('lessonid' => $lesson->id, 'prevpageid' => 0));
354 } else {
84c5f641 355 $updatelessonpage = $DB->get_record('lesson_pages', array('lessonid' => $lesson->id, 'id' => $pageid));
eebc821c 356 }
394c97c8 357
0a4abb73
SH
358 $unsupportedquestions = 0;
359
394c97c8 360 foreach ($questions as $question) { // Process and store each question
361 switch ($question->qtype) {
912ea4bc
RT
362 //TODO: Bad way to bypass category in data... Quickfix for MDL-27964
363 case 'category':
364 break;
394c97c8 365 // the good ones
e231a3ff
TH
366 case 'shortanswer' :
367 case 'numerical' :
368 case 'truefalse' :
369 case 'multichoice' :
370 case 'match' :
394c97c8 371 $count++;
372
912ea4bc
RT
373 //Show nice formated question in one line.
374 echo "<hr><p><b>$count</b>. ".$this->format_question_text($question)."</p>";
375
f7ffb898 376 $newpage = new stdClass;
394c97c8 377 $newpage->lessonid = $lesson->id;
90455bb3 378 $newpage->qtype = $this->qtypeconvert[$question->qtype];
394c97c8 379 switch ($question->qtype) {
e231a3ff 380 case 'shortanswer' :
90455bb3 381 if (isset($question->usecase)) {
382 $newpage->qoption = $question->usecase;
383 }
394c97c8 384 break;
e231a3ff 385 case 'multichoice' :
394c97c8 386 if (isset($question->single)) {
387 $newpage->qoption = !$question->single;
388 }
389 break;
390 }
391 $newpage->timecreated = $timenow;
392 if ($question->name != $question->questiontext) {
393 $newpage->title = $question->name;
394 } else {
395 $newpage->title = "Page $count";
396 }
397 $newpage->contents = $question->questiontext;
1c3b1f7a
EL
398 $newpage->contentsformat = isset($question->questionformat) ? $question->questionformat : FORMAT_HTML;
399
394c97c8 400 // set up page links
401 if ($pageid) {
402 // the new page follows on from this page
646fc290 403 if (!$page = $DB->get_record("lesson_pages", array("id" => $pageid))) {
771dc7b2 404 print_error('invalidpageid', 'lesson');
394c97c8 405 }
406 $newpage->prevpageid = $pageid;
407 $newpage->nextpageid = $page->nextpageid;
408 // insert the page and reset $pageid
6d40f12e 409 $newpageid = $DB->insert_record("lesson_pages", $newpage);
394c97c8 410 // update the linked list
6d40f12e 411 $DB->set_field("lesson_pages", "nextpageid", $newpageid, array("id" => $pageid));
394c97c8 412 } else {
413 // new page is the first page
414 // get the existing (first) page (if any)
646fc290 415 $params = array ("lessonid" => $lesson->id, "prevpageid" => 0);
416 if (!$page = $DB->get_record_select("lesson_pages", "lessonid = :lessonid AND prevpageid = :prevpageid", $params)) {
394c97c8 417 // there are no existing pages
418 $newpage->prevpageid = 0; // this is a first page
419 $newpage->nextpageid = 0; // this is the only page
646fc290 420 $newpageid = $DB->insert_record("lesson_pages", $newpage);
394c97c8 421 } else {
422 // there are existing pages put this at the start
423 $newpage->prevpageid = 0; // this is a first page
424 $newpage->nextpageid = $page->id;
646fc290 425 $newpageid = $DB->insert_record("lesson_pages", $newpage);
394c97c8 426 // update the linked list
6d40f12e 427 $DB->set_field("lesson_pages", "prevpageid", $newpageid, array("id" => $page->id));
394c97c8 428 }
429 }
eebc821c 430
394c97c8 431 // reset $pageid and put the page ID in $question, used in save_question_option()
432 $pageid = $newpageid;
433 $question->id = $newpageid;
86342d63 434
394c97c8 435 $this->questionids[] = $question->id;
436
efbe07b0
JMV
437 // Import images in question text.
438 if (isset($question->questiontextitemid)) {
439 $questiontext = file_save_draft_area_files($question->questiontextitemid,
440 $this->importcontext->id, 'mod_lesson', 'page_contents', $newpageid,
441 null , $question->questiontext);
442 // Update content with recoded urls.
443 $DB->set_field("lesson_pages", "contents", $questiontext, array("id" => $newpageid));
444 }
445
394c97c8 446 // Now to save all the answers and type-specific options
447
448 $question->lessonid = $lesson->id; // needed for foreign key
90455bb3 449 $question->qtype = $this->qtypeconvert[$question->qtype];
0a4abb73 450 $result = lesson_save_question_options($question, $lesson);
394c97c8 451
452 if (!empty($result->error)) {
d68ccdba 453 echo $OUTPUT->notification($result->error);
394c97c8 454 return false;
455 }
456
457 if (!empty($result->notice)) {
d68ccdba 458 echo $OUTPUT->notification($result->notice);
394c97c8 459 return true;
460 }
461 break;
462 // the Bad ones
463 default :
0a4abb73
SH
464 $unsupportedquestions++;
465 break;
394c97c8 466 }
eebc821c 467 }
84c5f641
DW
468 // Update the prev links if there were existing pages.
469 if (!empty($updatelessonpage)) {
470 if ($addquestionontop) {
471 $DB->set_field("lesson_pages", "prevpageid", $pageid, array("id" => $updatelessonpage->id));
472 } else {
473 $DB->set_field("lesson_pages", "prevpageid", $pageid, array("id" => $updatelessonpage->nextpageid));
474 }
394c97c8 475 }
0a4abb73
SH
476 if ($unsupportedquestions) {
477 echo $OUTPUT->notification(get_string('unknownqtypesnotimported', 'lesson', $unsupportedquestions));
478 }
394c97c8 479 return true;
480 }
481
912ea4bc
RT
482 /**
483 * Count all non-category questions in the questions array.
484 *
485 * @param array questions An array of question objects.
486 * @return int The count.
487 *
488 */
489 protected function count_questions($questions) {
490 $count = 0;
491 if (!is_array($questions)) {
492 return $count;
493 }
494 foreach ($questions as $question) {
495 if (!is_object($question) || !isset($question->qtype) ||
496 ($question->qtype == 'category')) {
497 continue;
498 }
499 $count++;
500 }
501 return $count;
502 }
394c97c8 503
504 function readdata($filename) {
505 /// Returns complete file with an array, one item per line
506
507 if (is_readable($filename)) {
508 $filearray = file($filename);
509
510 /// Check for Macintosh OS line returns (ie file on one line), and fix
6dbcacee 511 if (preg_match("/\r/", $filearray[0]) AND !preg_match("/\n/", $filearray[0])) {
394c97c8 512 return explode("\r", $filearray[0]);
513 } else {
514 return $filearray;
515 }
516 }
517 return false;
518 }
519
ba15c346 520 protected function readquestions($lines) {
86342d63
PS
521 /// Parses an array of lines into an array of questions,
522 /// where each item is a question object as defined by
523 /// readquestion(). Questions are defined as anything
394c97c8 524 /// between blank lines.
86342d63 525
394c97c8 526 $questions = array();
527 $currentquestion = array();
528
529 foreach ($lines as $line) {
530 $line = trim($line);
531 if (empty($line)) {
532 if (!empty($currentquestion)) {
533 if ($question = $this->readquestion($currentquestion)) {
534 $questions[] = $question;
535 }
536 $currentquestion = array();
537 }
538 } else {
539 $currentquestion[] = $line;
540 }
541 }
542
543 if (!empty($currentquestion)) { // There may be a final question
544 if ($question = $this->readquestion($currentquestion)) {
545 $questions[] = $question;
546 }
547 }
548
549 return $questions;
550 }
551
552
e8eb2b00 553 protected function readquestion($lines) {
86342d63
PS
554 /// Given an array of lines known to define a question in
555 /// this format, this function converts it into a question
394c97c8 556 /// object suitable for processing and insertion into Moodle.
557
558 echo "<p>This flash question format has not yet been completed!</p>";
559
ecea65ca 560 return null;
394c97c8 561 }
562
cacb8fa0
TH
563 /**
564 * Construct a reasonable default question name, based on the start of the question text.
565 * @param string $questiontext the question text.
566 * @param string $default default question name to use if the constructed one comes out blank.
567 * @return string a reasonable question name.
568 */
569 public function create_default_question_name($questiontext, $default) {
570 $name = $this->clean_question_name(shorten_text($questiontext, 80));
571 if ($name) {
572 return $name;
573 } else {
574 return $default;
575 }
576 }
577
578 /**
579 * Ensure that a question name does not contain anything nasty, and will fit in the DB field.
580 * @param string $name the raw question name.
581 * @return string a safe question name.
582 */
583 public function clean_question_name($name) {
584 $name = clean_param($name, PARAM_TEXT); // Matches what the question editing form does.
585 $name = trim($name);
586 $trimlength = 251;
2f1e464a 587 while (core_text::strlen($name) > 255 && $trimlength > 0) {
cacb8fa0
TH
588 $name = shorten_text($name, $trimlength);
589 $trimlength -= 10;
590 }
591 return $name;
592 }
593
90455bb3 594 function defaultquestion() {
595 // returns an "empty" question
596 // Somewhere to specify question parameters that are not handled
597 // by import but are required db fields.
86342d63 598 // This should not be overridden.
508fe4d8 599 global $CFG;
600
90455bb3 601 $question = new stdClass();
0a4abb73 602 $question->shuffleanswers = get_config('quiz', 'shuffleanswers');
912ea4bc 603 $question->defaultmark = 1;
508fe4d8 604 $question->image = "";
605 $question->usecase = 0;
606 $question->multiplier = array();
607 $question->generalfeedback = '';
608 $question->correctfeedback = '';
609 $question->partiallycorrectfeedback = '';
610 $question->incorrectfeedback = '';
611 $question->answernumbering = 'abc';
612 $question->penalty = 0.1;
613 $question->length = 1;
90455bb3 614 $question->qoption = 0;
615 $question->layout = 1;
86342d63 616
912ea4bc
RT
617 // this option in case the questiontypes class wants
618 // to know where the data came from
619 $question->export_process = true;
620 $question->import_process = true;
621
90455bb3 622 return $question;
623 }
394c97c8 624
625 function importpostprocess() {
0a4abb73
SH
626 /// Does any post-processing that may be desired
627 /// Argument is a simple array of question ids that
628 /// have just been added.
394c97c8 629 return true;
630 }
631
912ea4bc
RT
632 /**
633 * Convert the question text to plain text, so it can safely be displayed
634 * during import to let the user see roughly what is going on.
635 */
636 protected function format_question_text($question) {
637 $formatoptions = new stdClass();
638 $formatoptions->noclean = true;
efbe07b0
JMV
639 // The html_to_text call strips out all URLs, but format_text complains
640 // if it finds @@PLUGINFILE@@ tokens. So, we need to replace
641 // @@PLUGINFILE@@ with a real URL, but it doesn't matter what.
642 // We use http://example.com/.
643 $text = str_replace('@@PLUGINFILE@@/', 'http://example.com/', $question->questiontext);
644 return html_to_text(format_text($text,
912ea4bc
RT
645 $question->questiontextformat, $formatoptions), 0, false);
646 }
bba5e716
TH
647
648 /**
649 * Since the lesson module tries to re-use the question bank import classes in
650 * a crazy way, this is necessary to stop things breaking.
651 */
652 protected function add_blank_combined_feedback($question) {
653 return $question;
654 }
394c97c8 655}
656
86342d63 657
bba5e716
TH
658/**
659 * Since the lesson module tries to re-use the question bank import classes in
660 * a crazy way, this is necessary to stop things breaking. This should be exactly
661 * the same as the class defined in question/format.php.
662 */
663class qformat_based_on_xml extends qformat_default {
664 /**
665 * A lot of imported files contain unwanted entities.
666 * This method tries to clean up all known problems.
667 * @param string str string to correct
668 * @return string the corrected string
669 */
670 public function cleaninput($str) {
671
672 $html_code_list = array(
673 "&#039;" => "'",
674 "&#8217;" => "'",
675 "&#8220;" => "\"",
676 "&#8221;" => "\"",
677 "&#8211;" => "-",
678 "&#8212;" => "-",
679 );
680 $str = strtr($str, $html_code_list);
2f1e464a
PS
681 // Use core_text entities_to_utf8 function to convert only numerical entities.
682 $str = core_text::entities_to_utf8($str, false);
bba5e716
TH
683 return $str;
684 }
685
686 /**
687 * Return the array moodle is expecting
688 * for an HTML text. No processing is done on $text.
689 * qformat classes that want to process $text
690 * for instance to import external images files
691 * and recode urls in $text must overwrite this method.
692 * @param array $text some HTML text string
693 * @return array with keys text, format and files.
694 */
695 public function text_field($text) {
696 return array(
697 'text' => trim($text),
698 'format' => FORMAT_HTML,
699 'files' => array(),
700 );
701 }
702
703 /**
704 * Return the value of a node, given a path to the node
705 * if it doesn't exist return the default value.
706 * @param array xml data to read
707 * @param array path path to node expressed as array
708 * @param mixed default
709 * @param bool istext process as text
710 * @param string error if set value must exist, return false and issue message if not
711 * @return mixed value
712 */
713 public function getpath($xml, $path, $default, $istext=false, $error='') {
714 foreach ($path as $index) {
715 if (!isset($xml[$index])) {
716 if (!empty($error)) {
717 $this->error($error);
718 return false;
719 } else {
720 return $default;
721 }
722 }
723
724 $xml = $xml[$index];
725 }
726
727 if ($istext) {
728 if (!is_string($xml)) {
729 $this->error(get_string('invalidxml', 'qformat_xml'));
730 }
731 $xml = trim($xml);
732 }
733
734 return $xml;
735 }
736}