Completed
Pull Request — 1.11.x (#1285)
by José
43:07
created

MoodleImport::readResourceModule()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 15
c 2
b 0
f 0
nc 5
nop 1
dl 0
loc 23
rs 8.5906
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * Class MoodleImport
6
 *
7
 * @author José Loguercio <[email protected]>
8
 * @package chamilo.library
9
 */
10
11
class MoodleImport
12
{
13
    /**
14
     * Read and validate the moodleFile
15
     *
16
     * @param resource $uploadedFile *.* mbz file moodle course backup
17
     * @return bool
18
     */
19
    public function readMoodleFile($uploadedFile)
20
    {
21
        $file = $uploadedFile['tmp_name'];
22
23
        if (is_file($file) && is_readable($file)) {
24
            $package = new PclZip($file);
25
            $packageContent = $package->listContent();
26
            $mainFileKey = 0;
27
            foreach ($packageContent as $index => $value) {
28
                if ($value['filename'] == 'moodle_backup.xml') {
29
                    $mainFileKey = $index;
30
                    break;
31
                }
32
            }
33
34
            if (!$mainFileKey) {
35
                Display::addFlash(Display::return_message(get_lang('FailedToImportThisIsNotAMoodleFile'), 'error'));
36
            }
37
38
            $folder = api_get_unique_id();
39
            $destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder;
40
            $coursePath = api_get_path(SYS_COURSE_PATH).api_get_course_path().'/';
41
            $courseInfo = api_get_course_info();
42
43
            mkdir($destinationDir, api_get_permissions_for_new_directories(), true);
44
45
            $package->extract(
46
                PCLZIP_OPT_PATH,
47
                $destinationDir
48
            );
49
50
            $xml = @file_get_contents($destinationDir.'/moodle_backup.xml');
51
52
            $doc = new DOMDocument();
53
            $res = @$doc->loadXML($xml);
54
            if ($res) {
55
                $activities = $doc->getElementsByTagName('activity');
56
                foreach ($activities as $activity) {
57
                    if ($activity->childNodes->length) {
58
                        $currentItem = [];
59
60
                        foreach($activity->childNodes as $item) {
61
                            $currentItem[$item->nodeName] = $item->nodeValue;
62
                        }
63
64
                        $moduleName = isset($currentItem['modulename']) ? $currentItem['modulename'] : false;
65
                        switch ($moduleName) {
66
                            case 'forum':
67
                                require_once '../forum/forumfunction.inc.php';
68
                                $catForumValues = [];
69
70
                                // Read the current forum module xml.
71
                                $moduleDir = $currentItem['directory'];
72
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'.$moduleName.'.xml');
73
                                $moduleValues = $this->readForumModule($moduleXml);
74
75
                                // Create a Forum category based on Moodle forum type.
76
                                $catForumValues['forum_category_title'] = $moduleValues['type'];
77
                                $catForumValues['forum_category_comment'] = '';
78
                                $catId = store_forumcategory($catForumValues);
79
                                $forumValues = [];
80
                                $forumValues['forum_title'] = $moduleValues['name'];
81
                                $forumValues['forum_image'] = '';
82
                                $forumValues['forum_comment'] = $moduleValues['intro'];
83
                                $forumValues['forum_category'] = $catId;
84
85
                                store_forum($forumValues);
86
                                break;
87
                            case 'quiz':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
88
89
                                // Read the current quiz module xml.
90
                                // The quiz case is the very complicate process of all the import.
91
                                // Please if you want to review the script, try to see the readingXML functions.
92
                                // The readingXML functions in this clases do all the mayor work here.
93
94
                                $moduleDir = $currentItem['directory'];
95
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'.$moduleName.'.xml');
96
                                $questionsXml = @file_get_contents($destinationDir.'/questions.xml');
97
                                $moduleValues = $this->readQuizModule($moduleXml);
98
99
                                // At this point we got all the prepared resources from Moodle file
100
                                // $moduleValues variable contains all the necesary info to the quiz import
101
                                // var_dump($moduleValues['question_instances']); // <-- uncomment this to see the final array
102
103
                                // Lets do this ...
104
                                $exercise = new Exercise();
105
                                $exercise->updateTitle(Exercise::format_title_variable($moduleValues['name']));
106
                                $exercise->updateDescription($moduleValues['intro']);
107
                                $exercise->updateAttempts($moduleValues['attempts_number']);
108
                                $exercise->updateFeedbackType(0);
109
                                $exercise->setRandom(intval($moduleValues['shufflequestions']) + 1);
110
                                $exercise->updateRandomAnswers($moduleValues['shuffleanswers']);
111
                                $exercise->updateExpiredTime($moduleValues['timelimit']);
112
113
                                if ($moduleValues['questionsperpage'] == 1) {
114
                                    $exercise->updateType(2);
115
                                } else {
116
                                    $exercise->updateType(1);
117
                                }
118
119
                                // Create the new Quiz
120
                                $exercise->save();
121
122
                                // Ok, we got the Quiz and create it, now its time to add the Questions
123
                                foreach ($moduleValues['question_instances'] as $index => $question) {
124
                                    $questionsValues = $this->readMainQuestionsXml($questionsXml, $question['questionid']);
125
                                    $moduleValues['question_instances'][$index] = $questionsValues;
126
                                    // Set Question Type
127
                                    $qType = $moduleValues['question_instances'][$index]['qtype'];
128
                                    // Add the matched chamilo question type to the array
129
                                    $moduleValues['question_instances'][$index]['chamilo_qtype'] = $this->matchMoodleChamiloQuestionTypes($qType);
130
                                    $questionInstance = Question::getInstance($moduleValues['question_instances'][$index]['chamilo_qtype']);
131
                                    $questionInstance->updateTitle($moduleValues['question_instances'][$index]['name']);
132
                                    $questionInstance->updateDescription($moduleValues['question_instances'][$index]['questiontext']);
133
                                    $questionInstance->updateLevel(1);
134
                                    $questionInstance->updateCategory(0);
135
136
                                    //Save normal question if NOT media
137
                                    if ($questionInstance->type != MEDIA_QUESTION) {
138
                                        $questionInstance->save($exercise->id);
139
140
                                        // modify the exercise
141
                                        $exercise->addToList($questionInstance->id);
142
                                        $exercise->update_question_positions();
143
                                    }
144
145
                                    $objAnswer = new Answer($questionInstance->id);
146
147
                                    foreach ($moduleValues['question_instances'][$index]['plugin_qtype_'.$qType.'_question'] as $slot => $answer) {
148
                                        $responseAnswer = $this->processUniqueAnswer($objAnswer, $questionInstance, $answer, $slot + 1);
149
                                    }
150
                                }
151
152
                                break;
153
                            case 'resource':
154
                                // Read the current resource module xml.
155
                                $moduleDir = $currentItem['directory'];
156
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'.$moduleName.'.xml');
157
                                $filesXml = @file_get_contents($destinationDir.'/files.xml');
158
                                $moduleValues = $this->readResourceModule($moduleXml);
159
                                $mainFileModuleValues = $this->readMainFilesXml($filesXml, $moduleValues['contextid']);
160
                                $fileInfo = array_merge($moduleValues, $mainFileModuleValues, $currentItem);
161
                                $documentPath = $coursePath.'document/';
162
                                $currentResourceFilePath = $destinationDir.'/files/';
163
                                $dirs = new RecursiveDirectoryIterator($currentResourceFilePath);
164
                                foreach(new RecursiveIteratorIterator($dirs) as $file) {
165
                                    if (is_file($file) && strpos($file, $fileInfo['contenthash']) !== false) {
166
                                        $files = [];
167
                                        $files['file']['name'] = $fileInfo['filename'];
168
                                        $files['file']['tmp_name'] = $file->getPathname();
169
                                        $files['file']['type'] = $fileInfo['mimetype'];
170
                                        $files['file']['error'] = 0;
171
                                        $files['file']['size'] = $fileInfo['filesize'];
172
                                        $files['file']['from_file'] = true;
173
                                        $files['file']['move_file'] = true;
174
                                        $_POST['language'] = $courseInfo['language'];
175
                                        $_POST['moodle_import'] = true;
176
177
                                        DocumentManager::upload_document(
178
                                            $files,
179
                                            '/',
180
                                            $fileInfo['title'],
181
                                            '',
182
                                            null,
183
                                            null,
184
                                            true,
185
                                            true
186
                                        );
187
                                    }
188
                                }
189
190
                                break;
191
                            case 'url':
192
                                // Read the current url module xml.
193
                                $moduleDir = $currentItem['directory'];
194
                                $moduleXml = @file_get_contents($destinationDir.'/'.$moduleDir.'/'.$moduleName.'.xml');
195
                                $moduleValues = $this->readUrlModule($moduleXml);
196
                                $_POST['title'] = $moduleValues['name'];
197
                                $_POST['url'] = $moduleValues['externalurl'];
198
                                $_POST['description'] = $moduleValues['intro'];
199
                                $_POST['category_id'] = 0;
200
                                $_POST['target'] = '_blank';
201
202
                                Link::addlinkcategory("link");
203
                                break;
204
                        }
205
                    }
206
                }
207
            }
208
        }
209
210
        removeDir($destinationDir);
211
        return $packageContent[$mainFileKey];
212
    }
213
214
    /**
215
     * Read and validate the forum module XML
216
     *
217
     * @param resource $moduleXml XML file
218
     * @return mixed | array if is a valid xml file, false otherwise
219
     */
220 View Code Duplication
    public function readForumModule($moduleXml)
221
    {
222
        $moduleDoc = new DOMDocument();
223
        $moduleRes = @$moduleDoc->loadXML($moduleXml);
224
        if ($moduleRes) {
225
            $activities = $moduleDoc->getElementsByTagName('forum');
226
            $currentItem = [];
227
            foreach ($activities as $activity) {
228
                if ($activity->childNodes->length) {
229
                    foreach ($activity->childNodes as $item) {
230
                        $currentItem[$item->nodeName] = $item->nodeValue;
231
                    }
232
                }
233
            }
234
235
            return $currentItem;
236
        }
237
238
        return false;
239
    }
240
241
    /**
242
     * Read and validate the resource module XML
243
     *
244
     * @param resource $moduleXml XML file
245
     * @return mixed | array if is a valid xml file, false otherwise
246
     */
247
    public function readResourceModule($moduleXml)
248
    {
249
        $moduleDoc = new DOMDocument();
250
        $moduleRes = @$moduleDoc->loadXML($moduleXml);
251
        if ($moduleRes) {
252
            $activities = $moduleDoc->getElementsByTagName('resource');
253
            $mainActivity = $moduleDoc->getElementsByTagName('activity');
254
            $contextId = $mainActivity->item(0)->getAttribute('contextid');
255
            $currentItem = [];
256
            foreach ($activities as $activity) {
257
                if ($activity->childNodes->length) {
258
                    foreach($activity->childNodes as $item) {
259
                        $currentItem[$item->nodeName] = $item->nodeValue;
260
                    }
261
                }
262
            }
263
264
            $currentItem['contextid'] = $contextId;
265
            return $currentItem;
266
        }
267
268
        return false;
269
    }
270
271
    /**
272
     * Read and validate the url module XML
273
     *
274
     * @param resource $moduleXml XML file
275
     * @return mixed | array if is a valid xml file, false otherwise
276
     */
277 View Code Duplication
    public function readUrlModule($moduleXml)
278
    {
279
        $moduleDoc = new DOMDocument();
280
        $moduleRes = @$moduleDoc->loadXML($moduleXml);
281
        if ($moduleRes) {
282
            $activities = $moduleDoc->getElementsByTagName('url');
283
            $currentItem = [];
284
            foreach ($activities as $activity) {
285
                if ($activity->childNodes->length) {
286
                    foreach ($activity->childNodes as $item) {
287
                        $currentItem[$item->nodeName] = $item->nodeValue;
288
                    }
289
                }
290
            }
291
292
            return $currentItem;
293
        }
294
295
        return false;
296
    }
297
298
    /**
299
     * Read and validate the quiz module XML
300
     *
301
     * @param resource $moduleXml XML file
302
     * @return mixed | array if is a valid xml file, false otherwise
303
     */
304
    public function readQuizModule($moduleXml)
305
    {
306
        $moduleDoc = new DOMDocument();
307
        $moduleRes = @$moduleDoc->loadXML($moduleXml);
308
        if ($moduleRes) {
309
            $activities = $moduleDoc->getElementsByTagName('quiz');
310
            $currentItem = [];
311
            foreach ($activities as $activity) {
312
                if ($activity->childNodes->length) {
313
                    foreach ($activity->childNodes as $item) {
314
                        $currentItem[$item->nodeName] = $item->nodeValue;
315
                    }
316
                }
317
            }
318
319
            $questions = $moduleDoc->getElementsByTagName('question_instance');
320
321
            $questionList = [];
322
            $counter = 0;
323
            foreach ($questions as $question) {
324
                if ($question->childNodes->length) {
325
                    foreach ($question->childNodes as $item) {
326
                        $questionList[$counter][$item->nodeName] = $item->nodeValue;
327
                    }
328
                    $counter++;
329
                }
330
331
            }
332
            $currentItem['question_instances'] = $questionList;
333
            return $currentItem;
334
        }
335
336
        return false;
337
    }
338
339
    /**
340
     * Search the current file resource in main Files XML
341
     *
342
     * @param resource $filesXml XML file
343
     * @param int $contextId
344
     * @return mixed | array if is a valid xml file, false otherwise
345
     */
346
    public function readMainFilesXml($filesXml, $contextId)
347
    {
348
        $moduleDoc = new DOMDocument();
349
        $moduleRes = @$moduleDoc->loadXML($filesXml);
350
        if ($moduleRes) {
351
            $activities = $moduleDoc->getElementsByTagName('file');
352
            $currentItem = [];
353
            foreach ($activities as $activity) {
354
                if ($activity->childNodes->length) {
355
                    $isThisItemThatIWant = false;
356
                    foreach($activity->childNodes as $item) {
357
                        if (!$isThisItemThatIWant && $item->nodeName == 'contenthash') {
358
                            $currentItem['contenthash'] = $item->nodeValue;
359
                        }
360
                        if ($item->nodeName == 'contextid' && intval($item->nodeValue) == intval($contextId) && !$isThisItemThatIWant) {
361
                            $isThisItemThatIWant = true;
362
                            continue;
363
                        }
364
365
                        if ($isThisItemThatIWant && $item->nodeName == 'filename') {
366
                            $currentItem['filename'] = $item->nodeValue;
367
                        }
368
369
                        if ($isThisItemThatIWant && $item->nodeName == 'filesize') {
370
                            $currentItem['filesize'] = $item->nodeValue;
371
                        }
372
373
                        if ($isThisItemThatIWant && $item->nodeName == 'mimetype' && $item->nodeValue == 'document/unknown') {
374
                            break;
375
                        }
376
377
                        if ($isThisItemThatIWant && $item->nodeName == 'mimetype' && $item->nodeValue !== 'document/unknown') {
378
                            $currentItem['mimetype'] = $item->nodeValue;
379
                            break 2;
380
                        }
381
                    }
382
                }
383
            }
384
385
            return $currentItem;
386
        }
387
388
        return false;
389
    }
390
391
    /**
392
     * Search the current quiestion resource in main Questions XML
393
     *
394
     * @param resource $questionsXml XML file
395
     * @param int $questionId
396
     * @return mixed | array if is a valid xml file, false otherwise
397
     */
398
    public function readMainQuestionsXml($questionsXml, $questionId)
399
    {
400
        $moduleDoc = new DOMDocument();
401
        $moduleRes = @$moduleDoc->loadXML($questionsXml);
402
        if ($moduleRes) {
403
            $questions = $moduleDoc->getElementsByTagName('question');
404
            $currentItem = [];
405
            foreach ($questions as $question) {
406
                if (intval($question->getAttribute('id')) == $questionId) {
407
                    if ($question->childNodes->length) {
408
                        $currentItem['questionid'] = $questionId;
409
                        $questionType = '';
410
                        foreach($question->childNodes as $item) {
411
                            $currentItem[$item->nodeName] = $item->nodeValue;
412
                            if ($item->nodeName == 'qtype') {
413
                                $questionType = $item->nodeValue;
414
                            }
415
416
                            if ($item->nodeName == 'plugin_qtype_'.$questionType.'_question') {
417
                                $answer = $item->getElementsByTagName($this->getQuestionTypeAnswersTag($questionType));
418
                                $currentItem['plugin_qtype_'.$questionType.'_question'] = [];
419
                                for ($i = 0; $i <= $answer->length - 1; $i++) {
420
                                    $currentItem['plugin_qtype_'.$questionType.'_question'][$i]['answerid'] = $answer->item($i)->getAttribute('id');
421
                                    foreach ($answer->item($i)->childNodes as $properties) {
422
                                        $currentItem['plugin_qtype_'.$questionType.'_question'][$i][$properties->nodeName] = $properties->nodeValue;
423
                                    }
424
                                }
425
426
                                $typeValues = $item->getElementsByTagName($this->getQuestionTypeOptionsTag($questionType));
427
                                for ($i = 0; $i <= $typeValues->length - 1; $i++) {
428
                                    foreach ($typeValues->item($i)->childNodes as $properties) {
429
                                        $currentItem[$questionType.'_values'][$properties->nodeName] = $properties->nodeValue;
430
                                        if ($properties->nodeName == 'sequence') {
431
                                            $sequence = $properties->nodeValue;
432
                                            $sequenceIds = explode(',', $sequence);
433
                                            foreach ($sequenceIds as $qId) {
434
                                                $questionMatch = $this->readMainQuestionsXml($questionsXml, $qId);
435
                                                $currentItem['plugin_qtype_'.$questionType.'_question'][] = $questionMatch;
436
                                            }
437
                                        }
438
                                    }
439
                                }
440
                            }
441
                        }
442
                    }
443
                }
444
            }
445
446
            $this->traverseArray($currentItem, ['#text', 'question_hints', 'tags']);
447
            return $currentItem;
448
        }
449
450
        return false;
451
    }
452
453
    /**
454
     * return the correct question type options tag
455
     *
456
     * @param string $questionType name
457
     * @return string question type tag
458
     */
459
    public function getQuestionTypeOptionsTag($questionType)
460
    {
461
        switch ($questionType) {
462
            case 'match':
463
            case 'ddmatch':
464
                return 'matchoptions';
465
                break;
466
            default:
467
                return $questionType;
468
                break;
469
        }
470
    }
471
472
    /**
473
     * return the correct question type answers tag
474
     *
475
     * @param string $questionType name
476
     * @return string question type tag
477
     */
478
    public function getQuestionTypeAnswersTag($questionType)
479
    {
480
        switch ($questionType) {
481
            case 'match':
482
            case 'ddmatch':
483
                return 'match';
484
                break;
485
            default:
486
                return 'answer';
487
                break;
488
        }
489
    }
490
491
    /**
492
     *
493
     * @param string $moodleQuestionType
494
     * @return integer Chamilo question type
495
     */
496
    public function matchMoodleChamiloQuestionTypes($moodleQuestionType)
497
    {
498
        switch ($moodleQuestionType) {
499
            case 'multichoice':
500
                return UNIQUE_ANSWER;
501
                break;
502
            case 'multianswer':
503
            case 'shortanswer':
504
            case 'match':
505
                return FILL_IN_BLANKS;
506
                break;
507
            case 'essay':
508
                return FREE_ANSWER;
509
            case 'truefalse':
510
                return MULTIPLE_ANSWER_TRUE_FALSE;
511
        }
512
    }
513
514
    /**
515
     * Process Chamilo Unique Answer
516
     *
517
     * @param object $objAnswer
518
     * @param object $questionInstance
519
     * @param array $answerValues
520
     * @param integer $position
521
     * @return integer db response
522
     */
523
    public function processUniqueAnswer($objAnswer, $questionInstance, $answerValues, $position)
524
    {
525
        $questionWeighting = $nbrGoodAnswers = 0;
526
        $correct = intval($answerValues['fraction']) ? intval($answerValues['fraction']) : 0;
527
        $answer = $answerValues['answertext'];
528
        $comment = $answerValues['feedback'];
529
        $weighting = $answerValues['fraction'];
530
        $weighting = abs($weighting);
531
        if ($weighting > 0) {
532
            $questionWeighting += $weighting;
533
        }
534
        $goodAnswer =  $correct ? true : false;
535
536
        $objAnswer->createAnswer(
537
            $answer,
538
            $goodAnswer,
539
            $comment,
540
            $weighting,
541
            $position,
542
            null,
543
            null,
544
            ''
545
        );
546
547
        // saves the answers into the data base
548
        $objAnswer->save();
549
550
        // sets the total weighting of the question
551
        $questionInstance->updateWeighting($questionWeighting);
552
        $questionInstance->save();
553
    }
554
555
556
    /**
557
     * Litle utility to delete the unuseful tags
558
     *
559
     * @param $array
560
     * @param $keys
561
     */
562
    public function traverseArray(&$array, $keys) {
563
        foreach ($array as $key => &$value) {
564
            if (is_array($value)) {
565
                $this->traverseArray($value, $keys);
566
            } else {
567
                if (in_array($key, $keys)){
568
                    unset($array[$key]);
569
                }
570
            }
571
        }
572
    }
573
574
}