diff --git a/backup/util/structure/base_nested_element.class.php b/backup/util/structure/base_nested_element.class.php index 1cd23b032b7..b48defc8235 100644 --- a/backup/util/structure/base_nested_element.class.php +++ b/backup/util/structure/base_nested_element.class.php @@ -94,6 +94,10 @@ abstract class base_nested_element extends base_final_element { } protected function check_and_set_used($element) { + // First of all, check the element being added doesn't conflict with own final elements + if (array_key_exists($element->get_name(), $this->final_elements)) { + throw new base_element_struct_exception('baseelementchildnameconflict', $element->get_name()); + } $grandparent = $this->get_grandoptigroupelement_or_grandparent(); if ($existing = array_intersect($grandparent->get_used(), $element->get_used())) { // Check the element isn't being used already throw new base_element_struct_exception('baseelementexisting', implode($existing)); diff --git a/backup/util/structure/simpletest/testbasenestedelement.php b/backup/util/structure/simpletest/testbasenestedelement.php index 0f5ed645eed..206fab00e9c 100644 --- a/backup/util/structure/simpletest/testbasenestedelement.php +++ b/backup/util/structure/simpletest/testbasenestedelement.php @@ -393,5 +393,16 @@ class base_nested_element_test extends UnitTestCase { $this->assertTrue($e instanceof base_element_parent_exception); } + // Add child element already used by own final elements + $nested = new mock_base_nested_element('PARENT1', null, array('FINAL1', 'FINAL2')); + $child = new mock_base_nested_element('FINAL2', null, array('FINAL3', 'FINAL4')); + try { + $nested->add_child($child); + $this->fail("Expecting base_element_struct_exception exception, none occurred"); + } catch (Exception $e) { + $this->assertTrue($e instanceof base_element_struct_exception); + $this->assertEqual($e->errorcode, 'baseelementchildnameconflict'); + $this->assertEqual($e->a, 'FINAL2'); + } } } diff --git a/backup/util/xml/parser/processors/simplified_parser_processor.class.php b/backup/util/xml/parser/processors/simplified_parser_processor.class.php index 9dbe9b8a0fc..e87ac9d556f 100644 --- a/backup/util/xml/parser/processors/simplified_parser_processor.class.php +++ b/backup/util/xml/parser/processors/simplified_parser_processor.class.php @@ -104,13 +104,18 @@ abstract class simplified_parser_processor extends progressive_parser_processor $alltagswhitespace = false; continue; } + // If the path including the tag name matches another selected path - // (registered or parent) delete it, another chunk will contain that info + // (registered or parent) and is null or begins with linefeed, we know it's part + // of another chunk, delete it, another chunk will contain that info if ($this->path_is_selected($path . '/' . $key) || $this->path_is_selected_parent($path . '/' . $key)) { - unset($data['tags'][$key]); - continue; + if (!isset($value['cdata']) || substr($value['cdata'], 0, 1) === "\n") { + unset($data['tags'][$key]); + continue; + } } + // Convert to simple name => value array $data['tags'][$key] = isset($value['cdata']) ? $value['cdata'] : null; diff --git a/backup/util/xml/parser/progressive_parser.class.php b/backup/util/xml/parser/progressive_parser.class.php index 11f3c9c682e..91bc85fc4b2 100644 --- a/backup/util/xml/parser/progressive_parser.class.php +++ b/backup/util/xml/parser/progressive_parser.class.php @@ -201,7 +201,12 @@ class progressive_parser { // Entering a new inner level, publish all the information available if ($this->level > $this->prevlevel) { if (!empty($this->currtag) && (!empty($this->currtag['attrs']) || !empty($this->currtag['cdata']))) { - $this->topush['tags'][$this->currtag['name']] = $this->currtag; + // We always add the last not-empty repetition. Empty ones are ignored. + if (isset($this->topush['tags'][$this->currtag['name']]) && trim($this->currtag['cdata']) === '') { + // Do nothing, the tag already exists and the repetition is empty + } else { + $this->topush['tags'][$this->currtag['name']] = $this->currtag; + } } if (!empty($this->topush['tags'])) { $this->publish($this->topush); @@ -233,7 +238,12 @@ class progressive_parser { // Ending rencently started tag, add value to current tag if ($this->level == $this->prevlevel) { $this->currtag['cdata'] = $this->postprocess_cdata($this->accum); - $this->topush['tags'][$this->currtag['name']] = $this->currtag; + // We always add the last not-empty repetition. Empty ones are ignored. + if (isset($this->topush['tags'][$this->currtag['name']]) && trim($this->currtag['cdata']) === '') { + // Do nothing, the tag already exists and the repetition is empty + } else { + $this->topush['tags'][$this->currtag['name']] = $this->currtag; + } $this->currtag = array(); } diff --git a/backup/util/xml/parser/simpletest/fixtures/test4.xml b/backup/util/xml/parser/simpletest/fixtures/test4.xml index f5865c47da9..4a4fa67416c 100755 --- a/backup/util/xml/parser/simpletest/fixtures/test4.xml +++ b/backup/util/xml/parser/simpletest/fixtures/test4.xml @@ -106,6 +106,9 @@ 4 4 5 + + + diff --git a/mod/lesson/backup/moodle2/backup_lesson_stepslib.php b/mod/lesson/backup/moodle2/backup_lesson_stepslib.php index 0b90f423dea..c55bffe0a92 100644 --- a/mod/lesson/backup/moodle2/backup_lesson_stepslib.php +++ b/mod/lesson/backup/moodle2/backup_lesson_stepslib.php @@ -76,8 +76,11 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st 'minquestions','maxpages','timed','maxtime','retake','activitylink', 'mediafile','mediaheight','mediawidth','mediaclose','slideshow', 'width','height','bgcolor','displayleft','displayleftif','progressbar', - 'highscores','maxhighscores','available','deadline','timemodified' + 'showhighscores','maxhighscores','available','deadline','timemodified' )); + // Tell the lesson element about the showhighscores elements mapping to the highscores + // database field. + $lesson->set_source_alias('highscores', 'showhighscores'); // The lesson_pages table // Grouped within a `pages` element, important to note that page is relational diff --git a/mod/lesson/backup/moodle2/restore_lesson_stepslib.php b/mod/lesson/backup/moodle2/restore_lesson_stepslib.php index b716355b64c..00dc383f09d 100644 --- a/mod/lesson/backup/moodle2/restore_lesson_stepslib.php +++ b/mod/lesson/backup/moodle2/restore_lesson_stepslib.php @@ -62,6 +62,13 @@ class restore_lesson_activity_structure_step extends restore_activity_structure_ $data->deadline = $this->apply_date_offset($data->deadline); $data->timemodified = $this->apply_date_offset($data->timemodified); + // lesson->highscores can come both in data->highscores and + // data->showhighscores, handle both. MDL-26229 + if (isset($data->showhighscores)) { + $data->highscores = $data->showhighscores; + unset($data->showhighscores); + } + // insert the lesson record $newitemid = $DB->insert_record('lesson', $data); // immediately after inserting "activity" record, call this diff --git a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php index 7a05c5a49c1..655cb5f8474 100644 --- a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php @@ -39,7 +39,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru // Define each element separated $quiz = new backup_nested_element('quiz', array('id'), array( 'name', 'intro', 'introformat', 'timeopen', - 'timeclose', 'optionflags', 'penaltyscheme', 'attempts', + 'timeclose', 'optionflags', 'penaltyscheme', 'attempts_number', 'attemptonlast', 'grademethod', 'decimalpoints', 'questiondecimalpoints', 'review', 'questionsperpage', 'shufflequestions', 'shuffleanswers', 'questions', 'sumgrades', 'grade', 'timecreated', @@ -119,6 +119,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru } // Define source alias + $quiz->set_source_alias('attempts', 'attempts_number'); $grade->set_source_alias('grade', 'gradeval'); $attempt->set_source_alias('attempt', 'attemptnum'); diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index 30f8baa0ae2..72222419322 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -67,6 +67,13 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st $data->questions = $this->questions_recode_layout($data->questions); + // quiz->attempts can come both in data->attempts and + // data->attempts_number, handle both. MDL-26229 + if (isset($data->attempts_number)) { + $data->attempts = $data->attempts_number; + unset($data->attempts_number); + } + // insert the quiz record $newitemid = $DB->insert_record('quiz', $data); // immediately after inserting "activity" record, call this