From 9f68f2d5a6b3e397b67f237d2569471185ef6e49 Mon Sep 17 00:00:00 2001 From: Eloy Lafuente Date: Mon, 1 Nov 2010 15:49:01 +0000 Subject: [PATCH] MDL-24817 backup - added support for decode contents in plugins (and qtypes) --- backup/moodle2/backup_subplugin.class.php | 1 + backup/moodle2/restore_plugin.class.php | 53 +++++++++ backup/moodle2/restore_qtype_plugin.class.php | 19 +++ backup/moodle2/restore_stepslib.php | 7 +- backup/moodle2/restore_subplugin.class.php | 1 + .../helper/restore_decode_processor.class.php | 19 ++- backup/util/plan/restore_plan.class.php | 1 - question/restorelib.php | 109 ------------------ .../restore_qtype_match_plugin.class.php | 12 ++ question/type/match/questiontype.php | 33 ------ ...restore_qtype_multichoice_plugin.class.php | 17 ++- question/type/multichoice/questiontype.php | 40 ------- 12 files changed, 119 insertions(+), 193 deletions(-) delete mode 100644 question/restorelib.php diff --git a/backup/moodle2/backup_subplugin.class.php b/backup/moodle2/backup_subplugin.class.php index da1b4e7f400..75fc21dc9b6 100644 --- a/backup/moodle2/backup_subplugin.class.php +++ b/backup/moodle2/backup_subplugin.class.php @@ -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 { diff --git a/backup/moodle2/restore_plugin.class.php b/backup/moodle2/restore_plugin.class.php index bb0898de6e6..a53d2acc5ff 100644 --- a/backup/moodle2/restore_plugin.class.php +++ b/backup/moodle2/restore_plugin.class.php @@ -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 diff --git a/backup/moodle2/restore_qtype_plugin.class.php b/backup/moodle2/restore_qtype_plugin.class.php index 91cebff60ad..6a64cf22f1b 100644 --- a/backup/moodle2/restore_qtype_plugin.class.php +++ b/backup/moodle2/restore_qtype_plugin.class.php @@ -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; + } } diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 1dd36ebd752..23c06e59db9 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -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(); } } diff --git a/backup/moodle2/restore_subplugin.class.php b/backup/moodle2/restore_subplugin.class.php index 0f732dc4dc5..7f2b175a74e 100644 --- a/backup/moodle2/restore_subplugin.class.php +++ b/backup/moodle2/restore_subplugin.class.php @@ -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 { diff --git a/backup/util/helper/restore_decode_processor.class.php b/backup/util/helper/restore_decode_processor.class.php index dda998e1531..1fdddb3e2a3 100644 --- a/backup/util/helper/restore_decode_processor.class.php +++ b/backup/util/helper/restore_decode_processor.class.php @@ -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 diff --git a/backup/util/plan/restore_plan.class.php b/backup/util/plan/restore_plan.class.php index dc78f715ffa..0796b5183a6 100644 --- a/backup/util/plan/restore_plan.class.php +++ b/backup/util/plan/restore_plan.class.php @@ -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; } diff --git a/question/restorelib.php b/question/restorelib.php deleted file mode 100644 index c832e37ee1f..00000000000 --- a/question/restorelib.php +++ /dev/null @@ -1,109 +0,0 @@ - $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 "
"; - } - 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 "
"; - } - 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; - } - diff --git a/question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php b/question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php index 5560852265b..21e287016f3 100644 --- a/question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php +++ b/question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php @@ -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; + } } diff --git a/question/type/match/questiontype.php b/question/type/match/questiontype.php index 0756590cf7c..76b97a8a086 100644 --- a/question/type/match/questiontype.php +++ b/question/type/match/questiontype.php @@ -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 "
"; - } - backup_flush(300); - } - } - } - - return $status; - } - function find_file_links($question, $courseid){ // find links in the question_match_sub table. $urls = array(); diff --git a/question/type/multichoice/backup/moodle2/restore_qtype_multichoice_plugin.class.php b/question/type/multichoice/backup/moodle2/restore_qtype_multichoice_plugin.class.php index 2950b49a74f..728541e4429 100644 --- a/question/type/multichoice/backup/moodle2/restore_qtype_multichoice_plugin.class.php +++ b/question/type/multichoice/backup/moodle2/restore_qtype_multichoice_plugin.class.php @@ -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; + } } diff --git a/question/type/multichoice/questiontype.php b/question/type/multichoice/questiontype.php index 49c669c2720..aa555188c98 100644 --- a/question/type/multichoice/questiontype.php +++ b/question/type/multichoice/questiontype.php @@ -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 "
"; - } - 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