diff --git a/backup/moodle2/backup_format_plugin.class.php b/backup/moodle2/backup_format_plugin.class.php new file mode 100644 index 00000000000..d1af1b923f4 --- /dev/null +++ b/backup/moodle2/backup_format_plugin.class.php @@ -0,0 +1,50 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Class extending standard backup_plugin in order to implement some + * helper methods related with the course formats (format plugin) + * + * TODO: Finish phpdocs + */ +abstract class backup_format_plugin extends backup_plugin { + + protected $courseformat; // To store the format (course->format) of the instance + + public function __construct($plugintype, $pluginname, $optigroup, $step) { + + parent::__construct($plugintype, $pluginname, $optigroup, $step); + + $this->courseformat = backup_plan_dbops::get_courseformat_from_courseid($this->task->get_courseid()); + + } + + /** + * Return the condition encapsulated into sqlparam format + * to get evaluated by value, not by path nor processor setting + */ + protected function get_format_condition() { + return array('sqlparam' => $this->courseformat); + } +} diff --git a/backup/moodle2/backup_plan_builder.class.php b/backup/moodle2/backup_plan_builder.class.php index a1cb25cf834..89c8654534f 100644 --- a/backup/moodle2/backup_plan_builder.class.php +++ b/backup/moodle2/backup_plan_builder.class.php @@ -34,6 +34,7 @@ require_once($CFG->dirroot . '/backup/moodle2/backup_default_block_task.class.ph require_once($CFG->dirroot . '/backup/moodle2/backup_xml_transformer.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_subplugin.class.php'); require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php'); require_once($CFG->dirroot . '/backup/moodle2/backup_stepslib.php'); diff --git a/backup/moodle2/backup_plugin.class.php b/backup/moodle2/backup_plugin.class.php index bd1049819e8..da0abfa0cac 100644 --- a/backup/moodle2/backup_plugin.class.php +++ b/backup/moodle2/backup_plugin.class.php @@ -33,12 +33,16 @@ abstract class backup_plugin { protected $pluginname; protected $connectionpoint; protected $optigroup; // Optigroup, parent of all optigroup elements + protected $step; + protected $task; - public function __construct($plugintype, $pluginname, $optigroup) { + public function __construct($plugintype, $pluginname, $optigroup, $step) { $this->plugintype = $plugintype; $this->pluginname = $pluginname; - $this->optigroup = $optigroup; + $this->optigroup = $optigroup; $this->connectionpoint = ''; + $this->step = $step; + $this->task = $step->get_task(); } public function define_plugin_structure($connectionpoint) { @@ -52,6 +56,22 @@ abstract class backup_plugin { } } +// Protected API starts here + +// backup_step/structure_step/task wrappers + + /** + * Returns the value of one (task/plan) setting + */ + protected function get_setting_value($name) { + if (is_null($this->task)) { + throw new backup_step_exception('not_specified_backup_task'); + } + return $this->task->get_setting_value($name); + } + +// end of backup_step/structure_step/task wrappers + /** * Factory method that will return one backup_plugin_element (backup_optigroup_element) * with its name automatically calculated, based one the plugin being handled (type, name) diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index 269ad4bc251..6b4dbf34355 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -125,13 +125,21 @@ abstract class backup_activity_structure_step extends backup_structure_step { $backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php'; if (file_exists($backupfile)) { require_once($backupfile); - $backupsubplugin = new $classname($subplugintype, $name, $optigroup); + $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....) + */ + 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... @@ -310,6 +318,9 @@ class backup_module_structure_step extends backup_structure_step { $availability = new backup_nested_element('availability', array('id'), array( 'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax')); + // attach format plugin structure to $module element, only one allowed + $this->add_plugin_structure('format', $module, false); + // Define the tree $module->add_child($availinfo); $availinfo->add_child($availability); @@ -346,6 +357,9 @@ class backup_section_structure_step extends backup_structure_step { $section = new backup_nested_element('section', array('id'), array( 'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible')); + // attach format plugin structure to $section element, only one allowed + $this->add_plugin_structure('format', $section, false); + // Define sources $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID)); @@ -395,6 +409,9 @@ class backup_course_structure_step extends backup_structure_step { $module = new backup_nested_element('module', array(), array('modulename')); + // attach format plugin structure to $course element, only one allowed + $this->add_plugin_structure('format', $course, false); + // Build the tree $course->add_child($category); diff --git a/backup/moodle2/backup_subplugin.class.php b/backup/moodle2/backup_subplugin.class.php index 75fc21dc9b6..ca3e953d3fc 100644 --- a/backup/moodle2/backup_subplugin.class.php +++ b/backup/moodle2/backup_subplugin.class.php @@ -34,12 +34,16 @@ abstract class backup_subplugin { protected $subpluginname; protected $connectionpoint; protected $optigroup; // Optigroup, parent of all optigroup elements + protected $step; + protected $task; - public function __construct($subplugintype, $subpluginname, $optigroup) { + public function __construct($subplugintype, $subpluginname, $optigroup, $step) { $this->subplugintype = $subplugintype; $this->subpluginname = $subpluginname; $this->optigroup = $optigroup; $this->connectionpoint = ''; + $this->step = $step; + $this->task = $step->get_task(); } public function define_subplugin_structure($connectionpoint) { @@ -53,6 +57,22 @@ abstract class backup_subplugin { } } +// Protected API starts here + +// backup_step/structure_step/task wrappers + + /** + * Returns the value of one (task/plan) setting + */ + protected function get_setting_value($name) { + if (is_null($this->task)) { + throw new backup_step_exception('not_specified_backup_task'); + } + return $this->task->get_setting_value($name); + } + +// end of backup_step/structure_step/task wrappers + /** * Factory method that will return one backup_subplugin_element (backup_optigroup_element) * with its name automatically calculated, based one the subplugin being handled (type, name) diff --git a/backup/moodle2/restore_format_plugin.class.php b/backup/moodle2/restore_format_plugin.class.php new file mode 100644 index 00000000000..454d3a80d74 --- /dev/null +++ b/backup/moodle2/restore_format_plugin.class.php @@ -0,0 +1,33 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Class extending standard restore_plugin in order to implement some + * helper methods related with the course formats (format plugin) + * + * TODO: Finish phpdocs + */ +abstract class restore_format_plugin extends restore_plugin { + // Love these classes. :-) Nothing special to customize here for now +} diff --git a/backup/moodle2/restore_plan_builder.class.php b/backup/moodle2/restore_plan_builder.class.php index 46ea999c091..f685d3833b5 100644 --- a/backup/moodle2/restore_plan_builder.class.php +++ b/backup/moodle2/restore_plan_builder.class.php @@ -33,8 +33,10 @@ 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_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/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/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_plugin.class.php b/backup/moodle2/restore_plugin.class.php index 3f9f5bb73b9..8103adaeeec 100644 --- a/backup/moodle2/restore_plugin.class.php +++ b/backup/moodle2/restore_plugin.class.php @@ -202,6 +202,18 @@ abstract class restore_plugin { return $this->step->apply_date_offset($value); } + /** + * Returns the value of one (task/plan) setting + */ + protected function get_setting_value($name) { + if (is_null($this->task)) { + throw new restore_step_exception('not_specified_restore_task'); + } + return $this->task->get_setting_value($name); + } + +// end of restore_step/structure_step/task wrappers + /** * Simple helper function that returns the name for the restore_path_element * It's not mandatory to use it but recommended ;-) diff --git a/backup/moodle2/restore_section_task.class.php b/backup/moodle2/restore_section_task.class.php index 75bf7c3ef3d..5c03bcb7b85 100644 --- a/backup/moodle2/restore_section_task.class.php +++ b/backup/moodle2/restore_section_task.class.php @@ -32,12 +32,14 @@ class restore_section_task extends restore_task { protected $info; // info related to section gathered from backup file protected $contextid; // course context id + protected $sectionid; // new (target) id of the course section /** * Constructor - instantiates one object of this class */ public function __construct($name, $info, $plan = null) { $this->info = $info; + $this->sectionid = 0; parent::__construct($name, $plan); } @@ -49,10 +51,18 @@ class restore_section_task extends restore_task { return $this->get_basepath() . '/sections/section_' . $this->info->sectionid; } + public function set_sectionid($sectionid) { + $this->sectionid = $sectionid; + } + public function get_contextid() { return $this->contextid; } + public function get_sectionid() { + return $this->sectionid; + } + /** * Create all the steps that will be part of this task */ diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 9b150acffd6..b103850d678 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -920,7 +920,12 @@ class restore_process_categories_and_questions extends restore_execution_step { class restore_section_structure_step extends restore_structure_step { protected function define_structure() { - return array(new restore_path_element('section', '/section')); + $section = new restore_path_element('section', '/section'); + + // Apply for 'format' plugins optional paths at section level + $this->add_plugin_structure('format', $section); + + return array($section); } public function process_section($data) { @@ -962,6 +967,10 @@ class restore_section_structure_step extends restore_structure_step { // Annotate the section mapping, with restorefiles option if needed $this->set_mapping('course_section', $oldid, $newitemid, $restorefiles); + // set the new course_section id in the task + $this->task->set_sectionid($newitemid); + + // Commented out. We never modify course->numsections as far as that is used // by a lot of people to "hide" sections on purpose (so this remains as used to be in Moodle 1.x) // Note: We keep the code here, to know about and because of the possibility of making this @@ -991,12 +1000,15 @@ class restore_course_structure_step extends restore_structure_step { protected function define_structure() { - $course = new restore_path_element('course', '/course', true); // Grouped + $course = new restore_path_element('course', '/course'); $category = new restore_path_element('category', '/course/category'); $tag = new restore_path_element('tag', '/course/tags/tag'); - $allowed = new restore_path_element('allowed', '/course/allowed_modules/module'); + $allowed_module = new restore_path_element('allowed_module', '/course/allowed_modules/module'); - return array($course, $category, $tag, $allowed); + // Apply for 'format' plugins optional paths at course level + $this->add_plugin_structure('format', $course); + + return array($course, $category, $tag, $allowed_module); } /** @@ -1026,9 +1038,6 @@ class restore_course_structure_step extends restore_structure_step { $data->shortname= $shortname; $data->idnumber = ''; - // Category is set by UI when choosing the destination - unset($data->category); - $data->startdate= $this->apply_date_offset($data->startdate); if ($data->defaultgroupingid) { $data->defaultgroupingid = $this->get_mappingid('grouping', $data->defaultgroupingid); @@ -1050,34 +1059,56 @@ class restore_course_structure_step extends restore_structure_step { // Course record ready, update it $DB->update_record('course', $data); - // Course tags - if (!empty($CFG->usetags) && isset($coursetags)) { // if enabled in server and present in backup + // Role name aliases + restore_dbops::set_course_role_names($this->get_restoreid(), $this->get_courseid()); + } + + public function process_category($data) { + // Nothing to do with the category. UI sets it before restore starts + } + + public function process_tag($data) { + global $CFG, $DB; + + $data = (object)$data; + + if (!empty($CFG->usetags)) { // if enabled in server + // TODO: This is highly inneficient. Each time we add one tag + // we fetch all the existing because tag_set() deletes them + // so everything must be reinserted on each call $tags = array(); - foreach ($coursetags as $coursetag) { - $coursetag = (object)$coursetag; - $tags[] = $coursetag->rawname; + $existingtags = tag_get_tags('course', $this->get_courseid()); + // Re-add all the existitng tags + foreach ($existingtags as $existingtag) { + $tags[] = $existingtag->rawname; } + // Add the one being restored + $tags[] = $data->rawname; + // Send all the tags back to the course tag_set('course', $this->get_courseid(), $tags); } - // Course allowed modules - if (!empty($data->restrictmodules) && !empty($coursemodules)) { + } + + public function process_allowed_module($data) { + global $CFG, $DB; + + $data = (object)$data; + + // only if enabled by admin setting + if (!empty($CFG->restrictmodulesfor) && $CFG->restrictmodulesfor == 'all') { $available = get_plugin_list('mod'); - foreach ($coursemodules as $coursemodule) { - $mname = $coursemodule['modulename']; - if (array_key_exists($mname, $available)) { - if ($module = $DB->get_record('modules', array('name' => $mname, 'visible' => 1))) { - $rec = new stdclass(); - $rec->course = $this->get_courseid(); - $rec->module = $module->id; - if (!$DB->record_exists('course_allowed_modules', (array)$rec)) { - $DB->insert_record('course_allowed_modules', $rec); - } + $mname = $data->modulename; + if (array_key_exists($mname, $available)) { + if ($module = $DB->get_record('modules', array('name' => $mname, 'visible' => 1))) { + $rec = new stdclass(); + $rec->course = $this->get_courseid(); + $rec->module = $module->id; + if (!$DB->record_exists('course_allowed_modules', (array)$rec)) { + $DB->insert_record('course_allowed_modules', $rec); } } } } - // Role name aliases - restore_dbops::set_course_role_names($this->get_restoreid(), $this->get_courseid()); } protected function after_execute() { @@ -1963,11 +1994,15 @@ class restore_module_structure_step extends restore_structure_step { $paths = array(); - $paths[] = new restore_path_element('module', '/module'); + $module = new restore_path_element('module', '/module'); + $paths[] = $module; if ($CFG->enableavailability) { $paths[] = new restore_path_element('availability', '/module/availability_info/availability'); } + // Apply for 'format' plugins optional paths at module level + $this->add_plugin_structure('format', $module); + return $paths; } diff --git a/backup/moodle2/restore_subplugin.class.php b/backup/moodle2/restore_subplugin.class.php index 7f2b175a74e..d22a0d9c1f8 100644 --- a/backup/moodle2/restore_subplugin.class.php +++ b/backup/moodle2/restore_subplugin.class.php @@ -151,6 +151,18 @@ abstract class restore_subplugin { return $this->step->apply_date_offset($value); } + /** + * Returns the value of one (task/plan) setting + */ + protected function get_setting_value($name) { + if (is_null($this->task)) { + throw new restore_step_exception('not_specified_restore_task'); + } + return $this->task->get_setting_value($name); + } + +// end of restore_step/structure_step/task wrappers + /** * Simple helper function that returns the name for the restore_path_element * It's not mandatory to use it but recommended ;-) diff --git a/backup/util/dbops/backup_plan_dbops.class.php b/backup/util/dbops/backup_plan_dbops.class.php index 99592362bb6..a6f60fff3c7 100644 --- a/backup/util/dbops/backup_plan_dbops.class.php +++ b/backup/util/dbops/backup_plan_dbops.class.php @@ -119,6 +119,15 @@ abstract class backup_plan_dbops extends backup_dbops { return $sectionsarr; } + /** + * Given one course id, return its format in DB + */ + public static function get_courseformat_from_courseid($courseid) { + global $DB; + + return $DB->get_field('course', 'format', array('id' => $courseid)); + } + /** * Return the wwwroot of the $CFG->mnet_localhost_id host * caching it along the request diff --git a/backup/util/plan/backup_structure_step.class.php b/backup/util/plan/backup_structure_step.class.php index 29b76ac14b9..f62fee936a1 100644 --- a/backup/util/plan/backup_structure_step.class.php +++ b/backup/util/plan/backup_structure_step.class.php @@ -101,6 +101,14 @@ abstract class backup_structure_step extends backup_step { $structure->destroy(); } + /** + * As far as backup structure steps are implementing backup_plugin stuff, they need to + * have the parent task available for wrapping purposes (get course/context....) + */ + public function get_task() { + return $this->task; + } + // Protected API starts here /** @@ -133,7 +141,7 @@ abstract class backup_structure_step extends backup_step { $backupfile = $plugindir . '/backup/moodle2/' . $classname . '.class.php'; if (file_exists($backupfile)) { require_once($backupfile); - $backupplugin = new $classname($plugintype, $name, $optigroup); + $backupplugin = new $classname($plugintype, $name, $optigroup, $this); // Add plugin returned structure to optigroup $backupplugin->define_plugin_structure($element->get_name()); }