Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/backup/util/plan/restore_structure_step.class.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00030 abstract class restore_structure_step extends restore_step {
00031 
00032     protected $filename; // Name of the file to be parsed
00033     protected $contentprocessor; // xml parser processor being used
00034                                  // (need it here, apart from parser
00035                                  // thanks to serialized data to process -
00036                                  // say thanks to blocks!)
00037     protected $pathelements;  // Array of pathelements to process
00038     protected $elementsoldid; // Array to store last oldid used on each element
00039     protected $elementsnewid; // Array to store last newid used on each element
00040 
00041     protected $pathlock;      // Path currently locking processing of children
00042 
00043     const SKIP_ALL_CHILDREN = -991399; // To instruct the dispatcher about to ignore
00044                                        // all children below path processor returning it
00045 
00049     public function __construct($name, $filename, $task = null) {
00050         if (!is_null($task) && !($task instanceof restore_task)) {
00051             throw new restore_step_exception('wrong_restore_task_specified');
00052         }
00053         $this->filename = $filename;
00054         $this->contentprocessor = null;
00055         $this->pathelements = array();
00056         $this->elementsoldid = array();
00057         $this->elementsnewid = array();
00058         $this->pathlock = null;
00059         parent::__construct($name, $task);
00060     }
00061 
00062     final public function execute() {
00063 
00064         if (!$this->execute_condition()) { // Check any condition to execute this
00065             return;
00066         }
00067 
00068         $fullpath = $this->task->get_taskbasepath();
00069 
00070         // We MUST have one fullpath here, else, error
00071         if (empty($fullpath)) {
00072             throw new restore_step_exception('restore_structure_step_undefined_fullpath');
00073         }
00074 
00075         // Append the filename to the fullpath
00076         $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
00077 
00078         // And it MUST exist
00079         if (!file_exists($fullpath)) { // Shouldn't happen ever, but...
00080             throw new restore_step_exception('missing_moodle_backup_xml_file', $fullpath);
00081         }
00082 
00083         // Get restore_path elements array adapting and preparing it for processing
00084         $structure = $this->define_structure();
00085         if (!is_array($structure)) {
00086             throw new restore_step_exception('restore_step_structure_not_array', $this->get_name());
00087         }
00088         $this->prepare_pathelements($structure);
00089 
00090         // Create parser and processor
00091         $xmlparser = new progressive_parser();
00092         $xmlparser->set_file($fullpath);
00093         $xmlprocessor = new restore_structure_parser_processor($this->task->get_courseid(), $this);
00094         $this->contentprocessor = $xmlprocessor; // Save the reference to the contentprocessor
00095                                                  // as far as we are going to need it out
00096                                                  // from parser (blame serialized data!)
00097         $xmlparser->set_processor($xmlprocessor);
00098 
00099         // Add pathelements to processor
00100         foreach ($this->pathelements as $element) {
00101             $xmlprocessor->add_path($element->get_path(), $element->is_grouped());
00102         }
00103 
00104         // And process it, dispatch to target methods in step will start automatically
00105         $xmlparser->process();
00106 
00107         // Have finished, launch the after_execute method of all the processing objects
00108         $this->launch_after_execute_methods();
00109     }
00110 
00115     final public function process($data) {
00116         if (!array_key_exists($data['path'], $this->pathelements)) { // Incorrect path, must not happen
00117             throw new restore_step_exception('restore_structure_step_missing_path', $data['path']);
00118         }
00119         $element = $this->pathelements[$data['path']];
00120         $object = $element->get_processing_object();
00121         $method = $element->get_processing_method();
00122         $rdata = null;
00123         if (empty($object)) { // No processing object defined
00124             throw new restore_step_exception('restore_structure_step_missing_pobject', $object);
00125         }
00126         // Release the lock if we aren't anymore within children of it
00127         if (!is_null($this->pathlock) and strpos($data['path'], $this->pathlock) === false) {
00128             $this->pathlock = null;
00129         }
00130         if (is_null($this->pathlock)) { // Only dispatch if there isn't any lock
00131             $rdata = $object->$method($data['tags']); // Dispatch to proper object/method
00132         }
00133 
00134         // If the dispatched method returns SKIP_ALL_CHILDREN, we grab current path in order to
00135         // lock dispatching to any children
00136         if ($rdata === self::SKIP_ALL_CHILDREN) {
00137             // Check we haven't any previous lock
00138             if (!is_null($this->pathlock)) {
00139                 throw new restore_step_exception('restore_structure_step_already_skipping', $data['path']);
00140             }
00141             // Set the lock
00142             $this->pathlock = $data['path'] . '/'; // Lock everything below current path
00143 
00144         // Continue with normal processing of return values
00145         } else if ($rdata !== null) { // If the method has returned any info, set element data to it
00146             $element->set_data($rdata);
00147         } else {               // Else, put the original parsed data
00148             $element->set_data($data);
00149         }
00150     }
00151 
00161     public function set_mapping($itemname, $oldid, $newid, $restorefiles = false, $filesctxid = null, $parentid = null) {
00162         if ($restorefiles && $parentid) {
00163             throw new restore_step_exception('set_mapping_cannot_specify_both_restorefiles_and_parentitemid');
00164         }
00165         // If we haven't specified one context for the files, use the task one
00166         if (is_null($filesctxid)) {
00167             $parentitemid = $restorefiles ? $this->task->get_old_contextid() : null;
00168         } else { // Use the specified one
00169             $parentitemid = $restorefiles ? $filesctxid : null;
00170         }
00171         // We have passed one explicit parentid, apply it
00172         $parentitemid = !is_null($parentid) ? $parentid : $parentitemid;
00173 
00174         // Let's call the low level one
00175         restore_dbops::set_backup_ids_record($this->get_restoreid(), $itemname, $oldid, $newid, $parentitemid);
00176         // Now, if the itemname matches any pathelement->name, store the latest $newid
00177         if (array_key_exists($itemname, $this->elementsoldid)) { // If present in  $this->elementsoldid, is valid, put both ids
00178             $this->elementsoldid[$itemname] = $oldid;
00179             $this->elementsnewid[$itemname] = $newid;
00180         }
00181     }
00182 
00186     public function get_old_parentid($itemname) {
00187         return array_key_exists($itemname, $this->elementsoldid) ? $this->elementsoldid[$itemname] : null;
00188     }
00189 
00193     public function get_new_parentid($itemname) {
00194         return array_key_exists($itemname, $this->elementsnewid) ? $this->elementsnewid[$itemname] : null;
00195     }
00196 
00204     public function get_mappingid($itemname, $oldid, $ifnotfound = false) {
00205         $mapping = $this->get_mapping($itemname, $oldid);
00206         return $mapping ? $mapping->newitemid : $ifnotfound;
00207     }
00208 
00212     public function get_mapping($itemname, $oldid) {
00213         return restore_dbops::get_backup_ids_record($this->get_restoreid(), $itemname, $oldid);
00214     }
00215 
00219     public function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null, $olditemid = null) {
00220         $filesctxid = is_null($filesctxid) ? $this->task->get_old_contextid() : $filesctxid;
00221         restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component,
00222                                           $filearea, $filesctxid, $this->task->get_userid(), $mappingitemname, $olditemid);
00223     }
00224 
00230     public function apply_date_offset($value) {
00231 
00232         // empties don't offset - zeros (int and string), false and nulls return original value
00233         if (empty($value)) {
00234             return $value;
00235         }
00236 
00237         static $cache = array();
00238         // Lookup cache
00239         if (isset($cache[$this->get_restoreid()])) {
00240             return $value + $cache[$this->get_restoreid()];
00241         }
00242         // No cache, let's calculate the offset
00243         $original = $this->task->get_info()->original_course_startdate;
00244         $setting = 0;
00245         if ($this->setting_exists('course_startdate')) { // Seting may not exist (MDL-25019)
00246             $setting  = $this->get_setting_value('course_startdate');
00247         }
00248 
00249         // Original course has not startdate or setting doesn't exist, offset = 0
00250         if (empty($original) || empty($setting)) {
00251             $cache[$this->get_restoreid()] = 0;
00252 
00253         // Less than 24h of difference, offset = 0 (this avoids some problems with timezones)
00254         } else if (abs($setting - $original) < 24 * 60 * 60) {
00255             $cache[$this->get_restoreid()] = 0;
00256 
00257         // Re-enforce 'moodle/restore:rolldates' capability for the user in the course, just in case
00258         } else if (!has_capability('moodle/restore:rolldates',
00259                                    get_context_instance(CONTEXT_COURSE, $this->get_courseid()),
00260                                    $this->task->get_userid())) {
00261             $cache[$this->get_restoreid()] = 0;
00262 
00263         // Arrived here, let's calculate the real offset
00264         } else {
00265             $cache[$this->get_restoreid()] = $setting - $original;
00266         }
00267 
00268         // Return the passed value with cached offset applied
00269         return $value + $cache[$this->get_restoreid()];
00270     }
00271 
00276     public function get_task() {
00277         return $this->task;
00278     }
00279 
00280 // Protected API starts here
00281 
00289     protected function add_plugin_structure($plugintype, $element) {
00290 
00291         global $CFG;
00292 
00293         // Check the requested plugintype is a valid one
00294         if (!array_key_exists($plugintype, get_plugin_types($plugintype))) {
00295              throw new restore_step_exception('incorrect_plugin_type', $plugintype);
00296         }
00297 
00298         // Get all the restore path elements, looking across all the plugin dirs
00299         $pluginsdirs = get_plugin_list($plugintype);
00300         foreach ($pluginsdirs as $name => $pluginsdir) {
00301             // We need to add also backup plugin classes on restore, they may contain
00302             // some stuff used both in backup & restore
00303             $backupclassname = 'backup_' . $plugintype . '_' . $name . '_plugin';
00304             $backupfile = $pluginsdir . '/backup/moodle2/' . $backupclassname . '.class.php';
00305             if (file_exists($backupfile)) {
00306                 require_once($backupfile);
00307             }
00308             // Now add restore plugin classes and prepare stuff
00309             $restoreclassname = 'restore_' . $plugintype . '_' . $name . '_plugin';
00310             $restorefile = $pluginsdir . '/backup/moodle2/' . $restoreclassname . '.class.php';
00311             if (file_exists($restorefile)) {
00312                 require_once($restorefile);
00313                 $restoreplugin = new $restoreclassname($plugintype, $name, $this);
00314                 // Add plugin paths to the step
00315                 $this->prepare_pathelements($restoreplugin->define_plugin_structure($element));
00316             }
00317         }
00318     }
00319 
00333     protected function launch_after_execute_methods() {
00334         $alreadylaunched = array(); // To avoid multiple executions
00335         foreach ($this->pathelements as $key => $pathelement) {
00336             // Get the processing object
00337             $pobject = $pathelement->get_processing_object();
00338             // Skip null processors (child of grouped ones for sure)
00339             if (is_null($pobject)) {
00340                 continue;
00341             }
00342             // Skip restore structure step processors (this)
00343             if ($pobject instanceof restore_structure_step) {
00344                 continue;
00345             }
00346             // Skip already launched processing objects
00347             if (in_array($pobject, $alreadylaunched, true)) {
00348                 continue;
00349             }
00350             // Add processing object to array of launched ones
00351             $alreadylaunched[] = $pobject;
00352             // If the processing object has support for
00353             // launching after_execute methods, use it
00354             if (method_exists($pobject, 'launch_after_execute_methods')) {
00355                 $pobject->launch_after_execute_methods();
00356             }
00357         }
00358         // Finally execute own (restore_structure_step) after_execute method
00359         $this->after_execute();
00360 
00361     }
00362 
00373     public function launch_after_restore_methods() {
00374         $alreadylaunched = array(); // To avoid multiple executions
00375         foreach ($this->pathelements as $pathelement) {
00376             // Get the processing object
00377             $pobject = $pathelement->get_processing_object();
00378             // Skip null processors (child of grouped ones for sure)
00379             if (is_null($pobject)) {
00380                 continue;
00381             }
00382             // Skip restore structure step processors (this)
00383             if ($pobject instanceof restore_structure_step) {
00384                 continue;
00385             }
00386             // Skip already launched processing objects
00387             if (in_array($pobject, $alreadylaunched, true)) {
00388                 continue;
00389             }
00390             // Add processing object to array of launched ones
00391             $alreadylaunched[] = $pobject;
00392             // If the processing object has support for
00393             // launching after_restore methods, use it
00394             if (method_exists($pobject, 'launch_after_restore_methods')) {
00395                 $pobject->launch_after_restore_methods();
00396             }
00397         }
00398     }
00399 
00407     protected function after_execute() {
00408         // do nothing by default
00409     }
00410 
00415     protected function prepare_pathelements($elementsarr) {
00416 
00417         // First iteration, push them to new array, indexed by name
00418         // detecting duplicates in names or paths
00419         $names = array();
00420         $paths = array();
00421         foreach($elementsarr as $element) {
00422             if (!$element instanceof restore_path_element) {
00423                 throw new restore_step_exception('restore_path_element_wrong_class', get_class($element));
00424             }
00425             if (array_key_exists($element->get_name(), $names)) {
00426                 throw new restore_step_exception('restore_path_element_name_alreadyexists', $element->get_name());
00427             }
00428             if (array_key_exists($element->get_path(), $paths)) {
00429                 throw new restore_step_exception('restore_path_element_path_alreadyexists', $element->get_path());
00430             }
00431             $names[$element->get_name()] = true;
00432             $paths[$element->get_path()] = $element;
00433         }
00434         // Now, for each element not having one processing object, if
00435         // not child of grouped element, assign $this (the step itself) as processing element
00436         // Note method must exist or we'll get one @restore_path_element_exception
00437         foreach($paths as $key => $pelement) {
00438             if ($pelement->get_processing_object() === null && !$this->grouped_parent_exists($pelement, $paths)) {
00439                 $paths[$key]->set_processing_object($this);
00440             }
00441             // Populate $elementsoldid and $elementsoldid based on available pathelements
00442             $this->elementsoldid[$pelement->get_name()] = null;
00443             $this->elementsnewid[$pelement->get_name()] = null;
00444         }
00445         // Done, add them to pathelements (dupes by key - path - are discarded)
00446         $this->pathelements = array_merge($this->pathelements, $paths);
00447     }
00448 
00452     protected function grouped_parent_exists($pelement, $elements) {
00453         foreach ($elements as $element) {
00454             if ($pelement->get_path() == $element->get_path()) {
00455                 continue; // Don't compare against itself
00456             }
00457             // If element is grouped and parent of pelement, return true
00458             if ($element->is_grouped() and strpos($pelement->get_path() .  '/', $element->get_path()) === 0) {
00459                 return true;
00460             }
00461         }
00462         return false; // no grouped parent found
00463     }
00464 
00473     protected function execute_condition() {
00474         return true;
00475     }
00476 
00481     abstract protected function define_structure();
00482 }
 All Data Structures Namespaces Files Functions Variables Enumerations