mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 16:36:37 +02:00
MDL-24817 backup - added support for decode contents in plugins (and qtypes)
This commit is contained in:
parent
b5e58c1831
commit
9f68f2d5a6
12 changed files with 119 additions and 193 deletions
|
@ -26,6 +26,7 @@
|
|||
* Class implementing the subplugins support for moodle2 backups
|
||||
*
|
||||
* TODO: Finish phpdocs
|
||||
* TODO: Make this subclass of backup_plugin
|
||||
*/
|
||||
abstract class backup_subplugin {
|
||||
|
||||
|
|
|
@ -81,6 +81,59 @@ abstract class restore_plugin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
static public function get_restore_decode_contents($plugintype) {
|
||||
$decodecontents = array();
|
||||
// Check the requested plugintype is a valid one
|
||||
if (!array_key_exists($plugintype, 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 = 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
|
||||
*/
|
||||
static public 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
|
||||
|
|
|
@ -299,4 +299,23 @@ abstract class restore_qtype_plugin extends restore_plugin {
|
|||
// 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
|
||||
*/
|
||||
static public 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -348,8 +348,11 @@ class restore_gradebook_structure_step extends restore_structure_step {
|
|||
class restore_decode_interlinks extends restore_execution_step {
|
||||
|
||||
protected function define_execution() {
|
||||
// Just that
|
||||
$this->task->get_decoder()->execute();
|
||||
// Get the decoder (from the plan)
|
||||
$decoder = $this->task->get_decoder();
|
||||
restore_decode_processor::register_link_decoders($decoder); // Add decoder contents and rules
|
||||
// And launch it, everything will be processed
|
||||
$decoder->execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
* 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 {
|
||||
|
|
|
@ -128,12 +128,6 @@ class restore_decode_processor {
|
|||
}
|
||||
}
|
||||
|
||||
// Add the course format ones
|
||||
// TODO: Same than blocks, need to know how courseformats are going to handle restore
|
||||
|
||||
// Add local encodes
|
||||
// TODO: Any interest? 1.9 never had that.
|
||||
|
||||
// We have all the tasks registered, let's iterate over them, getting
|
||||
// contents and rules and adding them to the processor
|
||||
foreach ($tasks as $classname) {
|
||||
|
@ -154,6 +148,19 @@ class restore_decode_processor {
|
|||
$processor->add_rule($rule);
|
||||
}
|
||||
}
|
||||
|
||||
// Now process all the plugins contents (note plugins don't have support for rules)
|
||||
// TODO: Add other plugin types (course formats, local...) here if we add them to backup/restore
|
||||
$plugins = array('qtype');
|
||||
foreach ($plugins as $plugin) {
|
||||
$contents = restore_plugin::get_restore_decode_contents($plugin);
|
||||
if (!is_array($contents)) {
|
||||
throw new restore_decode_processor_exception('get_restore_decode_contents_not_array', $plugin);
|
||||
}
|
||||
foreach ($contents as $content) {
|
||||
$processor->add_content($content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Protected API starts here
|
||||
|
|
|
@ -61,7 +61,6 @@ class restore_plan extends base_plan implements loggable {
|
|||
|
||||
public function build() {
|
||||
restore_plan_builder::build_plan($this->controller); // We are moodle2 always, go straight to builder
|
||||
restore_decode_processor::register_link_decoders($this->decoder); // Add decoder contents and rules
|
||||
$this->built = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Recode content links in question texts.
|
||||
* @param object $restore the restore metadata object.
|
||||
* @return boolean whether the operation succeeded.
|
||||
*/
|
||||
function question_decode_content_links_caller($restore) {
|
||||
global $CFG, $QTYPES, $DB;
|
||||
$status = true;
|
||||
$i = 1; //Counter to send some output to the browser to avoid timeouts
|
||||
|
||||
// Get a list of which question types have custom field that will need decoding.
|
||||
$qtypeswithextrafields = array();
|
||||
$qtypeswithhtmlanswers = array();
|
||||
foreach ($QTYPES as $qtype => $qtypeclass) {
|
||||
$qtypeswithextrafields[$qtype] = method_exists($qtypeclass, 'decode_content_links_caller');
|
||||
$qtypeswithhtmlanswers[$qtype] = $qtypeclass->has_html_answers();
|
||||
}
|
||||
$extraprocessing = array();
|
||||
|
||||
$coursemodulecontexts = array();
|
||||
$context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
|
||||
$coursemodulecontexts[] = $context->id;
|
||||
$cms = $DB->get_records('course_modules', array('course'=>$restore->course_id), '', 'id');
|
||||
if ($cms){
|
||||
foreach ($cms as $cm){
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$coursemodulecontexts[] = $context->id;
|
||||
}
|
||||
}
|
||||
$coursemodulecontextslist = join($coursemodulecontexts, ',');
|
||||
// Decode links in questions.
|
||||
list($usql, $params) = $DB->get_in_or_equal(explode(',', $coursemodulecontextslist));
|
||||
if ($questions = $DB->get_records_sql("SELECT q.id, q.qtype, q.questiontext, q.generalfeedback
|
||||
FROM {question} q, {question_categories} qc
|
||||
WHERE q.category = qc.id
|
||||
AND qc.contextid $usql", $params)) {
|
||||
|
||||
foreach ($questions as $question) {
|
||||
$questiontext = restore_decode_content_links_worker($question->questiontext, $restore);
|
||||
$generalfeedback = restore_decode_content_links_worker($question->generalfeedback, $restore);
|
||||
if ($questiontext != $question->questiontext || $generalfeedback != $question->generalfeedback) {
|
||||
$question->questiontext = $questiontext;
|
||||
$question->generalfeedback = $generalfeedback;
|
||||
$DB->update_record('question', $question);
|
||||
}
|
||||
|
||||
// Do some output.
|
||||
if (++$i % 5 == 0 && !defined('RESTORE_SILENTLY')) {
|
||||
echo ".";
|
||||
if ($i % 100 == 0) {
|
||||
echo "<br />";
|
||||
}
|
||||
backup_flush(300);
|
||||
}
|
||||
|
||||
// Decode any questiontype specific fields.
|
||||
if ($qtypeswithextrafields[$question->qtype]) {
|
||||
if (!array_key_exists($question->qtype, $extraprocessing)) {
|
||||
$extraprocessing[$question->qtype] = array();
|
||||
}
|
||||
$extraprocessing[$question->qtype][] = $question->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decode links in answers.
|
||||
if ($answers = $DB->get_records_sql("SELECT qa.id, qa.answer, qa.feedback, q.qtype
|
||||
FROM {question_answers} qa, {question} q, {question_categories} qc
|
||||
WHERE qa.question = q.id
|
||||
AND q.category = qc.id
|
||||
AND qc.contextid $usql", $params)) {
|
||||
|
||||
foreach ($answers as $answer) {
|
||||
$feedback = restore_decode_content_links_worker($answer->feedback, $restore);
|
||||
if ($qtypeswithhtmlanswers[$answer->qtype]) {
|
||||
$answertext = restore_decode_content_links_worker($answer->answer, $restore);
|
||||
} else {
|
||||
$answertext = $answer->answer;
|
||||
}
|
||||
if ($feedback != $answer->feedback || $answertext != $answer->answer) {
|
||||
unset($answer->qtype);
|
||||
$answer->feedback = $feedback;
|
||||
$answer->answer = $answertext;
|
||||
$DB->update_record('question_answers', $answer);
|
||||
}
|
||||
|
||||
// Do some output.
|
||||
if (++$i % 5 == 0 && !defined('RESTORE_SILENTLY')) {
|
||||
echo ".";
|
||||
if ($i % 100 == 0) {
|
||||
echo "<br />";
|
||||
}
|
||||
backup_flush(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do extra work for certain question types.
|
||||
foreach ($extraprocessing as $qtype => $questionids) {
|
||||
if (!$QTYPES[$qtype]->decode_content_links_caller($questionids, $restore, $i)) {
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
|
@ -168,4 +168,16 @@ class restore_qtype_match_plugin extends restore_qtype_plugin {
|
|||
}
|
||||
return implode(',', $resultarr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of this qtype to be processed by the links decoder
|
||||
*/
|
||||
static public function define_decode_contents() {
|
||||
|
||||
$contents = array();
|
||||
|
||||
$contents[] = new restore_decode_content('question_match_sub', array('questiontext'), 'question_match_sub');
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -483,39 +483,6 @@ class question_match_qtype extends default_questiontype {
|
|||
return 1 / count($question->options->subquestions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode links in question type specific tables.
|
||||
* @return bool success or failure.
|
||||
*/
|
||||
function decode_content_links_caller($questionids, $restore, &$i) {
|
||||
global $DB;
|
||||
|
||||
$status = true;
|
||||
|
||||
// Decode links in the question_match_sub table.
|
||||
if ($subquestions = $DB->get_records_list('question_match_sub', 'question', $questionids, '', 'id, questiontext')) {
|
||||
|
||||
foreach ($subquestions as $subquestion) {
|
||||
$questiontext = restore_decode_content_links_worker($subquestion->questiontext, $restore);
|
||||
if ($questiontext != $subquestion->questiontext) {
|
||||
$subquestion->questiontext = $questiontext;
|
||||
$DB->update_record('question_match_sub', $subquestion);
|
||||
}
|
||||
|
||||
// Do some output.
|
||||
if (++$i % 5 == 0 && !defined('RESTORE_SILENTLY')) {
|
||||
echo ".";
|
||||
if ($i % 100 == 0) {
|
||||
echo "<br />";
|
||||
}
|
||||
backup_flush(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
function find_file_links($question, $courseid){
|
||||
// find links in the question_match_sub table.
|
||||
$urls = array();
|
||||
|
|
|
@ -75,8 +75,8 @@ class restore_qtype_multichoice_plugin extends restore_qtype_plugin {
|
|||
$data->answers = implode(',', $answersarr);
|
||||
// Insert record
|
||||
$newitemid = $DB->insert_record('question_multichoice', $data);
|
||||
// Create mapping (not needed, no files nor childs nor states here)
|
||||
//$this->set_mapping('question_multichoice', $oldid, $newitemid);
|
||||
// Create mapping (needed for decoding links)
|
||||
$this->set_mapping('question_multichoice', $oldid, $newitemid);
|
||||
} else {
|
||||
// Nothing to remap if the question already existed
|
||||
}
|
||||
|
@ -121,4 +121,17 @@ class restore_qtype_multichoice_plugin extends restore_qtype_plugin {
|
|||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of this qtype to be processed by the links decoder
|
||||
*/
|
||||
static public function define_decode_contents() {
|
||||
|
||||
$contents = array();
|
||||
|
||||
$fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
|
||||
$contents[] = new restore_decode_content('question_multichoice', $fields, 'question_multichoice');
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -420,46 +420,6 @@ class question_multichoice_qtype extends default_questiontype {
|
|||
return $totalfraction / count($question->options->answers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode links in question type specific tables.
|
||||
* @return bool success or failure.
|
||||
*/
|
||||
function decode_content_links_caller($questionids, $restore, &$i) {
|
||||
global $DB;
|
||||
|
||||
$status = true;
|
||||
|
||||
// Decode links in the question_multichoice table.
|
||||
if ($multichoices = $DB->get_records_list('question_multichoice', 'question',
|
||||
$questionids, '', 'id, correctfeedback, partiallycorrectfeedback, incorrectfeedback')) {
|
||||
|
||||
foreach ($multichoices as $multichoice) {
|
||||
$correctfeedback = restore_decode_content_links_worker($multichoice->correctfeedback, $restore);
|
||||
$partiallycorrectfeedback = restore_decode_content_links_worker($multichoice->partiallycorrectfeedback, $restore);
|
||||
$incorrectfeedback = restore_decode_content_links_worker($multichoice->incorrectfeedback, $restore);
|
||||
if ($correctfeedback != $multichoice->correctfeedback ||
|
||||
$partiallycorrectfeedback != $multichoice->partiallycorrectfeedback ||
|
||||
$incorrectfeedback != $multichoice->incorrectfeedback) {
|
||||
$subquestion->correctfeedback = $correctfeedback;
|
||||
$subquestion->partiallycorrectfeedback = $partiallycorrectfeedback;
|
||||
$subquestion->incorrectfeedback = $incorrectfeedback;
|
||||
$DB->update_record('question_multichoice', $multichoice);
|
||||
}
|
||||
|
||||
// Do some output.
|
||||
if (++$i % 5 == 0 && !defined('RESTORE_SILENTLY')) {
|
||||
echo ".";
|
||||
if ($i % 100 == 0) {
|
||||
echo "<br />";
|
||||
}
|
||||
backup_flush(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of the numbering styles supported. For each one, there
|
||||
* should be a lang string answernumberingxxx in teh qtype_multichoice
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue