Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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