Passed
Pull Request — 1.11.x (#5783)
by Yannick
08:32
created

QuizExport::createQuizXml()   B

Complexity

Conditions 7
Paths 64

Size

Total Lines 106
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 80
c 2
b 0
f 0
dl 0
loc 106
rs 7.503
cc 7
nc 64
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
namespace moodleexport;
6
7
use Exception;
8
use FillBlanks;
9
10
/**
11
 * Class QuizExport.
12
 *
13
 * Handles the export of quizzes within a course.
14
 */
15
class QuizExport extends ActivityExport
16
{
17
    /**
18
     * Export a quiz to the specified directory.
19
     *
20
     * @param int $activityId The ID of the quiz.
21
     * @param string $exportDir The directory where the quiz will be exported.
22
     * @param int $moduleId The ID of the module.
23
     * @param int $sectionId The ID of the section.
24
     */
25
    public function export($activityId, $exportDir, $moduleId, $sectionId): void
26
    {
27
        // Prepare the directory where the quiz export will be saved
28
        $quizDir = $this->prepareActivityDirectory($exportDir, 'quiz', $moduleId);
29
30
        // Retrieve quiz data
31
        $quizData = $this->getData($activityId, $sectionId);
32
33
        // Generate XML files
34
        $this->createQuizXml($quizData, $quizDir);
35
        $this->createModuleXml($quizData, $quizDir);
36
        $this->createGradesXml($quizData, $quizDir);
37
        $this->createCompletionXml($quizData, $quizDir);
38
        $this->createCommentsXml($quizData, $quizDir);
39
        $this->createCompetenciesXml($quizData, $quizDir);
40
        $this->createFiltersXml($quizData, $quizDir);
41
        $this->createGradeHistoryXml($quizData, $quizDir);
42
        $this->createInforefXml($quizData, $quizDir);
43
        $this->createRolesXml($quizData, $quizDir);
44
        $this->createCalendarXml($quizData, $quizDir);
45
    }
46
47
    /**
48
     * Retrieves the quiz data.
49
     */
50
    public function getData(int $quizId, int $sectionId): array
51
    {
52
        $quizResources = $this->course->resources[RESOURCE_QUIZ];
53
54
        foreach ($quizResources as $quiz) {
55
            if ($quiz->obj->iid == $quizId) {
56
                $contextid = $quiz->obj->c_id;
57
58
                return [
59
                    'id' => $quiz->obj->iid,
60
                    'name' => $quiz->obj->title,
61
                    'intro' => $quiz->obj->description,
62
                    'timeopen' => $quiz->obj->start_time ?? 0,
63
                    'timeclose' => $quiz->obj->end_time ?? 0,
64
                    'timelimit' => $quiz->obj->timelimit ?? 0,
65
                    'grademethod' => $quiz->obj->grademethod ?? 1,
66
                    'decimalpoints' => $quiz->obj->decimalpoints ?? 2,
67
                    'sumgrades' => $quiz->obj->sumgrades ?? 0,
68
                    'grade' => $quiz->obj->grade ?? 0,
69
                    'questionsperpage' => $quiz->obj->questionsperpage ?? 1,
70
                    'preferredbehaviour' => $quiz->obj->preferredbehaviour ?? 'deferredfeedback',
71
                    'shuffleanswers' => $quiz->obj->shuffleanswers ?? 1,
72
                    'questions' => $this->getQuestionsForQuiz($quizId),
73
                    'feedbacks' => $this->getFeedbacksForQuiz($quizId),
74
                    'sectionid' => $sectionId,
75
                    'moduleid' => $quiz->obj->iid ?? 0,
76
                    'modulename' => 'quiz',
77
                    'contextid' => $contextid,
78
                    'overduehandling' => $quiz->obj->overduehandling ?? 'autosubmit',
79
                    'graceperiod' => $quiz->obj->graceperiod ?? 0,
80
                    'canredoquestions' => $quiz->obj->canredoquestions ?? 0,
81
                    'attempts_number' => $quiz->obj->attempts_number ?? 0,
82
                    'attemptonlast' => $quiz->obj->attemptonlast ?? 0,
83
                    'questiondecimalpoints' => $quiz->obj->questiondecimalpoints ?? 2,
84
                    'reviewattempt' => $quiz->obj->reviewattempt ?? 0,
85
                    'reviewcorrectness' => $quiz->obj->reviewcorrectness ?? 0,
86
                    'reviewmarks' => $quiz->obj->reviewmarks ?? 0,
87
                    'reviewspecificfeedback' => $quiz->obj->reviewspecificfeedback ?? 0,
88
                    'reviewgeneralfeedback' => $quiz->obj->reviewgeneralfeedback ?? 0,
89
                    'reviewrightanswer' => $quiz->obj->reviewrightanswer ?? 0,
90
                    'reviewoverallfeedback' => $quiz->obj->reviewoverallfeedback ?? 0,
91
                    'timecreated' => $quiz->obj->insert_date ?? time(),
92
                    'timemodified' => $quiz->obj->lastedit_date ?? time(),
93
                    'password' => $quiz->obj->password ?? '',
94
                    'subnet' => $quiz->obj->subnet ?? '',
95
                    'browsersecurity' => $quiz->obj->browsersecurity ?? '-',
96
                    'delay1' => $quiz->obj->delay1 ?? 0,
97
                    'delay2' => $quiz->obj->delay2 ?? 0,
98
                    'showuserpicture' => $quiz->obj->showuserpicture ?? 0,
99
                    'showblocks' => $quiz->obj->showblocks ?? 0,
100
                    'completionattemptsexhausted' => $quiz->obj->completionattemptsexhausted ?? 0,
101
                    'completionpass' => $quiz->obj->completionpass ?? 0,
102
                    'completionminattempts' => $quiz->obj->completionminattempts ?? 0,
103
                    'allowofflineattempts' => $quiz->obj->allowofflineattempts ?? 0,
104
                    'users' => [],
105
                    'files' => [],
106
                ];
107
            }
108
        }
109
110
        return [];
111
    }
112
113
    /**
114
     * Retrieves the questions for a specific quiz.
115
     */
116
    private function getQuestionsForQuiz(int $quizId): array
117
    {
118
        $questions = [];
119
        $quizResources = $this->course->resources[RESOURCE_QUIZQUESTION] ?? [];
120
121
        foreach ($quizResources as $questionId => $questionData) {
122
            if (in_array($questionId, $this->course->resources[RESOURCE_QUIZ][$quizId]->obj->question_ids)) {
123
                $questions[] = [
124
                    'id' => $questionData->source_id,
125
                    'questiontext' => $questionData->question,
126
                    'qtype' => $this->mapQuestionType($questionData->quiz_type),
127
                    'questioncategoryid' => $questionData->question_category ?? 0,
128
                    'answers' => $this->getAnswersForQuestion($questionData->source_id),
129
                    'maxmark' => $questionData->ponderation ?? 1,
130
                ];
131
            }
132
        }
133
134
        return $questions;
135
    }
136
137
    /**
138
     * Maps the quiz type code to a descriptive string.
139
     */
140
    private function mapQuestionType(string $quizType): string
141
    {
142
        switch ($quizType) {
143
            case UNIQUE_ANSWER: return 'multichoice';
144
            case MULTIPLE_ANSWER: return 'multichoice_nosingle';
145
            case FILL_IN_BLANKS: return 'match';
146
            case FREE_ANSWER: return 'shortanswer';
147
            case CALCULATED_ANSWER: return 'calculated';
148
            case UPLOAD_ANSWER: return 'fileupload';
149
            default: return 'unknown';
150
        }
151
    }
152
153
    /**
154
     * Retrieves the answers for a specific question ID.
155
     */
156
    private function getAnswersForQuestion(int $questionId): array
157
    {
158
        $answers = [];
159
        $quizResources = $this->course->resources[RESOURCE_QUIZQUESTION] ?? [];
160
161
        foreach ($quizResources as $questionData) {
162
            if ($questionData->source_id == $questionId) {
163
                foreach ($questionData->answers as $answer) {
164
                    $answers[] = [
165
                        'text' => $answer['answer'],
166
                        'fraction' => $answer['correct'] == '1' ? 100 : 0,
167
                        'feedback' => $answer['comment'],
168
                    ];
169
                }
170
            }
171
        }
172
        return $answers;
173
    }
174
175
    /**
176
     * Retrieves feedbacks for a specific quiz.
177
     */
178
    private function getFeedbacksForQuiz(int $quizId): array
179
    {
180
        $feedbacks = [];
181
        $quizResources = $this->course->resources[RESOURCE_QUIZ] ?? [];
182
183
        foreach ($quizResources as $quiz) {
184
            if ($quiz->obj->iid == $quizId) {
185
                $feedbacks[] = [
186
                    'feedbacktext' => $quiz->obj->description ?? '',
187
                    'mingrade' => 0.00000,
188
                    'maxgrade' => $quiz->obj->grade ?? 10.00000,
189
                ];
190
            }
191
        }
192
193
        return $feedbacks;
194
    }
195
196
    /**
197
     * Creates the quiz.xml file.
198
     */
199
    private function createQuizXml(array $quizData, string $destinationDir): void
200
    {
201
        $xmlContent = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
202
        $xmlContent .= '<activity id="' . $quizData['id'] . '" moduleid="' . $quizData['moduleid'] . '" modulename="quiz" contextid="' . $quizData['contextid'] . '">' . PHP_EOL;
203
        $xmlContent .= '  <quiz id="' . $quizData['id'] . '">' . PHP_EOL;
204
        $xmlContent .= '    <name>' . htmlspecialchars($quizData['name']) . '</name>' . PHP_EOL;
205
        $xmlContent .= '    <intro>' . htmlspecialchars($quizData['intro']) . '</intro>' . PHP_EOL;
206
        $xmlContent .= '    <introformat>1</introformat>' . PHP_EOL;
207
        $xmlContent .= '    <timeopen>' . ($quizData['timeopen'] ?? 0) . '</timeopen>' . PHP_EOL;
208
        $xmlContent .= '    <timeclose>' . ($quizData['timeclose'] ?? 0) . '</timeclose>' . PHP_EOL;
209
        $xmlContent .= '    <timelimit>' . ($quizData['timelimit'] ?? 0) . '</timelimit>' . PHP_EOL;
210
        $xmlContent .= '    <overduehandling>' . ($quizData['overduehandling'] ?? 'autosubmit') . '</overduehandling>' . PHP_EOL;
211
        $xmlContent .= '    <graceperiod>' . ($quizData['graceperiod'] ?? 0) . '</graceperiod>' . PHP_EOL;
212
        $xmlContent .= '    <preferredbehaviour>' . htmlspecialchars($quizData['preferredbehaviour']) . '</preferredbehaviour>' . PHP_EOL;
213
        $xmlContent .= '    <canredoquestions>' . ($quizData['canredoquestions'] ?? 0) . '</canredoquestions>' . PHP_EOL;
214
        $xmlContent .= '    <attempts_number>' . ($quizData['attempts_number'] ?? 0) . '</attempts_number>' . PHP_EOL;
215
        $xmlContent .= '    <attemptonlast>' . ($quizData['attemptonlast'] ?? 0) . '</attemptonlast>' . PHP_EOL;
216
        $xmlContent .= '    <grademethod>' . $quizData['grademethod'] . '</grademethod>' . PHP_EOL;
217
        $xmlContent .= '    <decimalpoints>' . $quizData['decimalpoints'] . '</decimalpoints>' . PHP_EOL;
218
        $xmlContent .= '    <questiondecimalpoints>' . ($quizData['questiondecimalpoints'] ?? -1) . '</questiondecimalpoints>' . PHP_EOL;
219
220
        // Review options
221
        $xmlContent .= '    <reviewattempt>' . ($quizData['reviewattempt'] ?? 69888) . '</reviewattempt>' . PHP_EOL;
222
        $xmlContent .= '    <reviewcorrectness>' . ($quizData['reviewcorrectness'] ?? 4352) . '</reviewcorrectness>' . PHP_EOL;
223
        $xmlContent .= '    <reviewmarks>' . ($quizData['reviewmarks'] ?? 4352) . '</reviewmarks>' . PHP_EOL;
224
        $xmlContent .= '    <reviewspecificfeedback>' . ($quizData['reviewspecificfeedback'] ?? 4352) . '</reviewspecificfeedback>' . PHP_EOL;
225
        $xmlContent .= '    <reviewgeneralfeedback>' . ($quizData['reviewgeneralfeedback'] ?? 4352) . '</reviewgeneralfeedback>' . PHP_EOL;
226
        $xmlContent .= '    <reviewrightanswer>' . ($quizData['reviewrightanswer'] ?? 4352) . '</reviewrightanswer>' . PHP_EOL;
227
        $xmlContent .= '    <reviewoverallfeedback>' . ($quizData['reviewoverallfeedback'] ?? 4352) . '</reviewoverallfeedback>' . PHP_EOL;
228
229
        // Navigation and presentation settings
230
        $xmlContent .= '    <questionsperpage>' . $quizData['questionsperpage'] . '</questionsperpage>' . PHP_EOL;
231
        $xmlContent .= '    <navmethod>' . htmlspecialchars($quizData['navmethod']) . '</navmethod>' . PHP_EOL;
232
        $xmlContent .= '    <shuffleanswers>' . $quizData['shuffleanswers'] . '</shuffleanswers>' . PHP_EOL;
233
        $xmlContent .= '    <sumgrades>' . $quizData['sumgrades'] . '</sumgrades>' . PHP_EOL;
234
        $xmlContent .= '    <grade>' . $quizData['grade'] . '</grade>' . PHP_EOL;
235
236
        // Timing and security
237
        $xmlContent .= '    <timecreated>' . ($quizData['timecreated'] ?? time()) . '</timecreated>' . PHP_EOL;
238
        $xmlContent .= '    <timemodified>' . ($quizData['timemodified'] ?? time()) . '</timemodified>' . PHP_EOL;
239
        $xmlContent .= '    <password>' . (isset($quizData['password']) ? htmlspecialchars($quizData['password']) : '') . '</password>' . PHP_EOL;
240
        $xmlContent .= '    <subnet>' . (isset($quizData['subnet']) ? htmlspecialchars($quizData['subnet']) : '') . '</subnet>' . PHP_EOL;
241
        $xmlContent .= '    <browsersecurity>' . (isset($quizData['browsersecurity']) ? htmlspecialchars($quizData['browsersecurity']) : '-') . '</browsersecurity>' . PHP_EOL;
242
        $xmlContent .= '    <delay1>' . ($quizData['delay1'] ?? 0) . '</delay1>' . PHP_EOL;
243
        $xmlContent .= '    <delay2>' . ($quizData['delay2'] ?? 0) . '</delay2>' . PHP_EOL;
244
245
        // Additional options
246
        $xmlContent .= '    <showuserpicture>' . ($quizData['showuserpicture'] ?? 0) . '</showuserpicture>' . PHP_EOL;
247
        $xmlContent .= '    <showblocks>' . ($quizData['showblocks'] ?? 0) . '</showblocks>' . PHP_EOL;
248
        $xmlContent .= '    <completionattemptsexhausted>' . ($quizData['completionattemptsexhausted'] ?? 0) . '</completionattemptsexhausted>' . PHP_EOL;
249
        $xmlContent .= '    <completionpass>' . ($quizData['completionpass'] ?? 0) . '</completionpass>' . PHP_EOL;
250
        $xmlContent .= '    <completionminattempts>' . ($quizData['completionminattempts'] ?? 0) . '</completionminattempts>' . PHP_EOL;
251
        $xmlContent .= '    <allowofflineattempts>' . ($quizData['allowofflineattempts'] ?? 0) . '</allowofflineattempts>' . PHP_EOL;
252
253
        // Subplugin, if applicable
254
        $xmlContent .= '    <subplugin_quizaccess_seb_quiz>' . PHP_EOL;
255
        $xmlContent .= '    </subplugin_quizaccess_seb_quiz>' . PHP_EOL;
256
257
        // Add question instances
258
        $xmlContent .= '    <question_instances>' . PHP_EOL;
259
        foreach ($quizData['questions'] as $question) {
260
            $xmlContent .= '      <question_instance id="' . $question['id'] . '">' . PHP_EOL;
261
            $xmlContent .= '        <slot>' . $question['id'] . '</slot>' . PHP_EOL;
262
            $xmlContent .= '        <page>1</page>' . PHP_EOL;
263
            $xmlContent .= '        <requireprevious>0</requireprevious>' . PHP_EOL;
264
            $xmlContent .= '        <questionid>' . $question['id'] . '</questionid>' . PHP_EOL;
265
            $xmlContent .= '        <questioncategoryid>' . $question['questioncategoryid'] . '</questioncategoryid>' . PHP_EOL;
266
            $xmlContent .= '        <includingsubcategories>$@NULL@$</includingsubcategories>' . PHP_EOL;
267
            $xmlContent .= '        <maxmark>' . $question['maxmark'] . '</maxmark>' . PHP_EOL;
268
            $xmlContent .= '      </question_instance>' . PHP_EOL;
269
        }
270
        $xmlContent .= '    </question_instances>' . PHP_EOL;
271
272
        // Quiz sections
273
        $xmlContent .= '    <sections>' . PHP_EOL;
274
        $xmlContent .= '      <section id="'.$quizData['id'].'">' . PHP_EOL;
275
        $xmlContent .= '        <firstslot>1</firstslot>' . PHP_EOL;
276
        $xmlContent .= '        <shufflequestions>0</shufflequestions>' . PHP_EOL;
277
        $xmlContent .= '      </section>' . PHP_EOL;
278
        $xmlContent .= '    </sections>' . PHP_EOL;
279
280
        // Add feedbacks
281
        $xmlContent .= '    <feedbacks>' . PHP_EOL;
282
        foreach ($quizData['feedbacks'] as $feedback) {
283
            $xmlContent .= '      <feedback id="'.$quizData['id'].'">' . PHP_EOL;
284
            $xmlContent .= '        <feedbacktext>' . htmlspecialchars($feedback['feedbacktext']) . '</feedbacktext>' . PHP_EOL;
285
            $xmlContent .= '        <feedbacktextformat>1</feedbacktextformat>' . PHP_EOL;
286
            $xmlContent .= '        <mingrade>' . $feedback['mingrade'] . '</mingrade>' . PHP_EOL;
287
            $xmlContent .= '        <maxgrade>' . $feedback['maxgrade'] . '</maxgrade>' . PHP_EOL;
288
            $xmlContent .= '      </feedback>' . PHP_EOL;
289
        }
290
        $xmlContent .= '    </feedbacks>' . PHP_EOL;
291
292
        // Complete with placeholders for attempts and grades
293
        $xmlContent .= '    <overrides>' . PHP_EOL . '    </overrides>' . PHP_EOL;
294
        $xmlContent .= '    <grades>' . PHP_EOL . '    </grades>' . PHP_EOL;
295
        $xmlContent .= '    <attempts>' . PHP_EOL . '    </attempts>' . PHP_EOL;
296
297
        // Close the activity tag
298
        $xmlContent .= '  </quiz>' . PHP_EOL;
299
        $xmlContent .= '</activity>' . PHP_EOL;
300
301
        // Save the XML file
302
        $xmlFile = $destinationDir . '/quiz.xml';
303
        if (file_put_contents($xmlFile, $xmlContent) === false) {
304
            throw new Exception(get_lang('ErrorCreatingQuizXml'));
305
        }
306
    }
307
308
    /**
309
     * Exports a question in XML format.
310
     */
311
    public function exportQuestion(array $question): string
312
    {
313
        $xmlContent = '      <question id="' . ($question['id'] ?? '0') . '">' . PHP_EOL;
314
        $xmlContent .= '        <parent>0</parent>' . PHP_EOL;
315
        $xmlContent .= '        <name>' . htmlspecialchars($question['questiontext'] ?? 'No question text') . '</name>' . PHP_EOL;
316
        $xmlContent .= '        <questiontext>' . htmlspecialchars($question['questiontext'] ?? 'No question text') . '</questiontext>' . PHP_EOL;
317
        $xmlContent .= '        <questiontextformat>1</questiontextformat>' . PHP_EOL;
318
        $xmlContent .= '        <generalfeedback></generalfeedback>' . PHP_EOL;
319
        $xmlContent .= '        <generalfeedbackformat>1</generalfeedbackformat>' . PHP_EOL;
320
        $xmlContent .= '        <defaultmark>' . ($question['maxmark'] ?? '0') . '</defaultmark>' . PHP_EOL;
321
        $xmlContent .= '        <penalty>0.3333333</penalty>' . PHP_EOL;
322
        $xmlContent .= '        <qtype>' . htmlspecialchars(str_replace('_nosingle', '', $question['qtype']) ?? 'unknown') . '</qtype>' . PHP_EOL;
323
        $xmlContent .= '        <length>1</length>' . PHP_EOL;
324
        $xmlContent .= '        <stamp>moodle+' . time() . '+QUESTIONSTAMP</stamp>' . PHP_EOL;
325
        $xmlContent .= '        <version>moodle+' . time() . '+VERSIONSTAMP</version>' . PHP_EOL;
326
        $xmlContent .= '        <hidden>0</hidden>' . PHP_EOL;
327
        $xmlContent .= '        <timecreated>' . time() . '</timecreated>' . PHP_EOL;
328
        $xmlContent .= '        <timemodified>' . time() . '</timemodified>' . PHP_EOL;
329
        $xmlContent .= '        <createdby>2</createdby>' . PHP_EOL;
330
        $xmlContent .= '        <modifiedby>2</modifiedby>' . PHP_EOL;
331
332
        // Add question type-specific content
333
        switch ($question['qtype']) {
334
            case 'multichoice':
335
                $xmlContent .= $this->exportMultichoiceQuestion($question);
336
                break;
337
            case 'multichoice_nosingle':
338
                $xmlContent .= $this->exportMultichoiceNosingleQuestion($question);
339
                break;
340
            case 'truefalse':
341
                $xmlContent .= $this->exportTrueFalseQuestion($question);
342
                break;
343
            case 'shortanswer':
344
                $xmlContent .= $this->exportShortAnswerQuestion($question);
345
                break;
346
            case 'match':
347
                $xmlContent .= $this->exportMatchQuestion($question);
348
                break;
349
        }
350
351
        $xmlContent .= '      </question>' . PHP_EOL;
352
353
        return $xmlContent;
354
    }
355
356
    /**
357
     * Exports a multiple-choice question in XML format.
358
     */
359
    private function exportMultichoiceQuestion(array $question): string
360
    {
361
        $xmlContent = '        <plugin_qtype_multichoice_question>' . PHP_EOL;
362
        $xmlContent .= '          <answers>' . PHP_EOL;
363
        foreach ($question['answers'] as $answer) {
364
            $xmlContent .= $this->exportAnswer($answer);
365
        }
366
        $xmlContent .= '          </answers>' . PHP_EOL;
367
        $xmlContent .= '          <multichoice id="' . ($question['id'] ?? '0') . '">' . PHP_EOL;
368
        $xmlContent .= '            <layout>0</layout>' . PHP_EOL;
369
        $xmlContent .= '            <single>1</single>' . PHP_EOL;
370
        $xmlContent .= '            <shuffleanswers>1</shuffleanswers>' . PHP_EOL;
371
        $xmlContent .= '            <correctfeedback>Your answer is correct.</correctfeedback>' . PHP_EOL;
372
        $xmlContent .= '            <correctfeedbackformat>1</correctfeedbackformat>' . PHP_EOL;
373
        $xmlContent .= '            <partiallycorrectfeedback>Your answer is partially correct.</partiallycorrectfeedback>' . PHP_EOL;
374
        $xmlContent .= '            <partiallycorrectfeedbackformat>1</partiallycorrectfeedbackformat>' . PHP_EOL;
375
        $xmlContent .= '            <incorrectfeedback>Your answer is incorrect.</incorrectfeedback>' . PHP_EOL;
376
        $xmlContent .= '            <incorrectfeedbackformat>1</incorrectfeedbackformat>' . PHP_EOL;
377
        $xmlContent .= '            <answernumbering>abc</answernumbering>' . PHP_EOL;
378
        $xmlContent .= '            <shownumcorrect>1</shownumcorrect>' . PHP_EOL;
379
        $xmlContent .= '          </multichoice>' . PHP_EOL;
380
        $xmlContent .= '        </plugin_qtype_multichoice_question>' . PHP_EOL;
381
382
        return $xmlContent;
383
    }
384
385
    /**
386
     * Exports a multiple-choice question with single=0 in XML format.
387
     */
388
    private function exportMultichoiceNosingleQuestion(array $question): string
389
    {
390
        // Similar structure to exportMultichoiceQuestion, but with single=0
391
        $xmlContent = str_replace('<single>1</single>', '<single>0</single>', $this->exportMultichoiceQuestion($question));
392
        return $xmlContent;
393
    }
394
395
    /**
396
     * Exports a true/false question in XML format.
397
     */
398
    private function exportTrueFalseQuestion(array $question): string
399
    {
400
        $xmlContent = '        <plugin_qtype_truefalse_question>' . PHP_EOL;
401
        $xmlContent .= '          <answers>' . PHP_EOL;
402
        foreach ($question['answers'] as $answer) {
403
            $xmlContent .= $this->exportAnswer($answer);
404
        }
405
        $xmlContent .= '          </answers>' . PHP_EOL;
406
        $xmlContent .= '          <truefalse id="' . ($question['id'] ?? '0') . '">' . PHP_EOL;
407
        $xmlContent .= '            <trueanswer>' . ($question['answers'][0]['id'] ?? '0') . '</trueanswer>' . PHP_EOL;
408
        $xmlContent .= '            <falseanswer>' . ($question['answers'][1]['id'] ?? '0') . '</falseanswer>' . PHP_EOL;
409
        $xmlContent .= '          </truefalse>' . PHP_EOL;
410
        $xmlContent .= '        </plugin_qtype_truefalse_question>' . PHP_EOL;
411
412
        return $xmlContent;
413
    }
414
415
    /**
416
     * Exports a short answer question in XML format.
417
     */
418
    private function exportShortAnswerQuestion(array $question): string
419
    {
420
        $xmlContent = '        <plugin_qtype_shortanswer_question>' . PHP_EOL;
421
        $xmlContent .= '          <answers>' . PHP_EOL;
422
        foreach ($question['answers'] as $answer) {
423
            $xmlContent .= $this->exportAnswer($answer);
424
        }
425
        $xmlContent .= '          </answers>' . PHP_EOL;
426
        $xmlContent .= '          <shortanswer id="' . ($question['id'] ?? '0') . '">' . PHP_EOL;
427
        $xmlContent .= '            <usecase>0</usecase>' . PHP_EOL;
428
        $xmlContent .= '          </shortanswer>' . PHP_EOL;
429
        $xmlContent .= '        </plugin_qtype_shortanswer_question>' . PHP_EOL;
430
431
        return $xmlContent;
432
    }
433
434
    /**
435
     * Exports a matching question in XML format.
436
     */
437
    private function exportMatchQuestion(array $question): string
438
    {
439
        $xmlContent = '        <plugin_qtype_match_question>' . PHP_EOL;
440
        $xmlContent .= '          <matchoptions id="' . htmlspecialchars($question['id'] ?? '0') . '">' . PHP_EOL;
441
        $xmlContent .= '            <shuffleanswers>1</shuffleanswers>' . PHP_EOL;
442
        $xmlContent .= '            <correctfeedback>' . htmlspecialchars($question['correctfeedback'] ?? '') . '</correctfeedback>' . PHP_EOL;
443
        $xmlContent .= '            <correctfeedbackformat>0</correctfeedbackformat>' . PHP_EOL;
444
        $xmlContent .= '            <partiallycorrectfeedback>' . htmlspecialchars($question['partiallycorrectfeedback'] ?? '') . '</partiallycorrectfeedback>' . PHP_EOL;
445
        $xmlContent .= '            <partiallycorrectfeedbackformat>0</partiallycorrectfeedbackformat>' . PHP_EOL;
446
        $xmlContent .= '            <incorrectfeedback>' . htmlspecialchars($question['incorrectfeedback'] ?? '') . '</incorrectfeedback>' . PHP_EOL;
447
        $xmlContent .= '            <incorrectfeedbackformat>0</incorrectfeedbackformat>' . PHP_EOL;
448
        $xmlContent .= '            <shownumcorrect>0</shownumcorrect>' . PHP_EOL;
449
        $xmlContent .= '          </matchoptions>' . PHP_EOL;
450
        $xmlContent .= '          <matches>' . PHP_EOL;
451
452
        $res = FillBlanks::getAnswerInfo($question['answers'][0]['text']);
453
        $words = $res['words'];
454
        $common_words = $res['common_words'];
455
456
        for ($i = 0; $i < count($common_words); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
457
            $answer = htmlspecialchars(trim(strip_tags($common_words[$i])));
458
            if (!empty(trim($answer))) {
459
                $xmlContent .= '            <match id="' . ($i + 1) . '">' . PHP_EOL;
460
                $xmlContent .= '              <questiontext>' . $answer . '</questiontext>' . PHP_EOL;
461
                $xmlContent .= '              <questiontextformat>0</questiontextformat>' . PHP_EOL;
462
                $xmlContent .= '              <answertext>' . htmlspecialchars(explode('|', $words[$i])[0]) . '</answertext>' . PHP_EOL;
463
                $xmlContent .= '            </match>' . PHP_EOL;
464
            }
465
        }
466
467
        $xmlContent .= '          </matches>' . PHP_EOL;
468
        $xmlContent .= '        </plugin_qtype_match_question>' . PHP_EOL;
469
470
        return $xmlContent;
471
    }
472
473
    /**
474
     * Exports an answer in XML format.
475
     */
476
    private function exportAnswer(array $answer): string
477
    {
478
        return '            <answer id="' . ($answer['id'] ?? '0') . '">' . PHP_EOL .
479
            '              <answertext>' . htmlspecialchars($answer['text'] ?? 'No answer text') . '</answertext>' . PHP_EOL .
480
            '              <answerformat>1</answerformat>' . PHP_EOL .
481
            '              <fraction>' . ($answer['fraction'] ?? '0') . '</fraction>' . PHP_EOL .
482
            '              <feedback>' . htmlspecialchars($answer['feedback'] ?? '') . '</feedback>' . PHP_EOL .
483
            '              <feedbackformat>1</feedbackformat>' . PHP_EOL .
484
            '            </answer>' . PHP_EOL;
485
    }
486
}
487
488