first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,364 @@
<?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/>.
/**
* Defines backup_activity_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Provides all the settings and steps to perform one complete backup of the activity
*
* Activities are supposed to provide the subclass of this class in their file
* mod/MODULENAME/backup/moodle2/backup_MODULENAME_activity_task.class.php
* The expected name of the subclass is backup_MODULENAME_activity_task
*/
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
*
* @param string $name the task identifier
* @param int $moduleid course module id (id in course_modules table)
* @param backup_plan|null $plan the backup plan instance this task is part of
*/
public function __construct($name, $moduleid, $plan = null) {
// Check moduleid exists
if (!$coursemodule = get_coursemodule_from_id(false, $moduleid)) {
throw new 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 new 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 = context_module::instance($this->moduleid)->id;
parent::__construct($name, $plan);
}
/**
* @return int the course module id (id in the course_modules table)
*/
public function get_moduleid() {
return $this->moduleid;
}
/**
* @return int the course section id (id in the course_sections table)
*/
public function get_sectionid() {
return $this->sectionid;
}
/**
* @return string the name of the module, eg 'workshop' (from the modules table)
*/
public function get_modulename() {
return $this->modulename;
}
/**
* @return int the id of the activity instance (id in the activity's instances table)
*/
public function get_activityid() {
return $this->activityid;
}
/**
* @return int the id of the associated CONTEXT_MODULE instance
*/
public function get_contextid() {
return $this->contextid;
}
/**
* @return string full path to the directory where this task writes its 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() {
// If we have decided not to backup activities, prevent anything to be built
if (!$this->get_setting_value('activities')) {
$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_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, containing 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 if groups are to be backed up.
if ($this->get_setting_value('groups')) {
$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')) {
// Legacy logs.
$this->add_step(new backup_activity_logs_structure_step('activity_logs', 'logs.xml'));
// New log stores.
$this->add_step(new backup_activity_logstores_structure_step('activity_logstores', 'logstores.xml'));
}
// Generate the calendar events file (conditionally)
if ($this->get_setting_value('calendarevents')) {
$this->add_step(new backup_calendarevents_structure_step('activity_calendar', 'calendar.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'));
// Generate the grading file (conditionally)
$this->add_step(new backup_activity_grading_structure_step('activity_grading', 'grading.xml'));
// Generate the grade history file. The setting 'grade_histories' is handled in the step.
$this->add_step(new backup_activity_grade_history_structure_step('activity_grade_history', 'grade_history.xml'));
// Generate the competency file.
$this->add_step(new backup_activity_competencies_structure_step('activity_competencies', 'competencies.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'));
// Generate the xAPI state file (conditionally).
if ($this->get_setting_value('xapistate')) {
$this->add_step(new backup_xapistate_structure_step('activity_xapistate', 'xapistate.xml'));
}
// 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);
$this->plan->set_excluding_activities();
} else { // Setting tells us it's ok to execute
parent::execute();
}
}
/**
* Tries to look for the instance specific setting value, task specific setting value or the
* common plan setting value - in that order
*
* @param string $name the name of the setting
* @return mixed|null the value of the setting or null if not found
*/
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
/**
* Defines the common setting that any backup activity will have
*/
protected function define_settings() {
global $CFG;
require_once($CFG->libdir.'/questionlib.php');
// 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)
// Dependent of:
// - activities root setting
// - section_included setting (if exists)
$settingname = $settingprefix . 'included';
$activity_included = new backup_activity_generic_setting($settingname, base_setting::IS_BOOLEAN, true);
$activity_included->get_ui()->set_icon(new image_icon('monologo', get_string('pluginname', $this->modulename),
$this->modulename, array('class' => 'iconlarge icon-post ml-1')));
$this->add_setting($activity_included);
// Look for "activities" root setting
$activities = $this->plan->get_setting('activities');
$activities->add_dependency($activity_included);
if (question_module_uses_questions($this->modulename)) {
$questionbank = $this->plan->get_setting('questionbank');
$questionbank->add_dependency($activity_included);
}
// Look for "section_included" section setting (if exists)
$settingname = 'section_' . $this->sectionid . '_included';
if ($this->plan->setting_exists($settingname)) {
$section_included = $this->plan->get_setting($settingname);
$section_included->add_dependency($activity_included);
}
// Define activity_userinfo. Dependent of:
// - users root setting
// - section_userinfo setting (if exists)
// - activity_included setting
$settingname = $settingprefix . 'userinfo';
$activity_userinfo = new backup_activity_userinfo_setting($settingname, base_setting::IS_BOOLEAN, true);
//$activity_userinfo->get_ui()->set_label(get_string('includeuserinfo','backup'));
$activity_userinfo->get_ui()->set_label('-');
$this->add_setting($activity_userinfo);
// Look for "users" root setting
$users = $this->plan->get_setting('users');
$users->add_dependency($activity_userinfo);
// Look for "section_userinfo" section setting (if exists)
$settingname = 'section_' . $this->sectionid . '_userinfo';
if ($this->plan->setting_exists($settingname)) {
$section_userinfo = $this->plan->get_setting($settingname);
$section_userinfo->add_dependency($activity_userinfo);
}
// Look for "activity_included" setting
$activity_included->add_dependency($activity_userinfo);
// End of common activity settings, let's add the particular ones
$this->define_my_settings();
}
/**
* Defines activity specific settings to be added to the common ones
*
* This method is called from {@link self::define_settings()}. The activity module
* author may use it to define additional settings that influence the execution of
* the backup.
*
* Most activities just leave the method empty.
*
* @see self::define_settings() for the example how to define own settings
*/
abstract protected function define_my_settings();
/**
* Defines activity specific steps for this task
*
* This method is called from {@link self::build()}. Activities are supposed
* to call {self::add_step()} in it to include their specific steps in the
* backup plan.
*/
abstract protected function define_my_steps();
/**
* Encodes URLs to the activity instance's scripts into a site-independent form
*
* The current instance of the activity may be referenced from other places in
* the course by URLs like http://my.moodle.site/mod/workshop/view.php?id=42
* Obvisouly, such URLs are not valid any more once the course is restored elsewhere.
* For this reason the backup file does not store the original URLs but encodes them
* into a transportable form. During the restore, the reverse process is applied and
* the encoded URLs are replaced with the new ones valid for the target site.
*
* Every plugin must override this method in its subclass.
*
* @see backup_xml_transformer class that actually runs the transformation
* @param string $content some HTML text that eventually contains URLs to the activity instance scripts
* @return string the content with the URLs encoded
*/
public static function encode_content_links($content) {
throw new coding_exception('encode_content_links() method needs to be overridden in each subclass of backup_activity_task');
}
}
+219
View File
@@ -0,0 +1,219 @@
<?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 core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* 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 new backup_task_exception('block_task_block_instance_not_found', $blockid);
}
$this->blockid = $blockid;
$this->blockname = clean_param($block->blockname, PARAM_PLUGIN);
$this->contextid = context_block::instance($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 = context_module::instance($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;
}
// If "child" of activity task and it has been excluded, nothing to do
if (!empty($this->moduleid)) {
$includedsetting = $this->modulename . '_' . $this->moduleid . '_included';
if (!$this->get_setting_value($includedsetting)) {
$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() in 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 fileareas that each block controls
*/
abstract public function get_fileareas();
/**
* 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
*/
public static function encode_content_links($content) {
throw new coding_exception('encode_content_links() method needs to be overridden in each subclass of backup_block_task');
}
}
+219
View File
@@ -0,0 +1,219 @@
<?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/>.
/**
* Defines backup_course_task
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @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 = context_course::instance($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, tags and module restrictions
$this->add_step(new backup_course_structure_step('course_info', 'course.xml'));
// Generate the enrolment file (conditionally, prevent it in any IMPORT/HUB operation)
if ($this->plan->get_mode() != backup::MODE_IMPORT && $this->plan->get_mode() != backup::MODE_HUB) {
$this->add_step(new backup_enrolments_structure_step('course_enrolments', 'enrolments.xml'));
}
// Annotate enrolment custom fields.
$this->add_step(new backup_enrolments_execution_step('annotate_enrol_custom_fields'));
// Annotate all the groups and groupings belonging to the course. This can be optional.
if ($this->get_setting_value('groups')) {
$this->add_step(new backup_annotate_course_groups_and_groupings('annotate_course_groups'));
}
// Annotate the groups used in already annotated groupings (note this may be
// unnecessary now that we are annotating all the course groups and groupings in the
// step above). This is here to support course->defaultgroupingid.
// This may not be required to annotate if groups are not being backed up.
if ($this->get_setting_value('groups')) {
$this->add_step(new backup_annotate_groups_from_groupings('annotate_groups_from_groupings'));
}
// Annotate the question_categories belonging to the course context (conditionally).
if ($this->get_setting_value('questionbank')) {
$this->add_step(new backup_calculate_question_categories('course_question_categories'));
}
// 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 calender events file (conditionally)
if ($this->get_setting_value('calendarevents')) {
$this->add_step(new backup_calendarevents_structure_step('course_calendar', 'calendar.xml'));
}
// Generate the logs file (conditionally)
if ($this->get_setting_value('logs')) {
// Legacy logs.
$this->add_step(new backup_course_logs_structure_step('course_logs', 'logs.xml'));
// New log stores.
$this->add_step(new backup_course_logstores_structure_step('course_logstores', 'logstores.xml'));
// Last access to course logs.
$this->add_step(new backup_course_loglastaccess_structure_step('course_loglastaccess', 'loglastaccess.xml'));
}
// Generate the course competencies.
$this->add_step(new backup_course_competencies_structure_step('course_competencies', 'competencies.xml'));
// Annotate activity completion defaults.
$this->add_step(new backup_completion_defaults_structure_step('course_completion_defaults', 'completiondefaults.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'));
// Generate the content bank file (conditionally).
if ($this->get_setting_value('contentbankcontent')) {
$this->add_step(new backup_contentbankcontent_structure_step('course_contentbank', 'contentbank.xml'));
}
// At the end, mark it as built
$this->built = true;
}
/**
* Code the transformations to perform in the course in
* order to get transportable (encoded) links
* @param string $content content in which to encode links.
* @return string content with links encoded.
*/
public static function encode_content_links($content) {
// Link to the course main page (it also covers "&topic=xx" and "&week=xx"
// because they don't become transformed (section number) in backup/restore.
$content = self::encode_links_helper($content, 'COURSEVIEWBYID', '/course/view.php?id=');
// A few other key course links.
$content = self::encode_links_helper($content, 'GRADEINDEXBYID', '/grade/index.php?id=');
$content = self::encode_links_helper($content, 'GRADEREPORTINDEXBYID', '/grade/report/index.php?id=');
$content = self::encode_links_helper($content, 'BADGESVIEWBYID', '/badges/view.php?type=2&id=');
$content = self::encode_links_helper($content, 'USERINDEXVIEWBYID', '/user/index.php?id=');
$content = self::encode_links_helper($content, 'PLUGINFILEBYCONTEXT', '/pluginfile.php/');
$content = self::encode_links_helper($content, 'PLUGINFILEBYCONTEXTURLENCODED', '/pluginfile.php/', true);
return $content;
}
/**
* Helper method, used by encode_content_links.
* @param string $content content in which to encode links.
* @param string $name the name of this type of encoded link.
* @param string $path the path that identifies this type of link, up
* to the ?paramname= bit.
* @param bool $urlencoded whether to use urlencode() before replacing the path.
* @return string content with one type of link encoded.
*/
private static function encode_links_helper(string $content, string $name, string $path, bool $urlencoded = false) {
global $CFG;
// We want to convert both http and https links.
$root = $CFG->wwwroot;
$httpsroot = str_replace('http://', 'https://', $root);
$httproot = str_replace('https://', 'http://', $root);
$httpsbase = $httpsroot . $path;
$httpbase = $httproot . $path;
if ($urlencoded) {
$httpsbase = urlencode($httpsbase);
$httpbase = urlencode($httpbase);
}
$httpsbase = preg_quote($httpsbase, '/');
$httpbase = preg_quote($httpbase, '/');
$return = preg_replace('/(' . $httpsbase . ')([0-9]+)/', '$@' . $name . '*$2@$', $content);
$return = preg_replace('/(' . $httpbase . ')([0-9]+)/', '$@' . $name . '*$2@$', $return);
return $return;
}
// 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)
}
}
@@ -0,0 +1,35 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/**
* Base class for course report backup plugins.
*
* NOTE: When you back up a course, it potentially may run backup for all
* course reports. In order to control whether a particular report gets
* backed up, a course report should make use of the second and third
* parameters in get_plugin_element().
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 onwards The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class backup_coursereport_plugin extends backup_plugin {
// Use default parent behaviour
}
+290
View File
@@ -0,0 +1,290 @@
<?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/>.
/**
* Defines various element classes used in specific areas
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* 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 {@link backup_final_element} that provides base64 encoding.
*
* This final element transparently encodes with base64_encode() contents that
* normally are not safe for being stored in utf-8 xml files (binaries, serialized
* data...).
*/
class base64_encode_final_element extends backup_final_element {
/**
* Set the value for the final element, encoding it as utf-8/xml safe base64.
*
* @param string $value Original value coming from backup step source, usually db.
*/
public function set_value($value) {
// Avoid null being passed to base64_encode.
$value = $value ?? '';
parent::set_value(base64_encode($value));
}
}
/**
* Implementation of {@link backup_final_element} that provides symmetric-key AES-256 encryption of contents.
*
* This final element transparently encrypts, for secure storage and transport, any content
* that shouldn't be shown normally in plain text. Usually, passwords or keys that cannot use
* hashing algorithms, although potentially can encrypt any content. All information is encoded
* using base64.
*
* Features:
* - requires openssl extension to work. Without it contents are completely omitted.
* - automatically creates an appropriate default key for the site and stores it into backup_encryptkey config (bas64 encoded).
* - uses a different appropriate init vector for every operation, which is transmited with the encrypted contents.
* - all generated data is base64 encoded for safe transmission.
* - automatically adds "encrypted" attribute for easier detection.
* - implements HMAC for providing integrity.
*
* @copyright 2017 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class encrypted_final_element extends backup_final_element {
/** @var string cypher appropiate raw key for backups in the site. Defaults to backup_encryptkey config. */
protected $key = null;
/**
* Constructor - instantiates a encrypted_final_element, specifying its basic info.
*
* Overridden to automatically add the 'encrypted' attribute if missing.
*
* @param string $name name of the element
* @param array $attributes attributes this element will handle (optional, defaults to null)
*/
public function __construct($name, $attributes = null) {
parent::__construct($name, $attributes);
if (! $this->get_attribute('encrypted')) {
$this->add_attributes('encrypted');
}
}
/**
* Set the encryption key manually, overriding default backup_encryptkey config.
*
* @param string $key key to be used for encrypting. Required to be 256-bit key.
* Use a safe generation technique. See self::generate_encryption_random_key() below.
*/
protected function set_key($key) {
$bytes = strlen($key); // Get key length in bytes.
// Only accept keys with the expected (backup::CIPHERKEYLEN) key length. There are a number of hashing,
// random generators to achieve this esasily, like the one shown below to create the default
// site encryption key and ivs.
if ($bytes !== backup::CIPHERKEYLEN) {
$info = (object)array('expected' => backup::CIPHERKEYLEN, 'found' => $bytes);
throw new base_element_struct_exception('encrypted_final_element incorrect key length', $info);
}
// Everything went ok, store the key.
$this->key = $key;
}
/**
* Set the value of the field.
*
* This method sets the value of the element, encrypted using the specified key for it,
* defaulting to (and generating) backup_encryptkey config. HMAC is used for integrity.
*
* @param string $value plain-text content the will be stored encrypted and encoded.
*/
public function set_value($value) {
// No openssl available, skip this field completely.
if (!function_exists('openssl_encrypt')) {
return;
}
// No hmac available, skip this field completely.
if (!function_exists('hash_hmac')) {
return;
}
// Cypher not available, skip this field completely.
if (!in_array(backup::CIPHER, openssl_get_cipher_methods())) {
return;
}
// Ensure we have a good key, manual or default.
if (empty($this->key)) {
// The key has not been set manually, look for it at config (base64 encoded there).
$enckey = get_config('backup', 'backup_encryptkey');
if ($enckey === false) {
// Has not been set, calculate and save an appropiate random key automatically.
$enckey = base64_encode(self::generate_encryption_random_key(backup::CIPHERKEYLEN));
set_config('backup_encryptkey', $enckey, 'backup');
}
$this->set_key(base64_decode($enckey));
}
// Now we need an iv for this operation.
$iv = self::generate_encryption_random_key(openssl_cipher_iv_length(backup::CIPHER));
// Everything is ready, let's encrypt and prepend the 1-shot iv.
$value = $iv . openssl_encrypt($value ?? '', backup::CIPHER, $this->key, OPENSSL_RAW_DATA, $iv);
// Calculate the hmac of the value (iv + encrypted) and prepend it.
$hmac = hash_hmac('sha256', $value, $this->key, true);
$value = $hmac . $value;
// Ready, set the encoded value.
parent::set_value(base64_encode($value));
// Finally, if the field has an "encrypted" attribute, set it to true.
if ($att = $this->get_attribute('encrypted')) {
$att->set_value('true');
}
}
/**
* Generate an appropiate random key to be used for encrypting backup information.
*
* Normally used as site default encryption key (backup_encryptkey config) and also
* for calculating the init vectors.
*
* Note that until PHP 5.6.12 openssl_random_pseudo_bytes() did NOT
* use a "cryptographically strong algorithm" {@link https://bugs.php.net/bug.php?id=70014}
* But it's beyond my crypto-knowledge when it's worth finding a *real* better alternative.
*
* @param int $bytes Number of bytes to determine the key length expected.
*/
protected static function generate_encryption_random_key($bytes) {
return openssl_random_pseudo_bytes($bytes);
}
}
/**
* 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
* physical 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);
}
return parent::process($processor);
}
public function fill_values($values) {
// Fill values
parent::fill_values($values);
// Do our own tasks (copy file from moodle to backup)
try {
backup_file_manager::copy_file_moodle2backup($this->backupid, $values);
} catch (file_exception $e) {
$this->add_result(array('missing_files_in_pool' => true));
// Build helpful log message with all information necessary to identify
// file location.
$context = context::instance_by_id($values->contextid, IGNORE_MISSING);
$contextname = '';
if ($context) {
$contextname = ' \'' . $context->get_context_name() . '\'';
}
$message = 'Missing file in pool: ' . $values->filepath . $values->filename .
' (context ' . $values->contextid . $contextname . ', component ' .
$values->component . ', filearea ' . $values->filearea . ', itemid ' .
$values->itemid . ') [' . $e->debuginfo . ']';
$this->add_log($message, backup::LOG_WARNING);
}
}
}
/**
* Implementation of backup_optigroup_element to be used by plugins stuff.
* Split just for better separation and future specialisation
*/
class backup_plugin_element extends backup_optigroup_element { }
/**
* Implementation of backup_optigroup_element to be used by subplugins stuff.
* Split just for better separation and future specialisation
*/
class backup_subplugin_element extends backup_optigroup_element { }
@@ -0,0 +1,58 @@
<?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/>.
/**
* Defines backup_default_block_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* 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_fileareas() {
return array();
}
public function get_configdata_encoded_attributes() {
return array();
}
public static function encode_content_links($content) {
return $content;
}
}
@@ -0,0 +1,39 @@
<?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/>.
/**
* Defines backup_enrol_plugin class.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2014 University of Wisconsin
* @author Matt petro
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Base class for enrol backup plugins.
*
* @package core_backup
* @copyright 2014 University of Wisconsin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class backup_enrol_plugin extends backup_plugin {
// Use default parent behaviour.
}
+176
View File
@@ -0,0 +1,176 @@
<?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/>.
/**
* Defines backup_final_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* 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() {
global $CFG;
// Set the backup::VAR_CONTEXTID setting to course context as far as next steps require that
$coursectxid = context_course::instance($this->get_courseid())->id;
$this->add_setting(new backup_activity_generic_setting(backup::VAR_CONTEXTID, base_setting::IS_INTEGER, $coursectxid));
// Set the backup::VAR_COURSEID setting to course, we'll need that in some steps
$courseid = $this->get_courseid();
$this->add_setting(new backup_activity_generic_setting(backup::VAR_COURSEID, base_setting::IS_INTEGER, $courseid));
// 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'));
// Generate the questions file with the final annotated question_categories
$this->add_step(new backup_questions_structure_step('questions', 'questions.xml'));
// Annotate all the question files for the already annotated question
// categories (this is performed here and not in the structure step because
// it involves multiple contexts and as far as we are always backup-ing
// complete question banks we don't need to restrict at all and can be
// done in a single pass
$this->add_step(new backup_annotate_all_question_files('question_files'));
// Annotate all the user files (conditionally) (profile and icon files)
// 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') || !empty($this->get_kept_roles())) && !$this->get_setting_value('anonymize')) {
$this->add_step(new backup_annotate_all_user_files('user_files'));
}
// Generate the users file (conditionally) with the final annotated users
// including custom profile fields, preferences, tags, role assignments and
// overrides
if ($this->get_setting_value('users') || !empty($this->get_kept_roles())) {
$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 gradebook file with categories and course grade items. Do it conditionally, using
// execute_condition() so only will be excuted if ALL module grade_items in course have been exported
$this->add_step(new backup_gradebook_structure_step('course_gradebook','gradebook.xml'));
// Generate the grade history file, conditionally.
$this->add_step(new backup_grade_history_structure_step('course_grade_history','grade_history.xml'));
// Generate the course completion
$this->add_step(new backup_course_completion_structure_step('course_completion', 'completion.xml'));
// Conditionally generate the badges file.
if ($this->get_setting_value('badges')) {
$this->add_step(new backup_badges_structure_step('course_badges', 'badges.xml'));
}
// Generate the scales file with all the (final) annotated scales
$this->add_step(new backup_final_scales_structure_step('scaleslist', 'scales.xml'));
// Generate the outcomes file with all the (final) 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'));
require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php');
// Look for converter steps only in type course and mode general backup operations.
$conversion = false;
if ($this->plan->get_type() == backup::TYPE_1COURSE and $this->plan->get_mode() == backup::MODE_GENERAL) {
$converters = convert_helper::available_converters(false);
foreach ($converters as $value) {
if ($this->get_setting_value($value)) {
// Zip class.
$zip_contents = "{$value}_zip_contents";
$store_backup_file = "{$value}_store_backup_file";
$convert = "{$value}_backup_convert";
$this->add_step(new $convert("package_convert_{$value}"));
$this->add_step(new $zip_contents("zip_contents_{$value}"));
$this->add_step(new $store_backup_file("save_backupfile_{$value}"));
if (!$conversion) {
$conversion = true;
}
}
}
}
// On backup::MODE_IMPORT, we don't have to zip nor store the the file, skip these steps
if (($this->plan->get_mode() != backup::MODE_IMPORT) && !$conversion) {
// Generate the zip file (mbz extension)
$this->add_step(new backup_zip_contents('zip_contents'));
// Copy the generated zip (.mbz) file to final destination
$this->add_step(new backup_store_backup_file('save_backupfile'));
}
// Clean the temp dir (conditionally) and drop temp tables
$cleanstep = new drop_and_clean_temp_stuff('drop_and_clean_temp_stuff');
// Decide about to delete the temp dir (based on backup::MODE_IMPORT)
$cleanstep->skip_cleaning_temp_dir($this->plan->get_mode() == backup::MODE_IMPORT);
$this->add_step($cleanstep);
$this->built = true;
}
public function get_weight() {
// The final task takes ages, so give it 20 times the weight of a normal task.
return 20;
}
// 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)
}
}
@@ -0,0 +1,55 @@
<?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/>.
/**
* Defines backup_format_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* 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);
}
}
@@ -0,0 +1,38 @@
<?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/>.
/**
* Contains class backup_gradingform_plugin responsible for advanced grading form plugin backup
*
* @package core_backup
* @subpackage moodle2
* @copyright 2011 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Base class for backup all advanced grading form plugins
*
* As an example of implementation see {@link backup_gradingform_rubric_plugin}
*
* @copyright 2011 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @category backup
*/
abstract class backup_gradingform_plugin extends backup_plugin {
}
@@ -0,0 +1,34 @@
<?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/>.
/**
* Defines backup_local_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class extending standard backup_plugin in order to implement some
* helper methods related with the local plugins
*/
abstract class backup_local_plugin extends backup_plugin {}
@@ -0,0 +1,49 @@
<?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/>.
/**
* Defines backup_plagiarism_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class extending standard backup_plugin in order to implement some
* helper methods related with the plagiarism plugins (plagiarism plugin)
*
* TODO: Finish phpdocs
*/
abstract class backup_plagiarism_plugin extends backup_plugin {
public function define_plugin_structure($connectionpoint) {
global $CFG;
require_once($CFG->libdir . '/plagiarismlib.php');
//check if enabled at site level and plugin is enabled.
$enabledplugins = plagiarism_load_available_plugins();
if (!array_key_exists($this->pluginname, $enabledplugins)) {
return;
}
parent::define_plugin_structure($connectionpoint);
}
}
@@ -0,0 +1,198 @@
<?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/>.
/**
* Defines backup_plan_builder class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
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_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qbank_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_extrafields_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_gradingform_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_local_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_report_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_enrol_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');
require_once($CFG->dirroot . '/backup/moodle2/backup_custom_fields.php');
// Load all the activity tasks for moodle2 format
$mods = core_component::get_plugin_list('mod');
foreach ($mods as $mod => $moddir) {
$taskpath = $moddir . '/backup/moodle2/backup_' . $mod . '_activity_task.class.php';
if (plugin_supports('mod', $mod, FEATURE_BACKUP_MOODLE2)) {
if (file_exists($taskpath)) {
require_once($taskpath);
}
}
}
// Load all the block tasks for moodle2 format
$blocks = core_component::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
*/
public static 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 outputting
// 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
*/
public static function supported_backup_types() {
return array(backup::TYPE_1COURSE, backup::TYPE_1SECTION, backup::TYPE_1ACTIVITY);
}
// Protected API starts here
/**
* Build one 1-activity backup
*/
protected static function build_activity_plan($controller, $id) {
$plan = $controller->get_plan();
// Add the activity task, responsible for outputting
// all the module related information
try {
$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) {
try {
$plan->add_task(backup_factory::get_backup_block_task($controller->get_format(), $blockid, $id));
} catch (backup_task_exception $e) {
$a = stdClass();
$a->mid = $id;
$a->bid = $blockid;
$controller->log(get_string('error_block_for_module_not_found', 'backup', $a), backup::LOG_WARNING);
}
}
} catch (backup_task_exception $e) {
$controller->log(get_string('error_course_module_not_found', 'backup', $id), backup::LOG_WARNING);
}
}
/**
* Build one 1-section backup
*/
protected static function build_section_plan($controller, $id) {
$plan = $controller->get_plan();
// Add the section task, responsible for outputting
// 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, FEATURE_BACKUP_MOODLE2)) { // 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
*/
protected static function build_course_plan($controller, $id) {
$plan = $controller->get_plan();
// Add the course task, responsible for outputting
// 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));
}
}
}
+115
View File
@@ -0,0 +1,115 @@
<?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/>.
/**
* Defines backup_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class implementing the plugins support for moodle2 backups
*
* TODO: Finish phpdocs
*/
abstract class backup_plugin {
/** @var string */
protected $plugintype;
/** @var string */
protected $pluginname;
/** @var string */
protected $connectionpoint;
/** @var backup_optigroup_element */
protected $optigroup; // Optigroup, parent of all optigroup elements
/** @var backup_structure_step */
protected $step;
/** @var backup_course_task|backup_activity_task */
protected $task;
/**
* backup_plugin constructor.
*
* @param string $plugintype
* @param string $pluginname
* @param backup_optigroup_element $optigroup
* @param backup_structure_step $step
*/
public function __construct($plugintype, $pluginname, $optigroup, $step) {
$this->plugintype = $plugintype;
$this->pluginname = $pluginname;
$this->optigroup = $optigroup;
$this->connectionpoint = '';
$this->step = $step;
$this->task = $step->get_task();
}
public function define_plugin_structure($connectionpoint) {
$this->connectionpoint = $connectionpoint;
$methodname = 'define_' . $connectionpoint . '_plugin_structure';
if (method_exists($this, $methodname)) {
$this->$methodname();
}
}
// 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)
*/
protected function get_plugin_element($final_elements = null, $conditionparam = null, $conditionvalue = null) {
// Something exclusive for this backup_plugin_element (backup_optigroup_element)
// because it hasn't XML representation
$name = 'optigroup_' . $this->plugintype . '_' . $this->pluginname . '_' . $this->connectionpoint;
$optigroup_element = new backup_plugin_element($name, $final_elements, $conditionparam, $conditionvalue);
$this->optigroup->add_child($optigroup_element); // Add optigroup_element to stay connected since beginning
return $optigroup_element;
}
/**
* Simple helper function that suggests one name for the main nested element in plugins
* It's not mandatory to use it but recommended ;-)
*/
protected function get_recommended_name() {
return 'plugin_' . $this->plugintype . '_' . $this->pluginname . '_' . $this->connectionpoint;
}
}
@@ -0,0 +1,38 @@
<?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/>.
/**
* Defines backup_qbank_plugin class.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Base class for qbank backup plugins.
*
* @package core_backup
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class backup_qbank_plugin extends backup_plugin {
// Use default parent behaviour.
}
@@ -0,0 +1,83 @@
<?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/>.
/**
* Defines backup_qtype_extrafields_plugin class
*
* @package core_backup
* @copyright 2012 Oleg Sychev, Volgograd State Technical University
* @author Valeriy Streltsov <vostreltsov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/question/engine/bank.php');
/**
* Class extending backup_qtype_plugin in order to use extra fields method
*
* See qtype_shortanswer for an example
*
* @copyright 2012 Oleg Sychev, Volgograd State Technical University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_qtype_extrafields_plugin extends backup_qtype_plugin {
/**
* Returns the qtype information to attach to question element.
*/
protected function define_question_plugin_structure() {
$qtypeobj = question_bank::get_qtype($this->pluginname);
// Define the virtual plugin element with the condition to fulfill.
$plugin = $this->get_plugin_element(null, '../../qtype', $qtypeobj->name());
// Create one standard named plugin element (the visible container).
$pluginwrapper = new backup_nested_element($this->get_recommended_name());
// Connect the visible container ASAP.
$plugin->add_child($pluginwrapper);
// This qtype uses standard question_answers, add them here
// to the tree before any other information that will use them.
$this->add_question_question_answers($pluginwrapper);
$answers = $pluginwrapper->get_child('answers');
$answer = $answers->get_child('answer');
// Extra question fields.
$extraquestionfields = $qtypeobj->extra_question_fields();
if (!empty($extraquestionfields)) {
$tablename = array_shift($extraquestionfields);
$child = new backup_nested_element($qtypeobj->name(), array('id'), $extraquestionfields);
$pluginwrapper->add_child($child);
$child->set_source_table($tablename, array($qtypeobj->questionid_column_name() => backup::VAR_PARENTID));
}
// Extra answer fields.
$extraanswerfields = $qtypeobj->extra_answer_fields();
if (!empty($extraanswerfields)) {
$tablename = array_shift($extraanswerfields);
$child = new backup_nested_element('extraanswerdata', array('id'), $extraanswerfields);
$answer->add_child($child);
$child->set_source_table($tablename, array('answerid' => backup::VAR_PARENTID));
}
// Don't need to annotate ids nor files.
return $plugin;
}
}
@@ -0,0 +1,210 @@
<?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/>.
/**
* Defines backup_qtype_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class extending standard backup_plugin in order to implement some
* helper methods related with the questions (qtype plugin)
*
* TODO: Finish phpdocs
*/
abstract class backup_qtype_plugin extends backup_plugin {
/**
* Attach to $element (usually questions) the needed backup structures
* for question_answers for a given question
* Used by various qtypes (calculated, essay, multianswer,
* multichoice, numerical, shortanswer, truefalse)
*/
protected function add_question_question_answers($element) {
// Check $element is one nested_backup_element
if (! $element instanceof backup_nested_element) {
throw new backup_step_exception('question_answers_bad_parent_element', $element);
}
// Define the elements
$answers = new backup_nested_element('answers');
$answer = new backup_nested_element('answer', array('id'), array(
'answertext', 'answerformat', 'fraction', 'feedback',
'feedbackformat'));
// Build the tree
$element->add_child($answers);
$answers->add_child($answer);
// Set the sources
$answer->set_source_table('question_answers', array('question' => backup::VAR_PARENTID), 'id ASC');
// Aliases
$answer->set_source_alias('answer', 'answertext');
// don't need to annotate ids nor files
}
/**
* Attach to $element (usually questions) the needed backup structures
* for question_numerical_units for a given question
* Used both by calculated and numerical qtypes
*/
protected function add_question_numerical_units($element) {
// Check $element is one nested_backup_element
if (! $element instanceof backup_nested_element) {
throw new backup_step_exception('question_numerical_units_bad_parent_element', $element);
}
// Define the elements
$units = new backup_nested_element('numerical_units');
$unit = new backup_nested_element('numerical_unit', array('id'), array(
'multiplier', 'unit'));
// Build the tree
$element->add_child($units);
$units->add_child($unit);
// Set the sources
$unit->set_source_table('question_numerical_units', array('question' => backup::VAR_PARENTID), 'id ASC');
// don't need to annotate ids nor files
}
/**
* Attach to $element (usually questions) the needed backup structures
* for question_numerical_options for a given question
* Used both by calculated and numerical qtypes
*/
protected function add_question_numerical_options($element) {
// Check $element is one nested_backup_element
if (! $element instanceof backup_nested_element) {
throw new backup_step_exception('question_numerical_options_bad_parent_element', $element);
}
// Define the elements
$options = new backup_nested_element('numerical_options');
$option = new backup_nested_element('numerical_option', array('id'), array(
'showunits', 'unitsleft', 'unitgradingtype', 'unitpenalty'));
// Build the tree
$element->add_child($options);
$options->add_child($option);
// Set the sources
$option->set_source_table('question_numerical_options', array('question' => backup::VAR_PARENTID));
// don't need to annotate ids nor files
}
/**
* Attach to $element (usually questions) the needed backup structures
* for question_datasets for a given question
* Used by calculated qtypes
*/
protected function add_question_datasets($element) {
// Check $element is one nested_backup_element
if (! $element instanceof backup_nested_element) {
throw new backup_step_exception('question_datasets_bad_parent_element', $element);
}
// Define the elements
$definitions = new backup_nested_element('dataset_definitions');
$definition = new backup_nested_element('dataset_definition', array('id'), array(
'category', 'name', 'type', 'options',
'itemcount'));
$items = new backup_nested_element('dataset_items');
$item = new backup_nested_element('dataset_item', array('id'), array(
'number', 'value'));
// Build the tree
$element->add_child($definitions);
$definitions->add_child($definition);
$definition->add_child($items);
$items->add_child($item);
// Set the sources
$definition->set_source_sql('SELECT qdd.*
FROM {question_dataset_definitions} qdd
JOIN {question_datasets} qd ON qd.datasetdefinition = qdd.id
WHERE qd.question = ?', array(backup::VAR_PARENTID));
$item->set_source_table('question_dataset_items', array('definition' => backup::VAR_PARENTID));
// Aliases
$item->set_source_alias('itemnumber', 'number');
// don't need to annotate ids nor files
}
/**
* Returns all the components and fileareas used by all the installed qtypes
*
* The method introspects each qtype, asking it about fileareas used. Then,
* one 2-level array is returned. 1st level is the component name (qtype_xxxx)
* and 2nd level is one array of filearea => mappings to look
*
* Note that this function is used both in backup and restore, so it is important
* to use the same mapping names (usually, name of the table in singular) always
*
* TODO: Surely this can be promoted to backup_plugin easily and make it to
* work for ANY plugin, not only qtypes (but we don't need it for now)
*/
public static function get_components_and_fileareas($filter = null) {
$components = array();
// Get all the plugins of this type
$qtypes = core_component::get_plugin_list('qtype');
foreach ($qtypes as $name => $path) {
// Apply filter if specified
if (!is_null($filter) && $filter != $name) {
continue;
}
// Calculate the componentname
$componentname = 'qtype_' . $name;
// Get the plugin fileareas (all them MUST belong to the same component)
$classname = 'backup_qtype_' . $name . '_plugin';
if (class_exists($classname)) {
$elements = call_user_func(array($classname, 'get_qtype_fileareas'));
if ($elements) {
// If there are elements, add them to $components
$components[$componentname] = $elements;
}
}
}
return $components;
}
/**
* Returns one array with filearea => mappingname elements for the qtype
*
* Used by {@link get_components_and_fileareas} to know about all the qtype
* files to be processed both in backup and restore.
*/
public static function get_qtype_fileareas() {
// By default, return empty array, only qtypes having own fileareas will override this
return array();
}
}
@@ -0,0 +1,35 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/**
* Base class for report backup plugins.
*
* NOTE: When you back up a course, it potentially may run backup for all
* reports. In order to control whether a particular report gets
* backed up, a report should make use of the second and third
* parameters in get_plugin_element().
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class backup_report_plugin extends backup_plugin {
// Use default parent behaviour
}
+206
View File
@@ -0,0 +1,206 @@
<?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/>.
/**
* Defines backup_root_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Start task that provides all the settings common to all backups and some initialization 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
protected function converter_deps($main_setting, $converters) {
foreach ($this->settings as $setting) {
$name = $setting->get_name();
if (in_array($name, $converters)) {
$setvalue = convert_helper::export_converter_dependencies($name, $main_setting->get_name());
if ($setvalue !== false) {
$setting->add_dependency($main_setting, $setvalue, array('value' => $name));
}
}
}
}
/**
* Define the common setting that any backup type will have
*/
protected function define_settings() {
global $CFG;
require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php');
// Define filename setting
$filename = new backup_filename_setting('filename', base_setting::IS_FILENAME, 'backup.mbz');
$filename->set_ui_filename(get_string('filename', 'backup'), 'backup.mbz', array('size'=>50));
$this->add_setting($filename);
// Present converter settings only in type course and mode general backup operations.
$converters = array();
if ($this->plan->get_type() == backup::TYPE_1COURSE and $this->plan->get_mode() == backup::MODE_GENERAL) {
$converters = convert_helper::available_converters(false);
foreach ($converters as $cnv) {
$formatcnv = new backup_users_setting($cnv, base_setting::IS_BOOLEAN, false);
$formatcnv->set_ui(new backup_setting_ui_checkbox($formatcnv, get_string('backupformat'.$cnv, 'backup')));
$this->add_setting($formatcnv);
}
}
// Define users setting (keeping it on hand to define dependencies)
$users = new backup_users_setting('users', base_setting::IS_BOOLEAN, true);
$users->set_ui(new backup_setting_ui_checkbox($users, get_string('rootsettingusers', 'backup')));
$this->add_setting($users);
$this->converter_deps($users, $converters);
// Define anonymize (dependent of users)
$anonymize = new backup_anonymize_setting('anonymize', base_setting::IS_BOOLEAN, false);
$anonymize->set_ui(new backup_setting_ui_checkbox($anonymize, get_string('rootsettinganonymize', 'backup')));
$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);
$roleassignments->set_ui(new backup_setting_ui_checkbox($roleassignments, get_string('rootsettingroleassignments', 'backup')));
$this->add_setting($roleassignments);
$users->add_dependency($roleassignments);
// Define permission.
if ($this->plan->get_mode() == backup::MODE_IMPORT) {
$permissions = new backup_permissions_setting('permissions', base_setting::IS_BOOLEAN, false);
$permissions->set_ui(new backup_setting_ui_checkbox($permissions, get_string('rootsettingpermissions', 'backup')));
$this->add_setting($permissions);
}
// Define activities
$activities = new backup_activities_setting('activities', base_setting::IS_BOOLEAN, true);
$activities->set_ui(new backup_setting_ui_checkbox($activities, get_string('rootsettingactivities', 'backup')));
$this->add_setting($activities);
// Define blocks
$blocks = new backup_generic_setting('blocks', base_setting::IS_BOOLEAN, true);
$blocks->set_ui(new backup_setting_ui_checkbox($blocks, get_string('rootsettingblocks', 'backup')));
$this->add_setting($blocks);
$this->converter_deps($blocks, $converters);
// Define files.
$files = new backup_generic_setting('files', base_setting::IS_BOOLEAN, true);
$files->set_ui(new backup_setting_ui_checkbox($files, get_string('rootsettingfiles', 'backup')));
$this->add_setting($files);
$this->converter_deps($files, $converters);
// Define filters
$filters = new backup_generic_setting('filters', base_setting::IS_BOOLEAN, true);
$filters->set_ui(new backup_setting_ui_checkbox($filters, get_string('rootsettingfilters', 'backup')));
$this->add_setting($filters);
$this->converter_deps($filters, $converters);
// Define comments (dependent of users)
$comments = new backup_comments_setting('comments', base_setting::IS_BOOLEAN, true);
$comments->set_ui(new backup_setting_ui_checkbox($comments, get_string('rootsettingcomments', 'backup')));
$this->add_setting($comments);
$users->add_dependency($comments);
// Define badges (dependent of activities).
$badges = new backup_badges_setting('badges', base_setting::IS_BOOLEAN, true);
$badges->set_ui(new backup_setting_ui_checkbox($badges, get_string('rootsettingbadges', 'backup')));
$this->add_setting($badges);
$activities->add_dependency($badges);
$users->add_dependency($badges);
// Define calendar events.
$events = new backup_calendarevents_setting('calendarevents', base_setting::IS_BOOLEAN, true);
$events->set_ui(new backup_setting_ui_checkbox($events, get_string('rootsettingcalendarevents', 'backup')));
$this->add_setting($events);
// Define completion (dependent of users)
$completion = new backup_userscompletion_setting('userscompletion', base_setting::IS_BOOLEAN, true);
$completion->set_ui(new backup_setting_ui_checkbox($completion, get_string('rootsettinguserscompletion', 'backup')));
$this->add_setting($completion);
$users->add_dependency($completion);
// Define logs (dependent of users)
$logs = new backup_logs_setting('logs', base_setting::IS_BOOLEAN, true);
$logs->set_ui(new backup_setting_ui_checkbox($logs, get_string('rootsettinglogs', 'backup')));
$this->add_setting($logs);
$users->add_dependency($logs);
// Define grade_histories (dependent of users)
$gradehistories = new backup_generic_setting('grade_histories', base_setting::IS_BOOLEAN, true);
$gradehistories->set_ui(new backup_setting_ui_checkbox($gradehistories, get_string('rootsettinggradehistories', 'backup')));
$this->add_setting($gradehistories);
$users->add_dependency($gradehistories);
// The restore does not process the grade histories when some activities are ignored.
// So let's define a dependency to prevent false expectations from our users.
$activities->add_dependency($gradehistories);
// Define question bank inclusion setting.
$questionbank = new backup_generic_setting('questionbank', base_setting::IS_BOOLEAN, true);
$questionbank->set_ui(new backup_setting_ui_checkbox($questionbank, get_string('rootsettingquestionbank', 'backup')));
$this->add_setting($questionbank);
$groups = new backup_groups_setting('groups', base_setting::IS_BOOLEAN, true);
$groups->set_ui(new backup_setting_ui_checkbox($groups, get_string('rootsettinggroups', 'backup')));
$this->add_setting($groups);
// Define competencies inclusion setting if competencies are enabled.
$competencies = new backup_competencies_setting();
$competencies->set_ui(new backup_setting_ui_checkbox($competencies, get_string('rootsettingcompetencies', 'backup')));
$this->add_setting($competencies);
// Define custom fields inclusion setting if custom fields are used.
$customfields = new backup_customfield_setting('customfield', base_setting::IS_BOOLEAN, true);
$customfields->set_ui(new backup_setting_ui_checkbox($customfields, get_string('rootsettingcustomfield', 'backup')));
$this->add_setting($customfields);
// Define content bank content inclusion setting.
$contentbank = new backup_contentbankcontent_setting('contentbankcontent', base_setting::IS_BOOLEAN, true);
$contentbank->set_ui(new backup_setting_ui_checkbox($contentbank, get_string('rootsettingcontentbankcontent', 'backup')));
$this->add_setting($contentbank);
// Define xAPI state inclusion setting.
$xapistate = new backup_xapistate_setting('xapistate', base_setting::IS_BOOLEAN, true);
$xapistate->set_ui(new backup_setting_ui_checkbox($xapistate, get_string('rootsettingxapistate', 'backup')));
$this->add_setting($xapistate);
$users->add_dependency($xapistate);
// Define legacy file inclusion setting.
$legacyfiles = new backup_generic_setting('legacyfiles', base_setting::IS_BOOLEAN, true);
$legacyfiles->set_ui(new backup_setting_ui_checkbox($legacyfiles, get_string('rootsettinglegacyfiles', 'backup')));
$this->add_setting($legacyfiles);
}
}
@@ -0,0 +1,172 @@
<?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/>.
/**
* Defines backup_section_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* 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 new 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 = context_course::instance($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;
}
/**
* Exceptionally override the execute method, so, based in the section_included setting, we are able
* to skip the execution of one task completely
*/
public function execute() {
// Find section_included_setting
if (!$this->get_setting_value('included')) {
$this->log('section 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 = 'section_' . $this->sectionid . '_' . $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 section will have
*/
protected function define_settings() {
global $DB;
// All the settings related to this activity will include this prefix
$settingprefix = 'section_' . $this->sectionid . '_';
// All these are common settings to be shared by all sections
$section = $DB->get_record('course_sections', array('id' => $this->sectionid), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id' => $section->course), '*', MUST_EXIST);
// Define section_included (to decide if the whole task must be really executed)
$settingname = $settingprefix . 'included';
$section_included = new backup_section_included_setting($settingname, base_setting::IS_BOOLEAN, true);
$section_included->get_ui()->set_label(get_section_name($course, $section));
$this->add_setting($section_included);
// Define section_userinfo. Dependent of:
// - users root setting
// - section_included setting
$settingname = $settingprefix . 'userinfo';
$section_userinfo = new backup_section_userinfo_setting($settingname, base_setting::IS_BOOLEAN, true);
$section_userinfo->get_ui()->set_label(get_string('includeuserinfo','backup'));
$this->add_setting($section_userinfo);
// Look for "users" root setting
$users = $this->plan->get_setting('users');
$users->add_dependency($section_userinfo);
// Look for "section_included" section setting
$section_included->add_dependency($section_userinfo);
}
}
+219
View File
@@ -0,0 +1,219 @@
<?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/>.
/**
* Defines classes used to handle backup settings
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// TODO: Reduce these to the minimum because ui/dependencies are 100% separated
// Root backup settings
/**
* root generic setting to store different things without dependencies
*/
class backup_generic_setting extends root_backup_setting {}
/**
* root setting to handle backup file names (no dependencies nor anything else)
*/
class backup_filename_setting extends backup_generic_setting {
/**
* Instantiates a setting object
*
* @param string $name Name of the setting
* @param string $vtype Type of the setting, eg {@link base_setting::IS_TEXT}
* @param mixed $value Value of the setting
* @param bool $visibility Is the setting visible in the UI, eg {@link base_setting::VISIBLE}
* @param int $status Status of the setting with regards to the locking, eg {@link base_setting::NOT_LOCKED}
*/
public function __construct($name, $vtype, $value = null, $visibility = self::VISIBLE, $status = self::NOT_LOCKED) {
parent::__construct($name, $vtype, $value, $visibility, $status);
}
public function set_ui_filename($label, $value, array $options = null) {
$this->make_ui(self::UI_HTML_TEXTFIELD, $label, null, $options);
$this->set_value($value);
}
}
/**
* root setting to control if backup will include user information
* A lot of other settings are dependent 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 include permission information by roles
*/
class backup_permissions_setting extends backup_generic_setting {
}
/**
* root setting to control if backup will include group information depends on @backup_users_setting
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright 2014 Matt Sammarco
*/
class backup_groups_setting extends backup_generic_setting {
}
/**
* root setting to control if backup will include custom field information
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright 2018 Daniel Neis Araujo
*/
class backup_customfield_setting extends backup_generic_setting {
}
/**
* root setting to control if backup will include activities or no.
* A lot of other settings (_included at activity levels)
* are dependent of this setting
*/
class backup_activities_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
* available if the former is enabled (apart from security
* that can change it
*/
class backup_anonymize_setting extends root_backup_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 {}
/**
* 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 {}
/**
* 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 {}
/**
* root setting to control if backup will include badges or not,
* depends on @backup_activities_setting
*/
class backup_badges_setting extends backup_generic_setting {}
/**
* root setting to control if backup will include
* calender events 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_calendarevents_setting extends 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 {}
/**
* root setting to control if backup will include competencies or not.
*/
class backup_competencies_setting extends backup_generic_setting {
/**
* backup_competencies_setting constructor.
*/
public function __construct() {
$defaultvalue = false;
$visibility = base_setting::HIDDEN;
$status = base_setting::LOCKED_BY_CONFIG;
if (\core_competency\api::is_enabled()) {
$defaultvalue = true;
$visibility = base_setting::VISIBLE;
$status = base_setting::NOT_LOCKED;
}
parent::__construct('competencies', base_setting::IS_BOOLEAN, $defaultvalue, $visibility, $status);
}
}
// Section backup settings
/**
* generic section setting to pass various settings between tasks and steps
*/
class backup_section_generic_setting extends section_backup_setting {}
/**
* Setting to define if one section is included or no. Activities _included
* settings depend of them if available
*/
class backup_section_included_setting extends section_backup_setting {}
/**
* section backup setting to control if section will include
* user information or no, depends of @backup_users_setting
*/
class backup_section_userinfo_setting extends section_backup_setting {}
// Activity backup settings
/**
* generic activity setting to pass various settings between tasks and steps
*/
class backup_activity_generic_setting extends activity_backup_setting {}
/**
* activity backup setting to control if activity will
* be included or no, depends of @backup_activities_setting and
* optionally parent section included setting
*/
class backup_activity_included_setting extends activity_backup_setting {}
/**
* 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 {}
/**
* Root setting to control if backup will include content bank content or no
*/
class backup_contentbankcontent_setting extends backup_generic_setting {
}
/**
* Root setting to control if backup will include xAPI state or not.
*/
class backup_xapistate_setting extends backup_generic_setting {
}
File diff suppressed because it is too large Load Diff
+101
View File
@@ -0,0 +1,101 @@
<?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/>.
/**
* Defines backup_subplugin class
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class implementing the subplugins support for moodle2 backups
*
* TODO: Finish phpdocs
* TODO: Make this subclass of backup_plugin
*/
abstract class backup_subplugin {
protected $subplugintype;
protected $subpluginname;
protected $connectionpoint;
protected $optigroup; // Optigroup, parent of all optigroup elements
protected $step;
protected $task;
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) {
$this->connectionpoint = $connectionpoint;
$methodname = 'define_' . $connectionpoint . '_subplugin_structure';
if (method_exists($this, $methodname)) {
$this->$methodname();
}
}
// 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)
*/
protected function get_subplugin_element($final_elements = null, $conditionparam = null, $conditionvalue = null) {
// Something exclusive for this backup_subplugin_element (backup_optigroup_element)
// because it hasn't XML representation
$name = 'optigroup_' . $this->subplugintype . '_' . $this->subpluginname . '_' . $this->connectionpoint;
$optigroup_element = new backup_subplugin_element($name, $final_elements, $conditionparam, $conditionvalue);
$this->optigroup->add_child($optigroup_element); // Add optigroup_element to stay connected since beginning
return $optigroup_element;
}
/**
* Simple helper function that suggests one name for the main nested element in subplugins
* It's not mandatory to use it but recommended ;-)
*/
protected function get_recommended_name() {
return 'subplugin_' . $this->subplugintype . '_' . $this->subpluginname . '_' . $this->connectionpoint;
}
}
@@ -0,0 +1,81 @@
<?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/>.
/**
* Defines backup_theme_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 onwards The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Base class for theme backup plugins.
*
* NOTE: When you back up a course, it runs backup for ALL themes - not just
* the currently selected one.
*
* That means that if, for example, a course was once in theme A, and theme A
* had some data settings, but it is then changed to theme B, the data settings
* will still be included in the backup and restore. With the restored course,
* if you ever change it back to theme A, the settings will be ready.
*
* It also means that other themes which are not the one set up for the course,
* but might be seen by some users (eg user themes, session themes, mnet themes)
* can store data.
*
* If this behaviour is not desired for a particular theme's data, the subclass
* can call is_current_theme('myname') to check.
*/
abstract class backup_theme_plugin extends backup_plugin {
/**
* @var string Current theme for course (may not be the same as plugin).
*/
protected $coursetheme;
/**
* @param string $plugintype Plugin type (always 'theme')
* @param string $pluginname Plugin name (name of theme)
* @param backup_optigroup $optigroup Group that will contain this data
* @param backup_course_structure_step $step Backup step that this is part of
*/
public function __construct($plugintype, $pluginname, $optigroup, $step) {
parent::__construct($plugintype, $pluginname, $optigroup, $step);
$this->coursetheme = backup_plan_dbops::get_theme_from_courseid(
$this->task->get_courseid());
}
/**
* Return condition for whether this theme should be backed up (= if it
* is the same theme as the one used in this course). This condition has
* the theme used in the course. It will be compared against the name
* of the theme, by use of third parameter in get_plugin_element; in
* subclass, you should do:
* $plugin = $this->get_plugin_element(null, $this->get_theme_condition(), 'mytheme');
*/
protected function get_theme_condition() {
return array('sqlparam' => $this->coursetheme);
}
}
@@ -0,0 +1,38 @@
<?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/>.
/**
* Admin tool backup plugin base.
*
* @package core_backup
* @subpackage moodle2
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Admin tool backup plugin base class.
*
* @package core_backup
* @subpackage moodle2
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class backup_tool_plugin extends backup_plugin {
// Use default parent behaviour.
}
@@ -0,0 +1,208 @@
<?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/>.
/**
* Defines backup_xml_transformer class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// Cache for storing link encoders, so that we don't need to call
// register_link_encoders each time backup_xml_transformer is constructed
// TODO MDL-25290 replace global with MUC code.
global $LINKS_ENCODERS_CACHE;
$LINKS_ENCODERS_CACHE = array();
/**
* Class implementing the @xml_contenttransformed 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 silently, 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) {
// Array or object, debug and try our best recursively, shouldn't happen but...
if (is_array($content)) {
debugging('Backup XML transformer should not process arrays but plain content only', DEBUG_DEVELOPER);
foreach($content as $key => $plaincontent) {
$content[$key] = $this->process($plaincontent);
}
return $content;
} else if (is_object($content)) {
debugging('Backup XML transformer should not process objects but plain content only', DEBUG_DEVELOPER);
foreach((array)$content as $key => $plaincontent) {
$content[$key] = $this->process($plaincontent);
}
return (object)$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) < 32) { // Impossible to have one link in 32cc
return $content; // (http://10.0.0.1/file.php/1/1.jpg, 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
// Replace all calls to h5p/embed.php by $@H5PEMBED@$.
$content = $this->process_h5pembedphp_links($content);
$content = $this->encode_absolute_links($content); // Pass the content against all the found encoders
return $content;
}
private function process_filephp_links($content) {
global $CFG;
if (strpos($content, 'file.php') === false) { // No file.php, nothing to convert
return $content;
}
//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 &amp;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;
}
/**
* Replace all calls to /h5p/embed.php by $@H5PEMBED@$
* to allow restore the /h5p/embed.php url in
* other domains.
*
* @param string $content
* @return string
*/
private function process_h5pembedphp_links($content) {
global $CFG;
// No /h5p/embed.php, nothing to convert.
if (strpos($content, '/h5p/embed.php') === false) {
return $content;
}
return str_replace($CFG->wwwroot.'/h5p/embed.php', '$@H5PEMBED@$', $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;
}
private static function process_filephp_uses($matches) {
// Replace slashes (plain and encoded) and forcedownload=1 parameter
$search = array('/', '%2f', '%2F', '?forcedownload=1', '&forcedownload=1', '&amp;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;
}
/**
* Register all available content link encoders
*
* @return array encoder
* @todo MDL-25290 replace LINKS_ENCODERS_CACHE global with MUC code
*/
private function register_link_encoders() {
global $LINKS_ENCODERS_CACHE;
// If encoder is linked, then return cached encoder.
if (!empty($LINKS_ENCODERS_CACHE)) {
return $LINKS_ENCODERS_CACHE;
}
$encoders = array();
// Add the course encoder
$encoders['backup_course_task'] = 'encode_content_links';
// Add the module ones. Each module supporting moodle2 backups MUST have it
$mods = core_component::get_plugin_list('mod');
foreach ($mods as $mod => $moddir) {
if (plugin_supports('mod', $mod, FEATURE_BACKUP_MOODLE2) && class_exists('backup_' . $mod . '_activity_task')) {
$encoders['backup_' . $mod . '_activity_task'] = 'encode_content_links';
}
}
// Add the block encoders
$blocks = core_component::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.
$LINKS_ENCODERS_CACHE = $encoders;
return $encoders;
}
}
@@ -0,0 +1,366 @@
<?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/>.
/**
* Defines restore_activity_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* abstract activity task that provides all the properties and common tasks to be performed
* when one activity is being restored
*
* TODO: Finish phpdocs
*/
abstract class restore_activity_task extends restore_task {
protected $info; // info related to activity gathered from backup file
protected $modulename; // name of the module
protected $moduleid; // new (target) id of the course module
protected $oldmoduleid; // old (original) id of the course module
protected $oldmoduleversion; // old (original) version of the module
protected $contextid; // new (target) context of the activity
protected $oldcontextid;// old (original) context of the activity
protected $activityid; // new (target) id of the activity
protected $oldactivityid;// old (original) id of the activity
/**
* Constructor - instantiates one object of this class
*/
public function __construct($name, $info, $plan = null) {
$this->info = $info;
$this->modulename = $this->info->modulename;
$this->moduleid = 0;
$this->oldmoduleid = $this->info->moduleid;
$this->oldmoduleversion = 0;
$this->contextid = 0;
$this->oldcontextid = 0;
$this->activityid = 0;
$this->oldactivityid = 0;
parent::__construct($name, $plan);
}
/**
* Activity tasks have their own directory to read files
*/
public function get_taskbasepath() {
return $this->get_basepath() . '/' . $this->info->directory;
}
public function set_moduleid($moduleid) {
$this->moduleid = $moduleid;
}
public function set_old_moduleversion($oldmoduleversion) {
$this->oldmoduleversion = $oldmoduleversion;
}
public function set_activityid($activityid) {
$this->activityid = $activityid;
}
public function set_old_activityid($activityid) {
$this->oldactivityid = $activityid;
}
public function set_contextid($contextid) {
$this->contextid = $contextid;
}
public function set_old_contextid($contextid) {
$this->oldcontextid = $contextid;
}
public function get_modulename() {
return $this->modulename;
}
public function get_moduleid() {
return $this->moduleid;
}
/**
* Returns the old course module id (cmid of activity which will be restored)
*
* @return int
*/
public function get_old_moduleid() {
return $this->oldmoduleid;
}
public function get_old_moduleversion() {
return $this->oldmoduleversion;
}
public function get_activityid() {
return $this->activityid;
}
public function get_old_activityid() {
return $this->oldactivityid;
}
public function get_contextid() {
return $this->contextid;
}
public function get_old_contextid() {
return $this->oldcontextid;
}
/**
* Create all the steps that will be part of this task
*/
public function build() {
// If we have decided not to restore activities, prevent anything to be built
if (!$this->get_setting_value('activities')) {
$this->built = true;
return;
}
// Load he course_module estructure, generating it (with instance = 0)
// but allowing the creation of the target context needed in following steps
$this->add_step(new restore_module_structure_step('module_info', 'module.xml'));
// 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();
// Roles (optionally role assignments and always role overrides)
$this->add_step(new restore_ras_and_caps_structure_step('course_ras_and_caps', 'roles.xml'));
// Filters (conditionally)
if ($this->get_setting_value('filters')) {
$this->add_step(new restore_filters_structure_step('activity_filters', 'filters.xml'));
}
// Comments (conditionally)
if ($this->get_setting_value('comments')) {
$this->add_step(new restore_comments_structure_step('activity_comments', 'comments.xml'));
}
// Calendar events (conditionally)
if ($this->get_setting_value('calendarevents')) {
$this->add_step(new restore_calendarevents_structure_step('activity_calendar', 'calendar.xml'));
}
// Grades (module-related, rest of gradebook is restored later if possible: cats, calculations...)
$this->add_step(new restore_activity_grades_structure_step('activity_grades', 'grades.xml'));
// Advanced grading methods attached to the module
$this->add_step(new restore_activity_grading_structure_step('activity_grading', 'grading.xml'));
// Grade history. The setting 'grade_history' is handled in the step.
$this->add_step(new restore_activity_grade_history_structure_step('activity_grade_history', 'grade_history.xml'));
// Userscompletion (conditionally)
if ($this->get_setting_value('userscompletion')) {
$this->add_step(new restore_userscompletion_structure_step('activity_userscompletion', 'completion.xml'));
}
// Logs (conditionally)
if ($this->get_setting_value('logs')) {
// Legacy logs.
$this->add_step(new restore_activity_logs_structure_step('activity_logs', 'logs.xml'));
// New log stores.
$this->add_step(new restore_activity_logstores_structure_step('activity_logstores', 'logstores.xml'));
}
// Activity competencies.
$this->add_step(new restore_activity_competencies_structure_step('activity_competencies', 'competencies.xml'));
// Search reindexing, if enabled and if not restoring entire course.
if (\core_search\manager::is_indexing_enabled()) {
$wholecourse = $this->get_target() == backup::TARGET_NEW_COURSE;
$wholecourse = $wholecourse || ($this->setting_exists('overwrite_conf') && $this->get_setting_value('overwrite_conf'));
if (!$wholecourse) {
$this->add_step(new restore_activity_search_index('activity_search_index'));
}
}
// The xAPI state (conditionally).
if ($this->get_setting_value('xapistate')) {
$this->add_step(new restore_xapistate_structure_step('activity_xapistate', 'xapistate.xml'));
}
// 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);
$this->plan->set_excluding_activities(); // Inform plan we are excluding actvities
} 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->info->modulename . '_' . $this->info->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);
}
}
/**
* Define (add) particular steps that each activity can have
*/
abstract protected function define_my_steps();
/**
* Define the contents in the activity that must be
* processed by the link decoder
*/
public static function define_decode_contents() {
throw new coding_exception('define_decode_contents() method needs to be overridden in each subclass of restore_activity_task');
}
/**
* Define the decoding rules for links belonging
* to the activity to be executed by the link decoder
*/
public static function define_decode_rules() {
throw new coding_exception('define_decode_rules() method needs to be overridden in each subclass of restore_activity_task');
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* activity logs. It must return one array
* of {@link restore_log_rule} objects
*/
public static function define_restore_log_rules() {
throw new coding_exception('define_restore_log_rules() method needs to be overridden in each subclass of restore_activity_task');
}
// Protected API starts here
/**
* Define the common setting that any restore activity will have
*/
protected function define_settings() {
// All the settings related to this activity will include this prefix
$settingprefix = $this->info->modulename . '_' . $this->info->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)
// Dependent of:
// - activities root setting
// - section_included setting (if exists)
$settingname = $settingprefix . 'included';
$activity_included = new restore_activity_generic_setting($settingname, base_setting::IS_BOOLEAN, true);
$activity_included->get_ui()->set_icon(new image_icon('monologo', get_string('pluginname', $this->modulename),
$this->modulename, array('class' => 'iconlarge icon-post ml-1')));
$this->add_setting($activity_included);
// Look for "activities" root setting
$activities = $this->plan->get_setting('activities');
$activities->add_dependency($activity_included);
// Look for "section_included" section setting (if exists)
$settingname = 'section_' . $this->info->sectionid . '_included';
if ($this->plan->setting_exists($settingname)) {
$section_included = $this->plan->get_setting($settingname);
$section_included->add_dependency($activity_included);
}
// Define activity_userinfo. Dependent of:
// - users root setting
// - section_userinfo setting (if exists)
// - activity_included setting.
$settingname = $settingprefix . 'userinfo';
$defaultvalue = false;
if (isset($this->info->settings[$settingname]) && $this->info->settings[$settingname]) { // Only enabled when available
$defaultvalue = true;
}
$activity_userinfo = new restore_activity_userinfo_setting($settingname, base_setting::IS_BOOLEAN, $defaultvalue);
if (!$defaultvalue) {
// This is a bit hacky, but if there is no user data to restore, then
// we replace the standard check-box with a select menu with the
// single choice 'No', and the select menu is clever enough that if
// there is only one choice, it just displays a static string.
//
// It would probably be better design to have a special UI class
// setting_ui_checkbox_or_no, rather than this hack, but I am not
// going to do that today.
$activity_userinfo->set_ui(new backup_setting_ui_select($activity_userinfo, '-',
array(0 => get_string('no'))));
} else {
$activity_userinfo->get_ui()->set_label('-');
}
$this->add_setting($activity_userinfo);
// Look for "users" root setting
$users = $this->plan->get_setting('users');
$users->add_dependency($activity_userinfo);
// Look for "section_userinfo" section setting (if exists)
$settingname = 'section_' . $this->info->sectionid . '_userinfo';
if ($this->plan->setting_exists($settingname)) {
$section_userinfo = $this->plan->get_setting($settingname);
$section_userinfo->add_dependency($activity_userinfo);
}
// Look for "activity_included" setting.
$activity_included->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();
}
+213
View File
@@ -0,0 +1,213 @@
<?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/>.
/**
* Defines restore_block_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* abstract block task that provides all the properties and common steps to be performed
* when one block is being restored
*
* TODO: Finish phpdocs
*/
abstract class restore_block_task extends restore_task {
protected $taskbasepath; // To store the basepath of this block
protected $blockname; // Name of the block
protected $contextid; // new (target) context of the block
protected $oldcontextid;// old (original) context of the block
protected $blockid; // new (target) id of the block
protected $oldblockid; // old (original) id of the block
/**
* Constructor - instantiates one object of this class
*/
public function __construct($name, $taskbasepath, $plan = null) {
$this->taskbasepath = $taskbasepath;
$this->blockname = '';
$this->contextid = 0;
$this->oldcontextid = 0;
$this->blockid = 0;
$this->oldblockid = 0;
parent::__construct($name, $plan);
}
/**
* Block tasks have their own directory to write files
*/
public function get_taskbasepath() {
return $this->taskbasepath;
}
/**
* 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;
}
// If "child" of activity task and it has been excluded, nothing to do
$parent = basename(dirname(dirname($this->taskbasepath)));
if ($parent != 'course') {
$includedsetting = $parent . '_included';
if (!$this->get_setting_value($includedsetting)) {
$this->built = true;
return;
}
}
// Process the block.xml common file (instance + positions)
$this->add_step(new restore_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() in order to get the particular ones inserted in place.
$this->define_my_steps();
// Restore block role assignments and overrides (internally will observe the role_assignments setting)
$this->add_step(new restore_ras_and_caps_structure_step('block_ras_and_caps', 'roles.xml'));
// Restore block comments (conditionally)
if ($this->get_setting_value('comments')) {
$this->add_step(new restore_comments_structure_step('block_comments', 'comments.xml'));
}
// Search reindexing (if enabled).
if (\core_search\manager::is_indexing_enabled()) {
$wholecourse = $this->get_target() == backup::TARGET_NEW_COURSE;
$wholecourse = $wholecourse || $this->setting_exists('overwrite_conf') && $this->get_setting_value('overwrite_conf');
if (!$wholecourse) {
$this->add_step(new restore_block_search_index('block_search_index'));
}
}
// At the end, mark it as built
$this->built = true;
}
public function set_blockname($blockname) {
$this->blockname = $blockname;
}
public function get_blockname() {
return $this->blockname;
}
public function set_blockid($blockid) {
$this->blockid = $blockid;
}
public function get_blockid() {
return $this->blockid;
}
public function set_old_blockid($blockid) {
$this->oldblockid = $blockid;
}
public function get_old_blockid() {
return $this->oldblockid;
}
public function set_contextid($contextid) {
$this->contextid = $contextid;
}
public function get_contextid() {
return $this->contextid;
}
public function set_old_contextid($contextid) {
$this->oldcontextid = $contextid;
}
public function get_old_contextid() {
return $this->oldcontextid;
}
/**
* Define one array() of fileareas that each block controls
*/
abstract public function get_fileareas();
/**
* Define one array() of configdata attributes
* that need to be decoded
*/
abstract public function get_configdata_encoded_attributes();
/**
* Helper method to safely unserialize block configuration during restore
*
* @param string $configdata The original base64 encoded block config, as retrieved from the block_instances table
* @return stdClass
*/
protected function decode_configdata(string $configdata): stdClass {
return unserialize_object(base64_decode($configdata));
}
/**
* Define the contents in the activity that must be
* processed by the link decoder
*/
public static function define_decode_contents() {
throw new coding_exception('define_decode_contents() method needs to be overridden in each subclass of restore_block_task');
}
/**
* Define the decoding rules for links belonging
* to the activity to be executed by the link decoder
*/
public static function define_decode_rules() {
throw new coding_exception('define_decode_rules() method needs to be overridden in each subclass of restore_block_task');
}
// 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();
}
@@ -0,0 +1,236 @@
<?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/>.
/**
* Defines restore_course_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* course task that provides all the properties and common steps to be performed
* when one course is being restored
*
* TODO: Finish phpdocs
*/
class restore_course_task extends restore_task {
protected $info; // info related to course gathered from backup file
protected $contextid; // course context id
/**
* Constructor - instantiates one object of this class
*/
public function __construct($name, $info, $plan = null) {
$this->info = $info;
parent::__construct($name, $plan);
}
/**
* Course tasks have their own directory to read files
*/
public function get_taskbasepath() {
return $this->get_basepath() . '/course';
}
public function get_contextid() {
return $this->contextid;
}
/**
* Create all the steps that will be part of this task
*/
public function build() {
// Define the task contextid (the course one)
$this->contextid = context_course::instance($this->get_courseid())->id;
// Executed conditionally if restoring to new course or if overwrite_conf setting is enabled
if ($this->get_target() == backup::TARGET_NEW_COURSE || $this->get_setting_value('overwrite_conf') == true) {
$this->add_step(new restore_course_structure_step('course_info', 'course.xml'));
// Search reindexing (if enabled).
if (\core_search\manager::is_indexing_enabled()) {
$this->add_step(new restore_course_search_index('course_search_index'));
}
}
if ($this->get_setting_value('legacyfiles')) {
$this->add_step(new restore_course_legacy_files_step('legacy_files'));
}
// Deal with enrolment methods and user enrolments.
if ($this->plan->get_mode() == backup::MODE_IMPORT) {
// No need to do anything with enrolments.
} else if (!$this->get_setting_value('users') or $this->plan->get_mode() == backup::MODE_HUB) {
if ($this->get_setting_value('enrolments') == backup::ENROL_ALWAYS && $this->plan->get_mode() != backup::MODE_HUB) {
// Restore enrolment methods.
$this->add_step(new restore_enrolments_structure_step('course_enrolments', 'enrolments.xml'));
} else if ($this->get_target() == backup::TARGET_CURRENT_ADDING or $this->get_target() == backup::TARGET_EXISTING_ADDING) {
// Keep current enrolments unchanged.
} else {
// If no instances yet add default enrol methods the same way as when creating new course in UI.
$this->add_step(new restore_default_enrolments_step('default_enrolments'));
}
} else {
// Restore course enrolment data.
$this->add_step(new restore_enrolments_structure_step('course_enrolments', 'enrolments.xml'));
}
// Populate groups, this must be done after enrolments because only enrolled users may be in groups.
$this->add_step(new restore_groups_members_structure_step('create_groups_members', '../groups.xml'));
// Restore course role assignments and overrides (internally will observe the role_assignments setting),
// this must be done after all users are enrolled.
$this->add_step(new restore_ras_and_caps_structure_step('course_ras_and_caps', 'roles.xml'));
// Restore course filters (conditionally)
if ($this->get_setting_value('filters')) {
$this->add_step(new restore_filters_structure_step('course_filters', 'filters.xml'));
}
// Restore course comments (conditionally)
if ($this->get_setting_value('comments')) {
$this->add_step(new restore_comments_structure_step('course_comments', 'comments.xml'));
}
// Calendar events (conditionally)
if ($this->get_setting_value('calendarevents')) {
$this->add_step(new restore_calendarevents_structure_step('course_calendar', 'calendar.xml'));
}
// Course competencies.
$this->add_step(new restore_course_competencies_structure_step('course_competencies', 'competencies.xml'));
// Activity completion defaults.
$this->add_step(new restore_completion_defaults_structure_step('course_completion_defaults', 'completiondefaults.xml'));
// Content bank content (conditionally).
if ($this->get_setting_value('contentbankcontent')) {
$this->add_step(new restore_contentbankcontent_structure_step('course_contentbank', 'contentbank.xml'));
}
// At the end, mark it as built
$this->built = true;
}
/**
* Define the contents in the course that must be
* processed by the link decoder
*/
public static function define_decode_contents() {
$contents = array();
$contents[] = new restore_decode_content('course', 'summary');
$contents[] = new restore_decode_content('event', 'description');
return $contents;
}
/**
* Define the decoding rules for links belonging
* to the course to be executed by the link decoder
*/
public static function define_decode_rules() {
$rules = array();
// Link to the course main page (it also covers "&topic=xx" and "&week=xx"
// because they don't become transformed (section number) in backup/restore.
$rules[] = new restore_decode_rule('COURSEVIEWBYID', '/course/view.php?id=$1', 'course');
// A few other key course links.
$rules[] = new restore_decode_rule('GRADEINDEXBYID', '/grade/index.php?id=$1', 'course');
$rules[] = new restore_decode_rule('GRADEREPORTINDEXBYID', '/grade/report/index.php?id=$1', 'course');
$rules[] = new restore_decode_rule('BADGESVIEWBYID', '/badges/view.php?type=2&id=$1', 'course');
$rules[] = new restore_decode_rule('USERINDEXVIEWBYID', '/user/index.php?id=$1', 'course');
$rules[] = new restore_decode_rule('PLUGINFILEBYCONTEXT', '/pluginfile.php/$1', 'context');
$rules[] = new restore_decode_rule('PLUGINFILEBYCONTEXTURLENCODED', '/pluginfile.php/$1', 'context', true);
return $rules;
}
// Protected API starts here
/**
* Define the common setting that any restore course will have
*/
protected function define_settings() {
// Define overwrite_conf to decide if course configuration will be restored over existing one.
$overwrite = new restore_course_overwrite_conf_setting('overwrite_conf', base_setting::IS_BOOLEAN, false);
$overwrite->set_ui(new backup_setting_ui_select($overwrite, $overwrite->get_name(),
array(1 => get_string('yes'), 0 => get_string('no'))));
$overwrite->get_ui()->set_label(get_string('setting_overwrite_conf', 'backup'));
if ($this->get_target() == backup::TARGET_NEW_COURSE) {
$overwrite->set_value(true);
$overwrite->set_status(backup_setting::LOCKED_BY_CONFIG);
$overwrite->set_visibility(backup_setting::HIDDEN);
$course = (object)['fullname' => null, 'shortname' => null, 'startdate' => null];
} else {
$course = get_course($this->get_courseid());
}
$this->add_setting($overwrite);
$fullnamedefaultvalue = $this->get_info()->original_course_fullname;
$fullname = new restore_course_defaultcustom_setting('course_fullname', base_setting::IS_TEXT, $fullnamedefaultvalue);
$fullname->set_ui(new backup_setting_ui_defaultcustom($fullname, get_string('setting_course_fullname', 'backup'),
['customvalue' => $fullnamedefaultvalue, 'defaultvalue' => $course->fullname]));
$this->add_setting($fullname);
$shortnamedefaultvalue = $this->get_info()->original_course_shortname;
$shortname = new restore_course_defaultcustom_setting('course_shortname', base_setting::IS_TEXT, $shortnamedefaultvalue);
$shortname->set_ui(new backup_setting_ui_defaultcustom($shortname, get_string('setting_course_shortname', 'backup'),
['customvalue' => $shortnamedefaultvalue, 'defaultvalue' => $course->shortname]));
$this->add_setting($shortname);
$startdatedefaultvalue = $this->get_info()->original_course_startdate;
$startdate = new restore_course_defaultcustom_setting('course_startdate', base_setting::IS_INTEGER, $startdatedefaultvalue);
$startdate->set_ui(new backup_setting_ui_defaultcustom($startdate, get_string('setting_course_startdate', 'backup'),
['customvalue' => $startdatedefaultvalue, 'defaultvalue' => $course->startdate, 'type' => 'date_time_selector']));
$this->add_setting($startdate);
$keep_enrols = new restore_course_generic_setting('keep_roles_and_enrolments', base_setting::IS_BOOLEAN, false);
$keep_enrols->set_ui(new backup_setting_ui_select($keep_enrols, $keep_enrols->get_name(), array(1=>get_string('yes'), 0=>get_string('no'))));
$keep_enrols->get_ui()->set_label(get_string('setting_keep_roles_and_enrolments', 'backup'));
if ($this->get_target() != backup::TARGET_CURRENT_DELETING and $this->get_target() != backup::TARGET_EXISTING_DELETING) {
$keep_enrols->set_value(false);
$keep_enrols->set_status(backup_setting::LOCKED_BY_CONFIG);
$keep_enrols->set_visibility(backup_setting::HIDDEN);
}
$this->add_setting($keep_enrols);
$keep_groups = new restore_course_generic_setting('keep_groups_and_groupings', base_setting::IS_BOOLEAN, false);
$keep_groups->set_ui(new backup_setting_ui_select($keep_groups, $keep_groups->get_name(), array(1=>get_string('yes'), 0=>get_string('no'))));
$keep_groups->get_ui()->set_label(get_string('setting_keep_groups_and_groupings', 'backup'));
if ($this->get_target() != backup::TARGET_CURRENT_DELETING and $this->get_target() != backup::TARGET_EXISTING_DELETING) {
$keep_groups->set_value(false);
$keep_groups->set_status(backup_setting::LOCKED_BY_CONFIG);
$keep_groups->set_visibility(backup_setting::HIDDEN);
}
$this->add_setting($keep_groups);
}
}
@@ -0,0 +1,30 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/**
* Restore for course plugin: course report.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class restore_coursereport_plugin extends restore_plugin {
// Use default parent behaviour
}
@@ -0,0 +1,61 @@
<?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/>.
/**
* Defines restore_default_block_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Default block task to restore blocks not having own DB structures to be added
*
* TODO: Finish phpdocs
*/
class restore_default_block_task extends restore_block_task {
// Nothing to do, it's just the restore_block_task in action
// with required methods doing nothing special
protected function define_my_settings() {
}
protected function define_my_steps() {
}
public function get_fileareas() {
return array();
}
public function get_configdata_encoded_attributes() {
return array();
}
public static function define_decode_contents() {
return array();
}
public static function define_decode_rules() {
return array();
}
}
@@ -0,0 +1,39 @@
<?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/>.
/**
* Defines restore_enrol_plugin class.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2014 University of Wisconsin
* @author Matt petro
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Base class for enrol backup plugins.
*
* @package core_backup
* @copyright 2014 University of Wisconsin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class restore_enrol_plugin extends restore_plugin {
// Use default parent behaviour.
}
+206
View File
@@ -0,0 +1,206 @@
<?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/>.
/**
* Defines restore_final_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Final task that provides all the final steps necessary in order to finish one
* restore like gradebook, interlinks... apart from some final cleaning
*
* TODO: Finish phpdocs
*/
class restore_final_task extends restore_task {
/**
* Create all the steps that will be part of this task
*/
public function build() {
// Move all the CONTEXT_MODULE question qcats to their
// final (newly created) module context
$this->add_step(new restore_move_module_questions_categories('move_module_question_categories'));
// Create all the question files now that every question is in place
// and every category has its final contextid associated
$this->add_step(new restore_create_question_files('create_question_files'));
// Review all the block_position records in backup_ids in order
// match them now that all the contexts are created populating DB
// as needed. Only if we are restoring blocks.
if ($this->get_setting_value('blocks')) {
$this->add_step(new restore_review_pending_block_positions('review_block_positions'));
}
// Gradebook. Don't restore the gradebook unless activities are being restored.
if ($this->get_setting_value('activities')) {
$this->add_step(new restore_gradebook_structure_step('gradebook_step','gradebook.xml'));
$this->add_step(new restore_grade_history_structure_step('grade_history', 'grade_history.xml'));
}
// Course completion.
$this->add_step(new restore_course_completion_structure_step('course_completion', 'completion.xml'));
// Conditionally restore course badges.
if ($this->get_setting_value('badges')) {
$this->add_step(new restore_badges_structure_step('course_badges', 'badges.xml'));
}
// Review all the legacy module_availability records in backup_ids in
// order to match them with existing modules / grade items and convert
// into the new system.
$this->add_step(new restore_process_course_modules_availability('process_modules_availability'));
// Update restored availability data to account for changes in IDs
// during backup/restore.
$this->add_step(new restore_update_availability('update_availability'));
// Refresh action events conditionally.
if ($this->get_setting_value('activities')) {
$this->add_step(new restore_calendar_action_events('restoring_action_events'));
}
// Decode all the interlinks
$this->add_step(new restore_decode_interlinks('decode_interlinks'));
// Restore course logs (conditionally). They are restored here because we need all
// the activities to be already restored.
if ($this->get_setting_value('logs')) {
// Legacy logs.
$this->add_step(new restore_course_logs_structure_step('course_logs', 'course/logs.xml'));
// New log stores.
$this->add_step(new restore_course_logstores_structure_step('course_logstores', 'course/logstores.xml'));
// Last access to course logs.
$this->add_step(new restore_course_loglastaccess_structure_step('course_loglastaccess', 'course/loglastaccess.xml'));
}
// Review all the executed tasks having one after_restore method
// executing it to perform some final adjustments of information
// not available when the task was executed.
// This step is always the last one performing modifications on restored information
// Don't add any new step after it. Only aliases queue, cache rebuild and clean are allowed.
$this->add_step(new restore_execute_after_restore('executing_after_restore'));
// All files were sent to the filepool by now. We need to process
// the aliases yet as they were not actually created but stashed for us instead.
// We execute this step after executing_after_restore so that there can't be no
// more files sent to the filepool after this.
$this->add_step(new restore_process_file_aliases_queue('process_file_aliases_queue'));
// Rebuild course cache to see results, whoah!
$this->add_step(new restore_rebuild_course_cache('rebuild_course_cache'));
// Clean the temp dir (conditionally) and drop temp table
$this->add_step(new restore_drop_and_clean_temp_stuff('drop_and_clean_temp_stuff'));
// If restoring to a new course or overwriting config, reindex the whole course.
if (\core_search\manager::is_indexing_enabled()) {
$wholecourse = $this->get_target() == backup::TARGET_NEW_COURSE;
$wholecourse = $wholecourse || $this->setting_exists('overwrite_conf') && $this->get_setting_value('overwrite_conf');
if ($wholecourse) {
$this->add_step(new restore_course_search_index('course_search_index'));
}
}
$this->built = true;
}
/**
* Special method, only available in the restore_final_task, able to invoke the
* restore_plan execute_after_restore() method, so restore_execute_after_restore step
* will be able to launch all the after_restore() methods of the executed tasks
*/
public function launch_execute_after_restore() {
$this->plan->execute_after_restore();
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* course logs. It must return one array
* of {@link restore_log_rule} objects
*
* Note these are course logs, but are defined and restored
* in final task because we need all the activities to be
* restored in order to handle some log records properly
*/
public static function define_restore_log_rules() {
$rules = array();
// module 'course' rules
$rules[] = new restore_log_rule('course', 'view', 'view.php?id={course}', '{course}');
$rules[] = new restore_log_rule('course', 'guest', 'view.php?id={course}', null);
$rules[] = new restore_log_rule('course', 'user report', 'user.php?id={course}&user={user}&mode=[mode]', null);
$rules[] = new restore_log_rule('course', 'add mod', '../mod/[modname]/view.php?id={course_module}', '[modname] {[modname]}');
$rules[] = new restore_log_rule('course', 'update mod', '../mod/[modname]/view.php?id={course_module}', '[modname] {[modname]}');
$rules[] = new restore_log_rule('course', 'delete mod', 'view.php?id={course}', null);
$rules[] = new restore_log_rule('course', 'update', 'view.php?id={course}', '');
$rules[] = new restore_log_rule('course', 'enrol', 'view.php?id={course}', '{user}');
$rules[] = new restore_log_rule('course', 'unenrol', 'view.php?id={course}', '{user}');
$rules[] = new restore_log_rule('course', 'editsection', 'editsection.php?id={course_section}', null);
$rules[] = new restore_log_rule('course', 'new', 'view.php?id={course}', '');
$rules[] = new restore_log_rule('course', 'recent', 'recent.php?id={course}', '');
$rules[] = new restore_log_rule('course', 'report log', 'report/log/index.php?id={course}', '{course}');
$rules[] = new restore_log_rule('course', 'report live', 'report/live/index.php?id={course}', '{course}');
$rules[] = new restore_log_rule('course', 'report outline', 'report/outline/index.php?id={course}', '{course}');
$rules[] = new restore_log_rule('course', 'report participation', 'report/participation/index.php?id={course}', '{course}');
$rules[] = new restore_log_rule('course', 'report stats', 'report/stats/index.php?id={course}', '{course}');
$rules[] = new restore_log_rule('course', 'view section', 'view.php?id={course}&sectionid={course_section}', '{course_section}');
// module 'grade' rules
$rules[] = new restore_log_rule('grade', 'update', 'report/grader/index.php?id={course}', null);
// module 'user' rules
$rules[] = new restore_log_rule('user', 'view', 'view.php?id={user}&course={course}', '{user}');
$rules[] = new restore_log_rule('user', 'change password', 'view.php?id={user}&course={course}', '{user}');
$rules[] = new restore_log_rule('user', 'login', 'view.php?id={user}&course={course}', '{user}');
$rules[] = new restore_log_rule('user', 'logout', 'view.php?id={user}&course={course}', '{user}');
$rules[] = new restore_log_rule('user', 'view all', 'index.php?id={course}', '');
$rules[] = new restore_log_rule('user', 'update', 'view.php?id={user}&course={course}', '');
// rules from other tasks (activities) not belonging to one module instance (cmid = 0), so are restored here
$rules = array_merge($rules, restore_logs_processor::register_log_rules_for_course());
// Calendar rules.
$rules[] = new restore_log_rule('calendar', 'add', 'event.php?action=edit&id={event}', '[name]');
$rules[] = new restore_log_rule('calendar', 'edit', 'event.php?action=edit&id={event}', '[name]');
$rules[] = new restore_log_rule('calendar', 'edit all', 'event.php?action=edit&id={event}', '[name]');
// TODO: Other logs like 'upload'... will go here
return $rules;
}
// Protected API starts here
/**
* Define the common setting that any restore 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)
}
}
@@ -0,0 +1,38 @@
<?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/>.
/**
* Defines restore_format_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* 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
}
@@ -0,0 +1,49 @@
<?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/>.
/**
* Contains class restore_gradingform_plugin responsible for advanced grading form plugin backup
*
* @package core_backup
* @subpackage moodle2
* @copyright 2011 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Base class for restoring all advanced grading form plugins
*
* As an example of implementation see {@link restore_gradingform_rubric_plugin}
*
* @copyright 2011 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @category backup
*/
abstract class restore_gradingform_plugin extends restore_plugin {
/**
* Helper method returning the mapping identifierto use for
* grading form instance's itemid field
*
* @param array $areaname the name of the area the form is defined for
* @return string the mapping identifier
*/
public static function itemid_mapping($areaname) {
return 'grading_item_'.$areaname;
}
}
@@ -0,0 +1,34 @@
<?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/>.
/**
* Defines restore_local_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class extending standard restore_plugin in order to implement some
* helper methods related with local plugins
*/
abstract class restore_local_plugin extends restore_plugin {}
@@ -0,0 +1,51 @@
<?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/>.
/**
* Defines restore_plagiarism_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class extending standard restore_plugin in order to implement some
* helper methods related with the plagiarism plugins
*
* TODO: Finish phpdocs
*/
abstract class restore_plagiarism_plugin extends restore_plugin {
public function define_plugin_structure($connectionpoint) {
global $CFG;
if (!$connectionpoint instanceof restore_path_element) {
throw new restore_step_exception('restore_path_element_required', $connectionpoint);
}
//check if enabled at site level and plugin is enabled.
require_once($CFG->libdir . '/plagiarismlib.php');
$enabledplugins = plagiarism_load_available_plugins();
if (!array_key_exists($this->pluginname, $enabledplugins)) {
return array();
}
return parent::define_plugin_structure($connectionpoint);
}
}
@@ -0,0 +1,221 @@
<?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/>.
/**
* Defines restore_plan_builder class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/backup/moodle2/restore_root_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_course_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_section_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_activity_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_final_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_block_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_default_block_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_qbank_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_extrafields_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_local_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_theme_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_report_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_gradingform_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_enrol_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qbank_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_extrafields_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_local_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_report_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_gradingform_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_enrol_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');
// Load all the activity tasks for moodle2 format
$mods = core_component::get_plugin_list('mod');
foreach ($mods as $mod => $moddir) {
$taskpath = $moddir . '/backup/moodle2/restore_' . $mod . '_activity_task.class.php';
if (plugin_supports('mod', $mod, FEATURE_BACKUP_MOODLE2)) {
if (file_exists($taskpath)) {
require_once($taskpath);
}
}
}
// Load all the block tasks for moodle2 format
$blocks = core_component::get_plugin_list('block');
foreach ($blocks as $block => $blockdir) {
$taskpath = $blockdir . '/backup/moodle2/restore_' . $block . '_block_task.class.php';
if (file_exists($taskpath)) {
require_once($taskpath);
}
}
/**
* Abstract class defining the static method in charge of building the whole
* restore plan, based in @restore_controller preferences.
*
* TODO: Finish phpdocs
*/
abstract class restore_plan_builder {
/**
* Dispatches, based on type to specialised builders
*/
public static function build_plan($controller) {
$plan = $controller->get_plan();
// Add the root task, responsible for
// preparing everything, creating the
// needed structures (users, roles),
// preloading information to temp table
// and other init tasks
$plan->add_task(new restore_root_task('root_task'));
$controller->get_progress()->progress();
switch ($controller->get_type()) {
case backup::TYPE_1ACTIVITY:
self::build_activity_plan($controller, key($controller->get_info()->activities));
break;
case backup::TYPE_1SECTION:
self::build_section_plan($controller, key($controller->get_info()->sections));
break;
case backup::TYPE_1COURSE:
self::build_course_plan($controller, $controller->get_courseid());
break;
}
// Add the final task, responsible for closing
// all the pending bits (remapings, inter-links
// conversion...)
// and perform other various final actions.
$plan->add_task(new restore_final_task('final_task'));
$controller->get_progress()->progress();
}
// Protected API starts here
/**
* Restore one 1-activity backup
*/
protected static function build_activity_plan($controller, $activityid) {
$plan = $controller->get_plan();
$info = $controller->get_info();
$infoactivity = $info->activities[$activityid];
// Add the activity task, responsible for restoring
// all the module related information. So it conditionally
// as far as the module can be missing on restore
if ($task = restore_factory::get_restore_activity_task($infoactivity)) { // can be missing
$plan->add_task($task);
$controller->get_progress()->progress();
// For the given activity path, add as many block tasks as necessary
// TODO: Add blocks, we need to introspect xml here
$blocks = backup_general_helper::get_blocks_from_path($task->get_taskbasepath());
foreach ($blocks as $basepath => $name) {
if ($task = restore_factory::get_restore_block_task($name, $basepath)) {
$plan->add_task($task);
$controller->get_progress()->progress();
} else {
// TODO: Debug information about block not supported
}
}
} else { // Activity is missing in target site, inform plan about that
$plan->set_missing_modules();
}
}
/**
* Restore one 1-section backup
*/
protected static function build_section_plan($controller, $sectionid) {
$plan = $controller->get_plan();
$info = $controller->get_info();
$infosection = $info->sections[$sectionid];
// Add the section task, responsible for restoring
// all the section related information
$plan->add_task(restore_factory::get_restore_section_task($infosection));
$controller->get_progress()->progress();
// For the given section, add as many activity tasks as necessary
foreach ($info->activities as $activityid => $activity) {
if ($activity->sectionid != $infosection->sectionid) {
continue;
}
if (plugin_supports('mod', $activity->modulename, FEATURE_BACKUP_MOODLE2)) { // Check we support the format
self::build_activity_plan($controller, $activityid);
} else {
// TODO: Debug information about module not supported
}
}
}
/**
* Restore one 1-course backup
*/
protected static function build_course_plan($controller, $courseid) {
$plan = $controller->get_plan();
$info = $controller->get_info();
// Add the course task, responsible for restoring
// all the course related information
$task = restore_factory::get_restore_course_task($info->course, $courseid);
$plan->add_task($task);
$controller->get_progress()->progress();
// For the given course path, add as many block tasks as necessary
// TODO: Add blocks, we need to introspect xml here
$blocks = backup_general_helper::get_blocks_from_path($task->get_taskbasepath());
foreach ($blocks as $basepath => $name) {
if ($task = restore_factory::get_restore_block_task($name, $basepath)) {
$plan->add_task($task);
$controller->get_progress()->progress();
} else {
// TODO: Debug information about block not supported
}
}
// For the given course, add as many section tasks as necessary
foreach ($info->sections as $sectionid => $section) {
self::build_section_plan($controller, $sectionid);
}
}
}
+284
View File
@@ -0,0 +1,284 @@
<?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/>.
/**
* Defines restore_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class implementing the plugins support for moodle2 restore
*
* TODO: Finish phpdocs
*/
abstract class restore_plugin {
/** @var string */
protected $plugintype;
/** @var string */
protected $pluginname;
/** @var restore_path_element */
protected $connectionpoint;
/** @var restore_structure_step */
protected $step;
/** @var restore_course_task|restore_activity_task */
protected $task;
/**
* restore_plugin constructor.
*
* @param string $plugintype
* @param string $pluginname
* @param restore_structure_step $step
*/
public function __construct($plugintype, $pluginname, $step) {
$this->plugintype = $plugintype;
$this->pluginname = $pluginname;
$this->step = $step;
$this->task = $step->get_task();
$this->connectionpoint = '';
}
public function define_plugin_structure($connectionpoint) {
if (!$connectionpoint instanceof restore_path_element) {
throw new restore_step_exception('restore_path_element_required', $connectionpoint);
}
$paths = array();
$this->connectionpoint = $connectionpoint;
$methodname = 'define_' . basename($this->connectionpoint->get_path()) . '_plugin_structure';
if (method_exists($this, $methodname)) {
if ($pluginpaths = $this->$methodname()) {
foreach ($pluginpaths as $path) {
if ($path->get_processing_object() === null && !$this->step->grouped_parent_exists($path, $paths)) {
$path->set_processing_object($this);
}
$paths[] = $path;
}
}
}
return $paths;
}
/**
* after_execute dispatcher for any restore_plugin class
*
* This method will dispatch execution to the corresponding
* after_execute_xxx() method when available, with xxx
* being the connection point of the instance, so plugin
* classes with multiple connection points will support
* multiple after_execute methods, one for each connection point
*/
public function launch_after_execute_methods() {
// Check if the after_execute method exists and launch it
$afterexecute = 'after_execute_' . basename($this->connectionpoint->get_path());
if (method_exists($this, $afterexecute)) {
$this->$afterexecute();
}
}
/**
* after_restore dispatcher for any restore_plugin class
*
* This method will dispatch execution to the corresponding
* after_restore_xxx() method when available, with xxx
* being the connection point of the instance, so plugin
* classes with multiple connection points will support
* multiple after_restore methods, one for each connection point
*/
public function launch_after_restore_methods() {
// Check if the after_restore method exists and launch it
$afterrestore = 'after_restore_' . basename($this->connectionpoint->get_path());
if (method_exists($this, $afterrestore)) {
$this->$afterrestore();
}
}
/**
* Returns one array with all the decode contents
* to be processed by the links decoder
*
* This method, given one plugin type, returns one
* array of {@link restore_decode_content} objects
* that will be added to the restore decoder in order
* to perform modifications under the plugin contents.
*
* The objects are retrieved by calling to the {@link define_decode_contents}
* method (when available), first in the main restore_xxxx_plugin class
* and later on each of the available subclasses
*/
public static function get_restore_decode_contents($plugintype) {
$decodecontents = array();
// Check the requested plugintype is a valid one
if (!array_key_exists($plugintype, core_component::get_plugin_types($plugintype))) {
throw new backup_step_exception('incorrect_plugin_type', $plugintype);
}
// Check the base plugin class exists
$classname = 'restore_' . $plugintype . '_plugin';
if (!class_exists($classname)) {
throw new backup_step_exception('plugin_class_not_found', $classname);
}
// First, call to the define_plugin_decode_contents in the base plugin class
// (must exist by design in all the plugin base classes)
if (method_exists($classname, 'define_plugin_decode_contents')) {
$decodecontents = array_merge($decodecontents, call_user_func(array($classname, 'define_plugin_decode_contents')));
}
// Now, iterate over all the possible plugins available
// (only the needed ones have been loaded, so they will
// be the ones being asked here). Fetch their restore contents
// by calling (if exists) to their define_decode_contents() method
$plugins = core_component::get_plugin_list($plugintype);
foreach ($plugins as $plugin => $plugindir) {
$classname = 'restore_' . $plugintype . '_' . $plugin . '_plugin';
if (class_exists($classname)) {
if (method_exists($classname, 'define_decode_contents')) {
$decodecontents = array_merge($decodecontents, call_user_func(array($classname, 'define_decode_contents')));
}
}
}
return $decodecontents;
}
/**
* Define the contents in the plugin that must be
* processed by the link decoder
*/
public static function define_plugin_decode_contents() {
throw new coding_exception('define_plugin_decode_contents() method needs to be overridden in each subclass of restore_plugin');
}
// Protected API starts here
// restore_step/structure_step/task wrappers
protected function get_restoreid() {
if (is_null($this->task)) {
throw new restore_step_exception('not_specified_restore_task');
}
return $this->task->get_restoreid();
}
/**
* To send ids pairs to backup_ids_table and to store them into paths
*
* This method will send the given itemname and old/new ids to the
* backup_ids_temp table, and, at the same time, will save the new id
* into the corresponding restore_path_element for easier access
* by children. Also will inject the known old context id for the task
* in case it's going to be used for restoring files later
*/
protected function set_mapping($itemname, $oldid, $newid, $restorefiles = false, $filesctxid = null, $parentid = null) {
$this->step->set_mapping($itemname, $oldid, $newid, $restorefiles, $filesctxid, $parentid);
}
/**
* Returns the latest (parent) old id mapped by one pathelement
*/
protected function get_old_parentid($itemname) {
return $this->step->get_old_parentid($itemname);
}
/**
* Returns the latest (parent) new id mapped by one pathelement
*/
protected function get_new_parentid($itemname) {
return $this->step->get_new_parentid($itemname);
}
/**
* Return the new id of a mapping for the given itemname
*
* @param string $itemname the type of item
* @param int $oldid the item ID from the backup
* @param mixed $ifnotfound what to return if $oldid wasnt found. Defaults to false
*/
protected function get_mappingid($itemname, $oldid, $ifnotfound = false) {
return $this->step->get_mappingid($itemname, $oldid, $ifnotfound);
}
/**
* Return the complete mapping from the given itemname, itemid
*/
protected function get_mapping($itemname, $oldid) {
return $this->step->get_mapping($itemname, $oldid);
}
/**
* Add all the existing file, given their component and filearea and one backup_ids itemname to match with
*/
protected function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null, $olditemid = null) {
$this->step->add_related_files($component, $filearea, $mappingitemname, $filesctxid, $olditemid);
}
/**
* Apply course startdate offset based in original course startdate and course_offset_startdate setting
* Note we are using one static cache here, but *by restoreid*, so it's ok for concurrence/multiple
* executions in the same request
*/
protected function apply_date_offset($value) {
return $this->step->apply_date_offset($value);
}
/**
* 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 ;-)
*/
protected function get_namefor($name = '') {
$name = $name !== '' ? '_' . $name : '';
return $this->plugintype . '_' . $this->pluginname . $name;
}
/**
* Simple helper function that returns the base (prefix) of the path for the restore_path_element
* Useful if we used get_recommended_name() in backup. It's not mandatory to use it but recommended ;-)
*/
protected function get_pathfor($path = '') {
$path = trim($path, '/') !== '' ? '/' . trim($path, '/') : '';
return $this->connectionpoint->get_path() . '/' .
'plugin_' . $this->plugintype . '_' .
$this->pluginname . '_' . basename($this->connectionpoint->get_path()) . $path;
}
/**
* Get the task we are part of.
*
* @return restore_activity_task|restore_course_task the task.
*/
protected function get_task() {
return $this->task;
}
}
@@ -0,0 +1,39 @@
<?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/>.
/**
* Defines restore_qbank_plugin class.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Base class for qbank backup plugins.
*
* @package core_backup
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class restore_qbank_plugin extends restore_plugin {
// Use default parent behaviour.
}
@@ -0,0 +1,131 @@
<?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/>.
/**
* Defines restore_qtype_extrafields_plugin class
*
* @package core_backup
* @copyright 2012 Oleg Sychev, Volgograd State Technical University
* @author Valeriy Streltsov <vostreltsov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/question/engine/bank.php');
/**
* Class extending restore_qtype_plugin in order to use extra fields method
*
* See qtype_shortanswer for an example
*
* @copyright 2012 Oleg Sychev, Volgograd State Technical University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_qtype_extrafields_plugin extends restore_qtype_plugin {
/**
* Question type class for a particular question type
* @var question_type
*/
protected $qtypeobj;
/**
* Constructor
*
* @param string $plugintype plugin type
* @param string $pluginname plugin name
* @param restore_step $step step
*/
public function __construct($plugintype, $pluginname, $step) {
parent::__construct($plugintype, $pluginname, $step);
$this->qtypeobj = question_bank::get_qtype($this->pluginname);
}
/**
* Returns the paths to be handled by the plugin at question level.
*/
protected function define_question_plugin_structure() {
$paths = array();
// This qtype uses question_answers, add them.
$this->add_question_question_answers($paths);
// Add own qtype stuff.
$elepath = $this->get_pathfor('/' . $this->qtypeobj->name());
$paths[] = new restore_path_element($this->qtypeobj->name(), $elepath);
$elepath = $this->get_pathfor('/answers/answer/extraanswerdata');
$paths[] = new restore_path_element('extraanswerdata', $elepath);
return $paths;
}
/**
* Processes the extra answer data
*
* @param array $data extra answer data
*/
public function process_extraanswerdata($data) {
global $DB;
$extra = $this->qtypeobj->extra_answer_fields();
$tablename = array_shift($extra);
$oldquestionid = $this->get_old_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
if ($questioncreated) {
$data['answerid'] = $this->get_mappingid('question_answer', $data['id']);
$DB->insert_record($tablename, $data);
} else {
$DB->update_record($tablename, $data);
}
}
/**
* Process the qtype/... element.
*
* @param array $data question data
*/
public function really_process_extra_question_fields($data) {
global $DB;
$oldid = $data['id'];
// Detect if the question is created or mapped.
$oldquestionid = $this->get_old_parentid('question');
$newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
// If the question has been created by restore, we need to create its qtype_... too.
if ($questioncreated) {
$extraquestionfields = $this->qtypeobj->extra_question_fields();
$tablename = array_shift($extraquestionfields);
// Adjust some columns.
$qtfield = $this->qtypeobj->questionid_column_name();
$data[$qtfield] = $newquestionid;
// Insert record.
$newitemid = $DB->insert_record($tablename, $data);
// Create mapping.
$this->set_mapping($tablename, $oldid, $newitemid);
}
}
}
@@ -0,0 +1,396 @@
<?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/>.
/**
* Defines restore_qtype_plugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class extending standard restore_plugin in order to implement some
* helper methods related with the questions (qtype plugin)
*
* TODO: Finish phpdocs
*/
abstract class restore_qtype_plugin extends restore_plugin {
/*
* A simple answer to id cache for a single questions answers.
* @var array
*/
private $questionanswercache = array();
/*
* The id of the current question in the questionanswercache.
* @var int
*/
private $questionanswercacheid = null;
/**
* Add to $paths the restore_path_elements needed
* to handle question_answers for a given question
* Used by various qtypes (calculated, essay, multianswer,
* multichoice, numerical, shortanswer, truefalse)
*/
protected function add_question_question_answers(&$paths) {
// Check $paths is one array
if (!is_array($paths)) {
throw new restore_step_exception('paths_must_be_array', $paths);
}
$elename = 'question_answer';
$elepath = $this->get_pathfor('/answers/answer'); // we used get_recommended_name() so this works
$paths[] = new restore_path_element($elename, $elepath);
}
/**
* Add to $paths the restore_path_elements needed
* to handle question_numerical_units for a given question
* Used by various qtypes (calculated, numerical)
*/
protected function add_question_numerical_units(&$paths) {
// Check $paths is one array
if (!is_array($paths)) {
throw new restore_step_exception('paths_must_be_array', $paths);
}
$elename = 'question_numerical_unit';
$elepath = $this->get_pathfor('/numerical_units/numerical_unit'); // we used get_recommended_name() so this works
$paths[] = new restore_path_element($elename, $elepath);
}
/**
* Add to $paths the restore_path_elements needed
* to handle question_numerical_options for a given question
* Used by various qtypes (calculated, numerical)
*/
protected function add_question_numerical_options(&$paths) {
// Check $paths is one array
if (!is_array($paths)) {
throw new restore_step_exception('paths_must_be_array', $paths);
}
$elename = 'question_numerical_option';
$elepath = $this->get_pathfor('/numerical_options/numerical_option'); // we used get_recommended_name() so this works
$paths[] = new restore_path_element($elename, $elepath);
}
/**
* Add to $paths the restore_path_elements needed
* to handle question_datasets (defs and items) for a given question
* Used by various qtypes (calculated, numerical)
*/
protected function add_question_datasets(&$paths) {
// Check $paths is one array
if (!is_array($paths)) {
throw new restore_step_exception('paths_must_be_array', $paths);
}
$elename = 'question_dataset_definition';
$elepath = $this->get_pathfor('/dataset_definitions/dataset_definition'); // we used get_recommended_name() so this works
$paths[] = new restore_path_element($elename, $elepath);
$elename = 'question_dataset_item';
$elepath = $this->get_pathfor('/dataset_definitions/dataset_definition/dataset_items/dataset_item');
$paths[] = new restore_path_element($elename, $elepath);
}
/**
* Processes the answer element (question answers). Common for various qtypes.
* It handles both creation (if the question is being created) and mapping
* (if the question already existed and is being reused)
*/
public function process_question_answer($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
// Detect if the question is created or mapped
$oldquestionid = $this->get_old_parentid('question');
$newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
// In the past, there were some sloppily rounded fractions around. Fix them up.
$changes = array(
'-0.66666' => '-0.6666667',
'-0.33333' => '-0.3333333',
'-0.16666' => '-0.1666667',
'-0.142857' => '-0.1428571',
'0.11111' => '0.1111111',
'0.142857' => '0.1428571',
'0.16666' => '0.1666667',
'0.33333' => '0.3333333',
'0.333333' => '0.3333333',
'0.66666' => '0.6666667',
);
if (array_key_exists($data->fraction, $changes)) {
$data->fraction = $changes[$data->fraction];
}
// If the question has been created by restore, we need to create its question_answers too
if ($questioncreated) {
// Adjust some columns
$data->question = $newquestionid;
$data->answer = $data->answertext;
// Insert record
$newitemid = $DB->insert_record('question_answers', $data);
// The question existed, we need to map the existing question_answers
} else {
// Have we cached the current question?
if ($this->questionanswercacheid !== $newquestionid) {
// The question changed, purge and start again!
$this->questionanswercache = array();
$params = array('question' => $newquestionid);
$answers = $DB->get_records('question_answers', $params, '', 'id, answer');
$this->questionanswercacheid = $newquestionid;
// Cache all cleaned answers for a simple text match.
foreach ($answers as $answer) {
// MDL-30018: Clean in the same way as {@link xml_writer::xml_safe_utf8()}.
$clean = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $answer->answer); // Clean CTRL chars.
$clean = preg_replace("/\r\n|\r/", "\n", $clean); // Normalize line ending.
$this->questionanswercache[$clean] = $answer->id;
}
}
$rules = restore_course_task::define_decode_rules();
$rulesactivity = restore_quiz_activity_task::define_decode_rules();
$rules = array_merge($rules, $rulesactivity);
$decoder = $this->task->get_decoder();
foreach ($rules as $rule) {
$decoder->add_rule($rule);
}
$contentdecoded = $decoder->decode_content($data->answertext);
if ($contentdecoded) {
$data->answertext = $contentdecoded;
}
if (!isset($this->questionanswercache[$data->answertext])) {
// If we haven't found the matching answer, something has gone really wrong, the question in the DB
// is missing answers, throw an exception.
$info = new stdClass();
$info->filequestionid = $oldquestionid;
$info->dbquestionid = $newquestionid;
$info->answer = s($data->answertext);
throw new restore_step_exception('error_question_answers_missing_in_db', $info);
}
$newitemid = $this->questionanswercache[$data->answertext];
}
// Create mapping (we'll use this intensively when restoring question_states. And also answerfeedback files)
$this->set_mapping('question_answer', $oldid, $newitemid);
}
/**
* Processes the numerical_unit element (question numerical units). Common for various qtypes.
* It handles both creation (if the question is being created) and mapping
* (if the question already existed and is being reused)
*/
public function process_question_numerical_unit($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
// Detect if the question is created or mapped
$oldquestionid = $this->get_old_parentid('question');
$newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
// If the question has been created by restore, we need to create its question_numerical_units too
if ($questioncreated) {
// Adjust some columns
$data->question = $newquestionid;
// Insert record
$newitemid = $DB->insert_record('question_numerical_units', $data);
}
}
/**
* Processes the numerical_option element (question numerical options). Common for various qtypes.
* It handles both creation (if the question is being created) and mapping
* (if the question already existed and is being reused)
*/
public function process_question_numerical_option($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
// Detect if the question is created or mapped
$oldquestionid = $this->get_old_parentid('question');
$newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
// If the question has been created by restore, we need to create its question_numerical_options too
if ($questioncreated) {
// Adjust some columns
$data->question = $newquestionid;
// Insert record
$newitemid = $DB->insert_record('question_numerical_options', $data);
// Create mapping (not needed, no files nor childs nor states here)
//$this->set_mapping('question_numerical_option', $oldid, $newitemid);
}
}
/**
* Processes the dataset_definition element (question dataset definitions). Common for various qtypes.
* It handles both creation (if the question is being created) and mapping
* (if the question already existed and is being reused)
*/
public function process_question_dataset_definition($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
// Detect if the question is created or mapped
$oldquestionid = $this->get_old_parentid('question');
$newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
// If the question is mapped, nothing to do
if (!$questioncreated) {
return;
}
// Arrived here, let's see if the question_dataset_definition already exists in category or no
// (by category, name, type and enough items). Only for "shared" definitions (category != 0).
// If exists, reuse it, else, create it as "not shared" (category = 0)
$data->category = $this->get_mappingid('question_category', $data->category);
// If category is shared, look for definitions
$founddefid = null;
if ($data->category) {
$candidatedefs = $DB->get_records_sql("SELECT id, itemcount
FROM {question_dataset_definitions}
WHERE category = ?
AND name = ?
AND type = ?", array($data->category, $data->name, $data->type));
foreach ($candidatedefs as $candidatedef) {
if ($candidatedef->itemcount >= $data->itemcount) { // Check it has enough items
$founddefid = $candidatedef->id;
break; // end loop, shared definition match found
}
}
// If there were candidates but none fulfilled the itemcount condition, create definition as not shared
if ($candidatedefs && !$founddefid) {
$data->category = 0;
}
}
// If haven't found any shared definition match, let's create it
if (!$founddefid) {
$newitemid = $DB->insert_record('question_dataset_definitions', $data);
// Set mapping, so dataset items will know if they must be created
$this->set_mapping('question_dataset_definition', $oldid, $newitemid);
// If we have found one shared definition match, use it
} else {
$newitemid = $founddefid;
// Set mapping to 0, so dataset items will know they don't need to be created
$this->set_mapping('question_dataset_definition', $oldid, 0);
}
// Arrived here, we have one $newitemid (create or reused). Create the question_datasets record
$questiondataset = new stdClass();
$questiondataset->question = $newquestionid;
$questiondataset->datasetdefinition = $newitemid;
$DB->insert_record('question_datasets', $questiondataset);
}
/**
* Processes the dataset_item element (question dataset items). Common for various qtypes.
* It handles both creation (if the question is being created) and mapping
* (if the question already existed and is being reused)
*/
public function process_question_dataset_item($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
// Detect if the question is created or mapped
$oldquestionid = $this->get_old_parentid('question');
$newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
// If the question is mapped, nothing to do
if (!$questioncreated) {
return;
}
// Detect if the question_dataset_definition is being created
$newdefinitionid = $this->get_new_parentid('question_dataset_definition');
// If the definition is reused, nothing to do
if (!$newdefinitionid) {
return;
}
// let's create the question_dataset_items
$data->definition = $newdefinitionid;
$data->itemnumber = $data->number;
$DB->insert_record('question_dataset_items', $data);
}
/**
* Do any re-coding necessary in the student response.
* @param int $questionid the new id of the question
* @param int $sequencenumber of the step within the qusetion attempt.
* @param array the response data from the backup.
* @return array the recoded response.
*/
public function recode_response($questionid, $sequencenumber, array $response) {
return $response;
}
/**
* Decode legacy question_states.answer for this qtype. Used when restoring
* 2.0 attempt data.
*/
public function recode_legacy_state_answer($state) {
// By default, return answer unmodified, qtypes needing recode will override this
return $state->answer;
}
/**
* Return the contents of the questions stuff that must be processed by the links decoder
*
* Only common stuff to all plugins, in this case:
* - question: text and feedback
* - question_answers: text and feedbak
*
* Note each qtype will have, if needed, its own define_decode_contents method
*/
public static function define_plugin_decode_contents() {
$contents = array();
$contents[] = new restore_decode_content('question', array('questiontext', 'generalfeedback'), 'question_created');
$contents[] = new restore_decode_content('question_answers', array('answer', 'feedback'), 'question_answer');
return $contents;
}
}
@@ -0,0 +1,30 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/**
* Restore for plugin report.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class restore_report_plugin extends restore_plugin {
// Use default parent behaviour
}
+347
View File
@@ -0,0 +1,347 @@
<?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/>.
/**
* Defines restore_root_task class
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Start task that provides all the settings common to all restores and other initial steps
*
* TODO: Finish phpdocs
*/
class restore_root_task extends restore_task {
/**
* Create all the steps that will be part of this task
*/
public function build() {
// Conditionally create the temp table (can exist from prechecks) and delete old stuff
$this->add_step(new restore_create_and_clean_temp_stuff('create_and_clean_temp_stuff'));
// Now make sure the user that is running the restore can actually access the course
// before executing any other step (potentially performing permission checks)
$this->add_step(new restore_fix_restorer_access_step('fix_restorer_access'));
// If we haven't preloaded information, load all the included inforef records to temp_ids table
$this->add_step(new restore_load_included_inforef_records('load_inforef_records'));
// Load all the needed files to temp_ids table
$this->add_step(new restore_load_included_files('load_file_records', 'files.xml'));
// If we haven't preloaded information, load all the needed roles to temp_ids_table
$this->add_step(new restore_load_and_map_roles('load_and_map_roles'));
// If we haven't preloaded information and are restoring user info, load all the needed users to temp_ids table
$this->add_step(new restore_load_included_users('load_user_records'));
// If we haven't preloaded information and are restoring user info, process all those needed users
// marking for create/map them as needed. Any problem here will cause exception as far as prechecks have
// performed the same process so, it's not possible to have errors here
$this->add_step(new restore_process_included_users('process_user_records'));
// Unconditionally, create all the needed users calculated in the previous step
$this->add_step(new restore_create_included_users('create_users'));
// Unconditionally, load create all the needed groups and groupings
$this->add_step(new restore_groups_structure_step('create_groups_and_groupings', 'groups.xml'));
// Unconditionally, load create all the needed scales
$this->add_step(new restore_scales_structure_step('create_scales', 'scales.xml'));
// Unconditionally, load create all the needed outcomes
$this->add_step(new restore_outcomes_structure_step('create_scales', 'outcomes.xml'));
// If we haven't preloaded information, load all the needed categories and questions (reduced) to temp_ids_table
$this->add_step(new restore_load_categories_and_questions('load_categories_and_questions'));
// If we haven't preloaded information, process all the loaded categories and questions
// marking them for creation/mapping as needed. Any problem here will cause exception
// because this same process has been executed and reported by restore prechecks, so
// it is not possible to have errors here.
$this->add_step(new restore_process_categories_and_questions('process_categories_and_questions'));
// Unconditionally, create and map all the categories and questions
$this->add_step(new restore_create_categories_and_questions('create_categories_and_questions', 'questions.xml'));
// At the end, mark it as built
$this->built = true;
}
// Protected API starts here
/**
* Define the common setting that any restore type will have
*/
protected function define_settings() {
// Load all the root settings found in backup file from controller
$rootsettings = $this->get_info()->root_settings;
// Define users setting (keeping it on hand to define dependencies)
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['users']) && $rootsettings['users']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$users = new restore_users_setting('users', base_setting::IS_BOOLEAN, $defaultvalue);
$users->set_ui(new backup_setting_ui_checkbox($users, get_string('rootsettingusers', 'backup')));
$users->get_ui()->set_changeable($changeable);
$this->add_setting($users);
// Restore enrolment methods.
if ($changeable) {
$options = [
backup::ENROL_NEVER => get_string('rootsettingenrolments_never', 'backup'),
backup::ENROL_WITHUSERS => get_string('rootsettingenrolments_withusers', 'backup'),
backup::ENROL_ALWAYS => get_string('rootsettingenrolments_always', 'backup'),
];
$enroldefault = backup::ENROL_WITHUSERS;
} else {
// Users can not be restored, simplify the dropdown.
$options = [
backup::ENROL_NEVER => get_string('no'),
backup::ENROL_ALWAYS => get_string('yes')
];
$enroldefault = backup::ENROL_NEVER;
}
$enrolments = new restore_users_setting('enrolments', base_setting::IS_INTEGER, $enroldefault);
$enrolments->set_ui(new backup_setting_ui_select($enrolments, get_string('rootsettingenrolments', 'backup'),
$options));
$this->add_setting($enrolments);
// Define role_assignments (dependent of users)
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['role_assignments']) && $rootsettings['role_assignments']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$roleassignments = new restore_role_assignments_setting('role_assignments', base_setting::IS_BOOLEAN, $defaultvalue);
$roleassignments->set_ui(new backup_setting_ui_checkbox($roleassignments,get_string('rootsettingroleassignments', 'backup')));
$roleassignments->get_ui()->set_changeable($changeable);
$this->add_setting($roleassignments);
$users->add_dependency($roleassignments);
// Define permissions.
$defaultvalue = false; // Safer default.
$changeable = false;
// Enable when available, or key doesn't exist (backward compatibility).
if (!array_key_exists('permissions', $rootsettings) || !empty($rootsettings['permissions'])) {
$defaultvalue = true;
$changeable = true;
}
$permissions = new restore_permissions_setting('permissions', base_setting::IS_BOOLEAN, $defaultvalue);
$permissions->set_ui(new backup_setting_ui_checkbox($permissions, get_string('rootsettingpermissions', 'backup')));
$permissions->get_ui()->set_changeable($changeable);
$this->add_setting($permissions);
// Define activitites
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['activities']) && $rootsettings['activities']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$activities = new restore_activities_setting('activities', base_setting::IS_BOOLEAN, $defaultvalue);
$activities->set_ui(new backup_setting_ui_checkbox($activities, get_string('rootsettingactivities', 'backup')));
$activities->get_ui()->set_changeable($changeable);
$this->add_setting($activities);
// Define blocks
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['blocks']) && $rootsettings['blocks']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$blocks = new restore_generic_setting('blocks', base_setting::IS_BOOLEAN, $defaultvalue);
$blocks->set_ui(new backup_setting_ui_checkbox($blocks, get_string('rootsettingblocks', 'backup')));
$blocks->get_ui()->set_changeable($changeable);
$this->add_setting($blocks);
// Define filters
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['filters']) && $rootsettings['filters']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$filters = new restore_generic_setting('filters', base_setting::IS_BOOLEAN, $defaultvalue);
$filters->set_ui(new backup_setting_ui_checkbox($filters, get_string('rootsettingfilters', 'backup')));
$filters->get_ui()->set_changeable($changeable);
$this->add_setting($filters);
// Define comments (dependent of users)
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['comments']) && $rootsettings['comments']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$comments = new restore_comments_setting('comments', base_setting::IS_BOOLEAN, $defaultvalue);
$comments->set_ui(new backup_setting_ui_checkbox($comments, get_string('rootsettingcomments', 'backup')));
$comments->get_ui()->set_changeable($changeable);
$this->add_setting($comments);
$users->add_dependency($comments);
// Define badges (dependent of activities).
$defaultvalue = false; // Safer default.
$changeable = false;
if (isset($rootsettings['badges']) && $rootsettings['badges']) { // Only enabled when available.
$defaultvalue = true;
$changeable = true;
}
$badges = new restore_badges_setting('badges', base_setting::IS_BOOLEAN, $defaultvalue);
$badges->set_ui(new backup_setting_ui_checkbox($badges, get_string('rootsettingbadges', 'backup')));
$badges->get_ui()->set_changeable($changeable);
$this->add_setting($badges);
$activities->add_dependency($badges);
$users->add_dependency($badges);
// Define Calendar events.
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['calendarevents']) && $rootsettings['calendarevents']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$events = new restore_calendarevents_setting('calendarevents', base_setting::IS_BOOLEAN, $defaultvalue);
$events->set_ui(new backup_setting_ui_checkbox($events, get_string('rootsettingcalendarevents', 'backup')));
$events->get_ui()->set_changeable($changeable);
$this->add_setting($events);
// Define completion (dependent of users)
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['userscompletion']) && $rootsettings['userscompletion']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$completion = new restore_userscompletion_setting('userscompletion', base_setting::IS_BOOLEAN, $defaultvalue);
$completion->set_ui(new backup_setting_ui_checkbox($completion, get_string('rootsettinguserscompletion', 'backup')));
$completion->get_ui()->set_changeable($changeable);
$this->add_setting($completion);
$users->add_dependency($completion);
// Define logs (dependent of users)
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['logs']) && $rootsettings['logs']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$logs = new restore_logs_setting('logs', base_setting::IS_BOOLEAN, $defaultvalue);
$logs->set_ui(new backup_setting_ui_checkbox($logs, get_string('rootsettinglogs', 'backup')));
$logs->get_ui()->set_changeable($changeable);
$this->add_setting($logs);
$users->add_dependency($logs);
// Define grade_histories (dependent of users)
$defaultvalue = false; // Safer default
$changeable = false;
if (isset($rootsettings['grade_histories']) && $rootsettings['grade_histories']) { // Only enabled when available
$defaultvalue = true;
$changeable = true;
}
$gradehistories = new restore_grade_histories_setting('grade_histories', base_setting::IS_BOOLEAN, $defaultvalue);
$gradehistories->set_ui(new backup_setting_ui_checkbox($gradehistories, get_string('rootsettinggradehistories', 'backup')));
$gradehistories->get_ui()->set_changeable($changeable);
$this->add_setting($gradehistories);
$users->add_dependency($gradehistories);
// The restore does not process the grade histories when some activities are ignored.
// So let's define a dependency to prevent false expectations from our users.
$activities->add_dependency($gradehistories);
// Define groups and groupings.
$defaultvalue = false;
$changeable = false;
if (isset($rootsettings['groups']) && $rootsettings['groups']) { // Only enabled when available.
$defaultvalue = true;
$changeable = true;
} else if (!isset($rootsettings['groups'])) {
// It is likely this is an older backup that does not contain information on the group setting,
// in which case groups should be restored and this setting can be changed.
$defaultvalue = true;
$changeable = true;
}
$groups = new restore_groups_setting('groups', base_setting::IS_BOOLEAN, $defaultvalue);
$groups->set_ui(new backup_setting_ui_checkbox($groups, get_string('rootsettinggroups', 'backup')));
$groups->get_ui()->set_changeable($changeable);
$this->add_setting($groups);
// Competencies restore setting. Show when competencies is enabled and the setting is available.
$hascompetencies = !empty($rootsettings['competencies']);
$competencies = new restore_competencies_setting($hascompetencies);
$competencies->set_ui(new backup_setting_ui_checkbox($competencies, get_string('rootsettingcompetencies', 'backup')));
$this->add_setting($competencies);
// Custom fields.
$defaultvalue = false;
$changeable = false;
if (isset($rootsettings['customfield']) && $rootsettings['customfield']) { // Only enabled when available.
$defaultvalue = true;
$changeable = true;
}
$customfields = new restore_customfield_setting('customfield', base_setting::IS_BOOLEAN, $defaultvalue);
$customfields->set_ui(new backup_setting_ui_checkbox($customfields, get_string('rootsettingcustomfield', 'backup')));
$customfields->get_ui()->set_changeable($changeable);
$this->add_setting($customfields);
// Define Content bank content.
$defaultvalue = false;
$changeable = false;
if (isset($rootsettings['contentbankcontent']) && $rootsettings['contentbankcontent']) { // Only enabled when available.
$defaultvalue = true;
$changeable = true;
}
$contents = new restore_contentbankcontent_setting('contentbankcontent', base_setting::IS_BOOLEAN, $defaultvalue);
$contents->set_ui(new backup_setting_ui_checkbox($contents, get_string('rootsettingcontentbankcontent', 'backup')));
$contents->get_ui()->set_changeable($changeable);
$this->add_setting($contents);
// Define xAPI states.
$defaultvalue = false;
$changeable = false;
if (isset($rootsettings['xapistate']) && $rootsettings['xapistate']) { // Only enabled when available.
$defaultvalue = true;
$changeable = true;
}
$xapistate = new restore_xapistate_setting('xapistate', base_setting::IS_BOOLEAN, $defaultvalue);
$xapistate->set_ui(new backup_setting_ui_checkbox($xapistate, get_string('rootsettingxapistate', 'backup')));
$xapistate->get_ui()->set_changeable($changeable);
$this->add_setting($xapistate);
// Include legacy files.
$defaultvalue = true;
$changeable = true;
$legacyfiles = new restore_generic_setting('legacyfiles', base_setting::IS_BOOLEAN, $defaultvalue);
$legacyfiles->set_ui(new backup_setting_ui_checkbox($legacyfiles, get_string('rootsettinglegacyfiles', 'backup')));
$legacyfiles->get_ui()->set_changeable($changeable);
$this->add_setting($legacyfiles);
}
}
@@ -0,0 +1,207 @@
<?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/>.
/**
* Defines restore_section_task class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* section task that provides all the properties and common steps to be performed
* when one section is being restored
*
* TODO: Finish phpdocs
*/
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);
}
/**
* Section tasks have their own directory to read files
*/
public function get_taskbasepath() {
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
*/
public function build() {
// Define the task contextid (the course one)
$this->contextid = context_course::instance($this->get_courseid())->id;
// We always try to restore as much info from sections as possible, no matter of the type
// of restore (new, existing, deleting, import...). MDL-27764
$this->add_step(new restore_section_structure_step('course_info', 'section.xml'));
// At the end, mark it as built
$this->built = true;
}
/**
* Exceptionally override the execute method, so, based in the section_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 = 'section_' . $this->info->sectionid . '_' . $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);
}
}
/**
* Define the contents in the course that must be
* processed by the link decoder
*/
public static function define_decode_contents() {
$contents = array();
$contents[] = new restore_decode_content('course_sections', 'summary', 'course_section');
return $contents;
}
/**
* Define the decoding rules for links belonging
* to the sections to be executed by the link decoder
*/
public static function define_decode_rules() {
return array();
}
// Protected API starts here
/**
* Define the common setting that any restore section will have
*/
protected function define_settings() {
// All the settings related to this activity will include this prefix
$settingprefix = 'section_' . $this->info->sectionid . '_';
// All these are common settings to be shared by all sections
// Define section_included (to decide if the whole task must be really executed)
$settingname = $settingprefix . 'included';
$section_included = new restore_section_included_setting($settingname, base_setting::IS_BOOLEAN, true);
if (is_number($this->info->title)) {
$label = get_string('includesection', 'backup', $this->info->title);
} elseif (empty($this->info->title)) { // Don't throw error if title is empty, gracefully continue restore.
$this->log('Section title missing in backup for section id '.$this->info->sectionid, backup::LOG_WARNING, $this->name);
$label = get_string('unnamedsection', 'backup');
} else {
$label = $this->info->title;
}
$section_included->get_ui()->set_label($label);
$this->add_setting($section_included);
// Define section_userinfo. Dependent of:
// - users root setting
// - section_included setting.
$settingname = $settingprefix . 'userinfo';
$defaultvalue = false;
if (isset($this->info->settings[$settingname]) && $this->info->settings[$settingname]) { // Only enabled when available
$defaultvalue = true;
}
$section_userinfo = new restore_section_userinfo_setting($settingname, base_setting::IS_BOOLEAN, $defaultvalue);
if (!$defaultvalue) {
// This is a bit hacky, but if there is no user data to restore, then
// we replace the standard check-box with a select menu with the
// single choice 'No', and the select menu is clever enough that if
// there is only one choice, it just displays a static string.
//
// It would probably be better design to have a special UI class
// setting_ui_checkbox_or_no, rather than this hack, but I am not
// going to do that today.
$section_userinfo->set_ui(new backup_setting_ui_select($section_userinfo, get_string('includeuserinfo','backup'),
array(0 => get_string('no'))));
} else {
$section_userinfo->get_ui()->set_label(get_string('includeuserinfo','backup'));
}
$this->add_setting($section_userinfo);
// Look for "users" root setting.
$users = $this->plan->get_setting('users');
$users->add_dependency($section_userinfo);
// Look for "section_included" section setting.
$section_included->add_dependency($section_userinfo);
}
}
+256
View File
@@ -0,0 +1,256 @@
<?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/>.
/**
* Defines classes used to handle restore settings
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// TODO: Reduce these to the minimum because ui/dependencies are 100% separated
// Root restore settings
/**
* root generic setting to store different things without dependencies
*/
class restore_generic_setting extends root_backup_setting {}
/**
* root setting to control if restore will create user information
* A lot of other settings are dependent of this (module's user info,
* grades user info, messages, blogs...
*/
class restore_users_setting extends restore_generic_setting {}
/**
* root setting to control if restore will create override permission information by roles
*/
class restore_permissions_setting extends restore_generic_setting {
}
/**
* root setting to control if restore will create groups/grouping information. Depends on @restore_users_setting
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright 2014 Matt Sammarco
*/
class restore_groups_setting extends restore_generic_setting {
}
/**
* root setting to control if restore will include custom field information
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright 2018 Daniel Neis Araujo
*/
class restore_customfield_setting extends restore_generic_setting {
}
/**
* root setting to control if restore will create role assignments
* or no (any level), depends of @restore_users_setting
*/
class restore_role_assignments_setting extends root_backup_setting {}
/**
* root setting to control if restore will create activities
* A lot of other settings (_included at activity levels)
* are dependent of this setting
*/
class restore_activities_setting extends restore_generic_setting {}
/**
* root setting to control if restore will create
* comments or no, depends of @restore_users_setting
* exactly in the same way than @restore_role_assignments_setting so we extend from it
*/
class restore_comments_setting extends restore_role_assignments_setting {}
/**
* root setting to control if restore will create badges or not,
* depends on @restore_activities_setting
*/
class restore_badges_setting extends restore_generic_setting {}
/**
* root setting to control if competencies will also be restored.
*/
class restore_competencies_setting extends restore_generic_setting {
/**
* restore_competencies_setting constructor.
* @param bool $hascompetencies Flag whether to set the restore setting as checked and unlocked.
*/
public function __construct($hascompetencies) {
$defaultvalue = false;
$visibility = base_setting::HIDDEN;
$status = base_setting::LOCKED_BY_CONFIG;
if (\core_competency\api::is_enabled()) {
$visibility = base_setting::VISIBLE;
if ($hascompetencies) {
$defaultvalue = true;
$status = base_setting::NOT_LOCKED;
}
}
parent::__construct('competencies', base_setting::IS_BOOLEAN, $defaultvalue, $visibility, $status);
}
}
/**
* root setting to control if restore will create
* events or no, depends of @restore_users_setting
* exactly in the same way than @restore_role_assignments_setting so we extend from it
*/
class restore_calendarevents_setting extends restore_role_assignments_setting {}
/**
* root setting to control if restore will create
* completion info or no, depends of @restore_users_setting
* exactly in the same way than @restore_role_assignments_setting so we extend from it
*/
class restore_userscompletion_setting extends restore_role_assignments_setting {}
/**
* root setting to control if restore will create
* logs or no, depends of @restore_users_setting
* exactly in the same way than @restore_role_assignments_setting so we extend from it
*/
class restore_logs_setting extends restore_role_assignments_setting {}
/**
* root setting to control if restore will create
* grade_histories or no, depends of @restore_users_setting
* exactly in the same way than @restore_role_assignments_setting so we extend from it
*/
class restore_grade_histories_setting extends restore_role_assignments_setting {}
// Course restore settings
/**
* generic course setting to pass various settings between tasks and steps
*/
class restore_course_generic_setting extends course_backup_setting {}
/**
* Setting to define is we are going to overwrite course configuration
*/
class restore_course_overwrite_conf_setting extends restore_course_generic_setting {}
/**
* Setting to switch between current and new course name/startdate
*
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_course_defaultcustom_setting extends restore_course_generic_setting {
/**
* Validates that the value $value has type $vtype
* @param int $vtype
* @param mixed $value
* @return mixed
*/
public function validate_value($vtype, $value) {
if ($value === false) {
// Value "false" means default and is allowed for this setting type even if it does not match $vtype.
return $value;
}
return parent::validate_value($vtype, $value);
}
/**
* Special method for this element only. When value is "false" returns the default value.
* @return mixed
*/
public function get_normalized_value() {
$value = $this->get_value();
if ($value === false && $this->get_ui() instanceof backup_setting_ui_defaultcustom) {
$attributes = $this->get_ui()->get_attributes();
return $attributes['defaultvalue'];
}
return $value;
}
}
class restore_course_generic_text_setting extends restore_course_generic_setting {
public function __construct($name, $vtype, $value = null, $visibility = self::VISIBLE, $status = self::NOT_LOCKED) {
parent::__construct($name, $vtype, $value, $visibility, $status);
$this->set_ui(new backup_setting_ui_text($this, $name));
}
}
// Section restore settings
/**
* generic section setting to pass various settings between tasks and steps
*/
class restore_section_generic_setting extends section_backup_setting {}
/**
* Setting to define if one section is included or no. Activities _included
* settings depend of them if available
*/
class restore_section_included_setting extends restore_section_generic_setting {}
/**
* section backup setting to control if section will include
* user information or no, depends of @restore_users_setting
*/
class restore_section_userinfo_setting extends restore_section_generic_setting {}
// Activity backup settings
/**
* generic activity setting to pass various settings between tasks and steps
*/
class restore_activity_generic_setting extends activity_backup_setting {}
/**
* activity backup setting to control if activity will
* be included or no, depends of @restore_activities_setting and
* optionally parent section included setting
*/
class restore_activity_included_setting extends restore_activity_generic_setting {}
/**
* activity backup setting to control if activity will include
* user information or no, depends of @restore_users_setting
*/
class restore_activity_userinfo_setting extends restore_activity_generic_setting {}
/**
* root setting to control if restore will create content bank content or no
*/
class restore_contentbankcontent_setting extends restore_generic_setting {
}
/**
* Root setting to control if restore will create xAPI states or not.
*/
class restore_xapistate_setting extends restore_generic_setting {
}
File diff suppressed because it is too large Load Diff
+222
View File
@@ -0,0 +1,222 @@
<?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/>.
/**
* Defines restore_subplugin class
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class implementing the subplugins support for moodle2 restore
*
* TODO: Finish phpdocs
* TODO: Make this subclass of restore_plugin
* TODO: Add support for declaring decode_contents (not decode_rules)
*/
abstract class restore_subplugin {
/** @var string */
protected $subplugintype;
/** @var string */
protected $subpluginname;
/** @var restore_path_element */
protected $connectionpoint;
/** @var restore_step */
protected $step;
/** @var restore_task */
protected $task;
public function __construct($subplugintype, $subpluginname, $step) {
$this->subplugintype = $subplugintype;
$this->subpluginname = $subpluginname;
$this->step = $step;
$this->task = $step->get_task();
$this->connectionpoint = '';
}
public function define_subplugin_structure($connectionpoint) {
if (!$connectionpoint instanceof restore_path_element) {
throw new restore_step_exception('restore_path_element_required', $connectionpoint);
}
$paths = array();
$this->connectionpoint = $connectionpoint;
$methodname = 'define_' . basename($this->connectionpoint->get_path()) . '_subplugin_structure';
if (method_exists($this, $methodname)) {
if ($subbluginpaths = $this->$methodname()) {
foreach ($subbluginpaths as $path) {
$path->set_processing_object($this);
$paths[] = $path;
}
}
}
return $paths;
}
/**
* after_execute dispatcher for any restore_subplugin class
*
* This method will dispatch execution to the corresponding
* after_execute_xxx() method when available, with xxx
* being the connection point of the instance, so subplugin
* classes with multiple connection points will support
* multiple after_execute methods, one for each connection point
*/
public function launch_after_execute_methods() {
// Check if the after_execute method exists and launch it
$afterexecute = 'after_execute_' . basename($this->connectionpoint->get_path());
if (method_exists($this, $afterexecute)) {
$this->$afterexecute();
}
}
/**
* The after_restore dispatcher for any restore_subplugin class.
*
* This method will dispatch execution to the corresponding
* after_restore_xxx() method when available, with xxx
* being the connection point of the instance, so subplugin
* classes with multiple connection points will support
* multiple after_restore methods, one for each connection point.
*/
public function launch_after_restore_methods() {
// Check if the after_restore method exists and launch it.
$afterestore = 'after_restore_' . basename($this->connectionpoint->get_path());
if (method_exists($this, $afterestore)) {
$this->$afterestore();
}
}
// Protected API starts here
// restore_step/structure_step/task wrappers
protected function get_restoreid() {
if (is_null($this->task)) {
throw new restore_step_exception('not_specified_restore_task');
}
return $this->task->get_restoreid();
}
/**
* To send ids pairs to backup_ids_table and to store them into paths
*
* This method will send the given itemname and old/new ids to the
* backup_ids_temp table, and, at the same time, will save the new id
* into the corresponding restore_path_element for easier access
* by children. Also will inject the known old context id for the task
* in case it's going to be used for restoring files later
*/
protected function set_mapping($itemname, $oldid, $newid, $restorefiles = false, $filesctxid = null, $parentid = null) {
$this->step->set_mapping($itemname, $oldid, $newid, $restorefiles, $filesctxid, $parentid);
}
/**
* Returns the latest (parent) old id mapped by one pathelement
*/
protected function get_old_parentid($itemname) {
return $this->step->get_old_parentid($itemname);
}
/**
* Returns the latest (parent) new id mapped by one pathelement
*/
protected function get_new_parentid($itemname) {
return $this->step->get_new_parentid($itemname);
}
/**
* Return the new id of a mapping for the given itemname
*
* @param string $itemname the type of item
* @param int $oldid the item ID from the backup
* @param mixed $ifnotfound what to return if $oldid wasnt found. Defaults to false
*/
protected function get_mappingid($itemname, $oldid, $ifnotfound = false) {
return $this->step->get_mappingid($itemname, $oldid, $ifnotfound);
}
/**
* Return the complete mapping from the given itemname, itemid
*/
protected function get_mapping($itemname, $oldid) {
return $this->step->get_mapping($itemname, $oldid);
}
/**
* Add all the existing file, given their component and filearea and one backup_ids itemname to match with
*/
protected function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null, $olditemid = null) {
$this->step->add_related_files($component, $filearea, $mappingitemname, $filesctxid, $olditemid);
}
/**
* Apply course startdate offset based in original course startdate and course_offset_startdate setting
* Note we are using one static cache here, but *by restoreid*, so it's ok for concurrence/multiple
* executions in the same request
*/
protected function apply_date_offset($value) {
return $this->step->apply_date_offset($value);
}
/**
* Call the log function from the step.
*/
public function log($message, $level, $a = null, $depth = null, $display = false) {
return $this->step->log($message, $level, $a, $depth, $display);
}
/**
* 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 ;-)
*/
protected function get_namefor($name = '') {
$name = $name !== '' ? '_' . $name : '';
return $this->subplugintype . '_' . $this->subpluginname . $name;
}
/**
* Simple helper function that returns the base (prefix) of the path for the restore_path_element
* Useful if we used get_recommended_name() in backup. It's not mandatory to use it but recommended ;-)
*/
protected function get_pathfor($path = '') {
$path = trim($path, '/') !== '' ? '/' . trim($path, '/') : '';
return $this->connectionpoint->get_path() . '/' .
'subplugin_' . $this->subplugintype . '_' .
$this->subpluginname . '_' . basename($this->connectionpoint->get_path()) . $path;
}
}
@@ -0,0 +1,30 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/**
* Restore for course plugin: theme.
*
* @package core_backup
* @subpackage moodle2
* @category backup
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class restore_theme_plugin extends restore_plugin {
// Use default parent behaviour
}
@@ -0,0 +1,38 @@
<?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/>.
/**
* Admin tool restore plugin base.
*
* @package core_backup
* @subpackage moodle2
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Admin tool restore plugin base class.
*
* @package core_backup
* @subpackage moodle2
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class restore_tool_plugin extends restore_plugin {
// Use default parent behaviour.
}
@@ -0,0 +1,121 @@
<?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/>.
namespace core_backup;
use backup;
use base_element_struct_exception;
use encrypted_final_element;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_custom_fields.php');
/**
* Tests for the handling of encrypted contents in backup and restore.
*
* @package core_backup
* @copyright 2016 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_encrypted_content_test extends \advanced_testcase {
public function setUp(): void {
if (!function_exists('openssl_encrypt')) {
$this->markTestSkipped('OpenSSL extension is not loaded.');
} else if (!function_exists('hash_hmac')) {
$this->markTestSkipped('Hash extension is not loaded.');
} else if (!in_array(backup::CIPHER, openssl_get_cipher_methods())) {
$this->markTestSkipped('Expected cipher not available: ' . backup::CIPHER);
}
}
public function test_encrypted_final_element(): void {
$this->resetAfterTest(true);
// Some basic verifications.
$efe = new encrypted_final_element('test', array('encrypted'));
$this->assertInstanceOf('encrypted_final_element', $efe);
$this->assertSame('test', $efe->get_name());
$atts = $efe->get_attributes();
$this->assertCount(1, $atts);
$att = reset($atts);
$this->assertInstanceOf('backup_attribute', $att);
$this->assertSame('encrypted', $att->get_name());
// Using a manually defined (incorrect length) key.
$efe = new encrypted_final_element('test', array('encrypted'));
$key = 'this_in_not_correct_32_byte_key';
try {
set_config('backup_encryptkey', base64_encode($key), 'backup');
$efe->set_value('tiny_secret');
$this->fail('Expecting base_element_struct_exception exception, none happened');
} catch (\Exception $e) {
$this->assertInstanceOf('base_element_struct_exception', $e);
$this->assertEquals('encrypted_final_element incorrect key length', $e->errorcode);
}
// Using a manually defined (correct length) key.
$efe = new encrypted_final_element('test', array('testattr', 'encrypted'));
$key = hash('md5', 'Moodle rocks and this is not secure key, who cares, it is a test');
set_config('backup_encryptkey', base64_encode($key), 'backup');
$this->assertEmpty($efe->get_value());
$secret = 'This is a secret message that nobody else will be able to read but me 💩 ';
$efe->set_value($secret);
$atts = $efe->get_attributes();
$this->assertCount(2, $atts);
$this->assertArrayHasKey('encrypted', $atts); // We added it explicitly.
$this->assertTrue($atts['encrypted']->is_set());
$this->assertSame('true', $atts['encrypted']->get_value());
$this->assertNotEmpty($efe->get_value());
$this->assertTrue($efe->is_set());
// Get the crypted content and decrypt it manually.
$ctext = $efe->get_value();
$hmaclen = 32; // SHA256 is 32 bytes.
$ivlen = openssl_cipher_iv_length(backup::CIPHER);
list($hmac, $iv, $text) = array_values(unpack("a{$hmaclen}hmac/a{$ivlen}iv/a*text", base64_decode($ctext)));
$this->assertSame(hash_hmac('sha256', $iv . $text, $key, true), $hmac);
$this->assertSame($secret, openssl_decrypt($text, backup::CIPHER, $key, OPENSSL_RAW_DATA, $iv));
// Using the default site-generated key.
$efe = new encrypted_final_element('test', array('testattr'));
$this->assertEmpty($efe->get_value());
$secret = 'This is a secret message that nobody else will be able to read but me 💩 ';
$efe->set_value($secret);
$atts = $efe->get_attributes();
$this->assertCount(2, $atts);
$this->assertArrayHasKey('encrypted', $atts); // Was added automatcally, we did not specify it.
$this->assertTrue($atts['encrypted']->is_set());
$this->assertSame('true', $atts['encrypted']->get_value());
$this->assertNotEmpty($efe->get_value());
$this->assertTrue($efe->is_set());
// Get the crypted content and decrypt it manually.
$ctext = $efe->get_value();
$hmaclen = 32; // SHA256 is 32 bytes.
$ivlen = openssl_cipher_iv_length(backup::CIPHER);
list($hmac, $iv, $text) = array_values(unpack("a{$hmaclen}hmac/a{$ivlen}iv/a*text", base64_decode($ctext)));
$key = base64_decode(get_config('backup', 'backup_encryptkey'));
$this->assertSame(hash_hmac('sha256', $iv . $text, $key, true), $hmac);
$this->assertSame($secret, openssl_decrypt($text, backup::CIPHER, $key, OPENSSL_RAW_DATA, $iv));
}
}
@@ -0,0 +1,92 @@
<?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/>.
namespace core_backup;
use backup;
use backup_controller;
use backup_section_structure_step;
use backup_section_task;
/**
* Tests for Moodle 2 steplib classes.
*
* @package core_backup
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_stepslib_test extends \advanced_testcase {
/**
* Setup to include all libraries.
*/
public static function setUpBeforeClass(): void {
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_stepslib.php');
}
/**
* Test for the section structure step included elements.
*
* @covers \backup_section_structure_step::define_structure
*/
public function test_backup_section_structure_step(): void {
global $USER;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course(['numsections' => 3, 'format' => 'topics']);
$this->setAdminUser();
$step = new backup_section_structure_step('section_commons', 'section.xml');
// The backup_section_structure_step requires a complex dependency sequence
// but it does not have an easy dependency injection system.
// We create a real backup plan to get the task dependency sequence ready.
$bc = new backup_controller(
backup::TYPE_1COURSE,
$course->id,
backup::FORMAT_MOODLE,
backup::INTERACTIVE_NO,
backup::MODE_IMPORT,
$USER->id);
$tasks = $bc->get_plan()->get_tasks();
foreach ($tasks as $task) {
// We need only the task to backup section 1.
if ($task instanceof backup_section_task && $task->get_name() == "1") {
$task->add_step($step);
break;
}
}
$reflection = new \ReflectionClass($step);
$method = $reflection->getMethod('define_structure');
$structure = $method->invoke($step);
$bc->destroy();
$elements = $structure->get_final_elements();
$this->assertArrayHasKey('number', $elements);
$this->assertArrayHasKey('name', $elements);
$this->assertArrayHasKey('summary', $elements);
$this->assertArrayHasKey('summaryformat', $elements);
$this->assertArrayHasKey('sequence', $elements);
$this->assertArrayHasKey('visible', $elements);
$this->assertArrayHasKey('availabilityjson', $elements);
$this->assertArrayHasKey('component', $elements);
$this->assertArrayHasKey('itemid', $elements);
$this->assertArrayHasKey('timemodified', $elements);
}
}
@@ -0,0 +1,96 @@
<?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/>.
namespace core_backup;
use backup_xml_transformer;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plan_builder.class.php');
/**
* Tests for backup_xml_transformer.
*
* @package core_backup
* @subpackage moodle2
* @category test
* @copyright 2017 Dmitrii Metelkin (dmitriim@catalyst-au.net)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_xml_transformer_test extends \advanced_testcase {
/**
* Initial set up.
*/
public function setUp(): void {
parent::setUp();
$this->resetAfterTest(true);
}
/**
* Data provider for ::test_filephp_links_replace.
*
* @return array
*/
public function filephp_links_replace_data_provider() {
return array(
array('http://test.test/', 'http://test.test/'),
array('http://test.test/file.php/1', 'http://test.test/file.php/1'),
array('http://test.test/file.php/2/1.jpg', 'http://test.test/file.php/2/1.jpg'),
array('http://test.test/file.php/2', 'http://test.test/file.php/2'),
array('http://test.test/file.php/1/1.jpg', '$@FILEPHP@$$@SLASH@$1.jpg'),
array('http://test.test/file.php/1//1.jpg', '$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'),
array('http://test.test/file.php?file=/1', '$@FILEPHP@$'),
array('http://test.test/file.php?file=/2/1.jpg', 'http://test.test/file.php?file=/2/1.jpg'),
array('http://test.test/file.php?file=/2', 'http://test.test/file.php?file=/2'),
array('http://test.test/file.php?file=/1/1.jpg', '$@FILEPHP@$$@SLASH@$1.jpg'),
array('http://test.test/file.php?file=/1//1.jpg', '$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'),
array('http://test.test/file.php?file=%2f1', '$@FILEPHP@$'),
array('http://test.test/file.php?file=%2f2%2f1.jpg', 'http://test.test/file.php?file=%2f2%2f1.jpg'),
array('http://test.test/file.php?file=%2f2', 'http://test.test/file.php?file=%2f2'),
array('http://test.test/file.php?file=%2f1%2f1.jpg', '$@FILEPHP@$$@SLASH@$1.jpg'),
array('http://test.test/file.php?file=%2f1%2f%2f1.jpg', '$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'),
array('http://test.test/file.php?file=%2F1', '$@FILEPHP@$'),
array('http://test.test/file.php?file=%2F2%2F1.jpg', 'http://test.test/file.php?file=%2F2%2F1.jpg'),
array('http://test.test/file.php?file=%2F2', 'http://test.test/file.php?file=%2F2'),
array('http://test.test/file.php?file=%2F1%2F1.jpg', '$@FILEPHP@$$@SLASH@$1.jpg'),
array('http://test.test/file.php?file=%2F1%2F%2F1.jpg', '$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'),
array('http://test.test/h5p/embed.php?url=testurl', '$@H5PEMBED@$?url=testurl'),
);
}
/**
* Test that backup_xml_transformer replaces file php links to $@FILEPHP@$.
*
* @dataProvider filephp_links_replace_data_provider
* @param string $content Testing content.
* @param string $expected Expected result.
*/
public function test_filephp_links_replace($content, $expected): void {
global $CFG;
$CFG->wwwroot = 'http://test.test';
$transformer = new backup_xml_transformer(1);
$this->assertEquals($expected, $transformer->process($content));
}
}
@@ -0,0 +1,26 @@
@core @core_backup
Feature: Backup and restore of the question that was tagged
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following config values are set as admin:
| enableasyncbackup | 0 |
@javascript @_file_upload
Scenario: Restore the quiz containing the question that was tagged
Given I am on the "Course 1" "restore" page logged in as "admin"
And I press "Manage course backups"
And I upload "backup/moodle2/tests/fixtures/test_tags_backup.mbz" file to "Files" filemanager
And I press "Save changes"
And I restore "test_tags_backup.mbz" backup into a new course using this options:
| Schema | Course name | Course 2 |
| Schema | Course short name | C2 |
When I am on the "TF1" "core_question > edit" page logged in as admin
And I expand all fieldsets
Then I should see "Tag1-TF1"
And I should see "Tag2-TF1"
And I am on the "TF2" "core_question > edit" page logged in as admin
And I expand all fieldsets
And I should see "Tag1-TF2"
@@ -0,0 +1,40 @@
@core @core_backup
Feature: Import course's content's twice
In order to import content from a course more than one
As a teacher
I need to confirm that errors will not happen
Background:
Given the following config values are set as admin:
| enableglobalsearch | 1 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
| Course 2 | C2 | 0 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher1 | C2 | editingteacher |
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| online_users | Course | C1 | course-view-* | site-post |
And the following "activities" exist:
| activity | name | course | idnumber |
| quiz | Test quiz | C1 | quiz1 |
And I log in as "teacher1"
Scenario: Import course's contents to another course
Given I am on "Course 2" course homepage
And I should not see "Online users"
And I should not see "Test quiz"
And I import "Course 1" course into "Course 2" course using this options:
And I am on "Course 2" course homepage
And I should see "Online users"
And I should see "Test quiz"
When I import "Course 1" course into "Course 2" course using this options:
And I am on "Course 2" course homepage
Then I should see "Online users"
And I should see "Test quiz"
Binary file not shown.
@@ -0,0 +1,82 @@
<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
/**
* Various fixture course formats for backup unit tests
*
* @package core_backup
* @category test
* @copyright 2022 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com}
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/course/format/topics/lib.php');
/**
* Fixture course format with one option.
*/
class format_test_cs_options extends format_topics {
/**
* Override method format_topics::get_default_section_name to prevent PHPUnit errors related to the nonexistent
* format_test_cs_options lang file.
*
* @param \stdClass $section The section in question.
* @return string The section's name for display.
*/
public function get_default_section_name($section) {
if ($section->section == 0) {
return parent::get_default_section_name($section);
} else {
return get_string('sectionname', 'format_topics') . ' ' . $section->section;
}
}
public function section_format_options($foreditform = false) {
return array(
'numdaystocomplete' => array(
'type' => PARAM_INT,
'label' => 'Test days',
'element_type' => 'text',
'default' => 0,
),
);
}
}
/**
* Fixture course format with 2 options, 1 inherited.
*/
class format_test_cs2_options extends format_test_cs_options {
public function section_format_options($foreditform = false) {
return array(
'numdaystocomplete' => array(
'type' => PARAM_INT,
'label' => 'Test days',
'element_type' => 'text',
'default' => 0,
),
'secondparameter' => array(
'type' => PARAM_INT,
'label' => 'Test Parmater',
'element_type' => 'text',
'default' => 0,
),
) + parent::section_format_options($foreditform);
}
}
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,10 @@
<gradebook >
<attributes>
<calculations_freeze>20160511</calculations_freeze>
</attributes>
<grade_categories>
<grade_category id="10">
<depth>1</depth>
</grade_category>
</grade_categories>
</gradebook>
@@ -0,0 +1,7 @@
<gradebook calculations_freeze="20160511">
<grade_categories>
<grade_category id="10">
<depth>1</depth>
</grade_category>
</grade_categories>
</gradebook>
@@ -0,0 +1,10 @@
<gradebook some_other_value="false" >
<attributes>
<calculations_freeze>20160511</calculations_freeze>
</attributes>
<grade_categories>
<grade_category id="10">
<depth>1</depth>
</grade_category>
</grade_categories>
</gradebook>
@@ -0,0 +1,7 @@
<gradebook some_other_value="false" calculations_freeze="20160511">
<grade_categories>
<grade_category id="10">
<depth>1</depth>
</grade_category>
</grade_categories>
</gradebook>
@@ -0,0 +1,10 @@
<gradebook some_other_value="false" and_another_value="42">
<attributes>
<calculations_freeze>20160511</calculations_freeze>
</attributes>
<grade_categories>
<grade_category id="10">
<depth>1</depth>
</grade_category>
</grade_categories>
</gradebook>
@@ -0,0 +1,7 @@
<gradebook some_other_value="false" calculations_freeze="20160511" and_another_value="42">
<grade_categories>
<grade_category id="10">
<depth>1</depth>
</grade_category>
</grade_categories>
</gradebook>
@@ -0,0 +1,7 @@
<gradebookplugin some_other_value="false" calculations_freeze="20160511" and_another_value="42">
<grade_categories>
<grade_category id="10">
<depth>1</depth>
</grade_category>
</grade_categories>
</gradebookplugin>
@@ -0,0 +1,7 @@
<gradebookplugin some_other_value="false" calculations_freeze="20160511" and_another_value="42">
<grade_categories>
<grade_category id="10">
<depth>1</depth>
</grade_category>
</grade_categories>
</gradebookplugin>
Binary file not shown.
@@ -0,0 +1,208 @@
<?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/>.
namespace core_backup;
use backup;
use backup_controller;
use restore_dbops;
use restore_controller;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->dirroot . '/course/format/topics/lib.php');
require_once($CFG->libdir . '/completionlib.php');
require_once($CFG->dirroot . '/backup/moodle2/tests/fixtures/format_test_cs_options.php');
/**
* Tests for Moodle 2 course format section_options backup operation.
*
* @package core_backup
* @copyright 2014 Russell Smith
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class moodle2_course_format_test extends \advanced_testcase {
/**
* Tests a backup and restore adds the required section option data
* when the same course format is used.
*/
public function test_course_format_options_restore(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
$this->setAdminUser();
$CFG->enableavailability = true;
$CFG->enablecompletion = true;
// Create a course with some availability data set.
$generator = $this->getDataGenerator();
$course = $generator->create_course(
array('format' => 'test_cs_options', 'numsections' => 3,
'enablecompletion' => COMPLETION_ENABLED),
array('createsections' => true));
$courseobject = \core_courseformat\base::instance($course->id);
$section = $DB->get_record('course_sections',
array('course' => $course->id, 'section' => 1), '*', MUST_EXIST);
$data = array('id' => $section->id,
'numdaystocomplete' => 2);
$courseobject->update_section_format_options($data);
// Backup and restore it.
$this->backup_and_restore($course);
$sectionoptions = $courseobject->get_format_options(1);
$this->assertArrayHasKey('numdaystocomplete', $sectionoptions);
$this->assertEquals(2, $sectionoptions['numdaystocomplete']);
}
/**
* Tests an import into the same subject successfully
* restores the options without error.
*/
public function test_course_format_options_import_myself(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
$this->setAdminUser();
$CFG->enableavailability = true;
$CFG->enablecompletion = true;
// Create a course with some availability data set.
$generator = $this->getDataGenerator();
$course = $generator->create_course(
array('format' => 'test_cs_options', 'numsections' => 3,
'enablecompletion' => COMPLETION_ENABLED),
array('createsections' => true));
$courseobject = \core_courseformat\base::instance($course->id);
$section = $DB->get_record('course_sections',
array('course' => $course->id, 'section' => 1), '*', MUST_EXIST);
$data = array('id' => $section->id,
'numdaystocomplete' => 2);
$courseobject->update_section_format_options($data);
$this->backup_and_restore($course, $course, backup::TARGET_EXISTING_ADDING);
$sectionoptions = $courseobject->get_format_options(1);
$this->assertArrayHasKey('numdaystocomplete', $sectionoptions);
$this->assertArrayNotHasKey('secondparameter', $sectionoptions);
$this->assertEquals(2, $sectionoptions['numdaystocomplete']);
}
/**
* Tests that all section options are copied when the course format is changed.
* None of the data is copied.
*
* It is a future enhancement to copy;
* 1. Only the relevant options.
* 2. Only the data associated with relevant options.
*/
public function test_course_format_options_restore_new_format(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
$this->setAdminUser();
// Create a source course using the test_cs2_options format.
$generator = $this->getDataGenerator();
$course = $generator->create_course(
array('format' => 'test_cs2_options', 'numsections' => 3,
'enablecompletion' => COMPLETION_ENABLED),
array('createsections' => true));
// Create a target course using test_cs_options format.
$newcourse = $generator->create_course(
array('format' => 'test_cs_options', 'numsections' => 3,
'enablecompletion' => COMPLETION_ENABLED),
array('createsections' => true));
// Set section 2 to have both options, and a name.
$courseobject = \core_courseformat\base::instance($course->id);
$section = $DB->get_record('course_sections',
array('course' => $course->id, 'section' => 2), '*', MUST_EXIST);
$data = array('id' => $section->id,
'numdaystocomplete' => 2,
'secondparameter' => 8
);
$courseobject->update_section_format_options($data);
$DB->set_field('course_sections', 'name', 'Frogs', array('id' => $section->id));
// Backup and restore to the new course using 'add to existing' so it
// keeps the current (test_cs_options) format.
$this->backup_and_restore($course, $newcourse, backup::TARGET_EXISTING_ADDING);
// Check that the section contains the options suitable for the new
// format and that even the one with the same name as from the old format
// has NOT been set.
$newcourseobject = \core_courseformat\base::instance($newcourse->id);
$sectionoptions = $newcourseobject->get_format_options(2);
$this->assertArrayHasKey('numdaystocomplete', $sectionoptions);
$this->assertArrayNotHasKey('secondparameter', $sectionoptions);
$this->assertEquals(0, $sectionoptions['numdaystocomplete']);
// However, the name should have been changed, as this does not depend
// on the format.
$modinfo = get_fast_modinfo($newcourse->id);
$section = $modinfo->get_section_info(2);
$this->assertEquals('Frogs', $section->name);
}
/**
* Backs a course up and restores it.
*
* @param \stdClass $srccourse Course object to backup
* @param \stdClass $dstcourse Course object to restore into
* @param int $target Target course mode (backup::TARGET_xx)
* @return int ID of newly restored course
*/
protected function backup_and_restore($srccourse, $dstcourse = null,
$target = backup::TARGET_NEW_COURSE) {
global $USER, $CFG;
// Turn off file logging, otherwise it can't delete the file (Windows).
$CFG->backup_file_logger_level = backup::LOG_NONE;
// Do backup with default settings. MODE_IMPORT means it will just
// create the directory and not zip it.
$bc = new backup_controller(backup::TYPE_1COURSE, $srccourse->id,
backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT,
$USER->id);
$backupid = $bc->get_backupid();
$bc->execute_plan();
$bc->destroy();
// Do restore to new course with default settings.
if ($dstcourse !== null) {
$newcourseid = $dstcourse->id;
} else {
$newcourseid = restore_dbops::create_new_course(
$srccourse->fullname, $srccourse->shortname . '_2', $srccourse->category);
}
$rc = new restore_controller($backupid, $newcourseid,
backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id,
$target);
$this->assertTrue($rc->execute_precheck());
$rc->execute_plan();
$rc->destroy();
return $newcourseid;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,85 @@
<?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/>.
namespace core_backup;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->libdir . '/completionlib.php');
/**
* Test for restore_stepslib.
*
* @package core_backup
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_gradebook_structure_step_test extends \advanced_testcase {
/**
* Provide tests for rewrite_step_backup_file_for_legacy_freeze based upon fixtures.
*
* @return array
*/
public function rewrite_step_backup_file_for_legacy_freeze_provider() {
$fixturesdir = realpath(__DIR__ . '/fixtures/rewrite_step_backup_file_for_legacy_freeze/');
$tests = [];
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($fixturesdir),
\RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($iterator as $sourcefile) {
$pattern = '/\.test$/';
if (!preg_match($pattern, $sourcefile)) {
continue;
}
$expectfile = preg_replace($pattern, '.expectation', $sourcefile);
$test = array($sourcefile, $expectfile);
$tests[basename($sourcefile)] = $test;
}
return $tests;
}
/**
* @dataProvider rewrite_step_backup_file_for_legacy_freeze_provider
* @param string $source The source file to test
* @param string $expected The expected result of the transformation
*/
public function test_rewrite_step_backup_file_for_legacy_freeze($source, $expected): void {
$restore = $this->getMockBuilder('\restore_gradebook_structure_step')
->onlyMethods([])
->disableOriginalConstructor()
->getMock()
;
// Copy the file somewhere as the rewrite_step_backup_file_for_legacy_freeze will write the file.
$dir = make_request_directory(true);
$filepath = $dir . DIRECTORY_SEPARATOR . 'file.xml';
copy($source, $filepath);
$rc = new \ReflectionClass('\restore_gradebook_structure_step');
$rcm = $rc->getMethod('rewrite_step_backup_file_for_legacy_freeze');
$rcm->invoke($restore, $filepath);
// Check the result.
$this->assertFileEquals($expected, $filepath);
}
}
@@ -0,0 +1,445 @@
<?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/>.
namespace core_backup;
use mod_quiz\quiz_attempt;
use mod_quiz\quiz_settings;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php");
require_once($CFG->libdir . "/badgeslib.php");
require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
/**
* Restore date tests.
*
* @package core_backup
* @copyright 2017 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_stepslib_date_test extends \restore_date_testcase {
/**
* Restoring a manual grade item does not result in the timecreated or
* timemodified dates being changed.
*/
public function test_grade_item_date_restore(): void {
$course = $this->getDataGenerator()->create_course(['startdate' => time()]);
$params = new \stdClass();
$params->courseid = $course->id;
$params->fullname = 'unittestgradecalccategory';
$params->aggregation = GRADE_AGGREGATE_MEAN;
$params->aggregateonlygraded = 0;
$gradecategory = new \grade_category($params, false);
$gradecategory->insert();
$gradecategory->load_grade_item();
$gradeitems = new \grade_item();
$gradeitems->courseid = $course->id;
$gradeitems->categoryid = $gradecategory->id;
$gradeitems->itemname = 'manual grade_item';
$gradeitems->itemtype = 'manual';
$gradeitems->itemnumber = 0;
$gradeitems->needsupdate = false;
$gradeitems->gradetype = GRADE_TYPE_VALUE;
$gradeitems->grademin = 0;
$gradeitems->grademax = 10;
$gradeitems->iteminfo = 'Manual grade item used for unit testing';
$gradeitems->timecreated = time();
$gradeitems->timemodified = time();
$gradeitems->aggregationcoef = GRADE_AGGREGATE_SUM;
$gradeitems->insert();
$gradeitemparams = [
'itemtype' => 'manual',
'itemname' => $gradeitems->itemname,
'courseid' => $course->id,
];
$gradeitem = \grade_item::fetch($gradeitemparams);
// Do backup and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
$newgradeitemparams = [
'itemtype' => 'manual',
'itemname' => $gradeitems->itemname,
'courseid' => $course->id,
];
$newgradeitem = \grade_item::fetch($newgradeitemparams);
$this->assertEquals($gradeitem->timecreated, $newgradeitem->timecreated);
$this->assertEquals($gradeitem->timemodified, $newgradeitem->timemodified);
}
/**
* The course section timemodified date does not get rolled forward
* when the course is restored.
*/
public function test_course_section_date_restore(): void {
global $DB;
// Create a course.
$course = $this->getDataGenerator()->create_course(['startdate' => time()]);
// Get the second course section.
$section = $DB->get_record('course_sections', ['course' => $course->id, 'section' => '1']);
// Do a backup and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
$newsection = $DB->get_record('course_sections', ['course' => $newcourse->id, 'section' => '1']);
// Compare dates.
$this->assertEquals($section->timemodified, $newsection->timemodified);
}
/**
* Test that the timecreated and timemodified dates are not rolled forward when restoring
* badge data.
*/
public function test_badge_date_restore(): void {
global $DB, $USER;
// Create a course.
$course = $this->getDataGenerator()->create_course(['startdate' => time()]);
// Create a badge.
$fordb = new \stdClass();
$fordb->id = null;
$fordb->name = "Test badge";
$fordb->description = "Testing badges";
$fordb->timecreated = time();
$fordb->timemodified = time();
$fordb->usercreated = $USER->id;
$fordb->usermodified = $USER->id;
$fordb->issuername = "Test issuer";
$fordb->issuerurl = "http://issuer-url.domain.co.nz";
$fordb->issuercontact = "issuer@example.com";
$fordb->expiredate = time();
$fordb->expireperiod = null;
$fordb->type = BADGE_TYPE_COURSE;
$fordb->courseid = $course->id;
$fordb->messagesubject = "Test message subject";
$fordb->message = "Test message body";
$fordb->attachment = 1;
$fordb->notification = 0;
$fordb->status = BADGE_STATUS_INACTIVE;
$fordb->nextcron = time();
$DB->insert_record('badge', $fordb, true);
// Do a backup and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
$badges = badges_get_badges(BADGE_TYPE_COURSE, $newcourseid);
// Compare dates.
$badge = array_shift($badges);
$this->assertEquals($fordb->timecreated, $badge->timecreated);
$this->assertEquals($fordb->timemodified, $badge->timemodified);
$this->assertEquals($fordb->nextcron, $badge->nextcron);
// Expire date should be moved forward.
$this->assertNotEquals($fordb->expiredate, $badge->expiredate);
}
/**
* Test that course calendar events timemodified field is not rolled forward
* when restoring the course.
*/
public function test_calendarevents_date_restore(): void {
global $USER, $DB;
// Create course.
$course = $this->getDataGenerator()->create_course(['startdate' => time()]);
// Create calendar event.
$starttime = time();
$event = [
'name' => 'Start of assignment',
'description' => '',
'format' => 1,
'courseid' => $course->id,
'groupid' => 0,
'userid' => $USER->id,
'modulename' => 0,
'instance' => 0,
'eventtype' => 'course',
'timestart' => $starttime,
'timeduration' => 86400,
'visible' => 1
];
$calendarevent = \calendar_event::create($event, false);
// Backup and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
$newevent = $DB->get_record('event', ['courseid' => $newcourseid, 'eventtype' => 'course']);
// Compare dates.
$this->assertEquals($calendarevent->timemodified, $newevent->timemodified);
$this->assertNotEquals($calendarevent->timestart, $newevent->timestart);
}
/**
* Testing that the timeenrolled, timestarted, and timecompleted fields are not rolled forward / back
* when doing a course restore.
*/
public function test_course_completion_date_restore(): void {
global $DB;
// Create course with course completion enabled.
$course = $this->getDataGenerator()->create_course(['startdate' => time(), 'enablecompletion' => 1]);
// Enrol a user in the course.
$user = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
// Complete the course with a user.
$ccompletion = new \completion_completion(['course' => $course->id,
'userid' => $user->id,
'timeenrolled' => time(),
'timestarted' => time()
]);
// Now, mark the course as completed.
$ccompletion->mark_complete();
$this->assertEquals('100', \core_completion\progress::get_course_progress_percentage($course, $user->id));
// Back up and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
$newcompletion = \completion_completion::fetch(['course' => $newcourseid, 'userid' => $user->id]);
// Compare dates.
$this->assertEquals($ccompletion->timeenrolled, $newcompletion->timeenrolled);
$this->assertEquals($ccompletion->timestarted, $newcompletion->timestarted);
$this->assertEquals($ccompletion->timecompleted, $newcompletion->timecompleted);
}
/**
* Testing that the grade grade date information is not changed in the gradebook when a course
* restore is performed.
*/
public function test_grade_grade_date_restore(): void {
global $USER, $DB;
// Testing the restore of an overridden grade.
list($course, $assign) = $this->create_course_and_module('assign', []);
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
$assignobj = new \mod_assign_testable_assign(\context_module::instance($cm->id), $cm, $course);
$submission = $assignobj->get_user_submission($USER->id, true);
$grade = $assignobj->get_user_grade($USER->id, true);
$grade->grade = 75;
$assignobj->update_grade($grade);
// Find the grade item.
$gradeitemparams = [
'itemtype' => 'mod',
'iteminstance' => $assign->id,
'itemmodule' => 'assign',
'courseid' => $course->id,
];
$gradeitem = \grade_item::fetch($gradeitemparams);
// Next the grade grade.
$gradegrade = \grade_grade::fetch(['itemid' => $gradeitem->id, 'userid' => $USER->id]);
$gradegrade->set_overridden(true);
// Back up and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
// Find assignment.
$assignid = $DB->get_field('assign', 'id', ['course' => $newcourseid]);
// Find grade item.
$newgradeitemparams = [
'itemtype' => 'mod',
'iteminstance' => $assignid,
'itemmodule' => 'assign',
'courseid' => $newcourse->id,
];
$newgradeitem = \grade_item::fetch($newgradeitemparams);
// Find grade grade.
$newgradegrade = \grade_grade::fetch(['itemid' => $newgradeitem->id, 'userid' => $USER->id]);
// Compare dates.
$this->assertEquals($gradegrade->timecreated, $newgradegrade->timecreated);
$this->assertEquals($gradegrade->timemodified, $newgradegrade->timemodified);
$this->assertEquals($gradegrade->overridden, $newgradegrade->overridden);
}
/**
* Checking that the user completion of an activity relating to the timemodified field does not change
* when doing a course restore.
*/
public function test_usercompletion_date_restore(): void {
global $USER, $DB;
// More completion...
$course = $this->getDataGenerator()->create_course(['startdate' => time(), 'enablecompletion' => 1]);
$assign = $this->getDataGenerator()->create_module('assign', [
'course' => $course->id,
'completion' => COMPLETION_TRACKING_AUTOMATIC, // Show activity as complete when conditions are met.
'completionusegrade' => 1 // Student must receive a grade to complete this activity.
]);
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
$assignobj = new \mod_assign_testable_assign(\context_module::instance($cm->id), $cm, $course);
$submission = $assignobj->get_user_submission($USER->id, true);
$grade = $assignobj->get_user_grade($USER->id, true);
$grade->grade = 75;
$assignobj->update_grade($grade);
$coursemodulecompletion = $DB->get_record('course_modules_completion', ['coursemoduleid' => $cm->id]);
// Back up and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
// Find assignment.
$assignid = $DB->get_field('assign', 'id', ['course' => $newcourseid]);
$cm = $DB->get_record('course_modules', ['course' => $newcourse->id, 'instance' => $assignid]);
$newcoursemodulecompletion = $DB->get_record('course_modules_completion', ['coursemoduleid' => $cm->id]);
$this->assertEquals($coursemodulecompletion->timemodified, $newcoursemodulecompletion->timemodified);
}
/**
* Checking that the user completion of an activity relating to the view field does not change
* when doing a course restore.
* @covers ::backup_and_restore
*/
public function test_usercompletion_view_restore(): void {
global $DB;
// More completion...
$course = $this->getDataGenerator()->create_course(['startdate' => time(), 'enablecompletion' => 1]);
$student = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($student->id, $course->id, 'student');
$assign = $this->getDataGenerator()->create_module('assign', [
'course' => $course->id,
'completion' => COMPLETION_TRACKING_AUTOMATIC, // Show activity as complete when conditions are met.
'completionview' => 1
]);
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
// Mark the activity as completed.
$completion = new \completion_info($course);
$completion->set_module_viewed($cm, $student->id);
$coursemodulecompletion = $DB->get_record('course_modules_viewed', ['coursemoduleid' => $cm->id]);
// Back up and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
$assignid = $DB->get_field('assign', 'id', ['course' => $newcourseid]);
$cm = $DB->get_record('course_modules', ['course' => $newcourse->id, 'instance' => $assignid]);
$newcoursemodulecompletion = $DB->get_record('course_modules_viewed', ['coursemoduleid' => $cm->id]);
$this->assertEquals($coursemodulecompletion->timecreated, $newcoursemodulecompletion->timecreated);
}
/**
* Ensuring that the timemodified field of the question attempt steps table does not change when
* a course restore is done.
*/
public function test_question_attempt_steps_date_restore(): void {
global $DB;
$course = $this->getDataGenerator()->create_course(['startdate' => time()]);
// Make a quiz.
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
$quiz = $quizgenerator->create_instance(array('course' => $course->id, 'questionsperpage' => 0, 'grade' => 100.0,
'sumgrades' => 2));
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $quiz->id]);
// Create a couple of questions.
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
$cat = $questiongenerator->create_question_category();
$saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
$numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
// Add them to the quiz.
quiz_add_quiz_question($saq->id, $quiz);
quiz_add_quiz_question($numq->id, $quiz);
// Make a user to do the quiz.
$user1 = $this->getDataGenerator()->create_user();
$quizobj = quiz_settings::create($quiz->id, $user1->id);
// Start the attempt.
$quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
$timenow = time();
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $user1->id);
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
quiz_attempt_save_started($quizobj, $quba, $attempt);
// Process some responses from the student.
$attemptobj = quiz_attempt::create($attempt->id);
$prefix1 = $quba->get_field_prefix(1);
$prefix2 = $quba->get_field_prefix(2);
$tosubmit = array(1 => array('answer' => 'frog'),
2 => array('answer' => '3.14'));
$attemptobj->process_submitted_actions($timenow, false, $tosubmit);
// Finish the attempt.
$attemptobj = quiz_attempt::create($attempt->id);
$attemptobj->process_finish($timenow, false);
$questionattemptstepdates = [];
$originaliterator = $quba->get_attempt_iterator();
foreach ($originaliterator as $questionattempt) {
$questionattemptstepdates[] = ['originaldate' => $questionattempt->get_last_action_time()];
}
// Back up and restore.
$newcourseid = $this->backup_and_restore($course);
$newcourse = get_course($newcourseid);
// Get the quiz for this new restored course.
$quizdata = $DB->get_record('quiz', ['course' => $newcourseid]);
$quizobj = \mod_quiz\quiz_settings::create($quizdata->id, $user1->id);
$questionusage = $DB->get_record('question_usages', [
'component' => 'mod_quiz',
'contextid' => $quizobj->get_context()->id
]);
$newquba = \question_engine::load_questions_usage_by_activity($questionusage->id);
$restorediterator = $newquba->get_attempt_iterator();
$i = 0;
foreach ($restorediterator as $restoredquestionattempt) {
$questionattemptstepdates[$i]['restoredate'] = $restoredquestionattempt->get_last_action_time();
$i++;
}
foreach ($questionattemptstepdates as $dates) {
$this->assertEquals($dates['originaldate'], $dates['restoredate']);
}
}
}
@@ -0,0 +1,133 @@
<?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/>.
namespace core_backup;
use backup;
/**
* Tests for Moodle 2 restore steplib classes.
*
* @package core_backup
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_stepslib_test extends \advanced_testcase {
/**
* Setup to include all libraries.
*/
public static function setUpBeforeClass(): void {
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_stepslib.php');
}
/**
* Makes a backup of the course.
*
* @param \stdClass $course The course object.
* @return string Unique identifier for this backup.
*/
protected function backup_course(\stdClass $course): string {
global $CFG, $USER;
// Turn off file logging, otherwise it can't delete the file (Windows).
$CFG->backup_file_logger_level = backup::LOG_NONE;
// Do backup with default settings. MODE_IMPORT means it will just
// create the directory and not zip it.
$bc = new \backup_controller(
backup::TYPE_1COURSE,
$course->id,
backup::FORMAT_MOODLE,
backup::INTERACTIVE_NO,
backup::MODE_IMPORT,
$USER->id
);
$backupid = $bc->get_backupid();
$bc->execute_plan();
$bc->destroy();
return $backupid;
}
/**
* Restores a backup that has been made earlier.
*
* @param string $backupid The unique identifier of the backup.
* @return int The new course id.
*/
protected function restore_replacing_content(string $backupid): int {
global $CFG, $USER;
// Create course to restore into, and a user to do the restore.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
// Turn off file logging, otherwise it can't delete the file (Windows).
$CFG->backup_file_logger_level = backup::LOG_NONE;
// Do restore to new course with default settings.
$rc = new \restore_controller(
$backupid,
$course->id,
backup::INTERACTIVE_NO,
backup::MODE_GENERAL,
$USER->id,
backup::TARGET_EXISTING_DELETING
);
$precheck = $rc->execute_precheck();
$this->assertTrue($precheck);
$rc->get_plan()->get_setting('role_assignments')->set_value(true);
$rc->get_plan()->get_setting('permissions')->set_value(true);
$rc->execute_plan();
$rc->destroy();
return $course->id;
}
/**
* Test for the section structure step included elements.
*
* @covers \restore_section_structure_step::process_section
*/
public function test_restore_section_structure_step(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course(['numsections' => 2, 'format' => 'topics']);
$backupid = $this->backup_course($course);
$newcourseid = $this->restore_replacing_content($backupid);
$originalsections = get_fast_modinfo($course->id)->get_section_info_all();
$restoredsections = get_fast_modinfo($newcourseid)->get_section_info_all();
$this->assertEquals(count($originalsections), count($restoredsections));
$validatefields = ['name', 'summary', 'summaryformat', 'visible', 'component', 'itemid'];
foreach ($validatefields as $field) {
$this->assertEquals($originalsections[1]->$field, $restoredsections[1]->$field);
$this->assertEquals($originalsections[2]->$field, $restoredsections[2]->$field);
}
}
}