From 91d1105756bbb9ea0278dd1b43af529c590a22a3 Mon Sep 17 00:00:00 2001 From: Eloy Lafuente Date: Fri, 6 Aug 2010 02:20:21 +0000 Subject: [PATCH] MDL-22245 backup - added support for subplugins in activities --- backup/moodle2/backup_stepslib.php | 1 - backup/moodle2/restore_plan_builder.class.php | 2 +- backup/moodle2/restore_stepslib.php | 35 +++- backup/moodle2/restore_subplugin.class.php | 154 +++++++++++++++ .../plan/restore_structure_step.class.php | 177 +++++++++--------- 5 files changed, 275 insertions(+), 94 deletions(-) create mode 100644 backup/moodle2/restore_subplugin.class.php diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index 324d824c683..b4bfba6ff8a 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -109,7 +109,6 @@ abstract class backup_activity_structure_step extends backup_structure_step { $element->add_child($optigroup); // Add optigroup to stay connected since beginning // Get all the optigroup_elements, looking across all the subplugin dirs - $elements = array(); $subpluginsdirs = get_plugin_list($subplugintype); foreach ($subpluginsdirs as $name => $subpluginsdir) { $classname = 'backup_' . $subplugintype . '_' . $name . '_subplugin'; diff --git a/backup/moodle2/restore_plan_builder.class.php b/backup/moodle2/restore_plan_builder.class.php index ee5176a4f7f..9ad332a4b99 100644 --- a/backup/moodle2/restore_plan_builder.class.php +++ b/backup/moodle2/restore_plan_builder.class.php @@ -31,7 +31,7 @@ require_once($CFG->dirroot . '/backup/moodle2/restore_activity_task.class.php'); require_once($CFG->dirroot . '/backup/moodle2/restore_final_task.class.php'); require_once($CFG->dirroot . '/backup/moodle2/restore_block_task.class.php'); require_once($CFG->dirroot . '/backup/moodle2/restore_default_block_task.class.php'); -//require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php'); +require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php'); require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php'); require_once($CFG->dirroot . '/backup/moodle2/restore_stepslib.php'); diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 9a787b9061c..8a1359a426e 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -1118,8 +1118,39 @@ class restore_module_structure_step extends restore_structure_step { */ abstract class restore_activity_structure_step extends restore_structure_step { - protected function add_subplugin_structure() { - // TODO: Implement activities subplugin support (similar if possible to backup) + protected function add_subplugin_structure($subplugintype, $element) { + + 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 restore_step_exception('activity_missing_subplugins_php_file', $this->task->get_modulename()); + } + include($subpluginsfile); + if (!array_key_exists($subplugintype, $subplugins)) { + throw new restore_step_exception('incorrect_subplugin_type', $subplugintype); + } + // Get all the restore path elements, looking across all the subplugin dirs + $subpluginsdirs = get_plugin_list($subplugintype); + foreach ($subpluginsdirs as $name => $subpluginsdir) { + $classname = 'restore_' . $subplugintype . '_' . $name . '_subplugin'; + $restorefile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php'; + if (file_exists($restorefile)) { + require_once($restorefile); + $restoresubplugin = new $classname($subplugintype, $name, $this); + // Add subplugin paths to the step + $this->prepare_pathelements($restoresubplugin->define_subplugin_structure($element)); + } + } + } + + /** + * As far as activity restore steps are implementing restore_subplugin stuff, they need to + * have the parent task available for wrapping purposes (get course/context....) + */ + public function get_task() { + return $this->task; } /** diff --git a/backup/moodle2/restore_subplugin.class.php b/backup/moodle2/restore_subplugin.class.php new file mode 100644 index 00000000000..43eb56a1699 --- /dev/null +++ b/backup/moodle2/restore_subplugin.class.php @@ -0,0 +1,154 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Class implementing the subplugins support for moodle2 restore + * + * TODO: Finish phpdocs + */ +abstract class restore_subplugin { + + protected $subplugintype; + protected $subpluginname; + protected $connectionpoint; + protected $step; + protected $task; + + public function __construct($subplugintype, $subpluginname, $step) { + $this->subplugintype = $subplugintype; + $this->subpluginname = $subpluginname; + $this->step = $step; + $this->task = $step->get_task(); + $this->connectionpoint = ''; + } + + public function define_subplugin_structure($connectionpoint) { + if (!$connectionpoint instanceof restore_path_element) { + throw new restore_step_exception('restore_path_element_required', $connectionpoint); + } + + $paths = array(); + $this->connectionpoint = $connectionpoint; + $methodname = 'define_' . basename($this->connectionpoint->get_path()) . '_subplugin_structure'; + + if (method_exists($this, $methodname)) { + if ($subbluginpaths = $this->$methodname()) { + foreach ($subbluginpaths as $path) { + $path->set_processing_object($this); + $paths[] = $path; + } + } + } + return $paths; + } + +// Protected API starts here + +// restore_step/structure_step/task wrappers + + protected function get_restoreid() { + if (is_null($this->task)) { + throw new restore_step_exception('not_specified_restore_task'); + } + return $this->task->get_restoreid(); + } + + /** + * To send ids pairs to backup_ids_table and to store them into paths + * + * This method will send the given itemname and old/new ids to the + * backup_ids_temp table, and, at the same time, will save the new id + * into the corresponding restore_path_element for easier access + * by children. Also will inject the known old context id for the task + * in case it's going to be used for restoring files later + */ + protected function set_mapping($itemname, $oldid, $newid, $restorefiles = false, $filesctxid = null) { + $this->step->set_mapping($itemname, $oldid, $newid, $restorefiles, $filesctxid); + } + + /** + * Returns the latest (parent) old id mapped by one pathelement + */ + protected function get_old_parentid($itemname) { + return $this->step->get_old_parentid($itemname); + } + + /** + * Returns the latest (parent) new id mapped by one pathelement + */ + protected function get_new_parentid($itemname) { + return $this->step->get_new_parentid($itemname); + } + + /** + * Return the new id of a mapping for the given itemname + * + */ + protected function get_mappingid($itemname, $oldid) { + return $this->step->get_mappingid($itemname, $oldid); + } + + /** + * Return the complete mapping from the given itemname, itemid + */ + protected function get_mapping($itemname, $oldid) { + return $this->step->get_mapping($itemname, $oldid); + } + + /** + * Add all the existing file, given their component and filearea and one backup_ids itemname to match with + */ + protected function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null) { + $this->step->add_related_files($component, $filearea, $mappingitemname, $filesctxid); + } + + /** + * Apply course startdate offset based in original course startdate and course_offset_startdate setting + * Note we are using one static cache here, but *by restoreid*, so it's ok for concurrence/multiple + * executions in the same request + */ + protected function apply_date_offset($value) { + return $this->step->apply_date_offset($value); + } + + /** + * Simple helper function that returns the name for the restore_path_element + * It's not mandatory to use it but recommended ;-) + */ + protected function get_namefor($name = '') { + $name = $name !== '' ? '_' . $name : ''; + return $this->subplugintype . '_' . $this->subpluginname . $name; + } + + /** + * Simple helper function that returns the base (prefix) of the path for the restore_path_element + * Useful if we used get_recommended_name() in backup. It's not mandatory to use it but recommended ;-) + */ + protected function get_pathfor($path = '') { + $path = trim($path, '/') !== '' ? '/' . trim($path, '/') : ''; + return $this->connectionpoint->get_path() . '/' . + 'subplugin_' . $this->subplugintype . '_' . + $this->subpluginname . '_' . basename($this->connectionpoint->get_path()) . $path; + } +} diff --git a/backup/util/plan/restore_structure_step.class.php b/backup/util/plan/restore_structure_step.class.php index 8364b70447f..31ae2f4bc34 100644 --- a/backup/util/plan/restore_structure_step.class.php +++ b/backup/util/plan/restore_structure_step.class.php @@ -79,13 +79,7 @@ abstract class restore_structure_step extends restore_step { if (!is_array($structure)) { throw new restore_step_exception('restore_step_structure_not_array', $this->get_name()); } - $this->pathelements = $this->prepare_pathelements($structure); - - // Populate $elementsoldid and $elementsoldid based on available pathelements - foreach ($this->pathelements as $pathelement) { - $this->elementsoldid[$pathelement->get_name()] = null; - $this->elementsnewid[$pathelement->get_name()] = null; - } + $this->prepare_pathelements($structure); // Create parser and processor $xmlparser = new progressive_parser(); @@ -130,82 +124,6 @@ abstract class restore_structure_step extends restore_step { } } -// Protected API starts here - - /** - * This method will be executed after the whole structure step have been processed - * - * After execution method for code needed to be executed after the whole structure - * has been processed. Useful for cleaning tasks, files process and others. Simply - * overwrite in in your steps if needed - */ - protected function after_execute() { - // do nothing by default - } - - /** - * Prepare the pathelements for processing, looking for duplicates, applying - * processing objects and other adjustments - */ - protected function prepare_pathelements($elementsarr) { - - // First iteration, push them to new array, indexed by name - // detecting duplicates in names or paths - $names = array(); - $paths = array(); - foreach($elementsarr as $element) { - if (!$element instanceof restore_path_element) { - throw new restore_step_exception('restore_path_element_wrong_class', get_class($element)); - } - if (array_key_exists($element->get_name(), $names)) { - throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name()); - } - if (array_key_exists($element->get_path(), $paths)) { - throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path()); - } - $names[$element->get_name()] = true; - $elements[$element->get_path()] = $element; - } - // Now, for each element not having one processing object, if - // not child of grouped element, assign $this (the step itself) as processing element - // Note method must exist or we'll get one @restore_path_element_exception - foreach($elements as $key => $pelement) { - if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $elements)) { - $elements[$key]->set_processing_object($this); - } - } - // Done, return them - return $elements; - } - - /** - * Given one pathelement, return true if grouped parent was found - */ - protected function grouped_parent_exists($pelement, $elements) { - foreach ($elements as $element) { - if ($pelement->get_path() == $element->get_path()) { - continue; // Don't compare against itself - } - // If element is grouped and parent of pelement, return true - if ($element->is_grouped() and strpos($pelement->get_path() . '/', $element->get_path()) === 0) { - return true; - } - } - return false; // no grouped parent found - } - - /** - * To conditionally decide if one step will be executed or no - * - * For steps needing to be executed conditionally, based in dynamic - * conditions (at execution time vs at declaration time) you must - * override this function. It will return true if the step must be - * executed and false if not - */ - protected function execute_condition() { - return true; - } - /** * To send ids pairs to backup_ids_table and to store them into paths * @@ -215,7 +133,7 @@ abstract class restore_structure_step extends restore_step { * by children. Also will inject the known old context id for the task * in case it's going to be used for restoring files later */ - protected function set_mapping($itemname, $oldid, $newid, $restorefiles = false, $filesctxid = null) { + public function set_mapping($itemname, $oldid, $newid, $restorefiles = false, $filesctxid = null) { // If we haven't specified one context for the files, use the task one if ($filesctxid == null) { $parentitemid = $restorefiles ? $this->task->get_old_contextid() : null; @@ -234,14 +152,14 @@ abstract class restore_structure_step extends restore_step { /** * Returns the latest (parent) old id mapped by one pathelement */ - protected function get_old_parentid($itemname) { + public function get_old_parentid($itemname) { return array_key_exists($itemname, $this->elementsoldid) ? $this->elementsoldid[$itemname] : null; } /** * Returns the latest (parent) new id mapped by one pathelement */ - protected function get_new_parentid($itemname) { + public function get_new_parentid($itemname) { return array_key_exists($itemname, $this->elementsnewid) ? $this->elementsnewid[$itemname] : null; } @@ -249,7 +167,7 @@ abstract class restore_structure_step extends restore_step { * Return the new id of a mapping for the given itemname * */ - protected function get_mappingid($itemname, $oldid) { + public function get_mappingid($itemname, $oldid) { $mapping = $this->get_mapping($itemname, $oldid); return $mapping ? $mapping->newitemid : false; } @@ -257,14 +175,14 @@ abstract class restore_structure_step extends restore_step { /** * Return the complete mapping from the given itemname, itemid */ - protected function get_mapping($itemname, $oldid) { + public function get_mapping($itemname, $oldid) { return restore_dbops::get_backup_ids_record($this->get_restoreid(), $itemname, $oldid); } /** * Add all the existing file, given their component and filearea and one backup_ids itemname to match with */ - protected function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null) { + public function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null) { $filesctxid = is_null($filesctxid) ? $this->task->get_old_contextid() : $filesctxid; restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component, $filearea, $filesctxid, $this->task->get_userid(), $mappingitemname); @@ -275,7 +193,7 @@ abstract class restore_structure_step extends restore_step { * Note we are using one static cache here, but *by restoreid*, so it's ok for concurrence/multiple * executions in the same request */ - protected function apply_date_offset($value) { + public function apply_date_offset($value) { // 0 doesn't offset if ($value == 0) { @@ -314,6 +232,85 @@ abstract class restore_structure_step extends restore_step { return $value + $cache[$this->get_restoreid()]; } +// Protected API starts here + + /** + * This method will be executed after the whole structure step have been processed + * + * After execution method for code needed to be executed after the whole structure + * has been processed. Useful for cleaning tasks, files process and others. Simply + * overwrite in in your steps if needed + */ + protected function after_execute() { + // do nothing by default + } + + /** + * Prepare the pathelements for processing, looking for duplicates, applying + * processing objects and other adjustments + */ + protected function prepare_pathelements($elementsarr) { + + // First iteration, push them to new array, indexed by name + // detecting duplicates in names or paths + $names = array(); + $paths = array(); + foreach($elementsarr as $element) { + if (!$element instanceof restore_path_element) { + throw new restore_step_exception('restore_path_element_wrong_class', get_class($element)); + } + if (array_key_exists($element->get_name(), $names)) { + throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name()); + } + if (array_key_exists($element->get_path(), $paths)) { + throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path()); + } + $names[$element->get_name()] = true; + $paths[$element->get_path()] = $element; + } + // Now, for each element not having one processing object, if + // not child of grouped element, assign $this (the step itself) as processing element + // Note method must exist or we'll get one @restore_path_element_exception + foreach($paths as $key => $pelement) { + if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $paths)) { + $paths[$key]->set_processing_object($this); + } + // Populate $elementsoldid and $elementsoldid based on available pathelements + $this->elementsoldid[$pelement->get_name()] = null; + $this->elementsnewid[$pelement->get_name()] = null; + } + // Done, add them to pathelements (dupes by key - path - are discarded) + $this->pathelements = array_merge($this->pathelements, $paths); + } + + /** + * Given one pathelement, return true if grouped parent was found + */ + protected function grouped_parent_exists($pelement, $elements) { + foreach ($elements as $element) { + if ($pelement->get_path() == $element->get_path()) { + continue; // Don't compare against itself + } + // If element is grouped and parent of pelement, return true + if ($element->is_grouped() and strpos($pelement->get_path() . '/', $element->get_path()) === 0) { + return true; + } + } + return false; // no grouped parent found + } + + /** + * To conditionally decide if one step will be executed or no + * + * For steps needing to be executed conditionally, based in dynamic + * conditions (at execution time vs at declaration time) you must + * override this function. It will return true if the step must be + * executed and false if not + */ + protected function execute_condition() { + return true; + } + /** * Function that will return the structure to be processed by this restore_step. * Must return one array of @restore_path_element elements -- 2.43.0