Passed
Push — 1.11.x ( 065453...ddd2b0 )
by Yannick
09:02
created

QuizExport::getData()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 65
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 55
dl 0
loc 65
rs 8.9818
c 2
b 0
f 0
cc 4
nc 4
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 == -1) {
56
                continue;
57
            }
58
59
            if ($quiz->obj->iid == $quizId) {
60
                $contextid = $quiz->obj->c_id;
61
62
                return [
63
                    'id' => $quiz->obj->iid,
64
                    'name' => $quiz->obj->title,
65
                    'intro' => $quiz->obj->description,
66
                    'timeopen' => $quiz->obj->start_time ?? 0,
67
                    'timeclose' => $quiz->obj->end_time ?? 0,
68
                    'timelimit' => $quiz->obj->timelimit ?? 0,
69
                    'grademethod' => $quiz->obj->grademethod ?? 1,
70
                    'decimalpoints' => $quiz->obj->decimalpoints ?? 2,
71
                    'sumgrades' => $quiz->obj->sumgrades ?? 0,
72
                    'grade' => $quiz->obj->grade ?? 0,
73
                    'questionsperpage' => $quiz->obj->questionsperpage ?? 1,
74
                    'preferredbehaviour' => $quiz->obj->preferredbehaviour ?? 'deferredfeedback',
75
                    'shuffleanswers' => $quiz->obj->shuffleanswers ?? 1,
76
                    'questions' => $this->getQuestionsForQuiz($quizId),
77
                    'feedbacks' => $this->getFeedbacksForQuiz($quizId),
78
                    'sectionid' => $sectionId,
79
                    'moduleid' => $quiz->obj->iid ?? 0,
80
                    'modulename' => 'quiz',
81
                    'contextid' => $contextid,
82
                    'overduehandling' => $quiz->obj->overduehandling ?? 'autosubmit',
83
                    'graceperiod' => $quiz->obj->graceperiod ?? 0,
84
                    'canredoquestions' => $quiz->obj->canredoquestions ?? 0,
85
                    'attempts_number' => $quiz->obj->attempts_number ?? 0,
86
                    'attemptonlast' => $quiz->obj->attemptonlast ?? 0,
87
                    'questiondecimalpoints' => $quiz->obj->questiondecimalpoints ?? 2,
88
                    'reviewattempt' => $quiz->obj->reviewattempt ?? 0,
89
                    'reviewcorrectness' => $quiz->obj->reviewcorrectness ?? 0,
90
                    'reviewmarks' => $quiz->obj->reviewmarks ?? 0,
91
                    'reviewspecificfeedback' => $quiz->obj->reviewspecificfeedback ?? 0,
92
                    'reviewgeneralfeedback' => $quiz->obj->reviewgeneralfeedback ?? 0,
93
                    'reviewrightanswer' => $quiz->obj->reviewrightanswer ?? 0,
94
                    'reviewoverallfeedback' => $quiz->obj->reviewoverallfeedback ?? 0,
95
                    'timecreated' => $quiz->obj->insert_date ?? time(),
96
                    'timemodified' => $quiz->obj->lastedit_date ?? time(),
97
                    'password' => $quiz->obj->password ?? '',
98
                    'subnet' => $quiz->obj->subnet ?? '',
99
                    'browsersecurity' => $quiz->obj->browsersecurity ?? '-',
100
                    'delay1' => $quiz->obj->delay1 ?? 0,
101
                    'delay2' => $quiz->obj->delay2 ?? 0,
102
                    'showuserpicture' => $quiz->obj->showuserpicture ?? 0,
103
                    'showblocks' => $quiz->obj->showblocks ?? 0,
104
                    'completionattemptsexhausted' => $quiz->obj->completionattemptsexhausted ?? 0,
105
                    'completionpass' => $quiz->obj->completionpass ?? 0,
106
                    'completionminattempts' => $quiz->obj->completionminattempts ?? 0,
107
                    'allowofflineattempts' => $quiz->obj->allowofflineattempts ?? 0,
108
                    'users' => [],
109
                    'files' => [],
110
                ];
111
            }
112
        }
113
114
        return [];
115
    }
116
117
    /**
118
     * Retrieves the questions for a specific quiz.
119
     */
120
    private function getQuestionsForQuiz(int $quizId): array
121
    {
122
        $questions = [];
123
        $quizResources = $this->course->resources[RESOURCE_QUIZQUESTION] ?? [];
124
125
        foreach ($quizResources as $questionId => $questionData) {
126
            if (in_array($questionId, $this->course->resources[RESOURCE_QUIZ][$quizId]->obj->question_ids)) {
127
                $questions[] = [
128
                    'id' => $questionData->source_id,
129
                    'questiontext' => $questionData->question,
130
                    'qtype' => $this->mapQuestionType($questionData->quiz_type),
131
                    'questioncategoryid' => $questionData->question_category ?? 0,
132
                    'answers' => $this->getAnswersForQuestion($questionData->source_id),
133
                    'maxmark' => $questionData->ponderation ?? 1,
134
                ];
135
            }
136
        }
137
138
        return $questions;
139
    }
140
141
    /**
142
     * Maps the quiz type code to a descriptive string.
143
     */
144
    private function mapQuestionType(string $quizType): string
145
    {
146
        switch ($quizType) {
147
            case UNIQUE_ANSWER: return 'multichoice';
148
            case MULTIPLE_ANSWER: return 'multichoice_nosingle';
149
            case FILL_IN_BLANKS: return 'match';
150
            case FREE_ANSWER: return 'shortanswer';
151
            case CALCULATED_ANSWER: return 'calculated';
152
            case UPLOAD_ANSWER: return 'fileupload';
153
            default: return 'unknown';
154
        }
155
    }
156
157
    /**
158
     * Retrieves the answers for a specific question ID.
159
     */
160
    private function getAnswersForQuestion(int $questionId): array
161
    {
162
        $answers = [];
163
        $quizResources = $this->course->resources[RESOURCE_QUIZQUESTION] ?? [];
164
165
        foreach ($quizResources as $questionData) {
166
            if ($questionData->source_id == $questionId) {
167
                foreach ($questionData->answers as $answer) {
168
                    $answers[] = [
169
                        'text' => $answer['answer'],
170
                        'fraction' => $answer['correct'] == '1' ? 100 : 0,
171
                        'feedback' => $answer['comment'],
172
                    ];
173
                }
174
            }
175
        }
176
        return $answers;
177
    }
178
179
    /**
180
     * Retrieves feedbacks for a specific quiz.
181
     */
182
    private function getFeedbacksForQuiz(int $quizId): array
183
    {
184
        $feedbacks = [];
185
        $quizResources = $this->course->resources[RESOURCE_QUIZ] ?? [];
186
187
        foreach ($quizResources as $quiz) {
188
            if ($quiz->obj->iid == $quizId) {
189
                $feedbacks[] = [
190
                    'feedbacktext' => $quiz->obj->description ?? '',
191
                    'mingrade' => 0.00000,
192
                    'maxgrade' => $quiz->obj->grade ?? 10.00000,
193
                ];
194
            }
195
        }
196
197
        return $feedbacks;
198
    }
199
200
    /**
201
     * Creates the quiz.xml file.
202
     */
203
    private function createQuizXml(array $quizData, string $destinationDir): void
204
    {
205
        $xmlContent = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
206
        $xmlContent .= '<activity id="' . $quizData['id'] . '" moduleid="' . $quizData['moduleid'] . '" modulename="quiz" contextid="' . $quizData['contextid'] . '">' . PHP_EOL;
207
        $xmlContent .= '  <quiz id="' . $quizData['id'] . '">' . PHP_EOL;
208
        $xmlContent .= '    <name>' . htmlspecialchars($quizData['name']) . '</name>' . PHP_EOL;
209
        $xmlContent .= '    <intro>' . htmlspecialchars($quizData['intro']) . '</intro>' . PHP_EOL;
210
        $xmlContent .= '    <introformat>1</introformat>' . PHP_EOL;
211
        $xmlContent .= '    <timeopen>' . ($quizData['timeopen'] ?? 0) . '</timeopen>' . PHP_EOL;
212
        $xmlContent .= '    <timeclose>' . ($quizData['timeclose'] ?? 0) . '</timeclose>' . PHP_EOL;
213
        $xmlContent .= '    <timelimit>' . ($quizData['timelimit'] ?? 0) . '</timelimit>' . PHP_EOL;
214
        $xmlContent .= '    <overduehandling>' . ($quizData['overduehandling'] ?? 'autosubmit') . '</overduehandling>' . PHP_EOL;
215
        $xmlContent .= '    <graceperiod>' . ($quizData['graceperiod'] ?? 0) . '</graceperiod>' . PHP_EOL;
216
        $xmlContent .= '    <preferredbehaviour>' . htmlspecialchars($quizData['preferredbehaviour']) . '</preferredbehaviour>' . PHP_EOL;
217
        $xmlContent .= '    <canredoquestions>' . ($quizData['canredoquestions'] ?? 0) . '</canredoquestions>' . PHP_EOL;
218
        $xmlContent .= '    <attempts_number>' . ($quizData['attempts_number'] ?? 0) . '</attempts_number>' . PHP_EOL;
219
        $xmlContent .= '    <attemptonlast>' . ($quizData['attemptonlast'] ?? 0) . '</attemptonlast>' . PHP_EOL;
220
        $xmlContent .= '    <grademethod>' . $quizData['grademethod'] . '</grademethod>' . PHP_EOL;
221
        $xmlContent .= '    <decimalpoints>' . $quizData['decimalpoints'] . '</decimalpoints>' . PHP_EOL;
222
        $xmlContent .= '    <questiondecimalpoints>' . ($quizData['questiondecimalpoints'] ?? -1) . '</questiondecimalpoints>' . PHP_EOL;
223
224
        // Review options
225
        $xmlContent .= '    <reviewattempt>' . ($quizData['reviewattempt'] ?? 69888) . '</reviewattempt>' . PHP_EOL;
226
        $xmlContent .= '    <reviewcorrectness>' . ($quizData['reviewcorrectness'] ?? 4352) . '</reviewcorrectness>' . PHP_EOL;
227
        $xmlContent .= '    <reviewmarks>' . ($quizData['reviewmarks'] ?? 4352) . '</reviewmarks>' . PHP_EOL;
228
        $xmlContent .= '    <reviewspecificfeedback>' . ($quizData['reviewspecificfeedback'] ?? 4352) . '</reviewspecificfeedback>' . PHP_EOL;
229
        $xmlContent .= '    <reviewgeneralfeedback>' . ($quizData['reviewgeneralfeedback'] ?? 4352) . '</reviewgeneralfeedback>' . PHP_EOL;
230
        $xmlContent .= '    <reviewrightanswer>' . ($quizData['reviewrightanswer'] ?? 4352) . '</reviewrightanswer>' . PHP_EOL;
231
        $xmlContent .= '    <reviewoverallfeedback>' . ($quizData['reviewoverallfeedback'] ?? 4352) . '</reviewoverallfeedback>' . PHP_EOL;
232
233
        // Navigation and presentation settings
234
        $xmlContent .= '    <questionsperpage>' . $quizData['questionsperpage'] . '</questionsperpage>' . PHP_EOL;
235
        $xmlContent .= '    <navmethod>' . htmlspecialchars($quizData['navmethod']) . '</navmethod>' . PHP_EOL;
236
        $xmlContent .= '    <shuffleanswers>' . $quizData['shuffleanswers'] . '</shuffleanswers>' . PHP_EOL;
237
        $xmlContent .= '    <sumgrades>' . $quizData['sumgrades'] . '</sumgrades>' . PHP_EOL;
238
        $xmlContent .= '    <grade>' . $quizData['grade'] . '</grade>' . PHP_EOL;
239
240
        // Timing and security
241
        $xmlContent .= '    <timecreated>' . ($quizData['timecreated'] ?? time()) . '</timecreated>' . PHP_EOL;
242
        $xmlContent .= '    <timemodified>' . ($quizData['timemodified'] ?? time()) . '</timemodified>' . PHP_EOL;
243
        $xmlContent .= '    <password>' . (isset($quizData['password']) ? htmlspecialchars($quizData['password']) : '') . '</password>' . PHP_EOL;
244
        $xmlContent .= '    <subnet>' . (isset($quizData['subnet']) ? htmlspecialchars($quizData['subnet']) : '') . '</subnet>' . PHP_EOL;
245
        $xmlContent .= '    <browsersecurity>' . (isset($quizData['browsersecurity']) ? htmlspecialchars($quizData['browsersecurity']) : '-') . '</browsersecurity>' . PHP_EOL;
246
        $xmlContent .= '    <delay1>' . ($quizData['delay1'] ?? 0) . '</delay1>' . PHP_EOL;
247
        $xmlContent .= '    <delay2>' . ($quizData['delay2'] ?? 0) . '</delay2>' . PHP_EOL;
248
249
        // Additional options
250
        $xmlContent .= '    <showuserpicture>' . ($quizData['showuserpicture'] ?? 0) . '</showuserpicture>' . PHP_EOL;
251
        $xmlContent .= '    <showblocks>' . ($quizData['showblocks'] ?? 0) . '</showblocks>' . PHP_EOL;
252
        $xmlContent .= '    <completionattemptsexhausted>' . ($quizData['completionattemptsexhausted'] ?? 0) . '</completionattemptsexhausted>' . PHP_EOL;
253
        $xmlContent .= '    <completionpass>' . ($quizData['completionpass'] ?? 0) . '</completionpass>' . PHP_EOL;
254
        $xmlContent .= '    <completionminattempts>' . ($quizData['completionminattempts'] ?? 0) . '</completionminattempts>' . PHP_EOL;
255
        $xmlContent .= '    <allowofflineattempts>' . ($quizData['allowofflineattempts'] ?? 0) . '</allowofflineattempts>' . PHP_EOL;
256
257
        // Subplugin, if applicable
258
        $xmlContent .= '    <subplugin_quizaccess_seb_quiz>' . PHP_EOL;
259
        $xmlContent .= '    </subplugin_quizaccess_seb_quiz>' . PHP_EOL;
260
261
        // Add question instances
262
        $xmlContent .= '    <question_instances>' . PHP_EOL;
263
        foreach ($quizData['questions'] as $question) {
264
            $xmlContent .= '      <question_instance id="' . $question['id'] . '">' . PHP_EOL;
265
            $xmlContent .= '        <slot>' . $question['id'] . '</slot>' . PHP_EOL;
266
            $xmlContent .= '        <page>1</page>' . PHP_EOL;
267
            $xmlContent .= '        <requireprevious>0</requireprevious>' . PHP_EOL;
268
            $xmlContent .= '        <questionid>' . $question['id'] . '</questionid>' . PHP_EOL;
269
            $xmlContent .= '        <questioncategoryid>' . $question['questioncategoryid'] . '</questioncategoryid>' . PHP_EOL;
270
            $xmlContent .= '        <includingsubcategories>$@NULL@$</includingsubcategories>' . PHP_EOL;
271
            $xmlContent .= '        <maxmark>' . $question['maxmark'] . '</maxmark>' . PHP_EOL;
272
            $xmlContent .= '      </question_instance>' . PHP_EOL;
273
        }
274
        $xmlContent .= '    </question_instances>' . PHP_EOL;
275
276
        // Quiz sections
277
        $xmlContent .= '    <sections>' . PHP_EOL;
278
        $xmlContent .= '      <section id="'.$quizData['id'].'">' . PHP_EOL;
279
        $xmlContent .= '        <firstslot>1</firstslot>' . PHP_EOL;
280
        $xmlContent .= '        <shufflequestions>0</shufflequestions>' . PHP_EOL;
281
        $xmlContent .= '      </section>' . PHP_EOL;
282
        $xmlContent .= '    </sections>' . PHP_EOL;
283
284
        // Add feedbacks
285
        $xmlContent .= '    <feedbacks>' . PHP_EOL;
286
        foreach ($quizData['feedbacks'] as $feedback) {
287
            $xmlContent .= '      <feedback id="'.$quizData['id'].'">' . PHP_EOL;
288
            $xmlContent .= '        <feedbacktext>' . htmlspecialchars($feedback['feedbacktext']) . '</feedbacktext>' . PHP_EOL;
289
            $xmlContent .= '        <feedbacktextformat>1</feedbacktextformat>' . PHP_EOL;
290
            $xmlContent .= '        <mingrade>' . $feedback['mingrade'] . '</mingrade>' . PHP_EOL;
291
            $xmlContent .= '        <maxgrade>' . $feedback['maxgrade'] . '</maxgrade>' . PHP_EOL;
292
            $xmlContent .= '      </feedback>' . PHP_EOL;
293
        }
294
        $xmlContent .= '    </feedbacks>' . PHP_EOL;
295
296
        // Complete with placeholders for attempts and grades
297
        $xmlContent .= '    <overrides>' . PHP_EOL . '    </overrides>' . PHP_EOL;
298
        $xmlContent .= '    <grades>' . PHP_EOL . '    </grades>' . PHP_EOL;
299
        $xmlContent .= '    <attempts>' . PHP_EOL . '    </attempts>' . PHP_EOL;
300
301
        // Close the activity tag
302
        $xmlContent .= '  </quiz>' . PHP_EOL;
303
        $xmlContent .= '</activity>' . PHP_EOL;
304
305
        // Save the XML file
306
        $xmlFile = $destinationDir . '/quiz.xml';
307
        if (file_put_contents($xmlFile, $xmlContent) === false) {
308
            throw new Exception(get_lang('ErrorCreatingQuizXml'));
309
        }
310
    }
311
312
    /**
313
     * Exports a question in XML format.
314
     */
315
    public function exportQuestion(array $question): string
316
    {
317
        $xmlContent = '      <question id="' . ($question['id'] ?? '0') . '">' . PHP_EOL;
318
        $xmlContent .= '        <parent>0</parent>' . PHP_EOL;
319
        $xmlContent .= '        <name>' . htmlspecialchars($question['questiontext'] ?? 'No question text') . '</name>' . PHP_EOL;
320
        $xmlContent .= '        <questiontext>' . htmlspecialchars($question['questiontext'] ?? 'No question text') . '</questiontext>' . PHP_EOL;
321
        $xmlContent .= '        <questiontextformat>1</questiontextformat>' . PHP_EOL;
322
        $xmlContent .= '        <generalfeedback></generalfeedback>' . PHP_EOL;
323
        $xmlContent .= '        <generalfeedbackformat>1</generalfeedbackformat>' . PHP_EOL;
324
        $xmlContent .= '        <defaultmark>' . ($question['maxmark'] ?? '0') . '</defaultmark>' . PHP_EOL;
325
        $xmlContent .= '        <penalty>0.3333333</penalty>' . PHP_EOL;
326
        $xmlContent .= '        <qtype>' . htmlspecialchars(str_replace('_nosingle', '', $question['qtype']) ?? 'unknown') . '</qtype>' . PHP_EOL;
327
        $xmlContent .= '        <length>1</length>' . PHP_EOL;
328
        $xmlContent .= '        <stamp>moodle+' . time() . '+QUESTIONSTAMP</stamp>' . PHP_EOL;
329
        $xmlContent .= '        <version>moodle+' . time() . '+VERSIONSTAMP</version>' . PHP_EOL;
330
        $xmlContent .= '        <hidden>0</hidden>' . PHP_EOL;
331
        $xmlContent .= '        <timecreated>' . time() . '</timecreated>' . PHP_EOL;
332
        $xmlContent .= '        <timemodified>' . time() . '</timemodified>' . PHP_EOL;
333
        $xmlContent .= '        <createdby>2</createdby>' . PHP_EOL;
334
        $xmlContent .= '        <modifiedby>2</modifiedby>' . PHP_EOL;
335
336
        // Add question type-specific content
337
        switch ($question['qtype']) {
338
            case 'multichoice':
339
                $xmlContent .= $this->exportMultichoiceQuestion($question);
340
                break;
341
            case 'multichoice_nosingle':
342
                $xmlContent .= $this->exportMultichoiceNosingleQuestion($question);
343
                break;
344
            case 'truefalse':
345
                $xmlContent .= $this->exportTrueFalseQuestion($question);
346
                break;
347
            case 'shortanswer':
348
                $xmlContent .= $this->exportShortAnswerQuestion($question);
349
                break;
350
            case 'match':
351
                $xmlContent .= $this->exportMatchQuestion($question);
352
                break;
353
        }
354
355
        $xmlContent .= '      </question>' . PHP_EOL;
356
357
        return $xmlContent;
358
    }
359
360
    /**
361
     * Exports a multiple-choice question in XML format.
362
     */
363
    private function exportMultichoiceQuestion(array $question): string
364
    {
365
        $xmlContent = '        <plugin_qtype_multichoice_question>' . PHP_EOL;
366
        $xmlContent .= '          <answers>' . PHP_EOL;
367
        foreach ($question['answers'] as $answer) {
368
            $xmlContent .= $this->exportAnswer($answer);
369
        }
370
        $xmlContent .= '          </answers>' . PHP_EOL;
371
        $xmlContent .= '          <multichoice id="' . ($question['id'] ?? '0') . '">' . PHP_EOL;
372
        $xmlContent .= '            <layout>0</layout>' . PHP_EOL;
373
        $xmlContent .= '            <single>1</single>' . PHP_EOL;
374
        $xmlContent .= '            <shuffleanswers>1</shuffleanswers>' . PHP_EOL;
375
        $xmlContent .= '            <correctfeedback>Your answer is correct.</correctfeedback>' . PHP_EOL;
376
        $xmlContent .= '            <correctfeedbackformat>1</correctfeedbackformat>' . PHP_EOL;
377
        $xmlContent .= '            <partiallycorrectfeedback>Your answer is partially correct.</partiallycorrectfeedback>' . PHP_EOL;
378
        $xmlContent .= '            <partiallycorrectfeedbackformat>1</partiallycorrectfeedbackformat>' . PHP_EOL;
379
        $xmlContent .= '            <incorrectfeedback>Your answer is incorrect.</incorrectfeedback>' . PHP_EOL;
380
        $xmlContent .= '            <incorrectfeedbackformat>1</incorrectfeedbackformat>' . PHP_EOL;
381
        $xmlContent .= '            <answernumbering>abc</answernumbering>' . PHP_EOL;
382
        $xmlContent .= '            <shownumcorrect>1</shownumcorrect>' . PHP_EOL;
383
        $xmlContent .= '          </multichoice>' . PHP_EOL;
384
        $xmlContent .= '        </plugin_qtype_multichoice_question>' . PHP_EOL;
385
386
        return $xmlContent;
387
    }
388
389
    /**
390
     * Exports a multiple-choice question with single=0 in XML format.
391
     */
392
    private function exportMultichoiceNosingleQuestion(array $question): string
393
    {
394
        // Similar structure to exportMultichoiceQuestion, but with single=0
395
        $xmlContent = str_replace('<single>1</single>', '<single>0</single>', $this->exportMultichoiceQuestion($question));
396
        return $xmlContent;
397
    }
398
399
    /**
400
     * Exports a true/false question in XML format.
401
     */
402
    private function exportTrueFalseQuestion(array $question): string
403
    {
404
        $xmlContent = '        <plugin_qtype_truefalse_question>' . PHP_EOL;
405
        $xmlContent .= '          <answers>' . PHP_EOL;
406
        foreach ($question['answers'] as $answer) {
407
            $xmlContent .= $this->exportAnswer($answer);
408
        }
409
        $xmlContent .= '          </answers>' . PHP_EOL;
410
        $xmlContent .= '          <truefalse id="' . ($question['id'] ?? '0') . '">' . PHP_EOL;
411
        $xmlContent .= '            <trueanswer>' . ($question['answers'][0]['id'] ?? '0') . '</trueanswer>' . PHP_EOL;
412
        $xmlContent .= '            <falseanswer>' . ($question['answers'][1]['id'] ?? '0') . '</falseanswer>' . PHP_EOL;
413
        $xmlContent .= '          </truefalse>' . PHP_EOL;
414
        $xmlContent .= '        </plugin_qtype_truefalse_question>' . PHP_EOL;
415
416
        return $xmlContent;
417
    }
418
419
    /**
420
     * Exports a short answer question in XML format.
421
     */
422
    private function exportShortAnswerQuestion(array $question): string
423
    {
424
        $xmlContent = '        <plugin_qtype_shortanswer_question>' . PHP_EOL;
425
        $xmlContent .= '          <answers>' . PHP_EOL;
426
        foreach ($question['answers'] as $answer) {
427
            $xmlContent .= $this->exportAnswer($answer);
428
        }
429
        $xmlContent .= '          </answers>' . PHP_EOL;
430
        $xmlContent .= '          <shortanswer id="' . ($question['id'] ?? '0') . '">' . PHP_EOL;
431
        $xmlContent .= '            <usecase>0</usecase>' . PHP_EOL;
432
        $xmlContent .= '          </shortanswer>' . PHP_EOL;
433
        $xmlContent .= '        </plugin_qtype_shortanswer_question>' . PHP_EOL;
434
435
        return $xmlContent;
436
    }
437
438
    /**
439
     * Exports a matching question in XML format.
440
     */
441
    private function exportMatchQuestion(array $question): string
442
    {
443
        $xmlContent = '        <plugin_qtype_match_question>' . PHP_EOL;
444
        $xmlContent .= '          <matchoptions id="' . htmlspecialchars($question['id'] ?? '0') . '">' . PHP_EOL;
445
        $xmlContent .= '            <shuffleanswers>1</shuffleanswers>' . PHP_EOL;
446
        $xmlContent .= '            <correctfeedback>' . htmlspecialchars($question['correctfeedback'] ?? '') . '</correctfeedback>' . PHP_EOL;
447
        $xmlContent .= '            <correctfeedbackformat>0</correctfeedbackformat>' . PHP_EOL;
448
        $xmlContent .= '            <partiallycorrectfeedback>' . htmlspecialchars($question['partiallycorrectfeedback'] ?? '') . '</partiallycorrectfeedback>' . PHP_EOL;
449
        $xmlContent .= '            <partiallycorrectfeedbackformat>0</partiallycorrectfeedbackformat>' . PHP_EOL;
450
        $xmlContent .= '            <incorrectfeedback>' . htmlspecialchars($question['incorrectfeedback'] ?? '') . '</incorrectfeedback>' . PHP_EOL;
451
        $xmlContent .= '            <incorrectfeedbackformat>0</incorrectfeedbackformat>' . PHP_EOL;
452
        $xmlContent .= '            <shownumcorrect>0</shownumcorrect>' . PHP_EOL;
453
        $xmlContent .= '          </matchoptions>' . PHP_EOL;
454
        $xmlContent .= '          <matches>' . PHP_EOL;
455
456
        $res = FillBlanks::getAnswerInfo($question['answers'][0]['text']);
457
        $words = $res['words'];
458
        $common_words = $res['common_words'];
459
460
        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...
461
            $answer = htmlspecialchars(trim(strip_tags($common_words[$i])));
462
            if (!empty(trim($answer))) {
463
                $xmlContent .= '            <match id="' . ($i + 1) . '">' . PHP_EOL;
464
                $xmlContent .= '              <questiontext>' . $answer . '</questiontext>' . PHP_EOL;
465
                $xmlContent .= '              <questiontextformat>0</questiontextformat>' . PHP_EOL;
466
                $xmlContent .= '              <answertext>' . htmlspecialchars(explode('|', $words[$i])[0]) . '</answertext>' . PHP_EOL;
467
                $xmlContent .= '            </match>' . PHP_EOL;
468
            }
469
        }
470
471
        $xmlContent .= '          </matches>' . PHP_EOL;
472
        $xmlContent .= '        </plugin_qtype_match_question>' . PHP_EOL;
473
474
        return $xmlContent;
475
    }
476
477
    /**
478
     * Exports an answer in XML format.
479
     */
480
    private function exportAnswer(array $answer): string
481
    {
482
        return '            <answer id="' . ($answer['id'] ?? '0') . '">' . PHP_EOL .
483
            '              <answertext>' . htmlspecialchars($answer['text'] ?? 'No answer text') . '</answertext>' . PHP_EOL .
484
            '              <answerformat>1</answerformat>' . PHP_EOL .
485
            '              <fraction>' . ($answer['fraction'] ?? '0') . '</fraction>' . PHP_EOL .
486
            '              <feedback>' . htmlspecialchars($answer['feedback'] ?? '') . '</feedback>' . PHP_EOL .
487
            '              <feedbackformat>1</feedbackformat>' . PHP_EOL .
488
            '            </answer>' . PHP_EOL;
489
    }
490
}
491
492