diff --git a/composer.json b/composer.json index 7afb96bf1d2..24a2aac9bbc 100755 --- a/composer.json +++ b/composer.json @@ -42,6 +42,8 @@ "ext-xml": "*", "ext-json": "*", "ext-mbstring": "*", + "ext-libxml": "*", + "ext-dom": "*", "twig/twig": "1.*", "twig/extensions": "~1.0", "doctrine/orm": "~2.4", @@ -109,7 +111,8 @@ "culqi/culqi-php": "1.3.4", "knplabs/knp-components": "~1.3", "guzzlehttp/guzzle": "~6.0", - "onelogin/php-saml": "^3.0" + "onelogin/php-saml": "^3.0", + "symfony/dom-crawler": "~3.4" }, "require-dev": { "behat/behat": "@stable", diff --git a/main/admin/skills_import.php b/main/admin/skills_import.php index fc6baeded26..2b338cad4de 100755 --- a/main/admin/skills_import.php +++ b/main/admin/skills_import.php @@ -98,76 +98,6 @@ function parse_csv_data($file) return $skills; } -/** - * XML-parser: handle start of element. - */ -function element_start($parser, $data) -{ - $data = api_utf8_decode($data); - global $skill; - global $current_tag; - switch ($data) { - case 'Skill': - $skill = []; - break; - default: - $current_tag = $data; - } -} - -/** - * XML-parser: handle end of element. - */ -function element_end($parser, $data) -{ - $data = api_utf8_decode($data); - global $skill; - global $skills; - global $current_value; - switch ($data) { - case 'Skill': - $skills[] = $skill; - break; - default: - $skill[$data] = $current_value; - break; - } -} - -/** - * XML-parser: handle character data. - */ -function character_data($parser, $data) -{ - $data = trim(api_utf8_decode($data)); - global $current_value; - $current_value = $data; -} - -/** - * Read the XML-file. - * - * @param string $file Path to the XML-file - * - * @return array All userinformation read from the file - */ -function parse_xml_data($file) -{ - global $current_tag; - global $current_value; - global $skill; - global $skills; - $skills = []; - $parser = xml_parser_create('UTF-8'); - xml_set_element_handler($parser, 'element_start', 'element_end'); - xml_set_character_data_handler($parser, 'character_data'); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); - xml_parse($parser, api_utf8_encode_xml(file_get_contents($file))); - xml_parser_free($parser); - - return $skills; -} - $this_section = SECTION_PLATFORM_ADMIN; api_protect_admin_script(true); @@ -183,7 +113,7 @@ function parse_xml_data($file) $file_type = $_POST['file_type']; Security::clear_token(); $tok = Security::get_token(); - $allowed_file_mimetype = ['csv', 'xml']; + $allowed_file_mimetype = ['csv']; $error_kind_file = false; $error_message = ''; @@ -194,10 +124,6 @@ function parse_xml_data($file) $skills = parse_csv_data($_FILES['import_file']['tmp_name']); $errors = validate_data($skills); $error_kind_file = false; - } elseif (strcmp($file_type, 'xml') === 0 && $ext_import_file == $allowed_file_mimetype[1]) { - $skills = parse_xml_data($_FILES['import_file']['tmp_name']); - $errors = validate_data($skills); - $error_kind_file = false; } else { $error_kind_file = true; } @@ -222,8 +148,6 @@ function parse_xml_data($file) if (strcmp($file_type, 'csv') === 0) { save_data($skills_to_insert); - } elseif (strcmp($file_type, 'xml') === 0) { - save_data($skills_to_insert); } else { $error_message = get_lang('YouMustImportAFileAccordingToSelectedOption'); } @@ -272,7 +196,7 @@ function parse_xml_data($file) 'radio', 'file_type', '', - 'CSV ('.get_lang('ExampleCSVFile').')', + 'CSV ('.get_lang('ExampleCSVFile').')', 'csv' ); $form->addGroup($group, '', get_lang('FileType')); @@ -283,23 +207,6 @@ function parse_xml_data($file) $form->setDefaults($defaults); $form->display(); -$list = []; -$list_reponse = []; -$result_xml = ''; -$i = 0; -$count_fields = count($extra_fields); -if ($count_fields > 0) { - foreach ($extra_fields as $extra) { - $list[] = $extra[1]; - $list_reponse[] = 'xxx'; - $spaces = '        '; - $result_xml .= $spaces.'<'.$extra[1].'>xxx</'.$extra[1].'>'; - if ($i != $count_fields - 1) { - $result_xml .= '
'; - } - $i++; - } -} ?>

:

diff --git a/main/admin/user_import.php b/main/admin/user_import.php index d564519b938..bce7dd5c187 100644 --- a/main/admin/user_import.php +++ b/main/admin/user_import.php @@ -307,66 +307,6 @@ function parse_csv_data($file) return $users; } -/** - * XML-parser: handle start of element. - * - * @param string $parser Deprecated? - * @param string $data The data to be parsed - */ -function element_start($parser, $data) -{ - $data = api_utf8_decode($data); - global $user; - global $current_tag; - switch ($data) { - case 'Contact': - $user = []; - break; - default: - $current_tag = $data; - } -} - -/** - * XML-parser: handle end of element. - * - * @param string $parser Deprecated? - * @param string $data The data to be parsed - */ -function element_end($parser, $data) -{ - $data = api_utf8_decode($data); - global $user; - global $users; - global $current_value; - switch ($data) { - case 'Contact': - if ($user['Status'] == '5') { - $user['Status'] = STUDENT; - } - if ($user['Status'] == '1') { - $user['Status'] = COURSEMANAGER; - } - $users[] = $user; - break; - default: - $user[$data] = $current_value; - break; - } -} - -/** - * XML-parser: handle character data. - * - * @param string $parser Parser (deprecated?) - * @param string $data The data to be parsed - */ -function character_data($parser, $data) -{ - $data = trim(api_utf8_decode($data)); - global $current_value; - $current_value = $data; -} /** * Read the XML-file. @@ -377,16 +317,23 @@ function character_data($parser, $data) */ function parse_xml_data($file) { - global $users; - $users = []; - $parser = xml_parser_create('UTF-8'); - xml_set_element_handler($parser, 'element_start', 'element_end'); - xml_set_character_data_handler($parser, 'character_data'); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); - xml_parse($parser, api_utf8_encode_xml(file_get_contents($file))); - xml_parser_free($parser); + $crawler = new \Symfony\Component\DomCrawler\Crawler(); + $crawler->addXmlContent(file_get_contents($file)); + $crawler = $crawler->filter('Contacts > Contact '); + $array = []; + foreach ($crawler as $domElement) { + $row = []; + foreach ($domElement->childNodes as $node) { + if ($node->nodeName != '#text') { + $row[$node->nodeName] = $node->nodeValue; + } + } + if (!empty($row)) { + $array[] = $row; + } + } - return $users; + return $array; } $this_section = SECTION_PLATFORM_ADMIN; @@ -520,14 +467,14 @@ function parse_xml_data($file) 'radio', 'file_type', '', - 'CSV ('.get_lang('ExampleCSVFile').')', + 'CSV ('.get_lang('ExampleCSVFile').')', 'csv' ), $form->createElement( 'radio', 'file_type', null, - 'XML ('.get_lang('ExampleXMLFile').')', + 'XML ('.get_lang('ExampleXMLFile').')', 'xml' ), ]; diff --git a/main/admin/user_update_import.php b/main/admin/user_update_import.php index bb302f6d1ac..afedaac9432 100644 --- a/main/admin/user_update_import.php +++ b/main/admin/user_update_import.php @@ -254,86 +254,26 @@ function parse_csv_data($file) return $users; } -/** - * XML-parser: handle start of element. - * - * @param string $parser Deprecated? - * @param string $data The data to be parsed - */ -function element_start($parser, $data) -{ - $data = api_utf8_decode($data); - global $user; - global $current_tag; - switch ($data) { - case 'Contact': - $user = []; - break; - default: - $current_tag = $data; - } -} -/** - * XML-parser: handle end of element. - * - * @param string $parser Deprecated? - * @param string $data The data to be parsed - */ -function element_end($parser, $data) +function parse_xml_data($file) { - $data = api_utf8_decode($data); - global $user; - global $users; - global $current_value; - switch ($data) { - case 'Contact': - if ($user['Status'] == '5') { - $user['Status'] = STUDENT; - } - if ($user['Status'] == '1') { - $user['Status'] = COURSEMANAGER; + $crawler = new \Symfony\Component\DomCrawler\Crawler(); + $crawler->addXmlContent(file_get_contents($file)); + $crawler = $crawler->filter('Contacts > Contact '); + $array = []; + foreach ($crawler as $domElement) { + $row = []; + foreach ($domElement->childNodes as $node) { + if ($node->nodeName != '#text') { + $row[$node->nodeName] = $node->nodeValue; } - $users[] = $user; - break; - default: - $user[$data] = $current_value; - break; + } + if (!empty($row)) { + $array[] = $row; + } } -} -/** - * XML-parser: handle character data. - * - * @param string $parser Parser (deprecated?) - * @param string $data The data to be parsed - */ -function character_data($parser, $data) -{ - $data = trim(api_utf8_decode($data)); - global $current_value; - $current_value = $data; -} - -/** - * Read the XML-file. - * - * @param string $file Path to the XML-file - * - * @return array All user information read from the file - */ -function parse_xml_data($file) -{ - global $users; - $users = []; - $parser = xml_parser_create('UTF-8'); - xml_set_element_handler($parser, 'element_start', 'element_end'); - xml_set_character_data_handler($parser, 'character_data'); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); - xml_parse($parser, api_utf8_encode_xml(file_get_contents($file))); - xml_parser_free($parser); - - return $users; + return $array; } $this_section = SECTION_PLATFORM_ADMIN; @@ -429,7 +369,7 @@ function parse_xml_data($file) exit; } } -Display :: display_header($tool_name); +Display::display_header($tool_name); if (!empty($error_message)) { echo Display::return_message($error_message, 'error'); diff --git a/main/admin/usergroup_import.php b/main/admin/usergroup_import.php index 1b3fef693e3..b33e35873a9 100755 --- a/main/admin/usergroup_import.php +++ b/main/admin/usergroup_import.php @@ -100,7 +100,7 @@ function save_data($classes) 'radio', 'file_type', '', - 'CSV ('.get_lang('ExampleCSVFile').')', + 'CSV ('.get_lang('ExampleCSVFile').')', 'csv' ); $form->addGroup($group, '', get_lang('FileType'), null); diff --git a/main/exercise/export/exercise_import.inc.php b/main/exercise/export/exercise_import.inc.php index 9f4a16dd226..7180e09275e 100755 --- a/main/exercise/export/exercise_import.inc.php +++ b/main/exercise/export/exercise_import.inc.php @@ -2,6 +2,7 @@ /* For licensing terms, see /license.txt */ use Chamilo\CoreBundle\Component\Utils\ChamiloApi; +use Symfony\Component\DomCrawler\Crawler; /** * @copyright (c) 2001-2006 Universite catholique de Louvain (UCL) @@ -65,12 +66,7 @@ function get_and_unzip_uploaded_exercise($baseWorkDir, $uploadPath) */ function import_exercise($file) { - global $exercise_info; - global $element_pile; - global $non_HTML_tag_to_avoid; - global $record_item_body; - // used to specify the question directory where files could be found in relation in any question - global $questionTempDir; + global $exerciseInfo; global $resourcesLinks; $baseWorkDir = api_get_path(SYS_ARCHIVE_PATH).'qti2/'; @@ -85,12 +81,9 @@ function import_exercise($file) } // set some default values for the new exercise - $exercise_info = []; - $exercise_info['name'] = preg_replace('/.zip$/i', '', $file); - $exercise_info['question'] = []; - $element_pile = []; - // create parser and array to retrieve info from manifest - $element_pile = []; //pile to known the depth in which we are + $exerciseInfo = []; + $exerciseInfo['name'] = preg_replace('/.zip$/i', '', $file); + $exerciseInfo['question'] = []; // if file is not a .zip, then we cancel all if (!preg_match('/.zip$/i', $file)) { @@ -106,7 +99,7 @@ function import_exercise($file) // find the different manifests for each question and parse them. $exerciseHandle = opendir($baseWorkDir); - $file_found = false; + $fileFound = false; $result = false; $filePath = null; $resourcesLinks = []; @@ -125,7 +118,7 @@ function import_exercise($file) if ($isQti) { $result = qti_parse_file($baseWorkDir, $file, $questionFile); $filePath = $baseWorkDir.$file; - $file_found = true; + $fileFound = true; } else { $isManifest = isQtiManifest($baseWorkDir.'/'.$file.'/'.$questionFile); if ($isManifest) { @@ -139,7 +132,7 @@ function import_exercise($file) if ($isQti) { $result = qti_parse_file($baseWorkDir, '', $file); $filePath = $baseWorkDir.'/'.$file; - $file_found = true; + $fileFound = true; } else { $isManifest = isQtiManifest($baseWorkDir.'/'.$file); if ($isManifest) { @@ -149,7 +142,7 @@ function import_exercise($file) } } - if (!$file_found) { + if (!$fileFound) { return 'NoXMLFileFoundInTheZip'; } @@ -157,23 +150,20 @@ function import_exercise($file) return false; } - $doc = new DOMDocument(); - $doc->load($filePath); - // 1. Create exercise. $exercise = new Exercise(); - $exercise->exercise = $exercise_info['name']; + $exercise->exercise = $exerciseInfo['name']; // Random QTI support - if (isset($exercise_info['order_type'])) { - if ($exercise_info['order_type'] == 'Random') { + if (isset($exerciseInfo['order_type'])) { + if ($exerciseInfo['order_type'] == 'Random') { $exercise->setQuestionSelectionType(2); $exercise->random = -1; } } - if (!empty($exercise_info['description'])) { - $exercise->updateDescription(formatText(strip_tags($exercise_info['description']))); + if (!empty($exerciseInfo['description'])) { + $exercise->updateDescription(formatText(strip_tags($exerciseInfo['description']))); } $exercise->save(); @@ -181,7 +171,7 @@ function import_exercise($file) $courseId = api_get_course_int_id(); if (!empty($last_exercise_id)) { // For each question found... - foreach ($exercise_info['question'] as $question_array) { + foreach ($exerciseInfo['question'] as $question_array) { //2. Create question $question = new Ims2Question(); $question->type = $question_array['type']; @@ -300,7 +290,6 @@ function formatText($text) */ function qti_parse_file($exercisePath, $file, $questionFile) { - global $non_HTML_tag_to_avoid; global $record_item_body; global $questionTempDir; @@ -311,10 +300,13 @@ function qti_parse_file($exercisePath, $file, $questionFile) Display::addFlash(Display::return_message(get_lang('Error opening question\'s XML file'), 'error')); return false; - } else { - $data = fread($fp, filesize($questionFilePath)); } + $data = fread($fp, filesize($questionFilePath)); + + //close file + fclose($fp); + //parse XML question file //$data = str_replace(array('

', '

', '', ''), '', $data); $data = ChamiloApi::stripGivenTags($data, ['p', 'front']); @@ -327,53 +319,11 @@ function qti_parse_file($exercisePath, $file, $questionFile) //used global variable start values declaration: $record_item_body = false; - $non_HTML_tag_to_avoid = [ - "SIMPLECHOICE", - "CHOICEINTERACTION", - "INLINECHOICEINTERACTION", - "INLINECHOICE", - "SIMPLEMATCHSET", - "SIMPLEASSOCIABLECHOICE", - "TEXTENTRYINTERACTION", - "FEEDBACKINLINE", - "MATCHINTERACTION", - 'EXTENDEDTEXTINTERACTION', - "ITEMBODY", - "BR", - "IMG", - ]; - - $question_format_supported = true; - $xml_parser = xml_parser_create(); - xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, false); - if ($qtiMainVersion == 1) { - xml_set_element_handler( - $xml_parser, - 'startElementQti1', - 'endElementQti1' - ); - xml_set_character_data_handler($xml_parser, 'elementDataQti1'); - } else { - xml_set_element_handler( - $xml_parser, - 'startElementQti2', - 'endElementQti2' - ); - xml_set_character_data_handler($xml_parser, 'elementDataQti2'); - } - - if (!xml_parse($xml_parser, $data, feof($fp))) { - // if reading of the xml file in not successful : - // set errorFound, set error msg, break while statement + if ($qtiMainVersion != 2) { Display::addFlash( Display::return_message( - get_lang('Error reading XML file'). - sprintf( - '[%d:%d]', - xml_get_current_line_number($xml_parser), - xml_get_current_column_number($xml_parser) - ), + get_lang('UnsupportedQtiVersion'), 'error' ) ); @@ -381,682 +331,293 @@ function qti_parse_file($exercisePath, $file, $questionFile) return false; } - //close file - fclose($fp); - if (!$question_format_supported) { - Display::addFlash( - Display::return_message( - get_lang( - 'Unknown question format in file %file', - [ - '%file' => $questionFile, - ] - ), - 'error' - ) - ); - - return false; - } + parseQti2($data); return true; } /** - * Function used by the SAX xml parser when the parser meets a opening tag. + * Function used to parser a QTI2 xml file * - * @param object $parser xml parser created with "xml_parser_create()" - * @param string $name name of the element - * @param array $attributes + * @param string $xmlData */ -function startElementQti2($parser, $name, $attributes) +function parseQti2($xmlData) { - global $element_pile; - global $exercise_info; - global $current_question_ident; - global $current_answer_id; - global $current_match_set; - global $currentAssociableChoice; - global $current_question_item_body; - global $record_item_body; - global $non_HTML_tag_to_avoid; - global $current_inlinechoice_id; - global $cardinality; + global $exerciseInfo; global $questionTempDir; + global $resourcesLinks; - array_push($element_pile, $name); - $current_element = end($element_pile); - if (sizeof($element_pile) >= 2) { - $parent_element = $element_pile[sizeof($element_pile) - 2]; - } else { - $parent_element = ''; - } - - if (sizeof($element_pile) >= 3) { - $grand_parent_element = $element_pile[sizeof($element_pile) - 3]; - } else { - $grand_parent_element = ''; - } - if (sizeof($element_pile) >= 4) { - $great_grand_parent_element = $element_pile[sizeof($element_pile) - 4]; - } else { - $great_grand_parent_element = ''; - } + $crawler = new Crawler($xmlData); + $nodes = $crawler->filter('*'); + + $currentQuestionIdent = ''; + $currentAnswerId = ''; + $currentQuestionItemBody = ''; + $cardinality = ''; + $nonHTMLTagToAvoid = [ + "simpleChoice", + "choiceInteraction", + "inlineChoiceInteraction", + "inlineChoice", + "soMPLEMATCHSET", + "simpleAssociableChoice", + "textEntryInteraction", + "feedbackInline", + "matchInteraction", + 'extendedTextInteraction', + "itemBody", + "br", + "img", + ]; + $currentMatchSet = null; - if ($record_item_body) { - if ((!in_array($current_element, $non_HTML_tag_to_avoid))) { - $current_question_item_body .= "<".$name; - foreach ($attributes as $attribute_name => $attribute_value) { - $current_question_item_body .= " ".$attribute_name."=\"".$attribute_value."\""; - } - $current_question_item_body .= ">"; - } else { - //in case of FIB question, we replace the IMS-QTI tag b y the correct answer between "[" "]", - //we first save with claroline tags ,then when the answer will be parsed, the claroline tags will be replaced - if ($current_element == 'INLINECHOICEINTERACTION') { - $current_question_item_body .= "**claroline_start**".$attributes['RESPONSEIDENTIFIER']."**claroline_end**"; - } - if ($current_element == 'TEXTENTRYINTERACTION') { - $correct_answer_value = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id]; - $current_question_item_body .= "[".$correct_answer_value."]"; - } - if ($current_element == 'BR') { - $current_question_item_body .= "
"; - } + /** @var DOMElement $node */ + foreach ($nodes as $node) { + if ('#text' === $node->nodeName) { + continue; } - } - switch ($current_element) { - case 'ASSESSMENTITEM': - // retrieve current question - $current_question_ident = $attributes['IDENTIFIER']; - $exercise_info['question'][$current_question_ident] = []; - $exercise_info['question'][$current_question_ident]['answer'] = []; - $exercise_info['question'][$current_question_ident]['correct_answers'] = []; - $exercise_info['question'][$current_question_ident]['title'] = isset($attributes['TITLE']) ? $attributes['TITLE'] : ''; - $exercise_info['question'][$current_question_ident]['category'] = isset($attributes['CATEGORY']) ? $attributes['CATEGORY'] : ''; - $exercise_info['question'][$current_question_ident]['tempdir'] = $questionTempDir; - break; - case 'SECTION': - //retrieve exercise name - if (isset($attributes['TITLE']) && !empty($attributes['TITLE'])) { - $exercise_info['name'] = $attributes['TITLE']; - } - break; - case 'RESPONSEDECLARATION': - // Retrieve question type - if ('multiple' == $attributes['CARDINALITY']) { - $exercise_info['question'][$current_question_ident]['type'] = MCMA; - $cardinality = 'multiple'; - } - if ('single' == $attributes['CARDINALITY']) { - $exercise_info['question'][$current_question_ident]['type'] = MCUA; - $cardinality = 'single'; - } - //needed for FIB - $current_answer_id = $attributes['IDENTIFIER']; - break; - case 'INLINECHOICEINTERACTION': - $exercise_info['question'][$current_question_ident]['type'] = FIB; - $exercise_info['question'][$current_question_ident]['subtype'] = 'LISTBOX_FILL'; - $current_answer_id = $attributes['RESPONSEIDENTIFIER']; - break; - case 'INLINECHOICE': - $current_inlinechoice_id = $attributes['IDENTIFIER']; - break; - case 'TEXTENTRYINTERACTION': - $exercise_info['question'][$current_question_ident]['type'] = FIB; - $exercise_info['question'][$current_question_ident]['subtype'] = 'TEXTFIELD_FILL'; - $exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body; - //replace claroline tags - break; - case 'MATCHINTERACTION': - $exercise_info['question'][$current_question_ident]['type'] = MATCHING; - break; - case 'EXTENDEDTEXTINTERACTION': - $exercise_info['question'][$current_question_ident]['type'] = FREE_ANSWER; - $exercise_info['question'][$current_question_ident]['description'] = ''; - break; - case 'SIMPLEMATCHSET': - if (!isset($current_match_set)) { - $current_match_set = 1; - } else { - $current_match_set++; - } - $exercise_info['question'][$current_question_ident]['answer'][$current_match_set] = []; - break; - case 'SIMPLEASSOCIABLECHOICE': - $currentAssociableChoice = $attributes['IDENTIFIER']; - break; - //retrieve answers id for MCUA and MCMA questions - case 'SIMPLECHOICE': - $current_answer_id = $attributes['IDENTIFIER']; - if (!isset($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id])) { - $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id] = []; - } - break; - case 'MAPENTRY': - if ($parent_element == 'MAPPING' || $parent_element == 'MAPENTRY') { - $answer_id = $attributes['MAPKEY']; - if (!isset($exercise_info['question'][$current_question_ident]['weighting'])) { - $exercise_info['question'][$current_question_ident]['weighting'] = []; + switch ($node->nodeName) { + case 'assessmentItem': + $currentQuestionIdent = $node->getAttribute('identifier'); + + $exerciseInfo['question'][$currentQuestionIdent] = [ + 'answer' => [], + 'correct_answers' => [], + 'title' => $node->getAttribute('title'), + 'category' => $node->getAttribute('category'), + 'type' => '', + 'tempdir' => $questionTempDir, + ]; + break; + case 'section': + $title = $node->getAttribute('title'); + + if (!empty($title)) { + $exerciseInfo['name'] = $title; + } + break; + case 'responseDeclaration': + if ('multiple' === $node->getAttribute('cardinality')) { + $exerciseInfo['question'][$currentQuestionIdent]['type'] = MCMA; + $cardinality = 'multiple'; } - $exercise_info['question'][$current_question_ident]['weighting'][$answer_id] = $attributes['MAPPEDVALUE']; - } - break; - case 'MAPPING': - if (isset($attributes['DEFAULTVALUE'])) { - $exercise_info['question'][$current_question_ident]['default_weighting'] = $attributes['DEFAULTVALUE']; - } - // no break ? - case 'ITEMBODY': - $record_item_body = true; - $current_question_item_body = ''; - break; - case 'IMG': - $exercise_info['question'][$current_question_ident]['attached_file_url'] = $attributes['SRC']; - break; - case 'ORDER': - if (isset($attributes['ORDER_TYPE'])) { - $exercise_info['order_type'] = $attributes['ORDER_TYPE']; - } - break; - } -} - -/** - * Function used by the SAX xml parser when the parser meets a closing tag. - * - * @param $parser xml parser created with "xml_parser_create()" - * @param $name name of the element - */ -function endElementQti2($parser, $name) -{ - global $element_pile; - global $exercise_info; - global $current_question_ident; - global $record_item_body; - global $current_question_item_body; - global $non_HTML_tag_to_avoid; - global $cardinality; - - array_push($element_pile, $name); - $current_element = end($element_pile); - if (sizeof($element_pile) >= 2) { - $parent_element = $element_pile[sizeof($element_pile) - 2]; - } else { - $parent_element = ''; - } - if (sizeof($element_pile) >= 3) { - $grand_parent_element = $element_pile[sizeof($element_pile) - 3]; - } else { - $grand_parent_element = ''; - } - if (sizeof($element_pile) >= 4) { - $great_grand_parent_element = $element_pile[sizeof($element_pile) - 4]; - } else { - $great_grand_parent_element = ''; - } - //treat the record of the full content of itembody tag: - if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) { - $current_question_item_body .= ""; - } + if ('single' === $node->getAttribute('cardinality')) { + $exerciseInfo['question'][$currentQuestionIdent]['type'] = MCUA; + $cardinality = 'single'; + } - switch ($name) { - case 'ITEMBODY': - $record_item_body = false; - if ($exercise_info['question'][$current_question_ident]['type'] == FIB) { - $exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body; - } else { - if ($exercise_info['question'][$current_question_ident]['type'] == FREE_ANSWER) { - $current_question_item_body = trim($current_question_item_body); - if (!empty($current_question_item_body)) { - $exercise_info['question'][$current_question_ident]['description'] = $current_question_item_body; + $currentAnswerId = $node->getAttribute('identifier'); + break; + case 'inlineChoiceInteraction': + $exerciseInfo['question'][$currentQuestionIdent]['type'] = FIB; + $exerciseInfo['question'][$currentQuestionIdent]['subtype'] = 'LISTBOX_FILL'; + $currentAnswerId = $node->getAttribute('responseIdentifier'); + break; + case 'inlineChoice': + $answerIdentifier = $exerciseInfo['question'][$currentQuestionIdent]['correct_answers'][$currentAnswerId]; + + if ($node->getAttribute('identifier') == $answerIdentifier) { + $currentQuestionItemBody = str_replace( + "**claroline_start**".$currentAnswerId."**claroline_end**", + "[".$node->nodeValue."]", + $currentQuestionItemBody + ); + } else { + if (!isset($exerciseInfo['question'][$currentQuestionIdent]['wrong_answers'])) { + $exerciseInfo['question'][$currentQuestionIdent]['wrong_answers'] = []; } + + $exerciseInfo['question'][$currentQuestionIdent]['wrong_answers'][] = $node->nodeValue; + } + break; + case 'textEntryInteraction': + $exerciseInfo['question'][$currentQuestionIdent]['type'] = FIB; + $exerciseInfo['question'][$currentQuestionIdent]['subtype'] = 'TEXTFIELD_FILL'; + $exerciseInfo['question'][$currentQuestionIdent]['response_text'] = $currentQuestionItemBody; + break; + case 'matchInteraction': + $exerciseInfo['question'][$currentQuestionIdent]['type'] = MATCHING; + break; + case 'extendedTextInteraction': + $exerciseInfo['question'][$currentQuestionIdent]['type'] = FREE_ANSWER; + $exerciseInfo['question'][$currentQuestionIdent]['description'] = $node->nodeValue; + break; + case 'simpleMatchSet': + if (!isset($currentMatchSet)) { + $currentMatchSet = 1; } else { - $exercise_info['question'][$current_question_ident]['statement'] = $current_question_item_body; + $currentMatchSet++; + } + $exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentMatchSet] = []; + break; + case 'simpleAssociableChoice': + $currentAssociableChoice = $node->getAttribute('identifier'); + + $exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentMatchSet][$currentAssociableChoice] = trim($node->nodeValue); + break; + case 'simpleChoice': + $currentAnswerId = $node->getAttribute('identifier'); + if (!isset($exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentAnswerId])) { + $exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentAnswerId] = []; } - } - break; - } - array_pop($element_pile); -} - -/** - * @param $parser - * @param $data - */ -function elementDataQti2($parser, $data) -{ - global $element_pile; - global $exercise_info; - global $current_question_ident; - global $current_answer_id; - global $current_match_set; - global $currentAssociableChoice; - global $current_question_item_body; - global $record_item_body; - global $non_HTML_tag_to_avoid; - global $current_inlinechoice_id; - global $cardinality; - global $resourcesLinks; - - $current_element = end($element_pile); - if (sizeof($element_pile) >= 2) { - $parent_element = $element_pile[sizeof($element_pile) - 2]; - } else { - $parent_element = ''; - } - if (sizeof($element_pile) >= 3) { - $grand_parent_element = $element_pile[sizeof($element_pile) - 3]; - } else { - $grand_parent_element = ''; - } - if (sizeof($element_pile) >= 4) { - $great_grand_parent_element = $element_pile[sizeof($element_pile) - 4]; - } else { - $great_grand_parent_element = ''; - } - - //treat the record of the full content of itembody tag (needed for question statment and/or FIB text: - - if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) { - $current_question_item_body .= $data; - } - switch ($current_element) { - case 'EXTENDEDTEXTINTERACTION': - $exercise_info['question'][$current_question_ident]['description'] .= $data; - break; - case 'SIMPLECHOICE': - if (!isset($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'])) { - $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] = trim($data); - } else { - $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] .= ''.trim($data); - } - break; - case 'FEEDBACKINLINE': - if (!isset($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'])) { - $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] = trim($data); - } else { - $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] .= ' '.trim($data); - } - break; - case 'SIMPLEASSOCIABLECHOICE': - $exercise_info['question'][$current_question_ident]['answer'][$current_match_set][$currentAssociableChoice] = trim($data); - break; - case 'VALUE': - if ($parent_element == 'CORRECTRESPONSE') { - if ($cardinality == 'single') { - $exercise_info['question'][$current_question_ident]['correct_answers'][$data] = $data; + if (!isset($exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentAnswerId]['value'])) { + $exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentAnswerId]['value'] = trim( + $node->nodeValue + ); } else { - $exercise_info['question'][$current_question_ident]['correct_answers'][] = $data; + $exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentAnswerId]['value'] .= '' + .trim($node->nodeValue); } - } + break; + case 'mapEntry': + if (in_array($node->parentNode->nodeName, ['mapping', 'mapEntry'])) { + $answer_id = $node->getAttribute('mapKey'); - // Getting score of free answer - if ($grand_parent_element == 'OUTCOMEDECLARATION') { - if (!empty(trim($data))) { - $exercise_info['question'][$current_question_ident]['weighting'][0] = trim($data); - } - } + if (!isset($exerciseInfo['question'][$currentQuestionIdent]['weighting'])) { + $exerciseInfo['question'][$currentQuestionIdent]['weighting'] = []; + } - break; - case 'ITEMBODY': - // Replace relative links by links to the documents in the course - // $resourcesLinks is only defined by qtiProcessManifest() - if (isset($resourcesLinks) && isset($resourcesLinks['manifest']) && isset($resourcesLinks['web'])) { - foreach ($resourcesLinks['manifest'] as $key => $value) { - $data = preg_replace('|'.$value.'|', $resourcesLinks['web'][$key], $data); - } - } - $current_question_item_body .= $data; - break; - case 'INLINECHOICE': - // if this is the right answer, then we must replace the claroline tags in the FIB text bye the answer between "[" and "]" : - $answer_identifier = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id]; - if ($current_inlinechoice_id == $answer_identifier) { - $current_question_item_body = str_replace( - "**claroline_start**".$current_answer_id."**claroline_end**", - "[".$data."]", - $current_question_item_body - ); - } else { - if (!isset($exercise_info['question'][$current_question_ident]['wrong_answers'])) { - $exercise_info['question'][$current_question_ident]['wrong_answers'] = []; + $exerciseInfo['question'][$currentQuestionIdent]['weighting'][$answer_id] = $node->getAttribute( + 'mappedValue' + ); } - $exercise_info['question'][$current_question_ident]['wrong_answers'][] = $data; - } - break; - case 'MATTEXT': - if ($grand_parent_element == 'FLOW_MAT' && - ($great_grand_parent_element == 'PRESENTATION_MATERIAL' || $great_grand_parent_element == 'SECTION') - ) { - if (!empty(trim($data))) { - $exercise_info['description'] = $data; + break; + case 'mapping': + $defaultValue = $node->getAttribute('defaultValue'); + + if (!empty($defaultValue)) { + $exerciseInfo['question'][$currentQuestionIdent]['default_weighting'] = $defaultValue; } - } - break; - } -} + // no break ? + case 'itemBody': + $nodeValue = $node->nodeValue; -/** - * Function used by the SAX xml parser when the parser meets a opening tag for QTI1. - * - * @param object $parser xml parser created with "xml_parser_create()" - * @param string $name name of the element - * @param array $attributes - */ -function startElementQti1($parser, $name, $attributes) -{ - global $element_pile; - global $exercise_info; - global $current_question_ident; - global $current_answer_id; - global $current_match_set; - global $currentAssociableChoice; - global $current_question_item_body; - global $record_item_body; - global $non_HTML_tag_to_avoid; - global $current_inlinechoice_id; - global $cardinality; - global $questionTempDir; - global $lastLabelFieldName; - global $lastLabelFieldValue; - - array_push($element_pile, $name); - $current_element = end($element_pile); - if (sizeof($element_pile) >= 2) { - $parent_element = $element_pile[sizeof($element_pile) - 2]; - } else { - $parent_element = ''; - } - if (sizeof($element_pile) >= 3) { - $grand_parent_element = $element_pile[sizeof($element_pile) - 3]; - } else { - $grand_parent_element = ""; - } - if (sizeof($element_pile) >= 4) { - $great_grand_parent_element = $element_pile[sizeof($element_pile) - 4]; - } else { - $great_grand_parent_element = ""; - } + $currentQuestionItemBody = ''; - if ($record_item_body) { - if ((!in_array($current_element, $non_HTML_tag_to_avoid))) { - $current_question_item_body .= "<".$name; - foreach ($attributes as $attribute_name => $attribute_value) { - $current_question_item_body .= " ".$attribute_name."=\"".$attribute_value."\""; - } - $current_question_item_body .= ">"; - } else { - //in case of FIB question, we replace the IMS-QTI tag b y the correct answer between "[" "]", - //we first save with claroline tags ,then when the answer will be parsed, the claroline tags will be replaced - if ($current_element == 'INLINECHOICEINTERACTION') { - $current_question_item_body .= "**claroline_start**".$attributes['RESPONSEIDENTIFIER']."**claroline_end**"; - } - if ($current_element == 'TEXTENTRYINTERACTION') { - $correct_answer_value = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id]; - $current_question_item_body .= "[".$correct_answer_value."]"; - } - if ($current_element == 'BR') { - $current_question_item_body .= "
"; - } - } - } + /** @var DOMElement $childNode */ + foreach ($node->childNodes as $childNode) { + if ('#text' === $childNode->nodeName) { + continue; + } - switch ($current_element) { - case 'ASSESSMENT': - // This is the assessment element: we don't care, we just want questions - if (!empty($attributes['TITLE'])) { - $exercise_info['name'] = $attributes['TITLE']; - } - break; - case 'ITEM': - //retrieve current question - $current_question_ident = $attributes['IDENT']; - $exercise_info['question'][$current_question_ident] = []; - $exercise_info['question'][$current_question_ident]['answer'] = []; - $exercise_info['question'][$current_question_ident]['correct_answers'] = []; - $exercise_info['question'][$current_question_ident]['tempdir'] = $questionTempDir; - break; - case 'SECTION': - break; - case 'RESPONSE_LID': - // Retrieve question type - if ("multiple" == strtolower($attributes['RCARDINALITY'])) { - $cardinality = 'multiple'; - } - if ("single" == strtolower($attributes['RCARDINALITY'])) { - $cardinality = 'single'; - } - //needed for FIB - $current_answer_id = $attributes['IDENT']; - $current_question_item_body = ''; - break; - case 'RENDER_CHOICE': - break; - case 'RESPONSE_LABEL': - if (!empty($attributes['IDENT'])) { - $current_answer_id = $attributes['IDENT']; - //set the placeholder for the answer to come (in endElementQti1) - $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id] = ''; - } - break; - case 'DECVAR': - if ($parent_element == 'OUTCOMES' && $grand_parent_element == 'RESPROCESSING') { - // The following attributes are available - //$attributes['VARTYPE']; - //$attributes['DEFAULTVAL']; - //$attributes['MINVALUE']; - //$attributes['MAXVALUE']; - } - break; - case 'VAREQUAL': - if ($parent_element == 'CONDITIONVAR' && $grand_parent_element == 'RESPCONDITION') { - // The following attributes are available - //$attributes['RESPIDENT'] - } - break; - case 'SETVAR': - if ($parent_element == 'RESPCONDITION') { - // The following attributes are available - //$attributes['ACTION'] - } - break; - case 'IMG': - break; - case 'MATTEXT': - if ($parent_element == 'MATERIAL') { - if ($grand_parent_element == 'PRESENTATION') { - $exercise_info['question'][$current_question_ident]['title'] = $current_question_item_body; + if (!in_array($childNode->nodeName, $nonHTMLTagToAvoid)) { + $currentQuestionItemBody .= '<'.$childNode->nodeName; + + if ($childNode->attributes) { + foreach ($childNode->attributes as $attribute) { + $currentQuestionItemBody .= ' '.$attribute->nodeName.'="'.$attribute->nodeValue.'"'; + } + } + + $currentQuestionItemBody .= '>' + .$childNode->nodeValue + .'nodeName.'>'; + + continue; + } + + if ('inlineChoiceInteraction' === $childNode->nodeName) { + $currentQuestionItemBody .= "**claroline_start**" + .$childNode->attr('responseIdentifier') + ."**claroline_end**"; + + continue; + } + + if ('textEntryInteraction' === $childNode->nodeName) { + $correct_answer_value = $exerciseInfo['question'][$currentQuestionIdent]['correct_answers'][$currentAnswerId]; + $currentQuestionItemBody .= "[".$correct_answer_value."]"; + + continue; + } + + if ('br' === $childNode->nodeName) { + $currentQuestionItemBody .= '
'; + } } - } - break; - } -} -/** - * Function used by the SAX xml parser when the parser meets a closing tag for QTI1. - * - * @param object $parser xml parser created with "xml_parser_create()" - * @param string $name name of the element - * @param array $attributes The element attributes - */ -function endElementQti1($parser, $name, $attributes) -{ - global $element_pile; - global $exercise_info; - global $current_question_ident; - global $record_item_body; - global $current_question_item_body; - global $non_HTML_tag_to_avoid; - global $cardinality; - global $lastLabelFieldName; - global $lastLabelFieldValue; - global $resourcesLinks; + // Replace relative links by links to the documents in the course + // $resourcesLinks is only defined by qtiProcessManifest() + if (isset($resourcesLinks) && isset($resourcesLinks['manifest']) && isset($resourcesLinks['web'])) { + foreach ($resourcesLinks['manifest'] as $key => $value) { + $nodeValue = preg_replace('|'.$value.'|', $resourcesLinks['web'][$key], $nodeValue); + } + } - $current_element = end($element_pile); - if (sizeof($element_pile) >= 2) { - $parent_element = $element_pile[sizeof($element_pile) - 2]; - } else { - $parent_element = ""; - } - if (sizeof($element_pile) >= 3) { - $grand_parent_element = $element_pile[sizeof($element_pile) - 3]; - } else { - $grand_parent_element = ""; - } - if (sizeof($element_pile) >= 4) { - $great_grand_parent_element = $element_pile[sizeof($element_pile) - 4]; - } else { - $great_grand_parent_element = ""; - } + $currentQuestionItemBody .= $node->firstChild->nodeValue; - //treat the record of the full content of itembody tag : - if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) { - $current_question_item_body .= ""; - } + if ($exerciseInfo['question'][$currentQuestionIdent]['type'] == FIB) { + $exerciseInfo['question'][$currentQuestionIdent]['response_text'] = $currentQuestionItemBody; + } else { + if ($exerciseInfo['question'][$currentQuestionIdent]['type'] == FREE_ANSWER) { + $currentQuestionItemBody = trim($currentQuestionItemBody); - switch ($name) { - case 'MATTEXT': - if ($parent_element == 'MATERIAL') { - // For some reason an item in a hierarchy doesn't seem to - // catch the grandfather 'presentation', so we check for 'item' as a patch (great-grand-father) - if ($grand_parent_element == 'PRESENTATION' || $grand_parent_element == 'ITEM') { - $exercise_info['question'][$current_question_ident]['title'] = $current_question_item_body; - $current_question_item_body = ''; - } elseif ($grand_parent_element == 'RESPONSE_LABEL') { - $last = ''; - foreach ($exercise_info['question'][$current_question_ident]['answer'] as $key => $value) { - $last = $key; + if (!empty($currentQuestionItemBody)) { + $exerciseInfo['question'][$currentQuestionIdent]['description'] = $currentQuestionItemBody; + } + } else { + $exerciseInfo['question'][$currentQuestionIdent]['statement'] = $currentQuestionItemBody; } - $exercise_info['question'][$current_question_ident]['answer'][$last]['value'] = $current_question_item_body; - $current_question_item_body = ''; } - } - // no break ? - case 'RESPONSE_LID': - // Retrieve question type - if (!isset($exercise_info['question'][$current_question_ident]['type'])) { - if ("multiple" == strtolower($attributes['RCARDINALITY'])) { - $exercise_info['question'][$current_question_ident]['type'] = MCMA; + break; + case 'img': + $exerciseInfo['question'][$currentQuestionIdent]['attached_file_url'] = $node->getAttribute('src'); + break; + case 'order': + $orderType = $node->getAttribute('order_type'); + + if (!empty($orderType)) { + $exerciseInfo['order_type'] = $orderType; } - if ("single" == strtolower($attributes['RCARDINALITY'])) { - $exercise_info['question'][$current_question_ident]['type'] = MCUA; + break; + case 'feedbackInline': + if (!isset($exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentAnswerId]['feedback'])) { + $exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentAnswerId] = trim( + $node->nodeValue + ); + } else { + $exerciseInfo['question'][$currentQuestionIdent]['answer'][$currentAnswerId]['feedback'] .= '' + .trim( + $node->nodeValue + ); } - } - $current_question_item_body = ''; - //needed for FIB - $current_answer_id = $attributes['IDENT']; - break; - case 'ITEMMETADATA': - $current_question_item_body = ''; - break; - } - array_pop($element_pile); -} + break; + case 'value': + if ('correctResponse' === $node->parentNode->nodeName) { + $nodeValue = trim($node->nodeValue); -/** - * QTI1 element parser. - * - * @param $parser - * @param $data - */ -function elementDataQti1($parser, $data) -{ - global $element_pile; - global $exercise_info; - global $current_question_ident; - global $current_answer_id; - global $current_match_set; - global $currentAssociableChoice; - global $current_question_item_body; - global $record_item_body; - global $non_HTML_tag_to_avoid; - global $current_inlinechoice_id; - global $cardinality; - global $lastLabelFieldName; - global $lastLabelFieldValue; - global $resourcesLinks; - - $current_element = end($element_pile); - if (sizeof($element_pile) >= 2) { - $parent_element = $element_pile[sizeof($element_pile) - 2]; - } else { - $parent_element = ""; - } - //treat the record of the full content of itembody tag (needed for question statment and/or FIB text: + if ('single' === $cardinality) { + $exerciseInfo['question'][$currentQuestionIdent]['correct_answers'][$nodeValue] = $nodeValue; + } else { + $exerciseInfo['question'][$currentQuestionIdent]['correct_answers'][] = $nodeValue; + } + } - if ($record_item_body && (!in_array($current_element, $non_HTML_tag_to_avoid))) { - $current_question_item_body .= $data; - } + if ('outcomeDeclaration' === $node->parentNode->parentNode->nodeName) { + $nodeValue = trim($node->nodeValue); - switch ($current_element) { - case 'FIELDLABEL': - if (!empty($data)) { - $lastLabelFieldName = $current_element; - $lastLabelFieldValue = $data; - } - // no break ? - case 'FIELDENTRY': - $current_question_item_body = $data; - switch ($lastLabelFieldValue) { - case 'cc_profile': - // The following values might be proprietary in MATRIX software. No specific reference - // in QTI doc: http://www.imsglobal.org/question/qtiv1p2/imsqti_asi_infov1p2.html#1415855 - switch ($data) { - case 'cc.true_false.v0p1': - //this is a true-false question (translated to multiple choice in Chamilo because true-false comes with "I don't know") - $exercise_info['question'][$current_question_ident]['type'] = MCUA; - break; - case 'cc.multiple_choice.v0p1': - //this is a multiple choice (unique answer) question - $exercise_info['question'][$current_question_ident]['type'] = MCUA; - break; - case 'cc.multiple_response.v0p1': - //this is a multiple choice (unique answer) question - $exercise_info['question'][$current_question_ident]['type'] = MCMA; - break; + if (!empty($nodeValue)) { + $exerciseInfo['question'][$currentQuestionIdent]['weighting'][0] = $nodeValue; } - break; - case 'cc_weighting': - //defines the total weight of the question - $exercise_info['question'][$current_question_ident]['default_weighting'] = $lastLabelFieldValue; - break; - case 'assessment_question_identifierref': - //placeholder - not used yet - // Possible values are not defined by qti v1.2 - break; - } - break; - case 'MATTEXT': - // Replace relative links by links to the documents in the course - // $resourcesLinks is only defined by qtiProcessManifest() - if (isset($resourcesLinks) && isset($resourcesLinks['manifest']) && isset($resourcesLinks['web'])) { - foreach ($resourcesLinks['manifest'] as $key => $value) { - $data = preg_replace('|'.$value.'|', $resourcesLinks['web'][$key], $data); } - } - if (!empty($current_question_item_body)) { - $current_question_item_body .= $data; - } else { - $current_question_item_body = $data; - } - break; - case 'VAREQUAL': - $lastLabelFieldName = 'VAREQUAL'; - $lastLabelFieldValue = $data; - break; - case 'SETVAR': - if ($parent_element == 'RESPCONDITION') { - // The following attributes are available - //$attributes['ACTION'] - $exercise_info['question'][$current_question_ident]['correct_answers'][] = $lastLabelFieldValue; - $exercise_info['question'][$current_question_ident]['weighting'][$lastLabelFieldValue] = $data; - } - break; + break; + case 'mattext': + if ('flow_mat' === $node->parentNode->parentNode->nodeName && + ('presentation_material' === $node->parentNode->parentNode->parentNode->nodeName || + 'section' === $node->parentNode->parentNode->parentNode->nodeName + ) + ) { + $nodeValue = trim($node->nodeValue); + + if (!empty($nodeValue)) { + $exerciseInfo['description'] = $node->nodeValue; + } + } + break; + } } } diff --git a/main/gradebook/gradebook_view_result.php b/main/gradebook/gradebook_view_result.php index ea0c278b2b3..d2c7398f43b 100755 --- a/main/gradebook/gradebook_view_result.php +++ b/main/gradebook/gradebook_view_result.php @@ -247,12 +247,7 @@ $values = $import_result_form->exportValues(); $file_type = $_POST['file_type']; $file_name = $_FILES['import_file']['tmp_name']; - if ($file_type == 'csv') { - $results = Import :: csvToArray($file_name); - } else { - $results = parse_xml_data($file_name); - } - + $results = Import :: csvToArray($file_name); $nr_results_added = 0; foreach ($results as $index => $importedresult) { //check username & score diff --git a/main/gradebook/lib/GradebookUtils.php b/main/gradebook/lib/GradebookUtils.php index 27e345a0269..f3a41c214fd 100644 --- a/main/gradebook/lib/GradebookUtils.php +++ b/main/gradebook/lib/GradebookUtils.php @@ -651,40 +651,6 @@ public static function character_data($parser, $data) $current_value = $data; } - /** - * XML-parser: handle end of element. - */ - public static function element_end($parser, $data) - { - global $user; - global $users; - global $current_value; - switch ($data) { - case 'Result': - $users[] = $user; - break; - default: - $user[$data] = $current_value; - break; - } - } - - /** - * XML-parser: handle start of element. - */ - public static function element_start($parser, $data) - { - global $user; - global $current_tag; - switch ($data) { - case 'Result': - $user = []; - break; - default: - $current_tag = $data; - } - } - public static function overwritescore($resid, $importscore, $eval_max) { $result = Result::load($resid); @@ -697,30 +663,6 @@ public static function overwritescore($resid, $importscore, $eval_max) unset($result); } - /** - * Read the XML-file. - * - * @param string $file Path to the XML-file - * - * @return array All user information read from the file - */ - public static function parse_xml_data($file) - { - global $current_tag; - global $current_value; - global $user; - global $users; - $users = []; - $parser = xml_parser_create(); - xml_set_element_handler($parser, 'element_start', 'element_end'); - xml_set_character_data_handler($parser, "character_data"); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); - xml_parse($parser, file_get_contents($file)); - xml_parser_free($parser); - - return $users; - } - /** * register user info about certificate. * diff --git a/main/gradebook/lib/fe/dataform.class.php b/main/gradebook/lib/fe/dataform.class.php index 03f6a63b8de..c6d736f7d98 100755 --- a/main/gradebook/lib/fe/dataform.class.php +++ b/main/gradebook/lib/fe/dataform.class.php @@ -107,13 +107,16 @@ protected function build_import_form() $this->addElement('hidden', 'formSent'); $this->addElement('header', get_lang('ImportFileLocation')); $this->addElement('file', 'import_file', get_lang('Location')); - $allowed_file_types = [ - 'xml', - 'csv', - ]; - //$this->addRule('file', get_lang('InvalidExtension') . ' (' . implode(',', $allowed_file_types) . ')', 'filetype', $allowed_file_types); - $this->addElement('radio', 'file_type', get_lang('FileType'), 'CSV ('.get_lang('ExampleCSVFile').')', 'csv'); - $this->addElement('radio', 'file_type', null, 'XML ('.get_lang('ExampleXMLFile').')', 'xml'); + $this->addElement( + 'radio', + 'file_type', + get_lang('FileType'), + 'CSV (' + .get_lang('ExampleCSVFile') + .')', + 'csv' + ); + //$this->addElement('radio', 'file_type', null, 'XML ('.get_lang('ExampleXMLFile').')', 'xml'); $this->addElement('checkbox', 'overwrite', null, get_lang('OverwriteScores')); $this->addElement('checkbox', 'ignoreerrors', null, get_lang('IgnoreErrors')); $this->addButtonImport(get_lang('Ok')); diff --git a/main/group/import.php b/main/group/import.php index 81b167f0662..36e4232c4c5 100755 --- a/main/group/import.php +++ b/main/group/import.php @@ -38,7 +38,8 @@ null, Display::url( get_lang('ExampleCSVFile'), - api_get_path(WEB_CODE_PATH).'group/example.csv' + api_get_path(WEB_CODE_PATH).'group/example.csv', + ['download' => true] ) ); $form->addButtonImport(get_lang('Import')); diff --git a/main/inc/lib/myspace.lib.php b/main/inc/lib/myspace.lib.php index ed84487fb25..52f712026bc 100644 --- a/main/inc/lib/myspace.lib.php +++ b/main/inc/lib/myspace.lib.php @@ -2359,7 +2359,7 @@ public static function user_available_in_session($username, $course_list, $id_se * * @author Julio Montoya Armas */ - public function check_all_usernames($users, $course_list, $id_session) + public static function check_all_usernames($users, $course_list, $id_session) { $table_user = Database::get_main_table(TABLE_MAIN_USER); $usernames = []; @@ -2409,7 +2409,7 @@ public function check_all_usernames($users, $course_list, $id_session) * * @author Julio Montoya Armas */ - public function get_user_creator($users) + public static function get_user_creator($users) { $errors = []; foreach ($users as $index => $user) { @@ -2437,7 +2437,7 @@ public function get_user_creator($users) * * @param array $users list of users */ - public function validate_data($users, $id_session = null) + public static function validate_data($users, $id_session = null) { $errors = []; $new_users = []; @@ -2475,7 +2475,7 @@ public function validate_data($users, $id_session = null) /** * Adds missing user-information (which isn't required, like password, etc). */ - public function complete_missing_data($user) + public static function complete_missing_data($user) { // 1. Generate a password if it is necessary. if (!isset($user['Password']) || strlen($user['Password']) == 0) { @@ -2488,14 +2488,14 @@ public function complete_missing_data($user) /** * Saves imported data. */ - public function save_data($users, $course_list, $id_session) + public static function save_data($users, $course_list, $id_session) { $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION); $tbl_session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE); $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER); $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER); - $id_session = intval($id_session); + $id_session = (int) $id_session; $sendMail = $_POST['sendMail'] ? 1 : 0; // Adding users to the platform. @@ -2612,7 +2612,7 @@ public function save_data($users, $course_list, $id_session) $addedto = get_lang('UserNotAdded'); } - $registered_users .= UserManager::getUserProfileLink($userInfo)." - ".$addedto.'
'; + $registered_users .= UserManager::getUserProfileLink($userInfo).' - '.$addedto.'
'; } } else { $i = 0; @@ -2632,10 +2632,10 @@ public function save_data($users, $course_list, $id_session) $addedto = get_lang('UserNotAdded'); } $registered_users .= "". - api_get_person_name($user['FirstName'], $user['LastName'])." - ".$addedto.'
'; + Security::remove_XSS($userInfo['complete_user_name'])." - ".$addedto.'
'; } } - Display::addFlash(Display::return_message($registered_users)); + Display::addFlash(Display::return_message($registered_users, 'normal',false)); header('Location: course.php?id_session='.$id_session); exit; } @@ -2660,55 +2660,6 @@ public function parse_csv_data($file) return $users; } - /** - * XML-parser: the handler at the beginning of element. - */ - public function element_start($parser, $data) - { - $data = api_utf8_decode($data); - global $user; - global $current_tag; - switch ($data) { - case 'Contact': - $user = []; - break; - default: - $current_tag = $data; - } - } - - /** - * XML-parser: the handler at the end of element. - */ - public function element_end($parser, $data) - { - $data = api_utf8_decode($data); - global $user; - global $users; - global $current_value; - global $purification_option_for_usernames; - $user[$data] = $current_value; - switch ($data) { - case 'Contact': - $user['UserName'] = UserManager::purify_username($user['UserName'], $purification_option_for_usernames); - $users[] = $user; - break; - default: - $user[$data] = $current_value; - break; - } - } - - /** - * XML-parser: the handler for character data. - */ - public function character_data($parser, $data) - { - $data = trim(api_utf8_decode($data)); - global $current_value; - $current_value = $data; - } - /** * Reads XML-file. * @@ -2716,21 +2667,25 @@ public function character_data($parser, $data) * * @return array All userinformation read from the file */ - public function parse_xml_data($file) + public static function parse_xml_data($file) { - global $current_tag; - global $current_value; - global $user; - global $users; - $users = []; - $parser = xml_parser_create('UTF-8'); - xml_set_element_handler($parser, ['MySpace', 'element_start'], ['MySpace', 'element_end']); - xml_set_character_data_handler($parser, "character_data"); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); - xml_parse($parser, api_utf8_encode_xml(file_get_contents($file))); - xml_parser_free($parser); + $crawler = new \Symfony\Component\DomCrawler\Crawler(); + $crawler->addXmlContent(file_get_contents($file)); + $crawler = $crawler->filter('Contacts > Contact '); + $array = []; + foreach ($crawler as $domElement) { + $row = []; + foreach ($domElement->childNodes as $node) { + if ($node->nodeName != '#text') { + $row[$node->nodeName] = $node->nodeValue; + } + } + if (!empty($row)) { + $array[] = $row; + } + } - return $users; + return $array; } /** diff --git a/main/lp/aicc.class.php b/main/lp/aicc.class.php index 9fe1e570d3c..091216baccb 100755 --- a/main/lp/aicc.class.php +++ b/main/lp/aicc.class.php @@ -439,6 +439,7 @@ public function import_package($zip_file_info, $current_dir = '') $files_found = []; $subdir_isset = false; // The following loop should be stopped as soon as we found the right config files (.crs, .au, .des and .cst). + $realFileSize = 0; foreach ($zipContentArray as $thisContent) { if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) { // If a php file is found, do not authorize (security risk). diff --git a/main/lp/scorm.class.php b/main/lp/scorm.class.php index c372467c0dc..b36ce1f76be 100755 --- a/main/lp/scorm.class.php +++ b/main/lp/scorm.class.php @@ -1,6 +1,8 @@ manifest_encoding); - $doc = new DOMDocument(); - $res = @$doc->loadXML($xml); - if ($res === false) { + + $crawler = new Crawler(); + $crawler->addXmlContent($xml); + + $xmlErrors = libxml_get_errors(); + + if (!empty($xmlErrors)) { if ($this->debug > 0) { error_log('New LP - In scorm::parse_manifest() - Exception thrown when loading '.$file.' in DOMDocument'); } @@ -105,10 +111,11 @@ public function parse_manifest($file = '') } if ($this->debug > 1) { - error_log('New LP - Called (encoding:'.$doc->xmlEncoding.' - saved: '.$this->manifest_encoding.')', 0); + error_log('New LP - Called (encoding:'.$this->manifest_encoding.' - saved: '.$this->manifest_encoding.')', 0); } - $root = $doc->documentElement; + $root = $crawler->getNode(0); + if ($root->hasAttributes()) { $attributes = $root->attributes; if ($attributes->length !== 0) { @@ -220,7 +227,6 @@ public function parse_manifest($file = '') } } } - unset($doc); // End parsing using PHP5 DOMXML methods. } else { if ($this->debug > 1) { @@ -586,8 +592,8 @@ public function import_local_package($file_path, $currentDir = '') /** * Imports a zip file into the Chamilo structure. * - * @param string $zip_file_info Zip file info as given by $_FILES['userFile'] - * @param string $current_dir + * @param string $zipFileInfo Zip file info as given by $_FILES['userFile'] + * @param string $currentDir * @param array $courseInfo * @param bool $updateDirContents * @param learnpath $lpToCheck @@ -595,55 +601,58 @@ public function import_local_package($file_path, $currentDir = '') * @return string $current_dir Absolute path to the imsmanifest.xml file or empty string on error */ public function import_package( - $zip_file_info, - $current_dir = '', + $zipFileInfo, + $currentDir = '', $courseInfo = [], $updateDirContents = false, $lpToCheck = null ) { if ($this->debug > 0) { error_log( - 'In scorm::import_package('.print_r($zip_file_info, true).',"'.$current_dir.'") method' + 'In scorm::import_package('.print_r($zipFileInfo, true).',"'.$currentDir.'") method' ); } $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo; $maxFilledSpace = DocumentManager::get_course_quota($courseInfo['code']); - $zip_file_path = $zip_file_info['tmp_name']; - $zip_file_name = $zip_file_info['name']; + $zipFilePath = $zipFileInfo['tmp_name']; + $zipFileName = $zipFileInfo['name']; if ($this->debug > 1) { - error_log('New LP - import_package() - zip file path = '.$zip_file_path.', zip file name = '.$zip_file_name, 0); + error_log( + 'New LP - import_package() - zip file path = '.$zipFilePath.', zip file name = '.$zipFileName, + 0 + ); } - $course_rel_dir = api_get_course_path($courseInfo['code']).'/scorm'; // scorm dir web path starting from /courses - $course_sys_dir = api_get_path(SYS_COURSE_PATH).$course_rel_dir; // Absolute system path for this course. - $current_dir = api_replace_dangerous_char(trim($current_dir)); // Current dir we are in, inside scorm/ + $courseRelDir = api_get_course_path($courseInfo['code']).'/scorm'; // scorm dir web path starting from /courses + $courseSysDir = api_get_path(SYS_COURSE_PATH).$courseRelDir; // Absolute system path for this course. + $currentDir = api_replace_dangerous_char(trim($currentDir)); // Current dir we are in, inside scorm/ if ($this->debug > 1) { - error_log('New LP - import_package() - current_dir = '.$current_dir, 0); + error_log('New LP - import_package() - current_dir = '.$currentDir, 0); } // Get name of the zip file without the extension. - $file_info = pathinfo($zip_file_name); - $filename = $file_info['basename']; - $extension = $file_info['extension']; - $file_base_name = str_replace('.'.$extension, '', $filename); // Filename without its extension. - $this->zipname = $file_base_name; // Save for later in case we don't have a title. - $new_dir = api_replace_dangerous_char(trim($file_base_name)); - $this->subdir = $new_dir; + $fileInfo = pathinfo($zipFileName); + $filename = $fileInfo['basename']; + $extension = $fileInfo['extension']; + $fileBaseName = str_replace('.'.$extension, '', $filename); // Filename without its extension. + $this->zipname = $fileBaseName; // Save for later in case we don't have a title. + $newDir = api_replace_dangerous_char(trim($fileBaseName)); + $this->subdir = $newDir; if ($this->debug > 1) { - error_log('New LP - Received zip file name: '.$zip_file_path); + error_log('New LP - Received zip file name: '.$zipFilePath); error_log("New LP - subdir is first set to : ".$this->subdir); - error_log("New LP - base file name is : ".$file_base_name); + error_log("New LP - base file name is : ".$fileBaseName); } - $zipFile = new PclZip($zip_file_path); + $zipFile = new PclZip($zipFilePath); // Check the zip content (real size and file extension). $zipContentArray = $zipFile->listContent(); - $package_type = ''; - $manifest_list = []; + $packageType = ''; + $manifestList = []; // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?). $realFileSize = 0; foreach ($zipContentArray as $thisContent) { @@ -657,29 +666,29 @@ public function import_package( error_log("New LP - subdir is now ".$this->subdir); } } - $package_type = 'scorm'; - $manifest_list[] = $thisContent['filename']; + $packageType = 'scorm'; + $manifestList[] = $thisContent['filename']; } $realFileSize += $thisContent['size']; } // Now get the shortest path (basically, the imsmanifest that is the closest to the root). - $shortest_path = $manifest_list[0]; - $slash_count = substr_count($shortest_path, '/'); - foreach ($manifest_list as $manifest_path) { - $tmp_slash_count = substr_count($manifest_path, '/'); - if ($tmp_slash_count < $slash_count) { - $shortest_path = $manifest_path; - $slash_count = $tmp_slash_count; + $shortestPath = $manifestList[0]; + $slashCount = substr_count($shortestPath, '/'); + foreach ($manifestList as $manifestPath) { + $tmpSlashCount = substr_count($manifestPath, '/'); + if ($tmpSlashCount < $slashCount) { + $shortestPath = $manifestPath; + $slashCount = $tmpSlashCount; } } - $this->subdir .= '/'.dirname($shortest_path); // Do not concatenate because already done above. - $manifest = $shortest_path; + $this->subdir .= '/'.dirname($shortestPath); // Do not concatenate because already done above. + $manifest = $shortestPath; if ($this->debug) { - error_log("New LP - Package type is now: '$package_type'"); + error_log("New LP - Package type is now: '$packageType'"); } - if ($package_type == '') { + if ($packageType == '') { Display::addFlash( Display::return_message(get_lang('NotScormContent')) ); @@ -687,7 +696,7 @@ public function import_package( return false; } - if (!enough_size($realFileSize, $course_sys_dir, $maxFilledSpace)) { + if (!enough_size($realFileSize, $courseSysDir, $maxFilledSpace)) { if ($this->debug > 1) { error_log('New LP - Not enough space to store package'); } @@ -700,20 +709,20 @@ public function import_package( if ($updateDirContents && $lpToCheck) { $originalPath = str_replace('/.', '', $lpToCheck->path); - if ($originalPath != $new_dir) { + if ($originalPath != $newDir) { Display::addFlash(Display::return_message(get_lang('FileError'))); return false; } } - // It happens on Linux that $new_dir sometimes doesn't start with '/' - if ($new_dir[0] != '/') { - $new_dir = '/'.$new_dir; + // It happens on Linux that $newDir sometimes doesn't start with '/' + if ($newDir[0] != '/') { + $newDir = '/'.$newDir; } - if ($new_dir[strlen($new_dir) - 1] == '/') { - $new_dir = substr($new_dir, 0, -1); + if ($newDir[strlen($newDir) - 1] == '/') { + $newDir = substr($newDir, 0, -1); } /* Uncompressing phase */ @@ -723,15 +732,18 @@ public function import_package( - parse & change relative html links - make sure the filenames are secure (filter funny characters or php extensions) */ - if (is_dir($course_sys_dir.$new_dir) || - @mkdir($course_sys_dir.$new_dir, api_get_permissions_for_new_directories()) + if (is_dir($courseSysDir.$newDir) || + @mkdir( + $courseSysDir.$newDir, + api_get_permissions_for_new_directories() + ) ) { // PHP method - slower... if ($this->debug >= 1) { - error_log('New LP - Changing dir to '.$course_sys_dir.$new_dir); + error_log('New LP - Changing dir to '.$courseSysDir.$newDir); } - $saved_dir = getcwd(); - chdir($course_sys_dir.$new_dir); + $savedDir = getcwd(); + chdir($courseSysDir.$newDir); $unzippingState = $zipFile->extract(); for ($j = 0; $j < count($unzippingState); $j++) { @@ -743,34 +755,34 @@ public function import_package( } } - if (!empty($new_dir)) { - $new_dir = $new_dir.'/'; + if (!empty($newDir)) { + $newDir = $newDir.'/'; } // Rename files, for example with \\ in it. if ($this->debug >= 1) { - error_log('New LP - try to open: '.$course_sys_dir.$new_dir); + error_log('New LP - try to open: '.$courseSysDir.$newDir); } - if ($dir = @opendir($course_sys_dir.$new_dir)) { + if ($dir = @opendir($courseSysDir.$newDir)) { if ($this->debug >= 1) { - error_log('New LP - Opened dir '.$course_sys_dir.$new_dir); + error_log('New LP - Opened dir '.$courseSysDir.$newDir); } while ($file = readdir($dir)) { if ($file != '.' && $file != '..') { // TODO: RENAMING FILES CAN BE VERY DANGEROUS SCORM-WISE, avoid that as much as possible! - //$safe_file = api_replace_dangerous_char($file, 'strict'); - $find_str = ['\\', '.php', '.phtml']; - $repl_str = ['/', '.txt', '.txt']; - $safe_file = str_replace($find_str, $repl_str, $file); + //$safeFile = api_replace_dangerous_char($file, 'strict'); + $findStr = ['\\', '.php', '.phtml']; + $replStr = ['/', '.txt', '.txt']; + $safeFile = str_replace($findStr, $replStr, $file); if ($this->debug >= 1) { - error_log('Comparing: '.$safe_file); + error_log('Comparing: '.$safeFile); error_log('and: '.$file); } - if ($safe_file != $file) { - $mydir = dirname($course_sys_dir.$new_dir.$safe_file); + if ($safeFile != $file) { + $mydir = dirname($courseSysDir.$newDir.$safeFile); if (!is_dir($mydir)) { $mysubdirs = explode('/', $mydir); $mybasedir = '/'; @@ -786,10 +798,11 @@ public function import_package( } } } - @rename($course_sys_dir.$new_dir.$file, $course_sys_dir.$new_dir.$safe_file); + @rename($courseSysDir.$newDir.$file, $courseSysDir.$newDir.$safeFile); if ($this->debug >= 1) { error_log( - 'New LP - Renaming '.$course_sys_dir.$new_dir.$file.' to '.$course_sys_dir.$new_dir.$safe_file + 'New LP - Renaming '.$courseSysDir.$newDir.$file.' to '.$courseSysDir.$newDir + .$safeFile ); } } @@ -797,18 +810,18 @@ public function import_package( } closedir($dir); - chdir($saved_dir); + chdir($savedDir); - api_chmod_R($course_sys_dir.$new_dir, api_get_permissions_for_new_directories()); + api_chmod_R($courseSysDir.$newDir, api_get_permissions_for_new_directories()); if ($this->debug > 1) { - error_log('New LP - changed back to init dir: '.$course_sys_dir.$new_dir); + error_log('New LP - changed back to init dir: '.$courseSysDir.$newDir); } } } else { return false; } - return $course_sys_dir.$new_dir.$manifest; + return $courseSysDir.$newDir.$manifest; } /** diff --git a/main/mySpace/user_import.php b/main/mySpace/user_import.php index d5032722956..bba9bbf0bfc 100755 --- a/main/mySpace/user_import.php +++ b/main/mySpace/user_import.php @@ -46,8 +46,8 @@ } set_time_limit(0); - -if ($_POST['formSent'] && $_FILES['import_file']['size'] !== 0) { +$errors = []; +if (isset($_POST['formSent']) && $_POST['formSent'] && $_FILES['import_file']['size'] !== 0) { $file_type = $_POST['file_type']; $id_session = intval($_POST['id_session']); if ($file_type == 'csv') { @@ -76,19 +76,21 @@ MySpace::save_data($users, $course_list, $id_session); } } else { - header('Location: course.php?id_session='.$id_session.'&action=error_message&message='.urlencode(get_lang('NoSessionId'))); + Display::addFlash(Display::return_message(get_lang('NoSessionId'), 'warning')); + header('Location: course.php?id_session='.$id_session); exit; } } } else { - header('Location: course.php?id_session='.$id_session.'&action=error_message&message='.urlencode(get_lang('NoUsersRead'))); + Display::addFlash(Display::return_message(get_lang('NoUsersRead'), 'warning')); + header('Location: course.php?id_session='.$id_session); exit; } } Display :: display_header($tool_name); -if ($_FILES['import_file']['size'] == 0 && $_POST) { +if (isset($_FILES['import_file']) && $_FILES['import_file']['size'] == 0 && $_POST) { echo Display::return_message(get_lang('ThisFieldIsRequired'), 'error'); } @@ -110,8 +112,20 @@ $form->addRule('import_file', get_lang('ThisFieldIsRequired'), 'required'); $allowed_file_types = ['xml', 'csv']; $form->addRule('import_file', get_lang('InvalidExtension').' ('.implode(',', $allowed_file_types).')', 'filetype', $allowed_file_types); -$form->addElement('radio', 'file_type', get_lang('FileType'), 'XML ('.get_lang('ExampleXMLFile').')', 'xml'); -$form->addElement('radio', 'file_type', null, 'CSV ('.get_lang('ExampleCSVFile').')', 'csv'); +$form->addElement( + 'radio', + 'file_type', + get_lang('FileType'), + 'XML ('.get_lang('ExampleXMLFile').')', + 'xml' +); +$form->addElement( + 'radio', + 'file_type', + null, + 'CSV ('.get_lang('ExampleCSVFile').')', + 'csv' +); $form->addElement('radio', 'sendMail', get_lang('SendMailToUsers'), get_lang('Yes'), 1); $form->addElement('radio', 'sendMail', null, get_lang('No'), 0); $form->addElement('submit', 'submit', get_lang('Ok')); diff --git a/main/session/session_import.php b/main/session/session_import.php index 9238daab62a..f27dd633e63 100644 --- a/main/session/session_import.php +++ b/main/session/session_import.php @@ -482,7 +482,7 @@ Display::url( get_lang('ExampleCSVFile'), api_get_path(WEB_CODE_PATH).'admin/example_session.csv', - ['target' => '_blank'] + ['target' => '_blank', 'download' => null] ), ], 'CSV', @@ -496,7 +496,7 @@ Display::url( get_lang('ExampleXMLFile'), api_get_path(WEB_CODE_PATH).'admin/example_session.xml', - ['target' => '_blank'] + ['target' => '_blank', 'download' => null] ), ], 'XML',