--- /dev/null
+<?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
+ */
+
+/**
+ * abstract activity task that provides all the properties and common tasks to be performed
+ * when one activity is being backup
+ *
+ * TODO: Finish phpdocs
+ */
+abstract class backup_activity_task extends backup_task {
+
+ protected $moduleid;
+ protected $sectionid;
+ protected $modulename;
+ protected $activityid;
+ protected $contextid;
+
+ /**
+ * Constructor - instantiates one object of this class
+ */
+ public function __construct($name, $moduleid, $plan = null) {
+
+ // Check moduleid exists
+ if (!$coursemodule = get_coursemodule_from_id(false, $moduleid)) {
+ throw backup_task_exception('activity_task_coursemodule_not_found', $moduleid);
+ }
+ // Check activity supports this moodle2 backup format
+ if (!plugin_supports('mod', $coursemodule->modname, FEATURE_BACKUP_MOODLE2)) {
+ throw backup_task_exception('activity_task_activity_lacks_moodle2_backup_support', $coursemodule->modname);
+ }
+
+ $this->moduleid = $moduleid;
+ $this->sectionid = $coursemodule->section;
+ $this->modulename = $coursemodule->modname;
+ $this->activityid = $coursemodule->instance;
+ $this->contextid = get_context_instance(CONTEXT_MODULE, $this->moduleid)->id;
+
+ parent::__construct($name, $plan);
+ }
+
+ public function get_moduleid() {
+ return $this->moduleid;
+ }
+
+ public function get_sectionid() {
+ return $this->sectionid;
+ }
+
+ public function get_modulename() {
+ return $this->modulename;
+ }
+
+ public function get_activityid() {
+ return $this->activityid;
+ }
+
+ public function get_contextid() {
+ return $this->contextid;
+ }
+
+ /**
+ * Activity tasks have their own directory to write files
+ */
+ public function get_taskbasepath() {
+ return $this->get_basepath() . '/activities/' . $this->modulename . '_' . $this->moduleid;
+ }
+
+ /**
+ * Create all the steps that will be part of this task
+ */
+ public function build() {
+
+ // Add some extra settings that related processors are going to need
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_MODID, base_setting::IS_INTEGER, $this->moduleid));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_COURSEID, base_setting::IS_INTEGER, $this->get_courseid()));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_SECTIONID, base_setting::IS_INTEGER, $this->sectionid));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_MODNAME, base_setting::IS_FILENAME, $this->modulename));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_ACTIVITYID, base_setting::IS_INTEGER, $this->activityid));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_CONTEXTID, base_setting::IS_INTEGER, $this->contextid));
+
+ // Create the activity directory
+ $this->add_step(new create_taskbasepath_directory('create_activity_directory'));
+
+ // Generate the module.xml file, contaning general information for the
+ // activity and from its related course_modules record and availability
+ $this->add_step(new backup_module_structure_step('module_info', 'module.xml'));
+
+ // Annotate the groups used in already annotated groupings
+ $this->add_step(new backup_annotate_groups_from_groupings('annotate_groups'));
+
+ // Here we add all the common steps for any activity and, in the point of interest
+ // we call to define_my_steps() is order to get the particular ones inserted in place.
+ $this->define_my_steps();
+
+ // Generate the roles file (optionally role assignments and always role overrides)
+ $this->add_step(new backup_roles_structure_step('activity_roles', 'roles.xml'));
+
+ // Generate the filter file (conditionally)
+ if ($this->get_setting_value('filters')) {
+ $this->add_step(new backup_filters_structure_step('activity_filters', 'filters.xml'));
+ }
+
+ // Generate the comments file (conditionally)
+ if ($this->get_setting_value('comments')) {
+ $this->add_step(new backup_comments_structure_step('activity_comments', 'comments.xml'));
+ }
+
+ // Generate the userscompletion file (conditionally)
+ if ($this->get_setting_value('userscompletion')) {
+ $this->add_step(new backup_userscompletion_structure_step('activity_userscompletion', 'completion.xml'));
+ }
+
+ // Generate the logs file (conditionally)
+ if ($this->get_setting_value('logs')) {
+ $this->add_step(new backup_activity_logs_structure_step('activity_logs', 'logs.xml'));
+ }
+
+ // Fetch all the activity grade items and put them to backup_ids
+ $this->add_step(new backup_activity_grade_items_to_ids('fetch_activity_grade_items'));
+
+ // Generate the grades file
+ $this->add_step(new backup_activity_grades_structure_step('activity_grades', 'grades.xml'));
+
+ // Annotate the scales used in already annotated outcomes
+ $this->add_step(new backup_annotate_scales_from_outcomes('annotate_scales'));
+
+ // NOTE: Historical grade information is saved completely at course level only (see 1.9)
+ // not per activity nor per selected activities (all or nothing).
+
+ // Generate the inforef file (must be after ALL steps gathering annotations of ANY type)
+ $this->add_step(new backup_inforef_structure_step('activity_inforef', 'inforef.xml'));
+
+ // Migrate the already exported inforef entries to final ones
+ $this->add_step(new move_inforef_annotations_to_final('migrate_inforef'));
+
+ // At the end, mark it as built
+ $this->built = true;
+ }
+
+ /**
+ * Exceptionally override the execute method, so, based in the activity_included setting, we are able
+ * to skip the execution of one task completely
+ */
+ public function execute() {
+
+ // Find activity_included_setting
+ if (!$this->get_setting_value('included')) {
+ $this->log('activity skipped by _included setting', backup::LOG_DEBUG, $this->name);
+
+ } else { // Setting tells us it's ok to execute
+ parent::execute();
+ }
+ }
+
+
+ /**
+ * Specialisation that, first of all, looks for the setting within
+ * the task with the the prefix added and later, delegates to parent
+ * without adding anything
+ */
+ public function get_setting($name) {
+ $namewithprefix = $this->modulename . '_' . $this->moduleid . '_' . $name;
+ $result = null;
+ foreach ($this->settings as $key => $setting) {
+ if ($setting->get_name() == $namewithprefix) {
+ if ($result != null) {
+ throw new base_task_exception('multiple_settings_by_name_found', $namewithprefix);
+ } else {
+ $result = $setting;
+ }
+ }
+ }
+ if ($result) {
+ return $result;
+ } else {
+ // Fallback to parent
+ return parent::get_setting($name);
+ }
+ }
+
+// Protected API starts here
+
+ /**
+ * Define the common setting that any backup activity will have
+ */
+ protected function define_settings() {
+
+ // All the settings related to this activity will include this prefix
+ $settingprefix = $this->modulename . '_' . $this->moduleid . '_';
+
+ // All these are common settings to be shared by all activities
+
+ // Define activity_include (to decide if the whole task must be really executed)
+ $settingname = $settingprefix . 'included';
+ $activity_userinfo = new backup_activity_generic_setting($settingname, base_setting::IS_BOOLEAN, true);
+ $this->add_setting($activity_userinfo);
+
+ // Define activity_userinfo (dependent of root users setting)
+ $settingname = $settingprefix . 'userinfo';
+ $settingname = $this->modulename . '_' . $this->moduleid . '_userinfo';
+ $activity_userinfo = new backup_activity_userinfo_setting($settingname, base_setting::IS_BOOLEAN, true);
+ $this->add_setting($activity_userinfo);
+ // Look for "users" root setting
+ $users = $this->plan->get_setting('users');
+ $users->add_dependency($activity_userinfo);
+
+ // End of common activity settings, let's add the particular ones
+ $this->define_my_settings();
+ }
+
+ /**
+ * Define (add) particular settings that each activity can have
+ */
+ abstract protected function define_my_settings();
+
+ /**
+ * Define (add) particular steps that each activity can have
+ */
+ abstract protected function define_my_steps();
+
+ /**
+ * Code the transformations to perform in the activity in
+ * order to get transportable (encoded) links
+ */
+ abstract static public function encode_content_links($content);
+
+}
--- /dev/null
+<?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
+ */
+
+/**
+ * abstract block task that provides all the properties and common steps to be performed
+ * when one block is being backup
+ *
+ * TODO: Finish phpdocs
+ */
+abstract class backup_block_task extends backup_task {
+
+ protected $blockid;
+ protected $blockname;
+ protected $contextid;
+ protected $moduleid;
+ protected $modulename;
+ protected $parentcontextid;
+
+ /**
+ * Constructor - instantiates one object of this class
+ */
+ public function __construct($name, $blockid, $moduleid = null, $plan = null) {
+ global $DB;
+
+ // Check blockid exists
+ if (!$block = $DB->get_record('block_instances', array('id' => $blockid))) {
+ throw backup_task_exception('block_task_block_instance_not_found', $blockid);
+ }
+
+ $this->blockid = $blockid;
+ $this->blockname = $block->blockname;
+ $this->contextid = get_context_instance(CONTEXT_BLOCK, $this->blockid)->id;
+ $this->moduleid = $moduleid;
+ $this->modulename = null;
+ $this->parentcontextid = null;
+
+ // If moduleid passed, check exists, supports moodle2 format and save info
+ // Check moduleid exists
+ if (!empty($moduleid)) {
+ if (!$coursemodule = get_coursemodule_from_id(false, $moduleid)) {
+ throw new backup_task_exception('block_task_coursemodule_not_found', $moduleid);
+ }
+ // Check activity supports this moodle2 backup format
+ if (!plugin_supports('mod', $coursemodule->modname, FEATURE_BACKUP_MOODLE2)) {
+ throw new backup_task_exception('block_task_activity_lacks_moodle2_backup_support', $coursemodule->modname);
+ }
+
+ $this->moduleid = $moduleid;
+ $this->modulename = $coursemodule->modname;
+ $this->parentcontextid = get_context_instance(CONTEXT_MODULE, $this->moduleid)->id;
+ }
+
+ parent::__construct($name, $plan);
+ }
+
+ public function get_blockid() {
+ return $this->blockid;
+ }
+
+ public function get_blockname() {
+ return $this->blockname;
+ }
+
+ public function get_moduleid() {
+ return $this->moduleid;
+ }
+
+ public function get_modulename() {
+ return $this->modulename;
+ }
+
+ public function get_contextid() {
+ return $this->contextid;
+ }
+
+ public function get_parentcontextid() {
+ return $this->parentcontextid;
+ }
+
+ /**
+ * Block tasks have their own directory to write files
+ */
+ public function get_taskbasepath() {
+ $basepath = $this->get_basepath();
+
+ // Module blocks are under module dir
+ if (!empty($this->moduleid)) {
+ $basepath .= '/activities/' . $this->modulename . '_' . $this->moduleid .
+ '/blocks/' . $this->blockname . '_' . $this->blockid;
+
+ // Course blocks are under course dir
+ } else {
+ $basepath .= '/course/blocks/' . $this->blockname . '_' . $this->blockid;
+ }
+ return $basepath;
+ }
+
+ /**
+ * Create all the steps that will be part of this task
+ */
+ public function build() {
+
+ // If we have decided not to backup blocks, prevent anything to be built
+ if (!$this->get_setting_value('blocks')) {
+ $this->built = true;
+ return;
+ }
+
+ // Add some extra settings that related processors are going to need
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_BLOCKID, base_setting::IS_INTEGER, $this->blockid));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_BLOCKNAME, base_setting::IS_FILENAME, $this->blockname));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_MODID, base_setting::IS_INTEGER, $this->moduleid));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_MODNAME, base_setting::IS_FILENAME, $this->modulename));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_COURSEID, base_setting::IS_INTEGER, $this->get_courseid()));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_CONTEXTID, base_setting::IS_INTEGER, $this->contextid));
+
+ // Create the block directory
+ $this->add_step(new create_taskbasepath_directory('create_block_directory'));
+
+ // Create the block.xml common file (instance + positions)
+ $this->add_step(new backup_block_instance_structure_step('block_commons', 'block.xml'));
+
+ // Here we add all the common steps for any block and, in the point of interest
+ // we call to define_my_steps() is order to get the particular ones inserted in place.
+ $this->define_my_steps();
+
+ // Generate the roles file (optionally role assignments and always role overrides)
+ $this->add_step(new backup_roles_structure_step('block_roles', 'roles.xml'));
+
+ // Generate the comments file (conditionally)
+ if ($this->get_setting_value('comments')) {
+ $this->add_step(new backup_comments_structure_step('block_comments', 'comments.xml'));
+ }
+
+ // Generate the inforef file (must be after ALL steps gathering annotations of ANY type)
+ $this->add_step(new backup_inforef_structure_step('block_inforef', 'inforef.xml'));
+
+ // Migrate the already exported inforef entries to final ones
+ $this->add_step(new move_inforef_annotations_to_final('migrate_inforef'));
+
+ // At the end, mark it as built
+ $this->built = true;
+ }
+
+// Protected API starts here
+
+ /**
+ * Define the common setting that any backup block will have
+ */
+ protected function define_settings() {
+
+ // Nothing to add, blocks doesn't have common settings (for now)
+
+ // End of common activity settings, let's add the particular ones
+ $this->define_my_settings();
+ }
+
+ /**
+ * Define (add) particular settings that each block can have
+ */
+ abstract protected function define_my_settings();
+
+ /**
+ * Define (add) particular steps that each block can have
+ */
+ abstract protected function define_my_steps();
+
+ /**
+ * Define one array() of configdata attributes
+ * that need to be processed by the contenttransformer
+ */
+ abstract public function get_configdata_encoded_attributes();
+
+ /**
+ * Code the transformations to perform in the block in
+ * order to get transportable (encoded) links
+ */
+ abstract static public function encode_content_links($content);
+}
--- /dev/null
+<?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
+ */
+
+/**
+ * course task that provides all the properties and common steps to be performed
+ * when one course is being backup
+ *
+ * TODO: Finish phpdocs
+ */
+class backup_course_task extends backup_task {
+
+ protected $courseid;
+ protected $contextid;
+
+ /**
+ * Constructor - instantiates one object of this class
+ */
+ public function __construct($name, $courseid, $plan = null) {
+
+ $this->courseid = $courseid;
+ $this->contextid = get_context_instance(CONTEXT_COURSE, $this->courseid)->id;
+
+ parent::__construct($name, $plan);
+ }
+
+ public function get_contextid() {
+ return $this->contextid;
+ }
+
+ /**
+ * Course tasks have their own directory to write files
+ */
+ public function get_taskbasepath() {
+
+ return $this->get_basepath() . '/course';
+ }
+
+ /**
+ * Create all the steps that will be part of this task
+ */
+ public function build() {
+
+ // Add some extra settings that related processors are going to need
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_COURSEID, base_setting::IS_INTEGER, $this->get_courseid()));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_CONTEXTID, base_setting::IS_INTEGER, $this->contextid));
+
+ // Create the course directory
+ $this->add_step(new create_taskbasepath_directory('create_course_directory'));
+
+ // Create the course.xml file with course & category information
+ // annotating some bits, metacourse info, tags and module restrictions
+ $this->add_step(new backup_course_structure_step('course_info', 'course.xml'));
+
+ // Annotate the groups used in already annotated groupings
+ $this->add_step(new backup_annotate_groups_from_groupings('annotate_groups'));
+
+ // Generate the roles file (optionally role assignments and always role overrides)
+ $this->add_step(new backup_roles_structure_step('course_roles', 'roles.xml'));
+
+ // Generate the filter file (conditionally)
+ if ($this->get_setting_value('filters')) {
+ $this->add_step(new backup_filters_structure_step('course_filters', 'filters.xml'));
+ }
+
+ // Generate the comments file (conditionally)
+ if ($this->get_setting_value('comments')) {
+ $this->add_step(new backup_comments_structure_step('course_comments', 'comments.xml'));
+ }
+
+ // Generate the logs file (conditionally)
+ if ($this->get_setting_value('logs')) {
+ //$this->add_step(new backup_course_logs_structure_step('course_logs', 'logs.xml'));
+ }
+
+ // Generate the inforef file (must be after ALL steps gathering annotations of ANY type)
+ $this->add_step(new backup_inforef_structure_step('course', 'inforef.xml'));
+
+ // Migrate the already exported inforef entries to final ones
+ $this->add_step(new move_inforef_annotations_to_final('migrate_inforef'));
+
+ // At the end, mark it as built
+ $this->built = true;
+ }
+
+// Protected API starts here
+
+ /**
+ * Define the common setting that any backup section will have
+ */
+ protected function define_settings() {
+
+ // Nothing to add, sections doesn't have common settings (for now)
+
+ }
+}
--- /dev/null
+<?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
+ */
+
+/**
+ * Implementation of backup_final_element that provides one interceptor for anonymization of data
+ *
+ * This class overwrites the standard set_value() method, in order to get (by name)
+ * functions from backup_anonymizer_helper executed, producing anonymization of information
+ * to happen in a clean way
+ *
+ * TODO: Finish phpdocs
+ */
+class anonymizer_final_element extends backup_final_element {
+
+ public function set_value($value) {
+ // Get parent name
+ $pname = $this->get_parent()->get_name();
+ // Get my name
+ $myname = $this->get_name();
+ // Define class and function name
+ $classname = 'backup_anonymizer_helper';
+ $methodname= 'process_' . $pname . '_' . $myname;
+ // Invoke the interception method
+ $result = call_user_func(array($classname, $methodname), $value);
+ // Finally set it
+ parent::set_value($result);
+ }
+}
+
+/**
+ * Implementation of backup_final_element that provides special handling of mnethosturl
+ *
+ * This class overwrites the standard set_value() method, in order to decide,
+ * based on various config options, what to do with the field.
+ *
+ * TODO: Finish phpdocs
+ */
+class mnethosturl_final_element extends backup_final_element {
+
+ public function set_value($value) {
+ global $CFG;
+
+ $localhostwwwroot = backup_plan_dbops::get_mnet_localhost_wwwroot();
+
+ // If user wwwroot matches mnet local host one or if
+ // there isn't associated wwwroot, skip sending it to file
+ if ($localhostwwwroot == $value || empty($value)) {
+ // Do nothing
+ } else {
+ parent::set_value($value);
+ }
+ }
+}
+
+/**
+ * Implementation of backup_nested_element that provides special handling of files
+ *
+ * This class overwrites the standard fill_values() method, so it gets intercepted
+ * for each file record being set to xml, in order to copy, at the same file, the
+ * phisical file from moodle file storage to backup file storage
+ *
+ * TODO: Finish phpdocs
+ */
+class file_nested_element extends backup_nested_element {
+
+ protected $backupid;
+
+ public function process($processor) {
+ // Get current backupid from processor, we'll need later
+ if (is_null($this->backupid)) {
+ $this->backupid = $processor->get_var(backup::VAR_BACKUPID);
+ }
+ parent::process($processor);
+ }
+
+ public function fill_values($values) {
+ // Fill values
+ parent::fill_values($values);
+ // Do our own tasks (copy file from moodle to backup)
+ backup_file_manager::copy_file_moodle2backup($this->backupid, $values);
+ }
+}
--- /dev/null
+<?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
+ */
+
+/**
+ * Default block task to backup blocks that haven't own DB structures to be added
+ * when one block is being backup
+ *
+ * TODO: Finish phpdocs
+ */
+class backup_default_block_task extends backup_block_task {
+ // Nothing to do, it's just the backup_block_task in action
+ // with required methods doing nothing special
+
+ protected function define_my_settings() {
+ }
+
+ protected function define_my_steps() {
+ }
+
+ public function get_configdata_encoded_attributes() {
+ return array();
+ }
+
+ static public function encode_content_links($content) {
+ return $content;
+ }
+}
+
--- /dev/null
+<?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
+ */
+
+/**
+ * Final task that provides all the final steps necessary in order to finish one
+ * backup (mainly gathering references and creating the main xml) apart from
+ * some final cleaning
+ *
+ * TODO: Finish phpdocs
+ */
+class backup_final_task extends backup_task {
+
+ /**
+ * Create all the steps that will be part of this task
+ */
+ public function build() {
+
+ // Set the backup::VAR_CONTEXTID setting to course context as far as next steps require that
+ $coursectxid = get_context_instance(CONTEXT_COURSE, $this->get_courseid())->id;
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_CONTEXTID, base_setting::IS_INTEGER, $coursectxid));
+
+ // Generate the groups file with the final annotated groups and groupings
+ // including membership based on setting
+ $this->add_step(new backup_groups_structure_step('groups', 'groups.xml'));
+
+ // Annotate all the user files (conditionally) (private files, and profile)
+ // Because each user has its own context, we need a separate/specialised step here
+ // This step also ensures that the contexts for all the users exist, so next
+ // step can be safely executed (join between users and contexts)
+ // Not executed if backup is without users of anonymized
+ if ($this->get_setting_value('users') && !$this->get_setting_value('anonymize')) {
+ $this->add_step(new backup_annotate_all_user_files('user_files'));
+ }
+
+ // Generate the users file (conditonally) with the final annotated users
+ // including custom profile fields, preferences, tags, role assignments and
+ // overrides
+ if ($this->get_setting_value('users')) {
+ $this->add_step(new backup_users_structure_step('users', 'users.xml'));
+ }
+
+ // Generate the top roles file with all the final annotated roles
+ // that have been detected along the whole process. It's just
+ // the list of role definitions (no assignments nor permissions)
+ $this->add_step(new backup_final_roles_structure_step('roleslist', 'roles.xml'));
+
+ // Generate the scales file with all the annotated scales
+ $this->add_step(new backup_final_scales_structure_step('scaleslist', 'scales.xml'));
+
+ // Generate the outcomes file with all the annotated outcomes
+ $this->add_step(new backup_final_outcomes_structure_step('outcomeslist', 'outcomes.xml'));
+
+ // Migrate the pending annotations to final (prev steps may have added some files)
+ // This must be executed before backup files
+ $this->add_step(new move_inforef_annotations_to_final('migrate_inforef'));
+
+ // Generate the files.xml file with all the (final) annotated files. At the same
+ // time copy all the files from moodle storage to backup storage (uses custom
+ // backup_nested_element for that)
+ $this->add_step(new backup_final_files_structure_step('fileslist', 'files.xml'));
+
+ // Write the main moodle_backup.xml file, with all the information related
+ // to the backup, settings, license, versions and other useful information
+ $this->add_step(new backup_main_structure_step('mainfile', 'moodle_backup.xml'));
+
+ $this->built = true;
+ }
+
+// Protected API starts here
+
+ /**
+ * Define the common setting that any backup type will have
+ */
+ protected function define_settings() {
+ // This task has not settings (could have them, like destination or so in the future, let's see)
+ }
+}
--- /dev/null
+<?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
+ */
+
+require_once($CFG->dirroot . '/backup/moodle2/backup_root_task.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_activity_task.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_section_task.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_course_task.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_final_task.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_block_task.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_default_block_task.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_xml_transformer.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_stepslib.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_custom_fields.php');
+
+// Load all the activity tasks for moodle2 format
+$mods = get_plugin_list('mod');
+foreach ($mods as $mod => $moddir) {
+ if (plugin_supports('mod', $mod, FEATURE_BACKUP_MOODLE2)) {
+ require_once($moddir . '/backup/moodle2/backup_' . $mod . '_activity_task.class.php');
+ }
+}
+
+// Load all the block tasks for moodle2 format
+$blocks = get_plugin_list('block');
+foreach ($blocks as $block => $blockdir) {
+ $taskpath = $blockdir . '/backup/moodle2/backup_' . $block . '_block_task.class.php';
+ if (file_exists($taskpath)) {
+ require_once($taskpath);
+ }
+}
+
+/**
+ * Abstract class defining the static method in charge of building the whole
+ * backup plan, based in @backup_controller preferences.
+ *
+ * TODO: Finish phpdocs
+ */
+abstract class backup_plan_builder {
+
+ /**
+ * Dispatches, based on type to specialised builders
+ */
+ static public function build_plan($controller) {
+
+ $plan = $controller->get_plan();
+
+ // Add the root task, responsible for storing global settings
+ // and some init tasks
+ $plan->add_task(new backup_root_task('root_task'));
+
+ switch ($controller->get_type()) {
+ case backup::TYPE_1ACTIVITY:
+ self::build_activity_plan($controller, $controller->get_id());
+ break;
+ case backup::TYPE_1SECTION:
+ self::build_section_plan($controller, $controller->get_id());
+ break;
+ case backup::TYPE_1COURSE:
+ self::build_course_plan($controller, $controller->get_id());
+ break;
+ }
+
+ // Add the final task, responsible for outputing
+ // all the global xml files (groups, users,
+ // gradebook, questions, roles, files...) and
+ // the main moodle_backup.xml file
+ // and perform other various final actions.
+ $plan->add_task(new backup_final_task('final_task'));
+ }
+
+
+ /**
+ * Return one array of supported backup types
+ */
+ static public function supported_backup_types() {
+ return array(backup::TYPE_1COURSE, backup::TYPE_1SECTION, backup::TYPE_1ACTIVITY);
+ }
+
+// Protected API starts here
+
+ /**
+ * Build one 1-activity backup
+ */
+ static protected function build_activity_plan($controller, $id) {
+
+ $plan = $controller->get_plan();
+
+ // Add the activity task, responsible for outputing
+ // all the module related information
+ $plan->add_task(backup_factory::get_backup_activity_task($controller->get_format(), $id));
+
+ // For the given activity, add as many block tasks as necessary
+ $blockids = backup_plan_dbops::get_blockids_from_moduleid($id);
+ foreach ($blockids as $blockid) {
+ $plan->add_task(backup_factory::get_backup_block_task($controller->get_format(), $blockid, $id));
+ }
+ }
+
+ /**
+ * Build one 1-section backup
+ */
+ static protected function build_section_plan($controller, $id) {
+
+ $plan = $controller->get_plan();
+
+ // Add the section task, responsible for outputing
+ // all the section related information
+ $plan->add_task(backup_factory::get_backup_section_task($controller->get_format(), $id));
+
+ // For the given section, add as many activity tasks as necessary
+ $coursemodules = backup_plan_dbops::get_modules_from_sectionid($id);
+ foreach ($coursemodules as $coursemodule) {
+ if (plugin_supports('mod', $coursemodule->modname, $controller->get_format())) { // Check we support the format
+ self::build_activity_plan($controller, $coursemodule->id);
+ } else {
+ // TODO: Debug information about module not supported
+ }
+ }
+ }
+
+ /**
+ * Build one 1-course backup
+ */
+ static protected function build_course_plan($controller, $id) {
+
+ $plan = $controller->get_plan();
+
+ // Add the course task, responsible for outputing
+ // all the course related information
+ $plan->add_task(backup_factory::get_backup_course_task($controller->get_format(), $id));
+
+ // For the given course, add as many section tasks as necessary
+ $sections = backup_plan_dbops::get_sections_from_courseid($id);
+ foreach ($sections as $section) {
+ self::build_section_plan($controller, $section);
+ }
+
+ // For the given course, add as many block tasks as necessary
+ $blockids = backup_plan_dbops::get_blockids_from_courseid($id);
+ foreach ($blockids as $blockid) {
+ $plan->add_task(backup_factory::get_backup_block_task($controller->get_format(), $blockid));
+ }
+ }
+}
--- /dev/null
+<?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
+ */
+
+/**
+ * Start task that provides all the settings common to all backups and some initializaton steps
+ *
+ * TODO: Finish phpdocs
+ */
+class backup_root_task extends backup_task {
+
+ /**
+ * Create all the steps that will be part of this task
+ */
+ public function build() {
+
+ // Add all the steps needed to prepare any moodle2 backup to work
+ $this->add_step(new create_and_clean_temp_stuff('create_and_clean_temp_stuff'));
+
+ $this->built = true;
+ }
+
+// Protected API starts here
+
+ /**
+ * Define the common setting that any backup type will have
+ */
+ protected function define_settings() {
+
+ // Define filename setting
+ $this->add_setting(new backup_filename_setting('filename', base_setting::IS_FILENAME, 'backup.zip'));
+
+ // Define users setting (keeping it on hand to define dependencies)
+ $users = new backup_users_setting('users', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($users);
+
+ // Define anonymize (dependent of users)
+ $anonymize = new backup_anonymize_setting('anonymize', base_setting::IS_BOOLEAN, false);
+ $this->add_setting($anonymize);
+ $users->add_dependency($anonymize);
+
+ // Define role_assignments (dependent of users)
+ $roleassignments = new backup_role_assignments_setting('role_assignments', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($roleassignments);
+ $users->add_dependency($roleassignments);
+
+ // Define user_files (dependent of users)
+ $userfiles = new backup_user_files_setting('user_files', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($userfiles);
+ $users->add_dependency($userfiles);
+
+ // Define blocks
+ $blocks = new backup_generic_setting('blocks', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($blocks);
+
+ // Define filters
+ $filters = new backup_generic_setting('filters', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($filters);
+
+ // Define comments (dependent of users)
+ $comments = new backup_comments_setting('comments', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($comments);
+ $users->add_dependency($comments);
+
+ // Define completion (dependent of users)
+ $completion = new backup_userscompletion_setting('userscompletion', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($completion);
+ $users->add_dependency($completion);
+
+ // Define logs (dependent of users)
+ $logs = new backup_logs_setting('logs', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($logs);
+ $users->add_dependency($logs);
+
+ // Define grade_histories
+ $gradehistories = new backup_generic_setting('grade_histories', base_setting::IS_BOOLEAN, true);
+ $this->add_setting($gradehistories);
+ }
+}
--- /dev/null
+<?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
+ */
+
+/**
+ * section task that provides all the properties and common steps to be performed
+ * when one section is being backup
+ *
+ * TODO: Finish phpdocs
+ */
+class backup_section_task extends backup_task {
+
+ protected $sectionid;
+
+ /**
+ * Constructor - instantiates one object of this class
+ */
+ public function __construct($name, $sectionid, $plan = null) {
+ global $DB;
+
+ // Check section exists
+ if (!$section = $DB->get_record('course_sections', array('id' => $sectionid))) {
+ throw backup_task_exception('section_task_section_not_found', $sectionid);
+ }
+
+ $this->sectionid = $sectionid;
+
+ parent::__construct($name, $plan);
+ }
+
+ public function get_sectionid() {
+ return $this->sectionid;
+ }
+
+ /**
+ * Section tasks have their own directory to write files
+ */
+ public function get_taskbasepath() {
+
+ return $this->get_basepath() . '/sections/section_' . $this->sectionid;
+ }
+
+ /**
+ * Create all the steps that will be part of this task
+ */
+ public function build() {
+
+ // Set the backup::VAR_CONTEXTID setting to course context as far as next steps require that
+ $coursectxid = get_context_instance(CONTEXT_COURSE, $this->get_courseid())->id;
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_CONTEXTID, base_setting::IS_INTEGER, $coursectxid));
+
+ // Add some extra settings that related processors are going to need
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_SECTIONID, base_setting::IS_INTEGER, $this->sectionid));
+ $this->add_setting(new backup_activity_generic_setting(backup::VAR_COURSEID, base_setting::IS_INTEGER, $this->get_courseid()));
+
+ // Create the section directory
+ $this->add_step(new create_taskbasepath_directory('create_section_directory'));
+
+ // Create the section.xml common file (course_sections)
+ $this->add_step(new backup_section_structure_step('section_commons', 'section.xml'));
+
+ // Generate the inforef file (must be after ALL steps gathering annotations of ANY type)
+ $this->add_step(new backup_inforef_structure_step('section_inforef', 'inforef.xml'));
+
+ // Migrate the already exported inforef entries to final ones
+ $this->add_step(new move_inforef_annotations_to_final('migrate_inforef'));
+
+ // At the end, mark it as built
+ $this->built = true;
+ }
+
+// Protected API starts here
+
+ /**
+ * Define the common setting that any backup section will have
+ */
+ protected function define_settings() {
+
+ // Nothing to add, sections doesn't have common settings (for now)
+
+ }
+}
--- /dev/null
+<?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
+ */
+
+// Root backup settings
+
+/**
+ * root generic setting to store different things without dependencies
+ */
+class backup_generic_setting extends root_backup_setting {
+ public function process_change($setting, $ctype, $oldv) {
+ // Nothing to do, no dependencies
+ }
+}
+
+/**
+ * root setting to handle backup file names (no dependencies nor anything else)
+ */
+class backup_filename_setting extends backup_generic_setting {
+}
+
+/**
+ * root setting to control if backup will include user information
+ * A lot of other settings are dependant of this (module's user info,
+ * grades user info, messages, blogs...
+ */
+class backup_users_setting extends backup_generic_setting {
+}
+
+/**
+ * root setting to control if backup will generate anonymized
+ * user info or no, depends of @backup_users_setting so only is
+ * availabe if the former is enabled (apart from security
+ * that can change it
+ */
+class backup_anonymize_setting extends root_backup_setting {
+ public function process_change($setting, $ctype, $oldv) {
+ // If change detected in backup_users_setting, proceed
+ if ($setting instanceof backup_users_setting) {
+ switch ($ctype) {
+ case self::CHANGED_VALUE: // backup_users = false, this too, and locked
+ if (!$setting->get_value()) {
+ $this->set_value(false);
+ $this->set_status(self::LOCKED_BY_HIERARCHY);
+ }
+ break;
+ case self::CHANGED_VISIBILITY: // backup_users not visible, this too
+ if (!$setting->get_visibility()) {
+ $this->set_visibility(false);
+ }
+ break;
+ case self::CHANGED_STATUS: // backup_users unlocked, this too
+ if ($setting->get_status() == self::NOT_LOCKED) {
+ $this->set_status(self::NOT_LOCKED);
+ }
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * root setting to control if backup will include
+ * user files or no (images, local storage), depends of @backup_users_setting
+ * exactly in the same way than @backup_anonymize_setting so we extend from it
+ */
+class backup_user_files_setting extends backup_anonymize_setting {
+ // Nothing to do. All the logic is in backup_anonymize_setting
+}
+
+/**
+ * root setting to control if backup will include
+ * role assignments or no (any level), depends of @backup_users_setting
+ * exactly in the same way than @backup_anonymize_setting so we extend from it
+ */
+class backup_role_assignments_setting extends backup_anonymize_setting {
+ // Nothing to do. All the logic is in backup_anonymize_setting
+}
+
+/**
+ * root setting to control if backup will include
+ * logs or no (any level), depends of @backup_users_setting
+ * exactly in the same way than @backup_anonymize_setting so we extend from it
+ */
+class backup_logs_setting extends backup_anonymize_setting {
+ // Nothing to do. All the logic is in backup_anonymize_setting
+}
+
+/**
+ * root setting to control if backup will include
+ * comments or no (any level), depends of @backup_users_setting
+ * exactly in the same way than @backup_anonymize_setting so we extend from it
+ */
+class backup_comments_setting extends backup_anonymize_setting {
+ // Nothing to do. All the logic is in backup_anonymize_setting
+}
+
+/**
+ * root setting to control if backup will include
+ * users completion data or no (any level), depends of @backup_users_setting
+ * exactly in the same way than @backup_anonymize_setting so we extend from it
+ */
+class backup_userscompletion_setting extends backup_anonymize_setting {
+ // Nothing to do. All the logic is in backup_anonymize_setting
+}
+
+
+// Activity backup settings
+
+/**
+ * generic activity setting to pass various settings between tasks and steps
+ */
+class backup_activity_generic_setting extends activity_backup_setting {
+ public function process_change($setting, $ctype, $oldv) {
+ // Nothing to do, no dependencies
+ }
+}
+
+/**
+ * activity backup setting to control if activity will include
+ * user information or no, depends of @backup_users_setting
+ */
+class backup_activity_userinfo_setting extends activity_backup_setting {
+ public function process_change($setting, $ctype, $oldv) {
+ // If change detected in backup_users_setting, proceed
+ if ($setting instanceof backup_users_setting) {
+ switch ($ctype) {
+ case self::CHANGED_VALUE: // backup_users = false, this too, and locked
+ if (!$setting->get_value()) {
+ $this->set_value(false);
+ $this->set_status(self::LOCKED_BY_HIERARCHY);
+ }
+ break;
+ case self::CHANGED_VISIBILITY: // backup_users not visible, this too
+ if (!$setting->get_visibility()) {
+ $this->set_visibility(false);
+ }
+ break;
+ case self::CHANGED_STATUS: // backup_users unlocked, this too
+ if ($setting->get_status() == self::NOT_LOCKED) {
+ $this->set_status(self::NOT_LOCKED);
+ }
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+<?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
+ */
+
+/**
+ * Define all the backup steps that will be used by common tasks in backup
+ */
+class create_and_clean_temp_stuff extends backup_execution_step {
+
+ protected function define_execution() {
+ backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
+ backup_helper::clear_backup_dir($this->get_backupid()); // Empty temp dir, just in case
+ backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60)); // Delete > 4 hours temp dirs
+ backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
+ }
+}
+
+/**
+ * Create the directory where all the task (activity/block...) information will be stored
+ */
+class create_taskbasepath_directory extends backup_execution_step {
+
+ protected function define_execution() {
+ global $CFG;
+ $basepath = $this->task->get_taskbasepath();
+ if (!check_dir_exists($basepath, true, true)) {
+ throw new backup_step_exception('cannot_create_taskbasepath_directory', $basepath);
+ }
+ }
+}
+
+/**
+ * Abtract tructure step, parent of all the activity structure steps. Used to wrap the
+ * activity structure definition within the main <activity ...> tag
+ */
+abstract class backup_activity_structure_step extends backup_structure_step {
+
+ protected function prepare_activity_structure($activitystructure) {
+
+ // Create the wrap element
+ $activity = new backup_nested_element('activity', array('id', 'moduleid', 'modulename', 'contextid'), null);
+
+ // Build the tree
+ $activity->add_child($activitystructure);
+
+ // Set the source
+ $activityarr = array((object)array(
+ 'id' => $this->task->get_activityid(),
+ 'moduleid' => $this->task->get_moduleid(),
+ 'modulename' => $this->task->get_modulename(),
+ 'contextid' => $this->task->get_contextid()));
+
+ $activity->set_source_array($activityarr);
+
+ // Return the root element (activity)
+ return $activity;
+ }
+}
+
+/**
+ * Abtract structure step, parent of all the block structure steps. Used to wrap the
+ * block structure definition within the main <block ...> tag
+ */
+abstract class backup_block_structure_step extends backup_structure_step {
+
+ protected function prepare_block_structure($blockstructure) {
+
+ // Create the wrap element
+ $block = new backup_nested_element('block', array('id', 'blockname', 'contextid'), null);
+
+ // Build the tree
+ $block->add_child($blockstructure);
+
+ // Set the source
+ $blockarr = array((object)array(
+ 'id' => $this->task->get_blockid(),
+ 'blockname' => $this->task->get_blockname(),
+ 'contextid' => $this->task->get_contextid()));
+
+ $block->set_source_array($blockarr);
+
+ // Return the root element (block)
+ return $block;
+ }
+}
+
+/**
+ * structure step that will generate the module.xml file for the activity,
+ * acummulating various information about the activity, annotating groupings
+ * and completion/avail conf
+ */
+class backup_module_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define each element separated
+
+ $module = new backup_nested_element('module', array('id', 'version'), array(
+ 'modulename', 'sectionid', 'sectionnumber', 'idnumber',
+ 'added', 'score', 'indent', 'visible',
+ 'visibleold', 'groupmode', 'groupingid', 'groupmembersonly',
+ 'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected',
+ 'availablefrom', 'availableuntil', 'showavailability'));
+
+ $availinfo = new backup_nested_element('availability_info');
+ $availability = new backup_nested_element('availability', array('id'), array(
+ 'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
+
+ // Define the tree
+ $module->add_child($availinfo);
+ $availinfo->add_child($availability);
+
+ // Set the sources
+
+ $module->set_source_sql('
+ SELECT cm.*, m.version, m.name AS modulename, s.id AS sectionid, s.section AS sectionnumber
+ FROM {course_modules} cm
+ JOIN {modules} m ON m.id = cm.module
+ JOIN {course_sections} s ON s.id = cm.section
+ WHERE cm.id = ?', array(backup::VAR_MODID));
+
+ $availability->set_source_table('course_modules_availability', array('coursemoduleid' => backup::VAR_MODID));
+
+ // Define annotations
+ $module->annotate_ids('grouping', 'groupingid');
+
+ // Return the root element ($module)
+ return $module;
+ }
+}
+
+/**
+ * structure step that will genereate the section.xml file for the section
+ * annotating files
+ */
+class backup_section_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define each element separated
+
+ $section = new backup_nested_element('section', array('id'), array(
+ 'number', 'summary', 'sequence', 'visible'));
+
+ // Define sources
+
+ $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
+
+ // Set annotations
+ $section->annotate_files(array('course_section'), 'id');
+
+ return $section;
+ }
+}
+
+/**
+ * structure step that will generate the course.xml file for the course, including
+ * course category reference, tags, metacourse, modules restriction information
+ * and some annotations (files & groupings)
+ */
+class backup_course_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+ global $DB;
+
+ // Define each element separated
+
+ $course = new backup_nested_element('course', array('id', 'contextid'), array(
+ 'shortname', 'fullname', 'idnumber', 'password',
+ 'summary', 'summaryformat', 'format', 'showgrades',
+ 'newsitems', 'guest', 'startdate', 'enrolperiod',
+ 'numsections', 'marker', 'maxbytes', 'showreports',
+ 'visible', 'hiddensections', 'groupmode', 'groupmodeforce',
+ 'defaultgroupingid', 'lang', 'theme', 'cost',
+ 'currency', 'timecreated', 'timemodified', 'metacourse',
+ 'requested', 'restrictmodules', 'expirynotify', 'expirythreshold',
+ 'notifystudents', 'enrollable', 'enrolstartdate', 'enrolenddate',
+ 'enrol', 'defaultrole', 'enablecompletion'));
+
+ $category = new backup_nested_element('category', array('id'), array(
+ 'name', 'description'));
+
+ $tags = new backup_nested_element('tags');
+
+ $tag = new backup_nested_element('tag', array('id'), array(
+ 'name', 'rawname'));
+
+ $allowedmodules = new backup_nested_element('allowed_modules');
+
+ $module = new backup_nested_element('module', array('modulename'));
+
+ // Build the tree
+
+ $course->add_child($category);
+
+ $course->add_child($tags);
+ $tags->add_child($tag);
+
+ $course->add_child($allowedmodules);
+ $allowedmodules->add_child($module);
+
+ // Set the sources
+
+ $courserec = $DB->get_record('course', array('id' => $this->task->get_courseid()));
+ $courserec->contextid = $this->task->get_contextid();
+
+ $course->set_source_array(array($courserec));
+
+ $categoryrec = $DB->get_record('course_categories', array('id' => $courserec->category));
+
+ $category->set_source_array(array($categoryrec));
+
+ $tag->set_source_sql('SELECT t.id, t.name, t.rawname
+ FROM {tag} t
+ JOIN {tag_instance} ti ON ti.tagid = t.id
+ WHERE ti.itemtype = ?
+ AND ti.itemid = ?', array(
+ $this->is_sqlparam('course'),
+ backup::VAR_PARENTID));
+
+ $module->set_source_sql('SELECT m.name AS modulename
+ FROM {modules} m
+ JOIN {course_allowed_modules} cam ON m.id = cam.module
+ WHERE course = ?', array(backup::VAR_COURSEID));
+
+ // Some annotations
+
+ $course->annotate_ids('role', 'defaultrole');
+ $course->annotate_ids('grouping', 'defaultgroupingid');
+
+ $course->annotate_files(array('course_summary', 'course_content'), null);
+
+ // Return root element ($course)
+
+ return $course;
+ }
+}
+
+/**
+ * structure step that will generate the roles.xml file for the given context, observing
+ * the role_assignments setting to know if that part needs to be included
+ */
+class backup_roles_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // To know if we are including role assignments
+ $roleassignments = $this->get_setting_value('role_assignments');
+
+ // Define each element separated
+
+ $roles = new backup_nested_element('roles');
+
+ $overrides = new backup_nested_element('role_overrides');
+
+ $override = new backup_nested_element('override', array('id'), array(
+ 'roleid', 'capability', 'permission', 'timemodified',
+ 'modifierid'));
+
+ $assignments = new backup_nested_element('role_assignments');
+
+ $assignment = new backup_nested_element('assignment', array('id'), array(
+ 'roleid', 'userid', 'hidden', 'timestart',
+ 'timeend', 'timemodified', 'modifierid', 'enrol',
+ 'sortorder'));
+
+ // Build the tree
+ $roles->add_child($overrides);
+ $roles->add_child($assignments);
+
+ $overrides->add_child($override);
+ $assignments->add_child($assignment);
+
+ // Define sources
+
+ $override->set_source_table('role_capabilities', array('contextid' => backup::VAR_CONTEXTID));
+
+ // Assignments only added if specified
+ if ($roleassignments) {
+ $assignment->set_source_table('role_assignments', array('contextid' => backup::VAR_CONTEXTID));
+ }
+
+ // Define id annotations
+ $override->annotate_ids('role', 'roleid');
+
+ $assignment->annotate_ids('role', 'roleid');
+
+ $assignment->annotate_ids('user', 'userid');
+
+ return $roles;
+ }
+}
+
+/**
+ * structure step that will generate the roles.xml containing the
+ * list of roles used along the whole backup process. Just raw
+ * list of used roles from role table
+ */
+class backup_final_roles_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define elements
+
+ $rolesdef = new backup_nested_element('roles_definition');
+
+ $role = new backup_nested_element('role', array('id'), array(
+ 'name', 'shortname', 'nameincourse', 'description',
+ 'sortorder', 'archetype'));
+
+ // Build the tree
+
+ $rolesdef->add_child($role);
+
+ // Define sources
+
+ $role->set_source_sql("SELECT r.*, rn.name AS nameincourse
+ FROM {role} r
+ JOIN {backup_ids_temp} bi ON r.id = bi.itemid
+ LEFT JOIN {role_names} rn ON r.id = rn.roleid AND rn.contextid = ?
+ WHERE bi.backupid = ?
+ AND bi.itemname = 'rolefinal'", array(backup::VAR_CONTEXTID, backup::VAR_BACKUPID));
+
+ // Return main element (rolesdef)
+ return $rolesdef;
+ }
+}
+
+/**
+ * structure step that will generate the scales.xml containing the
+ * list of scales used along the whole backup process.
+ */
+class backup_final_scales_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define elements
+
+ $scalesdef = new backup_nested_element('scales_definition');
+
+ $scale = new backup_nested_element('scale', array('id'), array(
+ 'courseid', 'userid', 'name', 'scale',
+ 'description', 'descriptionformat', 'timemodified'));
+
+ // Build the tree
+
+ $scalesdef->add_child($scale);
+
+ // Define sources
+
+ $scale->set_source_sql("SELECT s.*
+ FROM {scale} s
+ JOIN {backup_ids_temp} bi ON s.id = bi.itemid
+ WHERE bi.backupid = ?
+ AND bi.itemname = 'scalefinal'", array(backup::VAR_BACKUPID));
+
+ // Return main element (scalesdef)
+ return $scalesdef;
+ }
+}
+
+/**
+ * structure step that will generate the outcomes.xml containing the
+ * list of outcomes used along the whole backup process.
+ */
+class backup_final_outcomes_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define elements
+
+ $outcomesdef = new backup_nested_element('outcomes_definition');
+
+ $outcome = new backup_nested_element('outcome', array('id'), array(
+ 'courseid', 'userid', 'shortname', 'fullname',
+ 'scaleid', 'description', 'descriptionformat', 'timecreated',
+ 'timemodified','usermodified'));
+
+ // Build the tree
+
+ $outcomesdef->add_child($outcome);
+
+ // Define sources
+
+ $outcome->set_source_sql("SELECT o.*
+ FROM {grade_outcomes} o
+ JOIN {backup_ids_temp} bi ON o.id = bi.itemid
+ WHERE bi.backupid = ?
+ AND bi.itemname = 'outcomefinal'", array(backup::VAR_BACKUPID));
+
+ // Return main element (outcomesdef)
+ return $outcomesdef;
+ }
+}
+
+/**
+ * structure step in charge of constructing the filters.xml file for all the filters found
+ * in activity
+ */
+class backup_filters_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define each element separated
+
+ $filters = new backup_nested_element('filters');
+
+ $actives = new backup_nested_element('filter_actives');
+
+ $active = new backup_nested_element('filter_active', null, array('filter', 'active'));
+
+ $configs = new backup_nested_element('filter_configs');
+
+ $config = new backup_nested_element('filter_config', null, array('filter', 'name', 'value'));
+
+ // Build the tree
+
+ $filters->add_child($actives);
+ $filters->add_child($configs);
+
+ $actives->add_child($active);
+ $configs->add_child($config);
+
+ // Define sources
+
+ list($activearr, $configarr) = filter_get_all_local_settings($this->task->get_contextid());
+
+ $active->set_source_array($activearr);
+ $config->set_source_array($configarr);
+
+ // Return the root element (filters)
+ return $filters;
+ }
+}
+
+/**
+ * structure step in charge of constructing the comments.xml file for all the comments found
+ * in a given context
+ */
+class backup_comments_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define each element separated
+
+ $comments = new backup_nested_element('comments');
+
+ $comment = new backup_nested_element('comment', array('id'), array(
+ 'commentarea', 'itemid', 'content', 'format',
+ 'userid', 'timecreated'));
+
+ // Build the tree
+
+ $comments->add_child($comment);
+
+ // Define sources
+
+ $comment->set_source_table('comments', array('contextid' => backup::VAR_CONTEXTID));
+
+ // Define id annotations
+
+ $comment->annotate_ids('user', 'userid');
+
+ // Return the root element (comments)
+ return $comments;
+ }
+}
+
+/**
+ * structure step in charge if constructing the completion.xml file for all the users completion
+ * information in a given activity
+ */
+class backup_userscompletion_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define each element separated
+
+ $completions = new backup_nested_element('completions');
+
+ $completion = new backup_nested_element('completion', array('id'), array(
+ 'userid', 'completionstate', 'viewed', 'timemodified'));
+
+ // Build the tree
+
+ $completions->add_child($completion);
+
+ // Define sources
+
+ $completion->set_source_table('course_modules_completion', array('coursemoduleid' => backup::VAR_MODID));
+
+ // Define id annotations
+
+ $completion->annotate_ids('user', 'userid');
+
+ // Return the root element (completions)
+ return $completions;
+ }
+}
+
+/**
+ * structure step in charge of constructing the main groups.xml file for all the groups and
+ * groupings information already annotated
+ */
+class backup_groups_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // To know if we are including users
+ $users = $this->get_setting_value('users');
+
+ // Define each element separated
+
+ $groups = new backup_nested_element('groups');
+
+ $group = new backup_nested_element('group', array('id'), array(
+ 'name', 'description', 'descriptionformat', 'enrolmentkey',
+ 'picture', 'hidepicture', 'timecreated', 'timemodified'));
+
+ $members = new backup_nested_element('group_members');
+
+ $member = new backup_nested_element('group_member', array('id'), array(
+ 'userid', 'timeadded'));
+
+ $groupings = new backup_nested_element('groupings');
+
+ $grouping = new backup_nested_element('grouping', 'id', array(
+ 'name', 'description', 'descriptionformat', 'configdata',
+ 'timecreated', 'timemodified'));
+
+ $groupinggroups = new backup_nested_element('grouping_groups');
+
+ $groupinggroup = new backup_nested_element('grouping_group', array('id'), array(
+ 'groupid', 'timeadded'));
+
+ // Build the tree
+
+ $groups->add_child($group);
+ $groups->add_child($groupings);
+
+ $group->add_child($members);
+ $members->add_child($member);
+
+ $groupings->add_child($grouping);
+ $grouping->add_child($groupinggroups);
+ $groupinggroups->add_child($groupinggroup);
+
+ // Define sources
+
+ $group->set_source_sql("
+ SELECT g.*
+ FROM {groups} g
+ JOIN {backup_ids_temp} bi ON g.id = bi.itemid
+ WHERE bi.backupid = ?
+ AND bi.itemname = 'groupfinal'", array(backup::VAR_BACKUPID));
+
+ // This only happens if we are including users
+ if ($users) {
+ $member->set_source_table('groups_members', array('groupid' => backup::VAR_PARENTID));
+ }
+
+ $grouping->set_source_sql("
+ SELECT g.*
+ FROM {groupings} g
+ JOIN {backup_ids_temp} bi ON g.id = bi.itemid
+ WHERE bi.backupid = ?
+ AND bi.itemname = 'groupingfinal'", array(backup::VAR_BACKUPID));
+
+ $groupinggroup->set_source_table('groupings_groups', array('groupingid' => backup::VAR_PARENTID));
+
+ // Define id annotations (as final)
+
+ $member->annotate_ids('userfinal', 'userid');
+
+ // Define file annotations
+
+ // TODO: Change "course_group_image" file area to the one finally used for group images
+ $group->annotate_files(array('course_group_description', 'course_group_image'), 'id');
+
+ // Return the root element (groups)
+ return $groups;
+ }
+}
+
+/**
+ * structure step in charge of constructing the main users.xml file for all the users already
+ * annotated (final). Includes custom profile fields, preferences, tags, role assignments and
+ * overrides.
+ */
+class backup_users_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+ global $CFG;
+
+ // To know if we are anonymizing users
+ $anonymize = $this->get_setting_value('anonymize');
+ // To know if we are including role assignments
+ $roleassignments = $this->get_setting_value('role_assignments');
+
+ // Define each element separated
+
+ $users = new backup_nested_element('users');
+
+ // Create the array of user fields by hand, as far as we have various bits to control
+ // anonymize option, password backup, mnethostid...
+
+ // First, the fields not needing anonymization nor special handling
+ $normalfields = array(
+ 'confirmed', 'policyagreed', 'deleted',
+ 'lang', 'theme', 'timezone', 'firstaccess',
+ 'lastaccess', 'lastlogin', 'currentlogin', 'secret',
+ 'mailformat', 'maildigest', 'maildisplay', 'htmleditor',
+ 'ajax', 'autosubscribe', 'trackforums', 'timecreated',
+ 'timemodified', 'trustbitmask', 'screenreader');
+
+ // Then, the fields potentially needing anonymization
+ $anonfields = array(
+ 'username', 'idnumber', 'firstname', 'lastname',
+ 'email', 'emailstop', 'lastip', 'picture',
+ 'url', 'description', 'description_format', 'imagealt', 'auth');
+
+ // Add anonymized fields to $userfields with custom final element
+ foreach ($anonfields as $field) {
+ if ($anonymize) {
+ $userfields[] = new anonymizer_final_element($field);
+ } else {
+ $userfields[] = $field; // No anonymization, normally added
+ }
+ }
+
+ // mnethosturl requires special handling (custom final element)
+ $userfields[] = new mnethosturl_final_element('mnethosturl');
+
+ // password added conditionally
+ if (!empty($CFG->includeuserpasswordsinbackup)) {
+ $userfields[] = 'password';
+ }
+
+ // Merge all the fields
+ $userfields = array_merge($userfields, $normalfields);
+
+ $user = new backup_nested_element('user', array('id', 'contextid'), $userfields);
+
+ $customfields = new backup_nested_element('custom_fields');
+
+ $customfield = new backup_nested_element('custom_field', array('id'), array(
+ 'field_name', 'field_type', 'field_data'));
+
+ $tags = new backup_nested_element('tags');
+
+ $tag = new backup_nested_element('tag', array('id'), array(
+ 'name', 'rawname'));
+
+ $preferences = new backup_nested_element('preferences');
+
+ $preference = new backup_nested_element('preference', array('id'), array(
+ 'name', 'value'));
+
+ $roles = new backup_nested_element('roles');
+
+ $overrides = new backup_nested_element('role_overrides');
+
+ $override = new backup_nested_element('override', array('id'), array(
+ 'roleid', 'capability', 'permission', 'timemodified',
+ 'modifierid'));
+
+ $assignments = new backup_nested_element('role_assignments');
+
+ $assignment = new backup_nested_element('assignment', array('id'), array(
+ 'roleid', 'userid', 'hidden', 'timestart',
+ 'timeend', 'timemodified', 'modifierid', 'enrol',
+ 'sortorder'));
+
+ // Build the tree
+
+ $users->add_child($user);
+
+ $user->add_child($customfields);
+ $customfields->add_child($customfield);
+
+ $user->add_child($tags);
+ $tags->add_child($tag);
+
+ $user->add_child($preferences);
+ $preferences->add_child($preference);
+
+ $user->add_child($roles);
+
+ $roles->add_child($overrides);
+ $roles->add_child($assignments);
+
+ $overrides->add_child($override);
+ $assignments->add_child($assignment);
+
+ // Define sources
+
+ $user->set_source_sql('SELECT u.*, c.id AS contextid, m.wwwroot AS mnethosturl
+ FROM {user} u
+ JOIN {backup_ids_temp} bi ON bi.itemid = u.id
+ JOIN {context} c ON c.instanceid = u.id
+ LEFT JOIN {mnet_host} m ON m.id = u.mnethostid
+ WHERE bi.backupid = ?
+ AND bi.itemname = ?
+ AND c.contextlevel = ?', array(
+ $this->is_sqlparam($this->get_backupid()),
+ $this->is_sqlparam('userfinal'),
+ $this->is_sqlparam(CONTEXT_USER)));
+
+ // All the rest on information is only added if we arent
+ // in an anonymized backup
+ if (!$anonymize) {
+ $customfield->set_source_sql('SELECT f.id, f.shortname, f.datatype, d.data
+ FROM {user_info_field} f
+ JOIN {user_info_data} d ON d.fieldid = f.id
+ WHERE d.userid = ?', array(backup::VAR_PARENTID));
+
+ $customfield->set_source_alias('shortname', 'field_name');
+ $customfield->set_source_alias('datatype', 'field_type');
+ $customfield->set_source_alias('data', 'field_data');
+
+ $tag->set_source_sql('SELECT t.id, t.name, t.rawname
+ FROM {tag} t
+ JOIN {tag_instance} ti ON ti.tagid = t.id
+ WHERE ti.itemtype = ?
+ AND ti.itemid = ?', array(
+ $this->is_sqlparam('user'),
+ backup::VAR_PARENTID));
+
+ $preference->set_source_table('user_preferences', array('userid' => backup::VAR_PARENTID));
+
+ $override->set_source_table('role_capabilities', array('contextid' => '/users/user/contextid'));
+
+ // Assignments only added if specified
+ if ($roleassignments) {
+ $assignment->set_source_table('role_assignments', array('contextid' => '/users/user/contextid'));
+ }
+
+ // Define id annotations (as final)
+ $override->annotate_ids('rolefinal', 'roleid');
+ }
+
+ // Return root element (users)
+ return $users;
+ }
+}
+
+/**
+ * structure step in charge of constructing the block.xml file for one
+ * given block (intance and positions). If the block has custom DB structure
+ * that will go to a separate file (different step defined in block class)
+ */
+class backup_block_instance_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+ global $DB;
+
+ // Define each element separated
+
+ $block = new backup_nested_element('block', array('id', 'version'), array(
+ 'blockname', 'showinsubcontexts', 'pagetypepattern', 'subpagepattern',
+ 'defaultregion', 'defaultweight', 'configdata'));
+
+ $positions = new backup_nested_element('block_positions', null, array(
+ 'contextid', 'pagetype', 'subpage', 'visible',
+ 'region', 'weight'));
+
+ // Build the tree
+
+ $block->add_child($positions);
+
+ // Transform configdata information if needed (process links and friends)
+ $blockrec = $DB->get_record('block_instances', array('id' => $this->task->get_blockid()));
+ if ($attrstotransform = $this->task->get_configdata_encoded_attributes()) {
+ $configdata = (array)unserialize(base64_decode($blockrec->configdata));
+ foreach ($configdata as $attribute => $value) {
+ if (in_array($attribute, $attrstotransform)) {
+ $configdata[$attribute] = $this->contenttransformer->process($value);
+ }
+ }
+ $blockrec->configdata = base64_encode(serialize((object)$configdata));
+ }
+ // Get the version of the block
+ $blockrec->version = $DB->get_field('block', 'version', array('name' => $this->task->get_blockname()));
+
+ // Define sources
+
+ $block->set_source_array(array($blockrec));
+
+ $positions->set_source_table('block_positions', array('blockinstanceid' => backup::VAR_PARENTID));
+
+ // Return the root element (block)
+ return $block;
+ }
+}
+
+/**
+ * structure step in charge of constructing the logs.xml file for all the log records found
+ * in activity
+ */
+class backup_activity_logs_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define each element separated
+
+ $logs = new backup_nested_element('logs');
+
+ $log = new backup_nested_element('log', array('id'), array(
+ 'time', 'userid', 'ip', 'module',
+ 'action', 'url', 'info'));
+
+ // Build the tree
+
+ $logs->add_child($log);
+
+ // Define sources
+
+ $log->set_source_table('log', array('cmid' => backup::VAR_MODID));
+
+ // Annotations
+ // NOTE: We don't annotate users from logs as far as they MUST be
+ // always annotated by the activity.
+
+ // Return the root element (logs)
+
+ return $logs;
+ }
+}
+
+/**
+ * structure in charge of constructing the inforef.xml file for all the items we want
+ * to have referenced there (users, roles, files...)
+ */
+class backup_inforef_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Items we want to include in the inforef file. NOTE: Important to keep this
+ // list 100% sync with the one in next step! Until we get better place for it (backup:CONST)
+ $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
+
+ // Build the tree
+
+ $inforef = new backup_nested_element('inforef');
+
+ // For each item, conditionally, if there are already records, build element
+ foreach ($items as $itemname) {
+ if (backup_structure_dbops::annotations_exist($this->get_backupid(), $itemname)) {
+ $elementroot = new backup_nested_element($itemname . 'ref');
+ $element = new backup_nested_element($itemname, array('id'));
+ $inforef->add_child($elementroot);
+ $elementroot->add_child($element);
+ $element->set_source_sql("
+ SELECT itemid AS id
+ FROM {backup_ids_temp}
+ WHERE backupid = ?
+ AND itemname = ?",
+ array(backup::VAR_BACKUPID, $this->is_sqlparam($itemname)));
+ }
+ }
+
+ // We don't annotate anything there, but rely in the next step
+ // (move_inforef_annotations_to_final) that will change all the
+ // already saved 'inforref' entries to their 'final' annotations.
+ return $inforef;
+ }
+}
+
+/**
+ * This step will get all the annotations already processed to inforef.xml file and
+ * transform them into 'final' annotations.
+ */
+class move_inforef_annotations_to_final extends backup_execution_step {
+
+ protected function define_execution() {
+
+ // Items we want to include in the inforef file. NOTE: Important to keep this
+ // list 100% sync with the one in prev step! Until we get better place for it (backup:CONST)
+ $items = array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item');
+ foreach ($items as $itemname) {
+ // Delegate to dbops
+ backup_structure_dbops::move_annotations_to_final($this->get_backupid(), $itemname);
+ }
+ }
+}
+
+/**
+ * structure in charge of constructing the files.xml file with all the
+ * annotated (final) files along the process. At, the same time, and
+ * using one specialised nested_element, will copy them form moodle storage
+ * to backup storage
+ */
+class backup_final_files_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // Define elements
+
+ $files = new backup_nested_element('files');
+
+ $file = new file_nested_element('file', array('id'), array(
+ 'contenthash', 'contextid', 'filearea', 'itemid',
+ 'filepath', 'filename', 'userid', 'filesize',
+ 'mimetype', 'status', 'timecreated', 'timemodified',
+ 'source', 'author', 'license'));
+
+ // Build the tree
+
+ $files->add_child($file);
+
+ // Define sources
+
+ $file->set_source_sql("SELECT f.*
+ FROM {files} f
+ JOIN {backup_ids_temp} bi ON f.id = bi.itemid
+ WHERE bi.backupid = ?
+ AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
+
+ return $files;
+ }
+}
+
+/**
+ * Structure step in charge of creating the main moodle_backup.xml file
+ * where all the information related to the backup, settings, license and
+ * other information needed on restore is added*/
+class backup_main_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ global $CFG;
+
+ $info = array();
+
+ $info['name'] = $this->get_setting_value('filename');
+ $info['moodle_version'] = $CFG->version;
+ $info['moodle_release'] = $CFG->release;
+ $info['backup_version'] = $CFG->backup_version;
+ $info['backup_release'] = $CFG->backup_release;
+ $info['backup_date'] = time();
+ $info['backup_uniqueid']= $this->get_backupid();
+ $info['original_wwwroot']=$CFG->wwwroot;
+ $info['original_site_identifier'] = get_site_identifier();
+ $info['original_course_id'] = $this->get_courseid();
+
+ // Get more information from controller
+ list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($this->get_backupid());
+
+ // Define elements
+
+ $moodle_backup = new backup_nested_element('moodle_backup');
+
+ $information = new backup_nested_element('information', null, array(
+ 'name', 'moodle_version', 'moodle_release', 'backup_version',
+ 'backup_release', 'backup_date', 'original_wwwroot',
+ 'original_site_identifier', 'original_course_id'));
+
+ $details = new backup_nested_element('details');
+
+ $detail = new backup_nested_element('detail', array('backup_id'), array(
+ 'type', 'format', 'interactive', 'mode',
+ 'execution', 'executiontime'));
+
+ $contents = new backup_nested_element('contents');
+
+ $activities = new backup_nested_element('activities');
+
+ $activity = new backup_nested_element('activity', null, array(
+ 'moduleid', 'sectionid', 'modulename', 'title',
+ 'directory'));
+
+ $sections = new backup_nested_element('sections');
+
+ $section = new backup_nested_element('section', null, array(
+ 'sectionid', 'title', 'directory'));
+
+ $course = new backup_nested_element('course', null, array(
+ 'courseid', 'title', 'directory'));
+
+ $settings = new backup_nested_element('settings');
+
+ $setting = new backup_nested_element('setting', null, array(
+ 'level', 'activity', 'name', 'value'));
+
+ // Build the tree
+
+ $moodle_backup->add_child($information);
+
+ $information->add_child($details);
+ $details->add_child($detail);
+
+ $information->add_child($contents);
+ if (!empty($cinfo['activities'])) {
+ $contents->add_child($activities);
+ $activities->add_child($activity);
+ }
+ if (!empty($cinfo['sections'])) {
+ $contents->add_child($sections);
+ $sections->add_child($section);
+ }
+ if (!empty($cinfo['course'])) {
+ $contents->add_child($course);
+ }
+
+ $information->add_child($settings);
+ $settings->add_child($setting);
+
+
+ // Set the sources
+
+ $information->set_source_array(array((object)$info));
+
+ $detail->set_source_array($dinfo);
+
+ $activity->set_source_array($cinfo['activities']);
+
+ $section->set_source_array($cinfo['sections']);
+
+ $course->set_source_array($cinfo['course']);
+
+ $setting->set_source_array($sinfo);
+
+ // Prepare some information to be sent to main moodle_backup.xml file
+ return $moodle_backup;
+ }
+
+}
+
+/**
+ * This step will search for all the activity (not calculations, categories nor aggregations) grade items
+ * and put them to the backup_ids tables, to be used later as base to backup them
+ */
+class backup_activity_grade_items_to_ids extends backup_execution_step {
+
+ protected function define_execution() {
+
+ // Fetch all activity grade items
+ if ($items = grade_item::fetch_all(array(
+ 'itemtype' => 'mod', 'itemmodule' => $this->task->get_modulename(),
+ 'iteminstance' => $this->task->get_activityid(), 'courseid' => $this->task->get_courseid()))) {
+ // Annotate them in backup_ids
+ foreach ($items as $item) {
+ backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'grade_item', $item->id);
+ }
+ }
+ }
+}
+
+/**
+ * This step will annotate all the groups belonging to already annotated groupings
+ */
+class backup_annotate_groups_from_groupings extends backup_execution_step {
+
+ protected function define_execution() {
+ global $DB;
+
+ // Fetch all the annotated groupings
+ if ($groupings = $DB->get_records('backup_ids_temp', array(
+ 'backupid' => $this->get_backupid(), 'itemname' => 'grouping'))) {
+ foreach ($groupings as $grouping) {
+ if ($groups = $DB->get_records('groupings_groups', array(
+ 'groupingid' => $grouping->itemid))) {
+ foreach ($groups as $group) {
+ backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'group', $group->groupid);
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * This step will annotate all the scales belonging to already annotated outcomes
+ */
+class backup_annotate_scales_from_outcomes extends backup_execution_step {
+
+ protected function define_execution() {
+ global $DB;
+
+ // Fetch all the annotated outcomes
+ if ($outcomes = $DB->get_records('backup_ids_temp', array(
+ 'backupid' => $this->get_backupid(), 'itemname' => 'outcome'))) {
+ foreach ($outcomes as $outcome) {
+ if ($scale = $DB->get_record('grade_outcomes', array(
+ 'id' => $outcome->itemid))) {
+ // Annotate as scalefinal because it's > 0
+ backup_structure_dbops::insert_backup_ids_record($this->get_backupid(), 'scalefinal', $scale->scaleid);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * This step will generate all the user annotations for the already
+ * annottated (final) users. Need to do this here because each user
+ * has its own context and structure tasks only are able to handle
+ * one context. Also, this step will guarantee that every user has
+ * its context created (req for other steps)
+ */
+class backup_annotate_all_user_files extends backup_execution_step {
+
+ protected function define_execution() {
+ global $DB;
+
+ // List of fileareas we are going to annotate
+ // TODO: Change "user_image" file area to the one finally used for user images
+ $fileareas = array(
+ 'user_private', 'user_profile', 'user_image');
+
+ // Fetch all annotated (final) users
+ $rs = $DB->get_recordset('backup_ids_temp', array(
+ 'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
+ foreach ($rs as $record) {
+ $userid = $record->itemid;
+ $userctxid = get_context_instance(CONTEXT_USER, $userid)->id;
+ // Proceed with every user filearea
+ foreach ($fileareas as $filearea) {
+ // We don't need to specify itemid ($userid - 4th param) as far as by
+ // context we can get all the associated files. See MDL-22092
+ backup_structure_dbops::annotate_files($this->get_backupid(), $userctxid, $filearea, null);
+ }
+ }
+ $rs->close();
+ }
+}
+
+/**
+ * structure step in charge of constructing the grades.xml file for all the grade items
+ * and letters related to one activity
+ */
+class backup_activity_grades_structure_step extends backup_structure_step {
+
+ protected function define_structure() {
+
+ // To know if we are including userinfo
+ $userinfo = $this->get_setting_value('userinfo');
+
+ // Define each element separated
+
+ $book = new backup_nested_element('activity_gradebook');
+
+ $items = new backup_nested_element('grade_items');
+
+ $item = new backup_nested_element('grade_item', array('id'), array(
+ 'categoryid', 'itemname', 'itemtype', 'itemmodule',
+ 'iteminstance', 'itemnumber', 'iteminfo', 'idnumber',
+ 'calculation', 'gradetype', 'grademax', 'grademin',
+ 'scaleid', 'outcomeid', 'gradepass', 'multfactor',
+ 'plusfactor', 'aggregationcoef', 'sortorder', 'display',
+ 'decimals', 'hidden', 'locked', 'locktime',
+ 'needsupdate', 'timecreated', 'timemodified'));
+
+ $grades = new backup_nested_element('grade_grades');
+
+ $grade = new backup_nested_element('grade_grade', array('id'), array(
+ 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin',
+ 'rawscaleid', 'usermodified', 'finalgrade', 'hidden',
+ 'locked', 'locktime', 'exported', 'overridden',
+ 'excluded', 'feedback', 'feedbackformat', 'information',
+ 'informationformat', 'timecreated', 'timemodified'));
+
+ $letters = new backup_nested_element('grade_letters');
+
+ $letter = new backup_nested_element('grade_letter', 'id', array(
+ 'lowerboundary', 'letter'));
+
+ // Build the tree
+
+ $book->add_child($items);
+ $items->add_child($item);
+
+ $item->add_child($grades);
+ $grades->add_child($grade);
+
+ $book->add_child($letters);
+ $letters->add_child($letter);
+
+ // Define sources
+
+ $item->set_source_sql("
+ SELECT gi.*
+ FROM {grade_items} gi
+ JOIN {backup_ids_temp} bi ON gi.id = bi.itemid
+ WHERE bi.backupid = ?
+ AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
+
+ // This only happens if we are including user info
+ if ($userinfo) {
+ $grade->set_source_table('grade_grades', array('itemid' => backup::VAR_PARENTID));
+ }
+
+ $letter->set_source_table('grade_letters', array('contextid' => backup::VAR_CONTEXTID));
+
+ // Annotations
+
+ $item->annotate_ids('scalefinal', 'scaleid'); // Straight as scalefinal because it's > 0
+ $item->annotate_ids('outcome', 'outcomeid');
+
+ $grade->annotate_ids('user', 'userid');
+ $grade->annotate_ids('user', 'usermodified');
+
+ // Return the root element (book)
+
+ return $book;
+ }
+}
--- /dev/null
+<?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 @xml_contenttrasnformed logic to be applied in moodle2 backups
+ *
+ * TODO: Finish phpdocs
+ */
+class backup_xml_transformer extends xml_contenttransformer {
+
+ private $absolute_links_encoders; // array of static methods to be called in order to
+ // perform the encoding of absolute links to all the
+ // contents sent to xml
+ private $courseid; // courseid this content belongs to
+ private $unicoderegexp; // to know if the site supports unicode regexp
+
+ public function __construct($courseid) {
+ $this->absolute_links_encoders = array();
+ $this->courseid = $courseid;
+ // Check if we support unicode modifiers in regular expressions
+ $this->unicoderegexp = @preg_match('/\pL/u', 'a'); // This will fail silenty, returning false,
+ // if regexp libraries don't support unicode
+ // Register all the available content link encoders
+ $this->absolute_links_encoders = $this->register_link_encoders();
+ }
+
+ public function process($content) {
+
+ if (is_null($content)) { // Some cases we know we can skip complete processing
+ return '$@NULL@$';
+ } else if ($content === '') {
+ return '';
+ } else if (is_numeric($content)) {
+ return $content;
+ } else if (strlen($content) < 36) { // Impossible to have one link in 36cc
+ return $content; // (http://10.0.0.1/mod/url/view.php?id=)
+ }
+
+ $content = $this->process_filephp_links($content); // Replace all calls to file.php by $@FILEPHP@$ in a normalised way
+ $content = $this->encode_absolute_links($content); // Pass the content against all the found encoders
+
+ return $content;
+ }
+
+ private function process_filephp_links($content) {
+ global $CFG;
+
+ //First, we check for every call to file.php inside the course
+ $search = array($CFG->wwwroot.'/file.php/' . $this->courseid,
+ $CFG->wwwroot.'/file.php?file=/' . $this->courseid,
+ $CFG->wwwroot.'/file.php?file=%2f' . $this->courseid,
+ $CFG->wwwroot.'/file.php?file=%2F' . $this->courseid);
+ $replace = array('$@FILEPHP@$', '$@FILEPHP@$', '$@FILEPHP@$', '$@FILEPHP@$');
+ $content = str_replace($search, $replace, $content);
+
+ // Now we look for any '$@FILEPHP@$' URLs, replacing:
+ // - slashes and %2F by $@SLASH@$
+ // - &forcedownload=1 &forcedownload=1 and ?forcedownload=1 by $@FORCEDOWNLOAD@$
+ // This way, backup contents will be neutral and independent of slasharguments configuration. MDL-18799
+ // Based in $this->unicoderegexp, decide the regular expression to use
+ if ($this->unicoderegexp) { //We can use unicode modifiers
+ $search = '/(\$@FILEPHP@\$)((?:(?:\/|%2f|%2F))(?:(?:\([-;:@#&=\pL0-9\$~_.+!*\',]*?\))|[-;:@#&=\pL0-9\$~_.+!*\',]|%[a-fA-F0-9]{2}|\/)*)?(\?(?:(?:(?:\([-;:@#&=\pL0-9\$~_.+!*\',]*?\))|[-;:@#&=?\pL0-9\$~_.+!*\',]|%[a-fA-F0-9]{2}|\/)*))?(?<![,.;])/u';
+ } else { //We cannot ue unicode modifiers
+ $search = '/(\$@FILEPHP@\$)((?:(?:\/|%2f|%2F))(?:(?:\([-;:@#&=a-zA-Z0-9\$~_.+!*\',]*?\))|[-;:@#&=a-zA-Z0-9\$~_.+!*\',]|%[a-fA-F0-9]{2}|\/)*)?(\?(?:(?:(?:\([-;:@#&=a-zA-Z0-9\$~_.+!*\',]*?\))|[-;:@#&=?a-zA-Z0-9\$~_.+!*\',]|%[a-fA-F0-9]{2}|\/)*))?(?<![,.;])/';
+ }
+ $content = preg_replace_callback($search, array('backup_xml_transformer', 'process_filephp_uses'), $content);
+
+ return $content;
+ }
+
+ private function encode_absolute_links($content) {
+ foreach ($this->absolute_links_encoders as $classname => $methodname) {
+ $content = call_user_func(array($classname, $methodname), $content);
+ }
+ return $content;
+ }
+
+ static private function process_filephp_uses($matches) {
+
+ // Replace slashes (plain and encoded) and forcedownload=1 parameter
+ $search = array('/', '%2f', '%2F', '?forcedownload=1', '&forcedownload=1', '&forcedownload=1');
+ $replace = array('$@SLASH@$', '$@SLASH@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$', '$@FORCEDOWNLOAD@$', '$@FORCEDOWNLOAD@$');
+
+ $result = $matches[1] . (isset($matches[2]) ? str_replace($search, $replace, $matches[2]) : '') . (isset($matches[3]) ? str_replace($search, $replace, $matches[3]) : '');
+
+ return $result;
+ }
+
+ private function register_link_encoders() {
+ $encoders = array();
+
+ // First of all, add the module ones. Each module supporting moodle2 backups MUST have it
+ $mods = get_plugin_list('mod');
+ foreach ($mods as $mod => $moddir) {
+ if (plugin_supports('mod', $mod, FEATURE_BACKUP_MOODLE2)) {
+ $encoders['backup_' . $mod . '_activity_task'] = 'encode_content_links';
+ }
+ }
+
+ // Add the block encodes
+ $blocks = get_plugin_list('block');
+ foreach ($blocks as $block => $blockdir) {
+ if (class_exists('backup_' . $block . '_block_task')) {
+ $encoders['backup_' . $block . '_block_task'] = 'encode_content_links';
+ }
+ }
+
+ // Add the course format encodes
+ // TODO: Same than blocks, need to know how courseformats are going to handle backup
+ // (1.9 was based in backuplib function, see code)
+
+ // Add local encodes
+ // TODO: Any interest? 1.9 never had that.
+
+ return $encoders;
+ }
+}