Completed
Push — master ( 17e47f...c8c08e )
by Julito
65:46 queued 26:40
created

exercise_import.inc.php ➔ elementData()   D

Complexity

Conditions 17
Paths 104

Size

Total Lines 81
Code Lines 61

Duplication

Lines 27
Ratio 33.33 %

Importance

Changes 0
Metric Value
cc 17
eloc 61
nc 104
nop 2
dl 27
loc 81
rs 4.913
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
 * @copyright (c) 2001-2006 Universite catholique de Louvain (UCL)
4
 *
5
 * @license http://www.gnu.org/copyleft/gpl.html (GPL) GENERAL PUBLIC LICENSE
6
 *
7
 * @package chamilo.exercise
8
 *
9
 * @author claro team <[email protected]>
10
 * @author Guillaume Lederer <[email protected]>
11
 */
12
13
/**
14
 * function to create a temporary directory (SAME AS IN MODULE ADMIN)
15
 */
16 View Code Duplication
function tempdir($dir, $prefix = 'tmp', $mode = 0777)
0 ignored issues
show
Best Practice introduced by
The function tempdir() has been defined more than once; this definition is ignored, only the first definition in main/exercise/export/aiken/aiken_import.inc.php (L23-32) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
17
{
18
    if (substr($dir, -1) != '/') {
19
        $dir .= '/';
20
    }
21
22
    do {
23
        $path = $dir . $prefix . mt_rand(0, 9999999);
24
    } while (!mkdir($path, $mode));
25
26
    return $path;
27
}
28
29
/**
30
 * Unzip the exercise in the temp folder
31
 * @param string The path of the temporary directory where the exercise was uploaded and unzipped
32
 * @param string
33
 * @return bool
34
 */
35
function get_and_unzip_uploaded_exercise($baseWorkDir, $uploadPath)
0 ignored issues
show
Best Practice introduced by
The function get_and_unzip_uploaded_exercise() has been defined more than once; this definition is ignored, only the first definition in main/exercise/export/aiken/aiken_import.inc.php (L60-79) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
36
{
37
    $_course = api_get_course_info();
38
    $_user = api_get_user_info();
39
40
    //Check if the file is valid (not to big and exists)
41 View Code Duplication
    if (!isset($_FILES['userFile']) || !is_uploaded_file($_FILES['userFile']['tmp_name'])) {
42
        // upload failed
43
        return false;
44
    }
45
46
    if (preg_match('/.zip$/i', $_FILES['userFile']['name']) &&
47
        handle_uploaded_document(
48
            $_course,
49
            $_FILES['userFile'],
50
            $baseWorkDir,
51
            $uploadPath,
52
            $_user['user_id'],
53
            0,
54
            null,
55
            1
56
        )
57
    ) {
58
        return true;
59
    }
60
    return false;
61
}
62
63
/**
64
 * Imports an exercise in QTI format if the XML structure can be found in it
65
 * @param array $file
66
 * @return an array as a backlog of what was really imported, and error or debug messages to display
67
 */
68
function import_exercise($file)
69
{
70
    global $exercise_info;
71
    global $element_pile;
72
    global $non_HTML_tag_to_avoid;
73
    global $record_item_body;
74
    // used to specify the question directory where files could be found in relation in any question
75
    global $questionTempDir;
76
77
    $archive_path = api_get_path(SYS_ARCHIVE_PATH) . 'qti2';
78
    $baseWorkDir = $archive_path;
79
80
    if (!is_dir($baseWorkDir)) {
81
        mkdir($baseWorkDir, api_get_permissions_for_new_directories(), true);
82
    }
83
84
    $uploadPath = '/';
85
86
    // set some default values for the new exercise
87
    $exercise_info = array();
88
    $exercise_info['name'] = preg_replace('/.zip$/i', '', $file);
89
    $exercise_info['question'] = array();
90
    $element_pile = array();
91
92
    // create parser and array to retrieve info from manifest
93
    $element_pile = array(); //pile to known the depth in which we are
94
    //$module_info = array (); //array to store the info we need
95
96
    // if file is not a .zip, then we cancel all
97
98
    if (!preg_match('/.zip$/i', $file)) {
99
100
        return 'UplZipCorrupt';
101
    }
102
103
    // unzip the uploaded file in a tmp directory
104
    if (!get_and_unzip_uploaded_exercise($baseWorkDir, $uploadPath)) {
105
106
        return 'UplZipCorrupt';
107
    }
108
109
    // find the different manifests for each question and parse them.
110
111
    $exerciseHandle = opendir($baseWorkDir);
112
    //$question_number = 0;
113
    $file_found = false;
114
    $operation = false;
115
    $result = false;
116
    $filePath = null;
117
118
    // parse every subdirectory to search xml question files
119
    while (false !== ($file = readdir($exerciseHandle))) {
120
121
        if (is_dir($baseWorkDir . '/' . $file) && $file != "." && $file != "..") {
122
123
            // Find each manifest for each question repository found
124
            $questionHandle = opendir($baseWorkDir . '/' . $file);
125 View Code Duplication
            while (false !== ($questionFile = readdir($questionHandle))) {
126
                if (preg_match('/.xml$/i', $questionFile)) {
127
                    $result = parse_file($baseWorkDir, $file, $questionFile);
128
                    $filePath = $baseWorkDir . $file;
129
                    $file_found = true;
130
                }
131
            }
132
        } elseif (preg_match('/.xml$/i', $file)) {
133
134
            // Else ignore file
135
            $result = parse_file($baseWorkDir, '', $file);
136
            $filePath = $baseWorkDir . '/' . $file;
137
            $file_found = true;
138
        }
139
    }
140
141
    if (!$file_found) {
142
143
        return 'NoXMLFileFoundInTheZip';
144
    }
145
146
    if ($result == false) {
147
        return false;
148
    }
149
150
    $doc = new DOMDocument();
151
    $doc->load($filePath);
152
    $encoding = $doc->encoding;
153
154
    // 1. Create exercise.
155
    $exercise = new Exercise();
156
    $exercise->exercise = $exercise_info['name'];
157
158
    $exercise->save();
159
    $last_exercise_id = $exercise->selectId();
160
    if (!empty($last_exercise_id)) {
161
        // For each question found...
162
        foreach ($exercise_info['question'] as $question_array) {
163
            //2. Create question
164
            $question = new Ims2Question();
165
            $question->type = $question_array['type'];
166
            $question->setAnswer();
167
            $question->updateTitle(formatText($question_array['title']));
168
            //$question->updateDescription($question_array['title']);
169
            $type = $question->selectType();
170
            $question->type = constant($type);
171
            $question->save($last_exercise_id);
172
            $last_question_id = $question->selectId();
173
            //3. Create answer
174
            $answer = new Answer($last_question_id);
175
            $answer->new_nbrAnswers = count($question_array['answer']);
176
            $totalCorrectWeight = 0;
177
            foreach ($question_array['answer'] as $key => $answers) {
178
                $split = explode('_', $key);
179
                $i = $split[1];
180
                // Answer
181
                $answer->new_answer[$i] = formatText($answers['value']);
182
                // Comment
183
                $answer->new_comment[$i] = isset($answers['feedback']) ? formatText($answers['feedback']) : null;
184
                // Position
185
                $answer->new_position[$i] = $i;
186
                // Correct answers
187
                if (in_array($key, $question_array['correct_answers'])) {
188
                    $answer->new_correct[$i] = 1;
189
                } else {
190
                    $answer->new_correct[$i] = 0;
191
                }
192
                $answer->new_weighting[$i] = $question_array['weighting'][$key];
193
                if ($answer->new_correct[$i]) {
194
                    $totalCorrectWeight = $answer->new_weighting[$i];
195
                }
196
            }
197
            $question->updateWeighting($totalCorrectWeight);
198
            $question->save($last_exercise_id);
199
            $answer->save();
200
        }
201
202
        // delete the temp dir where the exercise was unzipped
203
        my_delete($baseWorkDir . $uploadPath);
204
        return $last_exercise_id;
205
    }
206
207
    return false;
208
}
209
210
/**
211
 * We assume the file charset is UTF8
212
 **/
213
function formatText($text)
214
{
215
    return api_html_entity_decode($text);
216
}
217
218
/**
219
 * Parses a given XML file and fills global arrays with the elements
220
 * @param $exercisePath
221
 * @param $file
222
 * @param $questionFile
223
 * @return bool
224
 */
225
function parse_file($exercisePath, $file, $questionFile)
226
{
227
    global $non_HTML_tag_to_avoid;
228
    global $record_item_body;
229
    global $questionTempDir;
230
231
    $questionTempDir = $exercisePath . '/' . $file . '/';
232
    $questionFilePath = $questionTempDir . $questionFile;
233
234
    if (!($fp = fopen($questionFilePath, 'r'))) {
235
        Display:: display_error_message(get_lang('Error opening question\'s XML file'));
236
        return false;
237
    } else {
238
        $data = fread($fp, filesize($questionFilePath));
239
    }
240
241
    //parse XML question file
242
    $data = str_replace(array('<p>', '</p>', '<front>', '</front>'), '', $data);
243
244
    //used global variable start values declaration :
245
246
    $record_item_body = false;
247
    $non_HTML_tag_to_avoid = array(
248
        "SIMPLECHOICE",
249
        "CHOICEINTERACTION",
250
        "INLINECHOICEINTERACTION",
251
        "INLINECHOICE",
252
        "SIMPLEMATCHSET",
253
        "SIMPLEASSOCIABLECHOICE",
254
        "TEXTENTRYINTERACTION",
255
        "FEEDBACKINLINE",
256
        "MATCHINTERACTION",
257
        "ITEMBODY",
258
        "BR",
259
        "IMG"
260
    );
261
262
    $question_format_supported = true;
263
264
    $xml_parser = xml_parser_create();
265
    xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, false);
266
    xml_set_element_handler($xml_parser, 'startElement', 'endElement');
267
    xml_set_character_data_handler($xml_parser, 'elementData');
268
    if (!xml_parse($xml_parser, $data, feof($fp))) {
269
        // if reading of the xml file in not successful :
270
        // set errorFound, set error msg, break while statement
271
        Display:: display_error_message(get_lang('Error reading XML file'));
272
        return false;
273
    }
274
275
    //close file
276
    fclose($fp);
277
    if (!$question_format_supported) {
278
        Display:: display_error_message(
279
            get_lang(
280
                'Unknown question format in file %file',
281
                array(
282
                '%file' => $questionFile
283
                )
284
            )
285
        );
286
        return false;
287
    }
288
    return true;
289
}
290
291
/**
292
 * Function used by the SAX xml parser when the parser meets a opening tag
293
 *
294
 * @param object $parser xml parser created with "xml_parser_create()"
295
 * @param string $name name of the element
296
 * @param array $attributes
297
 */
298
function startElement($parser, $name, $attributes)
299
{
300
    global $element_pile;
301
    global $exercise_info;
302
    global $current_question_ident;
303
    global $current_answer_id;
304
    global $current_match_set;
305
    global $currentAssociableChoice;
306
    global $current_question_item_body;
307
    global $record_item_body;
308
    global $non_HTML_tag_to_avoid;
309
    global $current_inlinechoice_id;
310
    global $cardinality;
311
    global $questionTempDir;
312
313
    array_push($element_pile, $name);
314
    $current_element = end($element_pile);
315 View Code Duplication
    if (sizeof($element_pile) >= 2) {
316
        $parent_element = $element_pile[sizeof($element_pile) - 2];
317
    } else {
318
        $parent_element = "";
319
    }
320 View Code Duplication
    if (sizeof($element_pile) >= 3) {
321
        $grant_parent_element = $element_pile[sizeof($element_pile) - 3];
322
    } else {
323
        $grant_parent_element = "";
324
    }
325
326
    if ($record_item_body) {
327
328
        if ((!in_array($current_element, $non_HTML_tag_to_avoid))) {
329
            $current_question_item_body .= "<" . $name;
330
331
            foreach ($attributes as $attribute_name => $attribute_value) {
332
                $current_question_item_body .= " " . $attribute_name . "=\"" . $attribute_value . "\"";
333
            }
334
            $current_question_item_body .= ">";
335
        } else {
336
            //in case of FIB question, we replace the IMS-QTI tag b y the correct answer between "[" "]",
337
            //we first save with claroline tags ,then when the answer will be parsed, the claroline tags will be replaced
338
339
            if ($current_element == 'INLINECHOICEINTERACTION') {
340
341
                $current_question_item_body .= "**claroline_start**" . $attributes['RESPONSEIDENTIFIER'] . "**claroline_end**";
342
            }
343
            if ($current_element == 'TEXTENTRYINTERACTION') {
344
                $correct_answer_value = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id];
345
                $current_question_item_body .= "[" . $correct_answer_value . "]";
346
347
            }
348
            if ($current_element == 'BR') {
349
                $current_question_item_body .= "<br />";
350
            }
351
        }
352
    }
353
    switch ($current_element) {
354
        case 'ASSESSMENTITEM':
355
            //retrieve current question
356
            $current_question_ident = $attributes['IDENTIFIER'];
357
            $exercise_info['question'][$current_question_ident] = array();
358
            $exercise_info['question'][$current_question_ident]['answer'] = array();
359
            $exercise_info['question'][$current_question_ident]['correct_answers'] = array();
360
            $exercise_info['question'][$current_question_ident]['title'] = $attributes['TITLE'];
361
            $exercise_info['question'][$current_question_ident]['tempdir'] = $questionTempDir;
362
            break;
363
        case 'SECTION':
364
            //retrieve exercise name
365
            $exercise_info['name'] = $attributes['TITLE'];
366
            break;
367
        case 'RESPONSEDECLARATION':
368
            // Retrieve question type
369 View Code Duplication
            if ("multiple" == $attributes['CARDINALITY']) {
370
                $exercise_info['question'][$current_question_ident]['type'] = 'MCMA';
371
                $cardinality = 'multiple';
372
            }
373 View Code Duplication
            if ("single" == $attributes['CARDINALITY']) {
374
                $exercise_info['question'][$current_question_ident]['type'] = 'MCUA';
375
                $cardinality = 'single';
376
            }
377
            //needed for FIB
378
            $current_answer_id = $attributes['IDENTIFIER'];
379
            break;
380
        case 'INLINECHOICEINTERACTION':
381
            $exercise_info['question'][$current_question_ident]['type'] = 'FIB';
382
            $exercise_info['question'][$current_question_ident]['subtype'] = 'LISTBOX_FILL';
383
            $current_answer_id = $attributes['RESPONSEIDENTIFIER'];
384
            break;
385
        case 'INLINECHOICE':
386
            $current_inlinechoice_id = $attributes['IDENTIFIER'];
387
            break;
388
        case 'TEXTENTRYINTERACTION':
389
            $exercise_info['question'][$current_question_ident]['type'] = 'FIB';
390
            $exercise_info['question'][$current_question_ident]['subtype'] = 'TEXTFIELD_FILL';
391
            $exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body;
392
            //replace claroline tags
393
            break;
394
        case 'MATCHINTERACTION':
395
            $exercise_info['question'][$current_question_ident]['type'] = 'MATCHING';
396
            break;
397
        case 'SIMPLEMATCHSET':
398
            if (!isset ($current_match_set)) {
399
                $current_match_set = 1;
400
            } else {
401
                $current_match_set++;
402
            }
403
            $exercise_info['question'][$current_question_ident]['answer'][$current_match_set] = array();
404
            break;
405
        case 'SIMPLEASSOCIABLECHOICE':
406
            $currentAssociableChoice = $attributes['IDENTIFIER'];
407
            break;
408
        //retrieve answers id for MCUA and MCMA questions
409
        case 'SIMPLECHOICE':
410
            $current_answer_id = $attributes['IDENTIFIER'];
411
            if (!isset($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id])) {
412
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id] = array();
413
            }
414
            break;
415
        case 'MAPENTRY':
416
            if ($parent_element == "MAPPING") {
417
                $answer_id = $attributes['MAPKEY'];
418
419 View Code Duplication
                if (!isset ($exercise_info['question'][$current_question_ident]['weighting'])) {
420
                    $exercise_info['question'][$current_question_ident]['weighting'] = array();
421
                }
422
                $exercise_info['question'][$current_question_ident]['weighting'][$answer_id] = $attributes['MAPPEDVALUE'];
423
            }
424
            break;
425
        case 'MAPPING':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
426
            if (isset ($attributes['DEFAULTVALUE'])) {
427
                $exercise_info['question'][$current_question_ident]['default_weighting'] = $attributes['DEFAULTVALUE'];
428
            }
429
        case 'ITEMBODY':
430
            $record_item_body = true;
431
            $current_question_item_body = '';
432
            break;
433
        case 'IMG':
434
            $exercise_info['question'][$current_question_ident]['attached_file_url'] = $attributes['SRC'];
435
            break;
436
    }
437
}
438
439
/**
440
 * Function used by the SAX xml parser when the parser meets a closing tag
441
 *
442
 * @param $parser xml parser created with "xml_parser_create()"
443
 * @param $name name of the element
444
 */
445
function endElement($parser, $name)
446
{
447
    global $element_pile;
448
    global $exercise_info;
449
    global $current_question_ident;
450
    global $record_item_body;
451
    global $current_question_item_body;
452
    global $non_HTML_tag_to_avoid;
453
    global $cardinality;
454
455
    $current_element = end($element_pile);
456
457
    //treat the record of the full content of itembody tag :
458
459
    if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) {
460
        $current_question_item_body .= "</" . $name . ">";
461
    }
462
463
    switch ($name) {
464
        case 'ITEMBODY':
465
            $record_item_body = false;
466
            if ($exercise_info['question'][$current_question_ident]['type'] == 'FIB') {
467
                $exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body;
468
            } else {
469
                $exercise_info['question'][$current_question_ident]['statement'] = $current_question_item_body;
470
            }
471
            break;
472
    }
473
    array_pop($element_pile);
474
475
}
476
477
/**
478
 * @param $parser
479
 * @param $data
480
 */
481
function elementData($parser, $data)
482
{
483
    global $element_pile;
484
    global $exercise_info;
485
    global $current_question_ident;
486
    global $current_answer_id;
487
    global $current_match_set;
488
    global $currentAssociableChoice;
489
    global $current_question_item_body;
490
    global $record_item_body;
491
    global $non_HTML_tag_to_avoid;
492
    global $current_inlinechoice_id;
493
    global $cardinality;
494
495
    $current_element = end($element_pile);
496 View Code Duplication
    if (sizeof($element_pile) >= 2) {
497
        $parent_element = $element_pile[sizeof($element_pile) - 2];
498
    } else {
499
        $parent_element = "";
500
    }
501 View Code Duplication
    if (sizeof($element_pile) >= 3) {
502
        $grant_parent_element = $element_pile[sizeof($element_pile) - 3];
503
    } else {
504
        $grant_parent_element = "";
505
    }
506
507
    //treat the record of the full content of itembody tag (needed for question statment and/or FIB text:
508
509
    if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) {
510
        $current_question_item_body .= $data;
511
    }
512
513
    switch ($current_element) {
514 View Code Duplication
        case 'SIMPLECHOICE':
515
            if (!isset ($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'])) {
516
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] = trim($data);
517
            } else {
518
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] .= '' . trim($data);
519
            }
520
            break;
521 View Code Duplication
        case 'FEEDBACKINLINE':
522
            if (!isset ($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'])) {
523
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] = trim($data);
524
            } else {
525
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] .= ' ' . trim($data);
526
            }
527
            break;
528
        case 'SIMPLEASSOCIABLECHOICE':
529
            $exercise_info['question'][$current_question_ident]['answer'][$current_match_set][$currentAssociableChoice] = trim($data);
530
            break;
531
        case 'VALUE':
532
            if ($parent_element == "CORRECTRESPONSE") {
533
                if ($cardinality == "single") {
534
                    $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id] = $data;
535
                } else {
536
                    $exercise_info['question'][$current_question_ident]['correct_answers'][] = $data;
537
                }
538
            }
539
            break;
540
541
        case 'ITEMBODY':
542
            $current_question_item_body .= $data;
543
            break;
544
        case 'INLINECHOICE':
545
            // if this is the right answer, then we must replace the claroline tags in the FIB text bye the answer between "[" and "]" :
546
            $answer_identifier = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id];
547
            if ($current_inlinechoice_id == $answer_identifier) {
548
                $current_question_item_body = str_replace(
549
                    "**claroline_start**" . $current_answer_id . "**claroline_end**",
550
                    "[" . $data . "]",
551
                    $current_question_item_body
552
                );
553
            } else {
554 View Code Duplication
                if (!isset ($exercise_info['question'][$current_question_ident]['wrong_answers'])) {
555
                    $exercise_info['question'][$current_question_ident]['wrong_answers'] = array();
556
                }
557
                $exercise_info['question'][$current_question_ident]['wrong_answers'][] = $data;
558
            }
559
            break;
560
    }
561
}
562