Completed
Push — 1.10.x ( 71e5e5...da67d2 )
by Yannick
43:55
created

exercise_import.inc.php ➔ elementDataQti1()   D

Complexity

Conditions 18
Paths 108

Size

Total Lines 87
Code Lines 62

Duplication

Lines 5
Ratio 5.75 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 18
eloc 62
c 1
b 0
f 1
nc 108
nop 2
dl 5
loc 87
rs 4.6748

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/exercice/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
 * @param string $baseWorkDir
34
 * @param string $uploadPath
35
 * @return bool
36
 */
37
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/exercice/export/aiken/aiken_import.inc.php (L62-81) 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...
38
{
39
    $_course = api_get_course_info();
40
    $_user = api_get_user_info();
41
42
    //Check if the file is valid (not to big and exists)
43 View Code Duplication
    if (!isset($_FILES['userFile']) || !is_uploaded_file($_FILES['userFile']['tmp_name'])) {
44
        // upload failed
45
        return false;
46
    }
47
48
    if (preg_match('/.zip$/i', $_FILES['userFile']['name']) &&
49
        handle_uploaded_document(
50
            $_course,
51
            $_FILES['userFile'],
52
            $baseWorkDir,
53
            $uploadPath,
54
            $_user['user_id'],
55
            0,
56
            null,
57
            1
58
        )
59
    ) {
60
        return true;
61
    }
62
    return false;
63
}
64
65
/**
66
 * Imports an exercise in QTI format if the XML structure can be found in it
67
 * @param array $file
68
 * @return string|array as a backlog of what was really imported, and error or debug messages to display
69
 */
70
function import_exercise($file)
71
{
72
    global $exercise_info;
73
    global $element_pile;
74
    global $non_HTML_tag_to_avoid;
75
    global $record_item_body;
76
    // used to specify the question directory where files could be found in relation in any question
77
    global $questionTempDir;
78
79
    $archive_path = api_get_path(SYS_ARCHIVE_PATH) . 'qti2';
80
    $baseWorkDir = $archive_path;
81
82
    if (!is_dir($baseWorkDir)) {
83
        mkdir($baseWorkDir, api_get_permissions_for_new_directories(), true);
84
    }
85
86
    $uploadPath = '/';
87
88
    // set some default values for the new exercise
89
    $exercise_info = array();
90
    $exercise_info['name'] = preg_replace('/.zip$/i', '', $file);
91
    $exercise_info['question'] = array();
92
    $element_pile = array();
93
94
    // create parser and array to retrieve info from manifest
95
    $element_pile = array(); //pile to known the depth in which we are
96
    //$module_info = array (); //array to store the info we need
97
98
    // if file is not a .zip, then we cancel all
99
100
    if (!preg_match('/.zip$/i', $file)) {
101
102
        return 'UplZipCorrupt';
103
    }
104
105
    // unzip the uploaded file in a tmp directory
106
    if (!get_and_unzip_uploaded_exercise($baseWorkDir, $uploadPath)) {
107
108
        return 'UplZipCorrupt';
109
    }
110
111
    // find the different manifests for each question and parse them.
112
113
    $exerciseHandle = opendir($baseWorkDir);
114
    //$question_number = 0;
115
    $file_found = false;
116
    $operation = false;
117
    $result = false;
118
    $filePath = null;
119
120
    // parse every subdirectory to search xml question files
121
    while (false !== ($file = readdir($exerciseHandle))) {
122
        if (is_dir($baseWorkDir . '/' . $file) && $file != "." && $file != "..") {
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 = qti_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 = qti_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
148
        return false;
149
    }
150
151
    $doc = new DOMDocument();
152
    $doc->load($filePath);
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
            if (strlen($question_array['title']) < 50) {
168
                $question->updateTitle(formatText(strip_tags($question_array['title'])) . '...');
169
            } else {
170
                $question->updateTitle(formatText(substr(strip_tags($question_array['title']), 0, 50)));
171
                $question->updateDescription($question_array['title']);
172
            }
173
            //$question->updateDescription($question_array['title']);
174
            $question->save($last_exercise_id);
175
            $last_question_id = $question->selectId();
176
            //3. Create answer
177
            $answer = new Answer($last_question_id);
178
            $answer->new_nbrAnswers = count($question_array['answer']);
179
            $totalCorrectWeight = 0;
180
            $j = 1;
181
            $matchAnswerIds = array();
182
            foreach ($question_array['answer'] as $key => $answers) {
183
                if (preg_match('/_/', $key)) {
184
                    $split = explode('_', $key);
185
                    $i = $split[1];
186
                } else {
187
                    $i = $j;
188
                    $j++;
189
                    $matchAnswerIds[$key] = $j;
190
                }
191
                // Answer
192
                $answer->new_answer[$i] = formatText($answers['value']);
193
                // Comment
194
                $answer->new_comment[$i] = isset($answers['feedback']) ? formatText($answers['feedback']) : null;
195
                // Position
196
                $answer->new_position[$i] = $i;
197
                // Correct answers
198
                if (in_array($key, $question_array['correct_answers'])) {
199
                    $answer->new_correct[$i] = 1;
200
                } else {
201
                    $answer->new_correct[$i] = 0;
202
                }
203
                $answer->new_weighting[$i] = $question_array['weighting'][$key];
204
                if ($answer->new_correct[$i]) {
205
                    $totalCorrectWeight += $answer->new_weighting[$i];
206
                }
207
            }
208
            $question->updateWeighting($totalCorrectWeight);
209
            $question->save($last_exercise_id);
210
            $answer->save();
211
        }
212
213
        // delete the temp dir where the exercise was unzipped
214
        my_delete($baseWorkDir . $uploadPath);
215
        return $last_exercise_id;
216
    }
217
218
    return false;
219
}
220
221
/**
222
 * We assume the file charset is UTF8
223
 **/
224
function formatText($text)
225
{
226
    return api_html_entity_decode($text);
227
}
228
229
/**
230
 * Parses a given XML file and fills global arrays with the elements
231
 * @param string $exercisePath
232
 * @param string $file
233
 * @param string $questionFile
234
 * @return bool
235
 */
236
function qti_parse_file($exercisePath, $file, $questionFile)
237
{
238
    global $non_HTML_tag_to_avoid;
239
    global $record_item_body;
240
    global $questionTempDir;
241
242
    $questionTempDir = $exercisePath . '/' . $file . '/';
243
    $questionFilePath = $questionTempDir . $questionFile;
244
245
    if (!($fp = fopen($questionFilePath, 'r'))) {
246
        Display::addFlash(Display::return_message(get_lang('Error opening question\'s XML file'), 'error'));
247
248
        return false;
249
    } else {
250
        $data = fread($fp, filesize($questionFilePath));
251
    }
252
253
    //parse XML question file
254
    $data = str_replace(array('<p>', '</p>', '<front>', '</front>'), '', $data);
255
    $qtiVersion = array();
256
    $match = preg_match('/ims_qtiasiv(\d)p(\d)/', $data, $qtiVersion);
257
    $qtiMainVersion = 2; //by default, assume QTI version 2
258
    if ($match) {
259
        $qtiMainVersion = $qtiVersion[1];
260
    }
261
262
    //used global variable start values declaration :
263
264
    $record_item_body = false;
265
    $non_HTML_tag_to_avoid = array(
266
        "SIMPLECHOICE",
267
        "CHOICEINTERACTION",
268
        "INLINECHOICEINTERACTION",
269
        "INLINECHOICE",
270
        "SIMPLEMATCHSET",
271
        "SIMPLEASSOCIABLECHOICE",
272
        "TEXTENTRYINTERACTION",
273
        "FEEDBACKINLINE",
274
        "MATCHINTERACTION",
275
        "ITEMBODY",
276
        "BR",
277
        "IMG"
278
    );
279
280
    $question_format_supported = true;
281
282
    $xml_parser = xml_parser_create();
283
    xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, false);
284
    if ($qtiMainVersion == 1) {
285
        xml_set_element_handler($xml_parser, 'startElementQti1', 'endElementQti1');
286
        xml_set_character_data_handler($xml_parser, 'elementDataQti1');
287
    } else {
288
        xml_set_element_handler($xml_parser, 'startElementQti2', 'endElementQti2');
289
        xml_set_character_data_handler($xml_parser, 'elementDataQti2');
290
    }
291
    if (!xml_parse($xml_parser, $data, feof($fp))) {
292
        // if reading of the xml file in not successful :
293
        // set errorFound, set error msg, break while statement
294
        Display:: display_error_message(get_lang('Error reading XML file'));
295
        return false;
296
    }
297
298
    //close file
299
    fclose($fp);
300
    if (!$question_format_supported) {
301
        Display::addFlash(
302
            Display::return_message(
303
                get_lang(
304
                    'Unknown question format in file %file',
305
                    array(
306
                        '%file' => $questionFile,
307
                    )
308
                ),
309
                'error'
310
            )
311
        );
312
        return false;
313
    }
314
    return true;
315
}
316
317
/**
318
 * Function used by the SAX xml parser when the parser meets a opening tag
319
 *
320
 * @param object $parser xml parser created with "xml_parser_create()"
321
 * @param string $name name of the element
322
 * @param array $attributes
323
 */
324
function startElementQti2($parser, $name, $attributes)
325
{
326
    global $element_pile;
327
    global $exercise_info;
328
    global $current_question_ident;
329
    global $current_answer_id;
330
    global $current_match_set;
331
    global $currentAssociableChoice;
332
    global $current_question_item_body;
333
    global $record_item_body;
334
    global $non_HTML_tag_to_avoid;
335
    global $current_inlinechoice_id;
336
    global $cardinality;
337
    global $questionTempDir;
338
339
    array_push($element_pile, $name);
340
    $current_element = end($element_pile);
341 View Code Duplication
    if (sizeof($element_pile) >= 2) {
342
        $parent_element = $element_pile[sizeof($element_pile) - 2];
343
    } else {
344
        $parent_element = "";
345
    }
346 View Code Duplication
    if (sizeof($element_pile) >= 3) {
347
        $grant_parent_element = $element_pile[sizeof($element_pile) - 3];
348
    } else {
349
        $grant_parent_element = "";
350
    }
351
352 View Code Duplication
    if ($record_item_body) {
353
354
        if ((!in_array($current_element, $non_HTML_tag_to_avoid))) {
355
            $current_question_item_body .= "<" . $name;
356
            foreach ($attributes as $attribute_name => $attribute_value) {
357
                $current_question_item_body .= " " . $attribute_name . "=\"" . $attribute_value . "\"";
358
            }
359
            $current_question_item_body .= ">";
360
        } else {
361
            //in case of FIB question, we replace the IMS-QTI tag b y the correct answer between "[" "]",
362
            //we first save with claroline tags ,then when the answer will be parsed, the claroline tags will be replaced
363
364
            if ($current_element == 'INLINECHOICEINTERACTION') {
365
                $current_question_item_body .= "**claroline_start**" . $attributes['RESPONSEIDENTIFIER'] . "**claroline_end**";
366
            }
367
            if ($current_element == 'TEXTENTRYINTERACTION') {
368
                $correct_answer_value = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id];
369
                $current_question_item_body .= "[" . $correct_answer_value . "]";
370
371
            }
372
            if ($current_element == 'BR') {
373
                $current_question_item_body .= "<br />";
374
            }
375
        }
376
    }
377
378
    switch ($current_element) {
379
        case 'ASSESSMENTITEM':
380
            //retrieve current question
381
            $current_question_ident = $attributes['IDENTIFIER'];
382
            $exercise_info['question'][$current_question_ident] = array();
383
            $exercise_info['question'][$current_question_ident]['answer'] = array();
384
            $exercise_info['question'][$current_question_ident]['correct_answers'] = array();
385
            $exercise_info['question'][$current_question_ident]['title'] = $attributes['TITLE'];
386
            $exercise_info['question'][$current_question_ident]['tempdir'] = $questionTempDir;
387
            break;
388
        case 'SECTION':
389
            //retrieve exercise name
390
            if (isset($attributes['TITLE']) && !empty($attributes['TITLE'])) {
391
                $exercise_info['name'] = $attributes['TITLE'];
392
            }
393
            break;
394
        case 'RESPONSEDECLARATION':
395
            // Retrieve question type
396 View Code Duplication
            if ("multiple" == $attributes['CARDINALITY']) {
397
                $exercise_info['question'][$current_question_ident]['type'] = MCMA;
398
                $cardinality = 'multiple';
399
            }
400 View Code Duplication
            if ("single" == $attributes['CARDINALITY']) {
401
                $exercise_info['question'][$current_question_ident]['type'] = MCUA;
402
                $cardinality = 'single';
403
            }
404
            //needed for FIB
405
            $current_answer_id = $attributes['IDENTIFIER'];
406
            break;
407
        case 'INLINECHOICEINTERACTION':
408
            $exercise_info['question'][$current_question_ident]['type'] = FIB;
409
            $exercise_info['question'][$current_question_ident]['subtype'] = 'LISTBOX_FILL';
410
            $current_answer_id = $attributes['RESPONSEIDENTIFIER'];
411
            break;
412
        case 'INLINECHOICE':
413
            $current_inlinechoice_id = $attributes['IDENTIFIER'];
414
            break;
415
        case 'TEXTENTRYINTERACTION':
416
            $exercise_info['question'][$current_question_ident]['type'] = FIB;
417
            $exercise_info['question'][$current_question_ident]['subtype'] = 'TEXTFIELD_FILL';
418
            $exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body;
419
            //replace claroline tags
420
            break;
421
        case 'MATCHINTERACTION':
422
            $exercise_info['question'][$current_question_ident]['type'] = MATCHING;
423
            break;
424
        case 'SIMPLEMATCHSET':
425
            if (!isset($current_match_set)) {
426
                $current_match_set = 1;
427
            } else {
428
                $current_match_set++;
429
            }
430
            $exercise_info['question'][$current_question_ident]['answer'][$current_match_set] = array();
431
            break;
432
        case 'SIMPLEASSOCIABLECHOICE':
433
            $currentAssociableChoice = $attributes['IDENTIFIER'];
434
            break;
435
        //retrieve answers id for MCUA and MCMA questions
436
        case 'SIMPLECHOICE':
437
            $current_answer_id = $attributes['IDENTIFIER'];
438 View Code Duplication
            if (!isset($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id])) {
439
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id] = array();
440
            }
441
            break;
442
        case 'MAPENTRY':
443
            if ($parent_element == "MAPPING") {
444
                $answer_id = $attributes['MAPKEY'];
445 View Code Duplication
                if (!isset ($exercise_info['question'][$current_question_ident]['weighting'])) {
446
                    $exercise_info['question'][$current_question_ident]['weighting'] = array();
447
                }
448
                $exercise_info['question'][$current_question_ident]['weighting'][$answer_id] = $attributes['MAPPEDVALUE'];
449
            }
450
            break;
451
        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...
452
            if (isset ($attributes['DEFAULTVALUE'])) {
453
                $exercise_info['question'][$current_question_ident]['default_weighting'] = $attributes['DEFAULTVALUE'];
454
            }
455
        case 'ITEMBODY':
456
            $record_item_body = true;
457
            $current_question_item_body = '';
458
            break;
459
        case 'IMG':
460
            $exercise_info['question'][$current_question_ident]['attached_file_url'] = $attributes['SRC'];
461
            break;
462
    }
463
}
464
465
/**
466
 * Function used by the SAX xml parser when the parser meets a closing tag
467
 *
468
 * @param $parser xml parser created with "xml_parser_create()"
469
 * @param $name name of the element
470
 */
471
function endElementQti2($parser, $name)
472
{
473
    global $element_pile;
474
    global $exercise_info;
475
    global $current_question_ident;
476
    global $record_item_body;
477
    global $current_question_item_body;
478
    global $non_HTML_tag_to_avoid;
479
    global $cardinality;
480
481
    $current_element = end($element_pile);
482
483
    //treat the record of the full content of itembody tag :
484
485
    if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) {
486
        $current_question_item_body .= "</" . $name . ">";
487
    }
488
489
    switch ($name) {
490
        case 'ITEMBODY':
491
            $record_item_body = false;
492
            if ($exercise_info['question'][$current_question_ident]['type'] == FIB) {
493
                $exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body;
494
            } else {
495
                $exercise_info['question'][$current_question_ident]['statement'] = $current_question_item_body;
496
            }
497
            break;
498
    }
499
    array_pop($element_pile);
500
}
501
502
/**
503
 * @param $parser
504
 * @param $data
505
 */
506
function elementDataQti2($parser, $data)
507
{
508
    global $element_pile;
509
    global $exercise_info;
510
    global $current_question_ident;
511
    global $current_answer_id;
512
    global $current_match_set;
513
    global $currentAssociableChoice;
514
    global $current_question_item_body;
515
    global $record_item_body;
516
    global $non_HTML_tag_to_avoid;
517
    global $current_inlinechoice_id;
518
    global $cardinality;
519
520
    $current_element = end($element_pile);
521 View Code Duplication
    if (sizeof($element_pile) >= 2) {
522
        $parent_element = $element_pile[sizeof($element_pile) - 2];
523
    } else {
524
        $parent_element = "";
525
    }
526 View Code Duplication
    if (sizeof($element_pile) >= 3) {
527
        $grant_parent_element = $element_pile[sizeof($element_pile) - 3];
528
    } else {
529
        $grant_parent_element = "";
530
    }
531
532
    //treat the record of the full content of itembody tag (needed for question statment and/or FIB text:
533
534
    if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) {
535
        $current_question_item_body .= $data;
536
    }
537
538
    switch ($current_element) {
539 View Code Duplication
        case 'SIMPLECHOICE':
540
            if (!isset ($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'])) {
541
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] = trim($data);
542
            } else {
543
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] .= '' . trim($data);
544
            }
545
            break;
546 View Code Duplication
        case 'FEEDBACKINLINE':
547
            if (!isset ($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'])) {
548
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] = trim($data);
549
            } else {
550
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] .= ' ' . trim($data);
551
            }
552
            break;
553
        case 'SIMPLEASSOCIABLECHOICE':
554
            $exercise_info['question'][$current_question_ident]['answer'][$current_match_set][$currentAssociableChoice] = trim($data);
555
            break;
556
        case 'VALUE':
557
            if ($parent_element == "CORRECTRESPONSE") {
558
                if ($cardinality == "single") {
559
                    $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id] = $data;
560
                } else {
561
                    $exercise_info['question'][$current_question_ident]['correct_answers'][] = $data;
562
                }
563
            }
564
            break;
565
        case 'ITEMBODY':
566
            $current_question_item_body .= $data;
567
            break;
568
        case 'INLINECHOICE':
569
            // if this is the right answer, then we must replace the claroline tags in the FIB text bye the answer between "[" and "]" :
570
            $answer_identifier = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id];
571
            if ($current_inlinechoice_id == $answer_identifier) {
572
                $current_question_item_body = str_replace(
573
                    "**claroline_start**" . $current_answer_id . "**claroline_end**",
574
                    "[" . $data . "]",
575
                    $current_question_item_body
576
                );
577
            } else {
578 View Code Duplication
                if (!isset ($exercise_info['question'][$current_question_ident]['wrong_answers'])) {
579
                    $exercise_info['question'][$current_question_ident]['wrong_answers'] = array();
580
                }
581
                $exercise_info['question'][$current_question_ident]['wrong_answers'][] = $data;
582
            }
583
            break;
584
    }
585
}
586
587
/**
588
 * Function used by the SAX xml parser when the parser meets a opening tag for QTI1
589
 *
590
 * @param object $parser xml parser created with "xml_parser_create()"
591
 * @param string $name name of the element
592
 * @param array $attributes
593
 */
594
function startElementQti1($parser, $name, $attributes)
595
{
596
    global $element_pile;
597
    global $exercise_info;
598
    global $current_question_ident;
599
    global $current_answer_id;
600
    global $current_match_set;
601
    global $currentAssociableChoice;
602
    global $current_question_item_body;
603
    global $record_item_body;
604
    global $non_HTML_tag_to_avoid;
605
    global $current_inlinechoice_id;
606
    global $cardinality;
607
    global $questionTempDir;
608
    global $lastLabelFieldName;
609
    global $lastLabelFieldValue;
610
611
    array_push($element_pile, $name);
612
    $current_element = end($element_pile);
613 View Code Duplication
    if (sizeof($element_pile) >= 2) {
614
        $parent_element = $element_pile[sizeof($element_pile) - 2];
615
    } else {
616
        $parent_element = "";
617
    }
618 View Code Duplication
    if (sizeof($element_pile) >= 3) {
619
        $grand_parent_element = $element_pile[sizeof($element_pile) - 3];
620
    } else {
621
        $grand_parent_element = "";
622
    }
623 View Code Duplication
    if (sizeof($element_pile) >= 4) {
624
        $great_grand_parent_element = $element_pile[sizeof($element_pile) - 4];
625
    } else {
626
        $great_grand_parent_element = "";
627
    }
628
629
630 View Code Duplication
    if ($record_item_body) {
631
632
        if ((!in_array($current_element, $non_HTML_tag_to_avoid))) {
633
            $current_question_item_body .= "<" . $name;
634
            foreach ($attributes as $attribute_name => $attribute_value) {
635
                $current_question_item_body .= " " . $attribute_name . "=\"" . $attribute_value . "\"";
636
            }
637
            $current_question_item_body .= ">";
638
        } else {
639
            //in case of FIB question, we replace the IMS-QTI tag b y the correct answer between "[" "]",
640
            //we first save with claroline tags ,then when the answer will be parsed, the claroline tags will be replaced
641
642
            if ($current_element == 'INLINECHOICEINTERACTION') {
643
                $current_question_item_body .= "**claroline_start**" . $attributes['RESPONSEIDENTIFIER'] . "**claroline_end**";
644
            }
645
            if ($current_element == 'TEXTENTRYINTERACTION') {
646
                $correct_answer_value = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id];
647
                $current_question_item_body .= "[" . $correct_answer_value . "]";
648
649
            }
650
            if ($current_element == 'BR') {
651
                $current_question_item_body .= "<br />";
652
            }
653
        }
654
    }
655
656
    switch ($current_element) {
657
        case 'ASSESSMENT':
658
            // This is the assessment element: we don't care, we just want questions
659
            if (!empty($attributes['TITLE'])) {
660
                $exercise_info['name'] = $attributes['TITLE'];
661
            }
662
            break;
663
        case 'ITEM':
664
            //retrieve current question
665
            $current_question_ident = $attributes['IDENT'];
666
            $exercise_info['question'][$current_question_ident] = array();
667
            $exercise_info['question'][$current_question_ident]['answer'] = array();
668
            $exercise_info['question'][$current_question_ident]['correct_answers'] = array();
669
            //$exercise_info['question'][$current_question_ident]['title'] = $attributes['TITLE'];
670
            $exercise_info['question'][$current_question_ident]['tempdir'] = $questionTempDir;
671
            break;
672
        case 'SECTION':
673
            //retrieve exercise name
674
            //if (isset($attributes['TITLE']) && !empty($attributes['TITLE'])) {
675
            //    $exercise_info['name'] = $attributes['TITLE'];
676
            //}
677
            break;
678
        case 'RESPONSE_LID':
679
            // Retrieve question type
680
            if ("multiple" == strtolower($attributes['RCARDINALITY'])) {
681
                $cardinality = 'multiple';
682
            }
683
            if ("single" == strtolower($attributes['RCARDINALITY'])) {
684
                $cardinality = 'single';
685
            }
686
            //needed for FIB
687
            $current_answer_id = $attributes['IDENT'];
688
            $current_question_item_body = '';
689
            break;
690
        case 'RENDER_CHOICE';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

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

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

Loading history...
691
            break;
692
        case 'RESPONSE_LABEL':
693 View Code Duplication
            if (!empty($attributes['IDENT'])) {
694
                $current_answer_id = $attributes['IDENT'];
695
                //set the placeholder for the answer to come (in endElementQti1)
696
                $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id] = '';
697
            }
698
            break;
699
        case 'DECVAR':
700
            if ($parent_element == 'OUTCOMES' && $grand_parent_element == 'RESPROCESSING') {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
701
                // The following attributes are available
702
                //$attributes['VARTYPE'];
703
                //$attributes['DEFAULTVAL'];
704
                //$attributes['MINVALUE'];
705
                //$attributes['MAXVALUE'];
706
            }
707
            break;
708
        case 'VAREQUAL':
709
            if ($parent_element == 'CONDITIONVAR' && $grand_parent_element == 'RESPCONDITION') {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
710
                // The following attributes are available
711
                //$attributes['RESPIDENT']
712
            }
713
            break;
714
        case 'SETVAR':
715
            if ($parent_element == 'RESPCONDITION') {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
716
                // The following attributes are available
717
                //$attributes['ACTION']
718
            }
719
            break;
720
        case 'IMG':
721
            //$exercise_info['question'][$current_question_ident]['attached_file_url'] = $attributes['SRC'];
722
            break;
723
        case 'MATTEXT':
724
            if ($parent_element == 'MATERIAL') {
725
                if ($grand_parent_element == 'PRESENTATION') {
726
                    $exercise_info['question'][$current_question_ident]['title'] = $current_question_item_body;
727
                }
728
            }
729
    }
730
}
731
732
/**
733
 * Function used by the SAX xml parser when the parser meets a closing tag for QTI1
734
 *
735
 * @param object $parser xml parser created with "xml_parser_create()"
736
 * @param string $name name of the element
737
 * @param array $attributes The element attributes
738
 */
739
function endElementQti1($parser, $name, $attributes)
740
{
741
    global $element_pile;
742
    global $exercise_info;
743
    global $current_question_ident;
744
    global $record_item_body;
745
    global $current_question_item_body;
746
    global $non_HTML_tag_to_avoid;
747
    global $cardinality;
748
    global $lastLabelFieldName;
749
    global $lastLabelFieldValue;
750
751
    $current_element = end($element_pile);
752 View Code Duplication
    if (sizeof($element_pile) >= 2) {
753
        $parent_element = $element_pile[sizeof($element_pile) - 2];
754
    } else {
755
        $parent_element = "";
756
    }
757 View Code Duplication
    if (sizeof($element_pile) >= 3) {
758
        $grand_parent_element = $element_pile[sizeof($element_pile) - 3];
759
    } else {
760
        $grand_parent_element = "";
761
    }
762 View Code Duplication
    if (sizeof($element_pile) >= 4) {
763
        $great_grand_parent_element = $element_pile[sizeof($element_pile) - 4];
764
    } else {
765
        $great_grand_parent_element = "";
766
    }
767
768
    //treat the record of the full content of itembody tag :
769
770
    if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) {
771
        $current_question_item_body .= "</" . $name . ">";
772
    }
773
774
    switch ($name) {
775
        case 'MATTEXT':
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...
776
            if ($parent_element == 'MATERIAL') {
777
                if ($grand_parent_element == 'PRESENTATION') {
778
                    $exercise_info['question'][$current_question_ident]['title'] = $current_question_item_body;
779
                    $current_question_item_body = '';
780
                } elseif ($grand_parent_element == 'RESPONSE_LABEL') {
781
                    $last = '';
782
                    foreach ($exercise_info['question'][$current_question_ident]['answer'] as $key => $value) {
783
                        $last = $key;
784
                    }
785
                    $exercise_info['question'][$current_question_ident]['answer'][$last]['value'] = $current_question_item_body;
786
                    $current_question_item_body = '';
787
                }
788
            }
789
        case 'RESPONSE_LID':
790
            // Retrieve question type
791
            if (!isset($exercise_info['question'][$current_question_ident]['type'])) {
792 View Code Duplication
                if ("multiple" == strtolower($attributes['RCARDINALITY'])) {
793
                    $exercise_info['question'][$current_question_ident]['type'] = MCMA;
794
                }
795 View Code Duplication
                if ("single" == strtolower($attributes['RCARDINALITY'])) {
796
                    $exercise_info['question'][$current_question_ident]['type'] = MCUA;
797
                }
798
            }
799
            $current_question_item_body = '';
800
            //needed for FIB
801
            $current_answer_id = $attributes['IDENT'];
802
            break;
803
        case 'ITEMMETADATA':
804
            $current_question_item_body = '';
805
            break;
806
    }
807
    array_pop($element_pile);
808
}
809
810
/**
811
 * QTI1 element parser
812
 * @param $parser
813
 * @param $data
814
 */
815
function elementDataQti1($parser, $data)
816
{
817
    global $element_pile;
818
    global $exercise_info;
819
    global $current_question_ident;
820
    global $current_answer_id;
821
    global $current_match_set;
822
    global $currentAssociableChoice;
823
    global $current_question_item_body;
824
    global $record_item_body;
825
    global $non_HTML_tag_to_avoid;
826
    global $current_inlinechoice_id;
827
    global $cardinality;
828
    global $lastLabelFieldName;
829
    global $lastLabelFieldValue;
830
831
    $current_element = end($element_pile);
832 View Code Duplication
    if (sizeof($element_pile) >= 2) {
833
        $parent_element = $element_pile[sizeof($element_pile) - 2];
834
    } else {
835
        $parent_element = "";
836
    }
837
    //treat the record of the full content of itembody tag (needed for question statment and/or FIB text:
838
839
    if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) {
840
        $current_question_item_body .= $data;
841
    }
842
843
    switch ($current_element) {
844
        case 'FIELDLABEL':
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...
845
            if (!empty($data)) {
846
                $lastLabelFieldName = $current_element;
847
                $lastLabelFieldValue = $data;
848
            }
849
        case 'FIELDENTRY':
850
            $current_question_item_body = $data;
851
            switch ($lastLabelFieldValue) {
852
                case 'cc_profile':
853
                    // The following values might be proprietary in MATRIX software. No specific reference
854
                    // in QTI doc: http://www.imsglobal.org/question/qtiv1p2/imsqti_asi_infov1p2.html#1415855
855
                    switch ($data) {
856
                        case 'cc.true_false.v0p1':
857
                            //this is a true-false question (translated to multiple choice in Chamilo because true-false comes with "I don't know")
858
                            $exercise_info['question'][$current_question_ident]['type'] = MCUA;
859
                            break;
860
                        case 'cc.multiple_choice.v0p1':
861
                            //this is a multiple choice (unique answer) question
862
                            $exercise_info['question'][$current_question_ident]['type'] = MCUA;
863
                            break;
864
                        case 'cc.multiple_response.v0p1':
865
                            //this is a multiple choice (unique answer) question
866
                            $exercise_info['question'][$current_question_ident]['type'] = MCMA;
867
                            break;
868
                    }
869
                    break;
870
                case 'cc_weighting':
871
                    //defines the total weight of the question
872
                    $exercise_info['question'][$current_question_ident]['default_weighting'] = $lastLabelFieldValue;
873
                    break;
874
                case 'assessment_question_identifierref':
875
                    //placeholder - not used yet
876
                    // Possible values are not defined by qti v1.2
877
                    break;
878
            }
879
            break;
880
        case 'MATTEXT':
881
            if (!empty($current_question_item_body)) {
882
                $current_question_item_body .= $data;
883
            } else {
884
                $current_question_item_body = $data;
885
            }
886
            break;
887
        case 'VAREQUAL':
888
            $lastLabelFieldName = 'VAREQUAL';
889
            $lastLabelFieldValue = $data;
890
            break;
891
        case 'SETVAR':
892
            if ($parent_element == 'RESPCONDITION') {
893
                // The following attributes are available
894
                //$attributes['ACTION']
895
                $exercise_info['question'][$current_question_ident]['correct_answers'][] = $lastLabelFieldValue;
896
                $exercise_info['question'][$current_question_ident]['weighting'][$lastLabelFieldValue] = $data;
897
            }
898
            break;
899
900
    }
901
}
902