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