mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
MDL-46455 backup: Extend support for subplugins to any plugin.
In order to implement the backup and restore of log stores, that are created as subplugins of the tool_log plugin , we need to extend subplugins support from activities to virtually any plugin. Basically that implies moving the add_subplugin_structure() method from its current, restricted, activity level to general backup_structure_step. This commit implements the change in backup, covered with tests verifying old, bc behavior and also new, general one.
This commit is contained in:
parent
74fad2ce3d
commit
ba66edd074
@ -91,64 +91,10 @@ class create_taskbasepath_directory extends backup_execution_step {
|
||||
|
||||
/**
|
||||
* Abstract structure step, parent of all the activity structure steps. Used to wrap the
|
||||
* activity structure definition within the main <activity ...> tag. Also provides
|
||||
* subplugin support for activities (that must be properly declared)
|
||||
* activity structure definition within the main <activity ...> tag.
|
||||
*/
|
||||
abstract class backup_activity_structure_step extends backup_structure_step {
|
||||
|
||||
/**
|
||||
* Add subplugin structure to any element in the activity backup tree
|
||||
*
|
||||
* @param string $subplugintype type of subplugin as defined in activity db/subplugins.php
|
||||
* @param backup_nested_element $element element in the activity backup tree that
|
||||
* we are going to add subplugin information to
|
||||
* @param bool $multiple to define if multiple subplugins can produce information
|
||||
* for each instance of $element (true) or no (false)
|
||||
* @return void
|
||||
*/
|
||||
protected function add_subplugin_structure($subplugintype, $element, $multiple) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
// Check the requested subplugintype is a valid one
|
||||
$subpluginsfile = $CFG->dirroot . '/mod/' . $this->task->get_modulename() . '/db/subplugins.php';
|
||||
if (!file_exists($subpluginsfile)) {
|
||||
throw new backup_step_exception('activity_missing_subplugins_php_file', $this->task->get_modulename());
|
||||
}
|
||||
include($subpluginsfile);
|
||||
if (!array_key_exists($subplugintype, $subplugins)) {
|
||||
throw new backup_step_exception('incorrect_subplugin_type', $subplugintype);
|
||||
}
|
||||
|
||||
// Arrived here, subplugin is correct, let's create the optigroup
|
||||
$optigroupname = $subplugintype . '_' . $element->get_name() . '_subplugin';
|
||||
$optigroup = new backup_optigroup($optigroupname, null, $multiple);
|
||||
$element->add_child($optigroup); // Add optigroup to stay connected since beginning
|
||||
|
||||
// Get all the optigroup_elements, looking across all the subplugin dirs
|
||||
$subpluginsdirs = core_component::get_plugin_list($subplugintype);
|
||||
foreach ($subpluginsdirs as $name => $subpluginsdir) {
|
||||
$classname = 'backup_' . $subplugintype . '_' . $name . '_subplugin';
|
||||
$backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php';
|
||||
if (file_exists($backupfile)) {
|
||||
require_once($backupfile);
|
||||
$backupsubplugin = new $classname($subplugintype, $name, $optigroup, $this);
|
||||
// Add subplugin returned structure to optigroup
|
||||
$backupsubplugin->define_subplugin_structure($element->get_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* As far as activity backup steps are implementing backup_subplugin stuff, they need to
|
||||
* have the parent task available for wrapping purposes (get course/context....)
|
||||
*
|
||||
* @return backup_activity_task
|
||||
*/
|
||||
public function get_task() {
|
||||
return $this->task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps any activity backup structure within the common 'activity' element
|
||||
* that will include common to all activities information like id, context...
|
||||
|
@ -162,6 +162,83 @@ abstract class backup_structure_step extends backup_step {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add subplugin structure for a given plugin to any element in the structure backup tree.
|
||||
*
|
||||
* This method allows the injection of subplugins (of a specified plugin) data to any
|
||||
* element in any backup structure.
|
||||
*
|
||||
* NOTE: Initially subplugins were only available for activities (mod), so only the
|
||||
* {@link backup_activity_structure_step} class had support for them, always
|
||||
* looking for /mod/modulenanme subplugins. This new method is a generalization of the
|
||||
* existing one for activities, supporting all subplugins injecting information everywhere.
|
||||
*
|
||||
* @param string $subplugintype type of subplugin as defined in plugin's db/subplugins.php.
|
||||
* @param backup_nested_element $element element in the backup tree (anywhere) that
|
||||
* we are going to add subplugin information to.
|
||||
* @param bool $multiple to define if multiple subplugins can produce information
|
||||
* for each instance of $element (true) or no (false).
|
||||
* @param string $plugintype type of the plugin.
|
||||
* @param string $pluginname name of the plugin.
|
||||
* @return void
|
||||
*/
|
||||
protected function add_subplugin_structure($subplugintype, $element, $multiple, $plugintype = null, $pluginname = null) {
|
||||
|
||||
// Verify if this is a BC call for an activity backup. See NOTE above for this special case.
|
||||
if ($plugintype === null and $pluginname === null) {
|
||||
$plugintype = 'mod';
|
||||
$pluginname = $this->task->get_modulename();
|
||||
// TODO: Once all the calls have been changed to add both not null plugintype and pluginname, add a debugging here.
|
||||
}
|
||||
|
||||
// Check the requested plugintype is a valid one.
|
||||
if (!array_key_exists($plugintype, core_component::get_plugin_types())) {
|
||||
throw new backup_step_exception('incorrect_plugin_type', $plugintype);
|
||||
}
|
||||
|
||||
// Check the requested pluginname, for the specified plugintype, is a valid one.
|
||||
if (!array_key_exists($pluginname, core_component::get_plugin_list($plugintype))) {
|
||||
throw new backup_step_exception('incorrect_plugin_name', array($plugintype, $pluginname));
|
||||
}
|
||||
|
||||
// Check the requested subplugintype is a valid one.
|
||||
$subpluginsfile = core_component::get_component_directory($plugintype . '_' . $pluginname) . '/db/subplugins.php';
|
||||
if (!file_exists($subpluginsfile)) {
|
||||
throw new backup_step_exception('plugin_missing_subplugins_php_file', array($plugintype, $pluginname));
|
||||
}
|
||||
include($subpluginsfile);
|
||||
if (!array_key_exists($subplugintype, $subplugins)) {
|
||||
throw new backup_step_exception('incorrect_subplugin_type', $subplugintype);
|
||||
}
|
||||
|
||||
// Arrived here, subplugin is correct, let's create the optigroup.
|
||||
$optigroupname = $subplugintype . '_' . $element->get_name() . '_subplugin';
|
||||
$optigroup = new backup_optigroup($optigroupname, null, $multiple);
|
||||
$element->add_child($optigroup); // Add optigroup to stay connected since beginning.
|
||||
|
||||
// Every subplugin optionally can have a common/parent subplugin
|
||||
// class for shared stuff.
|
||||
$parentclass = 'backup_' . $plugintype . '_' . $pluginname . '_' . $subplugintype . '_subplugin';
|
||||
$parentfile = core_component::get_component_directory($plugintype . '_' . $pluginname) .
|
||||
'/backup/moodle2/' . $parentclass . '.class.php';
|
||||
if (file_exists($parentfile)) {
|
||||
require_once($parentfile);
|
||||
}
|
||||
|
||||
// Get all the optigroup_elements, looking over all the subplugin dirs.
|
||||
$subpluginsdirs = core_component::get_plugin_list($subplugintype);
|
||||
foreach ($subpluginsdirs as $name => $subpluginsdir) {
|
||||
$classname = 'backup_' . $subplugintype . '_' . $name . '_subplugin';
|
||||
$backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php';
|
||||
if (file_exists($backupfile)) {
|
||||
require_once($backupfile);
|
||||
$backupsubplugin = new $classname($subplugintype, $name, $optigroup, $this);
|
||||
// Add subplugin returned structure to optigroup.
|
||||
$backupsubplugin->define_subplugin_structure($element->get_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To conditionally decide if one step will be executed or no
|
||||
*
|
||||
@ -175,8 +252,9 @@ abstract class backup_structure_step extends backup_step {
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that will return the structure to be processed by this backup_step.
|
||||
* Must return one backup_nested_element
|
||||
* Define the structure to be processed by this backup step.
|
||||
*
|
||||
* @return backup_nested_element
|
||||
*/
|
||||
abstract protected function define_structure();
|
||||
}
|
||||
|
@ -61,6 +61,9 @@ class mock_backup_step extends backup_step {
|
||||
*/
|
||||
class mock_backup_task_basepath extends backup_task {
|
||||
|
||||
/** @var string name of the mod plugin (activity) being used in the tests */
|
||||
private $modulename;
|
||||
|
||||
public function build() {
|
||||
// Nothing to do
|
||||
}
|
||||
@ -73,6 +76,14 @@ class mock_backup_task_basepath extends backup_task {
|
||||
global $CFG;
|
||||
return $CFG->tempdir . '/test';
|
||||
}
|
||||
|
||||
public function set_modulename($modulename) {
|
||||
$this->modulename = $modulename;
|
||||
}
|
||||
|
||||
public function get_modulename() {
|
||||
return $this->modulename;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,7 +91,7 @@ class mock_backup_task_basepath extends backup_task {
|
||||
*/
|
||||
class mock_backup_structure_step extends backup_structure_step {
|
||||
|
||||
protected function define_structure() {
|
||||
public function define_structure() {
|
||||
|
||||
// Create really simple structure (1 nested with 1 attr and 2 fields)
|
||||
$test = new backup_nested_element('test',
|
||||
@ -91,6 +102,14 @@ class mock_backup_structure_step extends backup_structure_step {
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
public function add_plugin_structure($plugintype, $element, $multiple) {
|
||||
parent::add_plugin_structure($plugintype, $element, $multiple);
|
||||
}
|
||||
|
||||
public function add_subplugin_structure($subplugintype, $element, $multiple, $plugintype = null, $pluginname = null) {
|
||||
parent::add_subplugin_structure($subplugintype, $element, $multiple, $plugintype, $pluginname);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,6 +134,117 @@ class backup_step_testcase extends advanced_testcase {
|
||||
@remove_dir(dirname($file));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify the add_plugin_structure() backup method behavior and created structures.
|
||||
*/
|
||||
public function test_backup_structure_step_add_plugin_structure() {
|
||||
// Create mocked task, step and element.
|
||||
$bt = new mock_backup_task_basepath('taskname');
|
||||
$bs = new mock_backup_structure_step('steptest', null, $bt);
|
||||
$el = new backup_nested_element('question', array('id'), array('one', 'two', 'qtype'));
|
||||
// Wrong plugintype.
|
||||
try {
|
||||
$bs->add_plugin_structure('fakeplugin', $el, true);
|
||||
$this->assertTrue(false, 'base_step_exception expected');
|
||||
} catch (exception $e) {
|
||||
$this->assertTrue($e instanceof backup_step_exception);
|
||||
$this->assertEquals('incorrect_plugin_type', $e->errorcode);
|
||||
}
|
||||
// Correct plugintype qtype call (@ 'question' level).
|
||||
$bs->add_plugin_structure('qtype', $el, false);
|
||||
$ch = $el->get_children();
|
||||
$this->assertEquals(1, count($ch));
|
||||
$og = reset($ch);
|
||||
$this->assertTrue($og instanceof backup_optigroup);
|
||||
$ch = $og->get_children();
|
||||
$this->assertTrue(array_key_exists('optigroup_qtype_calculatedsimple_question', $ch));
|
||||
$this->assertTrue($ch['optigroup_qtype_calculatedsimple_question'] instanceof backup_plugin_element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the add_subplugin_structure() backup method behavior and created structures.
|
||||
*/
|
||||
public function test_backup_structure_step_add_subplugin_structure() {
|
||||
// Create mocked task, step and element.
|
||||
$bt = new mock_backup_task_basepath('taskname');
|
||||
$bs = new mock_backup_structure_step('steptest', null, $bt);
|
||||
$el = new backup_nested_element('workshop', array('id'), array('one', 'two', 'qtype'));
|
||||
// Wrong plugin type.
|
||||
try {
|
||||
$bs->add_subplugin_structure('fakesubplugin', $el, true, 'fakeplugintype', 'fakepluginname');
|
||||
$this->assertTrue(false, 'base_step_exception expected');
|
||||
} catch (exception $e) {
|
||||
$this->assertTrue($e instanceof backup_step_exception);
|
||||
$this->assertEquals('incorrect_plugin_type', $e->errorcode);
|
||||
}
|
||||
// Wrong plugin type.
|
||||
try {
|
||||
$bs->add_subplugin_structure('fakesubplugin', $el, true, 'mod', 'fakepluginname');
|
||||
$this->assertTrue(false, 'base_step_exception expected');
|
||||
} catch (exception $e) {
|
||||
$this->assertTrue($e instanceof backup_step_exception);
|
||||
$this->assertEquals('incorrect_plugin_name', $e->errorcode);
|
||||
}
|
||||
// Wrong plugin not having subplugins.
|
||||
try {
|
||||
$bs->add_subplugin_structure('fakesubplugin', $el, true, 'mod', 'page');
|
||||
$this->assertTrue(false, 'base_step_exception expected');
|
||||
} catch (exception $e) {
|
||||
$this->assertTrue($e instanceof backup_step_exception);
|
||||
$this->assertEquals('plugin_missing_subplugins_php_file', $e->errorcode);
|
||||
}
|
||||
// Wrong BC (defaulting to mod and modulename) use not having subplugins.
|
||||
try {
|
||||
$bt->set_modulename('page');
|
||||
$bs->add_subplugin_structure('fakesubplugin', $el, true);
|
||||
$this->assertTrue(false, 'base_step_exception expected');
|
||||
} catch (exception $e) {
|
||||
$this->assertTrue($e instanceof backup_step_exception);
|
||||
$this->assertEquals('plugin_missing_subplugins_php_file', $e->errorcode);
|
||||
}
|
||||
// Wrong subplugin type.
|
||||
try {
|
||||
$bs->add_subplugin_structure('fakesubplugin', $el, true, 'mod', 'workshop');
|
||||
$this->assertTrue(false, 'base_step_exception expected');
|
||||
} catch (exception $e) {
|
||||
$this->assertTrue($e instanceof backup_step_exception);
|
||||
$this->assertEquals('incorrect_subplugin_type', $e->errorcode);
|
||||
}
|
||||
// Wrong BC subplugin type.
|
||||
try {
|
||||
$bt->set_modulename('workshop');
|
||||
$bs->add_subplugin_structure('fakesubplugin', $el, true);
|
||||
$this->assertTrue(false, 'base_step_exception expected');
|
||||
} catch (exception $e) {
|
||||
$this->assertTrue($e instanceof backup_step_exception);
|
||||
$this->assertEquals('incorrect_subplugin_type', $e->errorcode);
|
||||
}
|
||||
// Correct call to workshopform subplugin (@ 'workshop' level).
|
||||
$bs->add_subplugin_structure('workshopform', $el, true, 'mod', 'workshop');
|
||||
$ch = $el->get_children();
|
||||
$this->assertEquals(1, count($ch));
|
||||
$og = reset($ch);
|
||||
$this->assertTrue($og instanceof backup_optigroup);
|
||||
$ch = $og->get_children();
|
||||
$this->assertTrue(array_key_exists('optigroup_workshopform_accumulative_workshop', $ch));
|
||||
$this->assertTrue($ch['optigroup_workshopform_accumulative_workshop'] instanceof backup_subplugin_element);
|
||||
|
||||
// Correct BC call to workshopform subplugin (@ 'assessment' level).
|
||||
$el = new backup_nested_element('assessment', array('id'), array('one', 'two', 'qtype'));
|
||||
$bt->set_modulename('workshop');
|
||||
$bs->add_subplugin_structure('workshopform', $el, true);
|
||||
$ch = $el->get_children();
|
||||
$this->assertEquals(1, count($ch));
|
||||
$og = reset($ch);
|
||||
$this->assertTrue($og instanceof backup_optigroup);
|
||||
$ch = $og->get_children();
|
||||
$this->assertTrue(array_key_exists('optigroup_workshopform_accumulative_assessment', $ch));
|
||||
$this->assertTrue($ch['optigroup_workshopform_accumulative_assessment'] instanceof backup_subplugin_element);
|
||||
|
||||
// TODO: Add some test covering a non-mod subplugin once we have some implemented in core.
|
||||
}
|
||||
|
||||
/**
|
||||
* wrong base_step class tests
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user