mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 16:36:37 +02:00
Merge remote-tracking branch 'moodle/master' into MDL-20636_master_new_question_engine
Conflicts: lib/db/upgrade.php mod/quiz/lib.php
This commit is contained in:
commit
54771d89d1
290 changed files with 4637 additions and 1419 deletions
34
backup/moodle2/backup_coursereport_plugin.class.php
Normal file
34
backup/moodle2/backup_coursereport_plugin.class.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Base class for course report backup plugins.
|
||||
*
|
||||
* NOTE: When you back up a course, it potentially may run backup for all
|
||||
* course reports. In order to control whether a particular report gets
|
||||
* backed up, a course report should make use of the second and third
|
||||
* parameters in get_plugin_element().
|
||||
*
|
||||
* @package moodlecore
|
||||
* @subpackage backup-moodle2
|
||||
* @copyright 2011 onwards The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class backup_coursereport_plugin extends backup_plugin {
|
||||
// Use default parent behaviour
|
||||
}
|
|
@ -36,6 +36,7 @@ require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
|
|||
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_subplugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php');
|
||||
|
|
|
@ -417,6 +417,10 @@ class backup_course_structure_step extends backup_structure_step {
|
|||
// save course data (in case of user theme, legacy theme, etc)
|
||||
$this->add_plugin_structure('theme', $course, true);
|
||||
|
||||
// attach course report plugin structure to $course element; multiple
|
||||
// course reports can save course data if required
|
||||
$this->add_plugin_structure('coursereport', $course, true);
|
||||
|
||||
// attach plagiarism plugin structure to $course element, only one allowed
|
||||
$this->add_plugin_structure('plagiarism', $course, false);
|
||||
|
||||
|
|
29
backup/moodle2/restore_coursereport_plugin.class.php
Normal file
29
backup/moodle2/restore_coursereport_plugin.class.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Restore for course plugin: course report.
|
||||
*
|
||||
* @package moodlecore
|
||||
* @subpackage backup-moodle2
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class restore_coursereport_plugin extends restore_plugin {
|
||||
// Use default parent behaviour
|
||||
}
|
|
@ -35,11 +35,13 @@ require_once($CFG->dirroot . '/backup/moodle2/restore_plugin.class.php');
|
|||
require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_format_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_theme_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_coursereport_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_plagiarism_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php');
|
||||
|
|
|
@ -80,6 +80,23 @@ abstract class restore_plugin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* after_restore dispatcher for any restore_plugin class
|
||||
*
|
||||
* This method will dispatch execution to the corresponding
|
||||
* after_restore_xxx() method when available, with xxx
|
||||
* being the connection point of the instance, so plugin
|
||||
* classes with multiple connection points will support
|
||||
* multiple after_restore methods, one for each connection point
|
||||
*/
|
||||
public function launch_after_restore_methods() {
|
||||
// Check if the after_restore method exists and launch it
|
||||
$afterrestore = 'after_restore_' . basename($this->connectionpoint->get_path());
|
||||
if (method_exists($this, $afterrestore)) {
|
||||
$this->$afterrestore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one array with all the decode contents
|
||||
* to be processed by the links decoder
|
||||
|
|
|
@ -1010,6 +1010,9 @@ class restore_course_structure_step extends restore_structure_step {
|
|||
// Apply for 'theme' plugins optional paths at course level
|
||||
$this->add_plugin_structure('theme', $course);
|
||||
|
||||
// Apply for 'course report' plugins optional paths at course level
|
||||
$this->add_plugin_structure('coursereport', $course);
|
||||
|
||||
// Apply for plagiarism plugins optional paths at course level
|
||||
$this->add_plugin_structure('plagiarism', $course);
|
||||
|
||||
|
|
|
@ -360,6 +360,43 @@ abstract class restore_structure_step extends restore_step {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch all the after_restore methods present in all the processing objects
|
||||
*
|
||||
* This method will launch all the after_restore methods that can be defined
|
||||
* both in restore_plugin class
|
||||
*
|
||||
* For restore_plugin classes the name of the method to be executed will be
|
||||
* "after_restore_" + connection point (as far as can be multiple connection
|
||||
* points in the same class)
|
||||
*/
|
||||
public function launch_after_restore_methods() {
|
||||
$alreadylaunched = array(); // To avoid multiple executions
|
||||
foreach ($this->pathelements as $pathelement) {
|
||||
// Get the processing object
|
||||
$pobject = $pathelement->get_processing_object();
|
||||
// Skip null processors (child of grouped ones for sure)
|
||||
if (is_null($pobject)) {
|
||||
continue;
|
||||
}
|
||||
// Skip restore structure step processors (this)
|
||||
if ($pobject instanceof restore_structure_step) {
|
||||
continue;
|
||||
}
|
||||
// Skip already launched processing objects
|
||||
if (in_array($pobject, $alreadylaunched, true)) {
|
||||
continue;
|
||||
}
|
||||
// Add processing object to array of launched ones
|
||||
$alreadylaunched[] = $pobject;
|
||||
// If the processing object has support for
|
||||
// launching after_restore methods, use it
|
||||
if (method_exists($pobject, 'launch_after_restore_methods')) {
|
||||
$pobject->launch_after_restore_methods();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be executed after the whole structure step have been processed
|
||||
*
|
||||
|
|
|
@ -100,6 +100,13 @@ abstract class restore_task extends base_task {
|
|||
* method if available
|
||||
*/
|
||||
public function execute_after_restore() {
|
||||
if ($this->executed) {
|
||||
foreach ($this->steps as $step) {
|
||||
if (method_exists($step, 'launch_after_restore_methods')) {
|
||||
$step->launch_after_restore_methods();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->executed && method_exists($this, 'after_restore')) {
|
||||
$this->after_restore();
|
||||
}
|
||||
|
|
|
@ -71,20 +71,23 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Notify start of path if selected and not under grouped
|
||||
* The parser fires this each time one path is going to be parsed
|
||||
*
|
||||
* @param string $path xml path which parsing has started
|
||||
*/
|
||||
public function before_path($path) {
|
||||
if ($this->path_is_selected($path) && !$this->grouped_parent_exists($path)) {
|
||||
if (!$this->grouped_parent_exists($path)) {
|
||||
parent::before_path($path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dispatch grouped chunks safely once their end tag happens.
|
||||
* Also notify end of path if selected and not under grouped
|
||||
* The parser fires this each time one path has been parsed
|
||||
*
|
||||
* @param string $path xml path which parsing has ended
|
||||
*/
|
||||
public function after_path($path) {
|
||||
// Have finished one grouped path, dispatch it
|
||||
if ($this->path_is_grouped($path)) {
|
||||
// Any accumulated information must be in
|
||||
// currentdata, properly built
|
||||
|
@ -95,7 +98,7 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
|
|||
}
|
||||
// Normal notification of path end
|
||||
// Only if path is selected and not child of grouped
|
||||
if ($this->path_is_selected($path) && !$this->grouped_parent_exists($path)) {
|
||||
if (!$this->grouped_parent_exists($path)) {
|
||||
parent::after_path($path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,12 +41,14 @@ abstract class simplified_parser_processor extends progressive_parser_processor
|
|||
protected $paths; // array of paths we are interested on
|
||||
protected $parentpaths; // array of parent paths of the $paths
|
||||
protected $parentsinfo; // array of parent attributes to be added as child tags
|
||||
protected $startendinfo;// array (stack) of startend information
|
||||
|
||||
public function __construct(array $paths) {
|
||||
public function __construct(array $paths = array()) {
|
||||
parent::__construct();
|
||||
$this->paths = array();
|
||||
$this->parentpaths = array();
|
||||
$this->parentsinfo = array();
|
||||
$this->startendinfo = array();
|
||||
// Add paths and parentpaths. We are looking for attributes there
|
||||
foreach ($paths as $key => $path) {
|
||||
$this->add_path($path);
|
||||
|
@ -95,6 +97,10 @@ abstract class simplified_parser_processor extends progressive_parser_processor
|
|||
|
||||
// If the path is a registered one, let's process it
|
||||
if ($this->path_is_selected($path)) {
|
||||
|
||||
// Send all the pending notify_path_start/end() notifications
|
||||
$this->process_pending_startend_notifications($path, 'start');
|
||||
|
||||
// First of all, look for attributes available at parentsinfo
|
||||
// in order to get them available as normal tags
|
||||
if (isset($this->parentsinfo[$parentpath][$tag]['attrs'])) {
|
||||
|
@ -146,29 +152,86 @@ abstract class simplified_parser_processor extends progressive_parser_processor
|
|||
} else {
|
||||
$this->chunks--; // Chunk skipped
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parser fires this each time one path is going to be parsed
|
||||
*
|
||||
* @param string $path xml path which parsing has started
|
||||
*/
|
||||
public function before_path($path) {
|
||||
if ($this->path_is_selected($path)) {
|
||||
$this->notify_path_start($path);
|
||||
$this->startendinfo[] = array('path' => $path, 'action' => 'start');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The parser fires this each time one path has been parsed
|
||||
*
|
||||
* @param string $path xml path which parsing has ended
|
||||
*/
|
||||
public function after_path($path) {
|
||||
$toprocess = false;
|
||||
// If the path being closed matches (same or parent) the first path in the stack
|
||||
// we process pending startend notifications until one matching end is found
|
||||
if ($element = reset($this->startendinfo)) {
|
||||
$elepath = $element['path'];
|
||||
$eleaction = $element['action'];
|
||||
if (strpos($elepath, $path) === 0) {
|
||||
$toprocess = true;
|
||||
}
|
||||
|
||||
// Also, if the stack of startend notifications is empty, we can process current end
|
||||
// path safely
|
||||
} else {
|
||||
$toprocess = true;
|
||||
}
|
||||
if ($this->path_is_selected($path)) {
|
||||
$this->notify_path_end($path);
|
||||
$this->startendinfo[] = array('path' => $path, 'action' => 'end');
|
||||
}
|
||||
// Send all the pending startend notifications if decided to do so
|
||||
if ($toprocess) {
|
||||
$this->process_pending_startend_notifications($path, 'end');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Protected API starts here
|
||||
|
||||
/**
|
||||
* Adjust start/end til finding one match start/end path (included)
|
||||
*
|
||||
* This will trigger all the pending {@see notify_path_start} and
|
||||
* {@see notify_path_end} calls for one given path and action
|
||||
*
|
||||
* @param string path the path to look for as limit
|
||||
* @param string action the action to look for as limit
|
||||
*/
|
||||
protected function process_pending_startend_notifications($path, $action) {
|
||||
|
||||
// Iterate until one matching path and action is found (or the array is empty)
|
||||
$elecount = count($this->startendinfo);
|
||||
$elematch = false;
|
||||
while ($elecount > 0 && !$elematch) {
|
||||
$element = array_shift($this->startendinfo);
|
||||
$elecount--;
|
||||
$elepath = $element['path'];
|
||||
$eleaction = $element['action'];
|
||||
|
||||
if ($elepath == $path && $eleaction == $action) {
|
||||
$elematch = true;
|
||||
}
|
||||
|
||||
if ($eleaction == 'start') {
|
||||
$this->notify_path_start($elepath);
|
||||
} else {
|
||||
$this->notify_path_end($elepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function postprocess_chunk($data) {
|
||||
$this->dispatch_chunk($data);
|
||||
}
|
||||
|
|
24
backup/util/xml/parser/simpletest/fixtures/test5.xml
Normal file
24
backup/util/xml/parser/simpletest/fixtures/test5.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<MOODLE_BACKUP>
|
||||
<COURSE ID="100">
|
||||
<SECTIONS>
|
||||
<SECTION>
|
||||
<ID>200</ID>
|
||||
<MODS>
|
||||
<MOD>
|
||||
<ID>300</ID>
|
||||
<ROLES_OVERRIDES>
|
||||
</ROLES_OVERRIDES>
|
||||
</MOD>
|
||||
<MOD />
|
||||
<MOD />
|
||||
<MOD ID="400" />
|
||||
<MOD>
|
||||
<ID>500</ID>
|
||||
</MOD>
|
||||
<MOD />
|
||||
<MOD />
|
||||
</MODS>
|
||||
</SECTION>
|
||||
</SECTIONS>
|
||||
</COURSE>
|
||||
</MOODLE_BACKUP>
|
|
@ -330,6 +330,80 @@ class progressive_parser_test extends UnitTestCase {
|
|||
sort($snotifs);
|
||||
sort($enotifs);
|
||||
$this->assertEqual($snotifs, $enotifs);
|
||||
|
||||
// Now verify that the start/process/end order is correct
|
||||
$allnotifs = $pr->get_all_notifications();
|
||||
$this->assertEqual(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
|
||||
// Check integrity of the notifications
|
||||
$errcount = $this->helper_check_notifications_order_integrity($allnotifs);
|
||||
$this->assertEqual($errcount, 0); // No errors found, plz
|
||||
}
|
||||
|
||||
/**
|
||||
* test how the simplified processor and the order of start/process/end events happens
|
||||
* with one real fragment of one backup 1.9 file, where some problems
|
||||
* were found by David, hence we honor him in the name of the test ;-)
|
||||
*/
|
||||
function test_simplified_david_backup19_file_fragment() {
|
||||
global $CFG;
|
||||
// Instantiate progressive_parser
|
||||
$pp = new progressive_parser();
|
||||
// Instantiate grouped_parser_processor
|
||||
$pr = new mock_simplified_parser_processor();
|
||||
// Add interesting paths
|
||||
$pr->add_path('/MOODLE_BACKUP/COURSE');
|
||||
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
|
||||
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
|
||||
$this->assertTrue($pr instanceof progressive_parser_processor);
|
||||
// Assign processor to parser
|
||||
$pp->set_processor($pr);
|
||||
// Set file from fixtures
|
||||
$pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test5.xml');
|
||||
// Process the file
|
||||
$pp->process();
|
||||
|
||||
// Get all the simplified chunks and perform various validations
|
||||
$chunks = $pr->get_chunks();
|
||||
$this->assertEqual(count($chunks), 3); // Only 3, because 7 (COURSE, ROLES_OVERRIDES and 5 MOD) are empty, aka no chunk
|
||||
|
||||
// Now check start notifications
|
||||
$snotifs = $pr->get_start_notifications();
|
||||
// Check we have received the correct number of notifications
|
||||
$this->assertEqual(count($snotifs), 10); // Start tags are dispatched for empties (ROLES_OVERRIDES)
|
||||
// Check first and last notifications
|
||||
$this->assertEqual($snotifs[0], '/MOODLE_BACKUP/COURSE');
|
||||
$this->assertEqual($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
|
||||
$this->assertEqual($snotifs[2], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$this->assertEqual($snotifs[3], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
|
||||
$this->assertEqual($snotifs[7], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$this->assertEqual($snotifs[8], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$this->assertEqual($snotifs[9], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
|
||||
// Now check end notifications
|
||||
$enotifs = $pr->get_end_notifications();
|
||||
// Check we have received the correct number of notifications
|
||||
$this->assertEqual(count($snotifs), 10); // End tags are dispatched for empties (ROLES_OVERRIDES)
|
||||
// Check first, and last notifications
|
||||
$this->assertEqual($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
|
||||
$this->assertEqual($enotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$this->assertEqual($enotifs[2], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$this->assertEqual($enotifs[3], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$this->assertEqual($enotifs[7], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$this->assertEqual($enotifs[8], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
|
||||
$this->assertEqual($enotifs[9], '/MOODLE_BACKUP/COURSE');
|
||||
|
||||
// Check start and end notifications are balanced
|
||||
sort($snotifs);
|
||||
sort($enotifs);
|
||||
$this->assertEqual($snotifs, $enotifs);
|
||||
|
||||
// Now verify that the start/process/end order is correct
|
||||
$allnotifs = $pr->get_all_notifications();
|
||||
$this->assertEqual(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
|
||||
// Check integrity of the notifications
|
||||
$errcount = $this->helper_check_notifications_order_integrity($allnotifs);
|
||||
$this->assertEqual($errcount, 0); // No errors found, plz
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -498,6 +572,127 @@ class progressive_parser_test extends UnitTestCase {
|
|||
sort($snotifs);
|
||||
sort($enotifs);
|
||||
$this->assertEqual($snotifs, $enotifs);
|
||||
|
||||
// Now verify that the start/process/end order is correct
|
||||
$allnotifs = $pr->get_all_notifications();
|
||||
$this->assertEqual(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
|
||||
// Check integrity of the notifications
|
||||
$errcount = $this->helper_check_notifications_order_integrity($allnotifs);
|
||||
$this->assertEqual($errcount, 0); // No errors found, plz
|
||||
}
|
||||
|
||||
/**
|
||||
* test how the grouped processor and the order of start/process/end events happens
|
||||
* with one real fragment of one backup 1.9 file, where some problems
|
||||
* were found by David, hence we honor him in the name of the test ;-)
|
||||
*/
|
||||
function test_grouped_david_backup19_file_fragment() {
|
||||
global $CFG;
|
||||
// Instantiate progressive_parser
|
||||
$pp = new progressive_parser();
|
||||
// Instantiate grouped_parser_processor
|
||||
$pr = new mock_grouped_parser_processor();
|
||||
// Add interesting paths
|
||||
$pr->add_path('/MOODLE_BACKUP/COURSE');
|
||||
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION', true);
|
||||
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
|
||||
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
|
||||
$this->assertTrue($pr instanceof progressive_parser_processor);
|
||||
// Assign processor to parser
|
||||
$pp->set_processor($pr);
|
||||
// Set file from fixtures
|
||||
$pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test5.xml');
|
||||
// Process the file
|
||||
$pp->process();
|
||||
|
||||
// Get all the simplified chunks and perform various validations
|
||||
$chunks = $pr->get_chunks();
|
||||
$this->assertEqual(count($chunks), 1); // Only 1, the SECTION one
|
||||
|
||||
// Now check start notifications
|
||||
$snotifs = $pr->get_start_notifications();
|
||||
// Check we have received the correct number of notifications
|
||||
$this->assertEqual(count($snotifs), 2);
|
||||
// Check first and last notifications
|
||||
$this->assertEqual($snotifs[0], '/MOODLE_BACKUP/COURSE');
|
||||
$this->assertEqual($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
|
||||
|
||||
// Now check end notifications
|
||||
$enotifs = $pr->get_end_notifications();
|
||||
// Check we have received the correct number of notifications
|
||||
$this->assertEqual(count($snotifs), 2); // End tags are dispatched for empties (ROLES_OVERRIDES)
|
||||
// Check first, and last notifications
|
||||
$this->assertEqual($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
|
||||
$this->assertEqual($enotifs[1], '/MOODLE_BACKUP/COURSE');
|
||||
|
||||
// Check start and end notifications are balanced
|
||||
sort($snotifs);
|
||||
sort($enotifs);
|
||||
$this->assertEqual($snotifs, $enotifs);
|
||||
|
||||
// Now verify that the start/process/end order is correct
|
||||
$allnotifs = $pr->get_all_notifications();
|
||||
$this->assertEqual(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
|
||||
// Check integrity of the notifications
|
||||
$errcount = $this->helper_check_notifications_order_integrity($allnotifs);
|
||||
$this->assertEqual($errcount, 0); // No errors found, plz
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function that given one array of ordered start/process/end notifications will
|
||||
* check it of integrity like:
|
||||
* - process only happens if start is the previous notification
|
||||
* - end only happens if dispatch is the previous notification
|
||||
* - start only happen with level > than last one and if there is no already started like that
|
||||
*
|
||||
* @param array $notifications ordered array of notifications with format [start|process|end]:path
|
||||
* @return int number of integrity problems found (errors)
|
||||
*/
|
||||
function helper_check_notifications_order_integrity($notifications) {
|
||||
$numerrors = 0;
|
||||
$notifpile = array('pilebase' => 'start');
|
||||
$lastnotif = 'start:pilebase';
|
||||
foreach ($notifications as $notif) {
|
||||
|
||||
$lastpiletype = end($notifpile);
|
||||
$lastpilepath = key($notifpile);
|
||||
$lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpilepath));
|
||||
|
||||
$lastnotiftype = preg_replace('/:.*/', '', $lastnotif);
|
||||
$lastnotifpath = preg_replace('/.*:/', '', $lastnotif);
|
||||
$lastnotiflevel = strlen(preg_replace('/[^\/]/', '', $lastnotifpath));
|
||||
|
||||
$notiftype = preg_replace('/:.*/', '', $notif);
|
||||
$notifpath = preg_replace('/.*:/', '', $notif);
|
||||
$notiflevel = strlen(preg_replace('/[^\/]/', '', $notifpath));
|
||||
|
||||
switch ($notiftype) {
|
||||
case 'process':
|
||||
if ($lastnotifpath != $notifpath or $lastnotiftype != 'start') {
|
||||
$numerrors++; // Only start for same path from last notification is allowed before process
|
||||
}
|
||||
$notifpile[$notifpath] = 'process'; // Update the status in the pile
|
||||
break;
|
||||
case 'end':
|
||||
if ($lastpilepath != $notifpath or ($lastpiletype != 'process' and $lastpiletype != 'start')) {
|
||||
$numerrors++; // Only process and start for same path from last pile is allowed before end
|
||||
}
|
||||
unset($notifpile[$notifpath]); // Delete from the pile
|
||||
break;
|
||||
case 'start':
|
||||
if (array_key_exists($notifpath, $notifpile) or $notiflevel <= $lastpilelevel) {
|
||||
$numerrors++; // Only non existing in pile and with level > last pile is allowed on start
|
||||
}
|
||||
$notifpile[$notifpath] = 'start'; // Add to the pile
|
||||
break;
|
||||
default:
|
||||
$numerrors++; // Incorrect type of notification => error
|
||||
}
|
||||
// Update lastnotif
|
||||
$lastnotif = $notif;
|
||||
}
|
||||
return $numerrors;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -572,17 +767,21 @@ class mock_simplified_parser_processor extends simplified_parser_processor {
|
|||
private $chunksarr = array(); // To accumulate the found chunks
|
||||
private $startarr = array(); // To accumulate all the notified path starts
|
||||
private $endarr = array(); // To accumulate all the notified path ends
|
||||
private $allnotif = array(); // To accumulate all the notified and dispatched events in an ordered way
|
||||
|
||||
public function dispatch_chunk($data) {
|
||||
$this->chunksarr[] = $data;
|
||||
$this->allnotif[] = 'process:' . $data['path'];
|
||||
}
|
||||
|
||||
public function notify_path_start($path) {
|
||||
$this->startarr[] = $path;
|
||||
$this->allnotif[] = 'start:' . $path;
|
||||
}
|
||||
|
||||
public function notify_path_end($path) {
|
||||
$this->endarr[] = $path;
|
||||
$this->allnotif[] = 'end:' . $path;
|
||||
}
|
||||
|
||||
public function get_chunks() {
|
||||
|
@ -596,6 +795,10 @@ class mock_simplified_parser_processor extends simplified_parser_processor {
|
|||
public function get_end_notifications() {
|
||||
return $this->endarr;
|
||||
}
|
||||
|
||||
public function get_all_notifications() {
|
||||
return $this->allnotif;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -606,17 +809,21 @@ class mock_grouped_parser_processor extends grouped_parser_processor {
|
|||
private $chunksarr = array(); // To accumulate the found chunks
|
||||
private $startarr = array(); // To accumulate all the notified path starts
|
||||
private $endarr = array(); // To accumulate all the notified path ends
|
||||
private $allnotif = array(); // To accumulate all the notified and dispatched events in an ordered way
|
||||
|
||||
public function dispatch_chunk($data) {
|
||||
$this->chunksarr[] = $data;
|
||||
$this->allnotif[] = 'process:' . $data['path'];
|
||||
}
|
||||
|
||||
public function notify_path_start($path) {
|
||||
$this->startarr[] = $path;
|
||||
$this->allnotif[] = 'start:' . $path;
|
||||
}
|
||||
|
||||
public function notify_path_end($path) {
|
||||
$this->endarr[] = $path;
|
||||
$this->allnotif[] = 'end:' . $path;
|
||||
}
|
||||
|
||||
public function get_chunks() {
|
||||
|
@ -630,4 +837,8 @@ class mock_grouped_parser_processor extends grouped_parser_processor {
|
|||
public function get_end_notifications() {
|
||||
return $this->endarr;
|
||||
}
|
||||
|
||||
public function get_all_notifications() {
|
||||
return $this->allnotif;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue