Skip to content

Commit

Permalink
MDL-22138 backup - I know smaller cathedrals than this, yay quizzes!
Browse files Browse the repository at this point in the history
  • Loading branch information
stronk7 committed Oct 24, 2010
1 parent c281862 commit 4194111
Show file tree
Hide file tree
Showing 54 changed files with 3,098 additions and 5,305 deletions.
6 changes: 3 additions & 3 deletions backup/moodle2/backup_final_task.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ public function build() {
// 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'));

// Generate the questions file with the final annotated question_categories
$this->add_step(new backup_questions_structure_step('questions', 'questions.xml'));

// Annotate all the user files (conditionally) (private, 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
Expand Down
50 changes: 49 additions & 1 deletion backup/moodle2/backup_qtype_plugin.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ protected function add_question_datasets($element) {
$items->add_child($item);

// Set the sources
$definition->set_source_sql('SELECT *
$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));
Expand All @@ -155,4 +155,52 @@ protected function add_question_datasets($element) {

// 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 = 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();
}
}
13 changes: 12 additions & 1 deletion backup/moodle2/backup_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ protected function prepare_activity_structure($activitystructure) {

/**
* Abstract structure step, to be used by all the activities using core questions stuff
* (namelu quiz module), supporting question plugins, states and sessions
* (namely quiz module), supporting question plugins, states and sessions
*/
abstract class backup_questions_activity_structure_step extends backup_activity_structure_step {

Expand Down Expand Up @@ -1562,10 +1562,20 @@ protected function define_execution() {
JOIN {backup_ids_temp} bi ON bi.itemid = qc.id
WHERE bi.backupid = ?
AND bi.itemname = 'question_categoryfinal'", array($this->get_backupid()));
// To know about qtype specific components/fileareas
$components = backup_qtype_plugin::get_components_and_fileareas();
// Let's loop
foreach($rs as $record) {
// We don't need to specify filearea nor itemid as far as by
// component and context it's enough to annotate the whole bank files
// This backups "questiontext", "generalfeedback" and "answerfeedback" fileareas (all them
// belonging to the "question" component
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, 'question', null, null);
// Again, it is enough to pick files only by context and component
// Do it for qtype specific components
foreach ($components as $component => $fileareas) {
backup_structure_dbops::annotate_files($this->get_backupid(), $record->contextid, $component, null, null);
}
}
$rs->close();
}
Expand Down Expand Up @@ -1620,6 +1630,7 @@ protected function define_structure() {
$question->set_source_table('question', array('category' => backup::VAR_PARENTID));

// don't need to annotate ids nor files
// (already done by {@link backup_annotate_all_question_files}

return $qcategories;
}
Expand Down
8 changes: 8 additions & 0 deletions backup/moodle2/restore_final_task.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ class restore_final_task extends restore_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.
Expand Down
4 changes: 4 additions & 0 deletions backup/moodle2/restore_plan_builder.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
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_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_stepslib.php');
Expand Down
172 changes: 172 additions & 0 deletions backup/moodle2/restore_plugin.class.php
Original file line number Diff line number Diff line change
@@ -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/>.

/**
* @package moodlecore
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

/**
* Class implementing the plugins support for moodle2 restore
*
* TODO: Finish phpdocs
* TODO: Add support for declaring decode_contents (not decode_rules)
*/
abstract class restore_plugin {

protected $plugintype;
protected $pluginname;
protected $connectionpoint;
protected $step;
protected $task;

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 ($bluginpaths = $this->$methodname()) {
foreach ($bluginpaths as $path) {
$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();
}
}

// 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
*
*/
protected function get_mappingid($itemname, $oldid) {
return $this->step->get_mappingid($itemname, $oldid);
}

/**
* Return the complete mapping from the given itemname, itemid
*/
protected function get_mapping($itemname, $oldid) {
return $this->step->get_mapping($itemname, $oldid);
}

/**
* Add all the existing file, given their component and filearea and one backup_ids itemname to match with
*/
protected function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null, $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);
}

/**
* 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;
}
}

0 comments on commit 4194111

Please sign in to comment.