MDL-22245 backup - added support for subplugins in activities
authorEloy Lafuente <stronk7@moodle.org>
Fri, 6 Aug 2010 02:20:21 +0000 (02:20 +0000)
committerEloy Lafuente <stronk7@moodle.org>
Fri, 6 Aug 2010 02:20:21 +0000 (02:20 +0000)
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_plan_builder.class.php
backup/moodle2/restore_stepslib.php
backup/moodle2/restore_subplugin.class.php [new file with mode: 0644]
backup/util/plan/restore_structure_step.class.php

index 324d824..b4bfba6 100644 (file)
@@ -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';
index ee5176a..9ad332a 100644 (file)
@@ -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');
 
index 9a787b9..8a1359a 100644 (file)
@@ -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 (file)
index 0000000..43eb56a
--- /dev/null
@@ -0,0 +1,154 @@
+<?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/>.
+
+/**
+ * @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;
+    }
+}
index 8364b70..31ae2f4 100644 (file)
@@ -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