diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index 6b4dbf34355..97cf2f7a70b 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -1656,19 +1656,25 @@ class backup_questions_structure_step extends backup_structure_step { $question = new backup_nested_element('question', array('id'), array( 'parent', 'name', 'questiontext', 'questiontextformat', - 'generalfeedback', 'generalfeedbackformat', 'defaultgrade', 'penalty', + 'generalfeedback', 'generalfeedbackformat', 'defaultmark', 'penalty', 'qtype', 'length', 'stamp', 'version', 'hidden', 'timecreated', 'timemodified', 'createdby', 'modifiedby')); // attach qtype plugin structure to $question element, only one allowed $this->add_plugin_structure('qtype', $question, false); + $qhints = new backup_nested_element('question_hints'); + + $qhint = new backup_nested_element('question_hint', array('id'), array( + 'hint', 'hintformat', 'shownumcorrect', 'clearwrong', 'options')); + // Build the tree $qcategories->add_child($qcategory); $qcategory->add_child($questions); - $questions->add_child($question); + $question->add_child($qhints); + $qhints->add_child($qhint); // Define the sources @@ -1682,6 +1688,8 @@ class backup_questions_structure_step extends backup_structure_step { $question->set_source_table('question', array('category' => backup::VAR_PARENTID)); + $qhint->set_source_table('question_hints', array('questionid' => backup::VAR_PARENTID)); + // don't need to annotate ids nor files // (already done by {@link backup_annotate_all_question_files} diff --git a/backup/moodle2/restore_qtype_plugin.class.php b/backup/moodle2/restore_qtype_plugin.class.php index fb97bebbc95..8a242f57a1f 100644 --- a/backup/moodle2/restore_qtype_plugin.class.php +++ b/backup/moodle2/restore_qtype_plugin.class.php @@ -115,6 +115,23 @@ abstract class restore_qtype_plugin extends restore_plugin { $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 diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 29396758ea4..cf2dc7a2739 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -2296,6 +2296,73 @@ class restore_create_categories_and_questions extends restore_structure_step { // we have loaded qcatids there for all parsed questions $data->category = $this->get_mappingid('question_category', $questionmapping->parentitemid); + // In the past, there were some very sloppy values of penalty. Fix them. + if ($data->penalty >= 0.33 && $question->penalty <= 0.34) { + $data->penalty = 0.3333333; + } + if ($data->penalty >= 0.66 && $question->penalty <= 0.67) { + $data->penalty = 0.6666667; + } + if ($data->penalty >= 1) { + $data->penalty = 1; + } + + $data->timecreated = $this->apply_date_offset($data->timecreated); + $data->timemodified = $this->apply_date_offset($data->timemodified); + + $userid = $this->get_mappingid('user', $data->createdby); + $data->createdby = $userid ? $userid : $this->task->get_userid(); + + $userid = $this->get_mappingid('user', $data->modifiedby); + $data->modifiedby = $userid ? $userid : $this->task->get_userid(); + + // With newitemid = 0, let's create the question + if (!$questionmapping->newitemid) { + $newitemid = $DB->insert_record('question', $data); + $this->set_mapping('question', $oldid, $newitemid); + // Also annotate them as question_created, we need + // that later when remapping parents (keeping the old categoryid as parentid) + $this->set_mapping('question_created', $oldid, $newitemid, false, null, $questionmapping->parentitemid); + } else { + // By performing this set_mapping() we make get_old/new_parentid() to work for all the + // children elements of the 'question' one (so qtype plugins will know the question they belong to) + $this->set_mapping('question', $oldid, $questionmapping->newitemid); + } + + // Note, we don't restore any question files yet + // as far as the CONTEXT_MODULE categories still + // haven't their contexts to be restored to + // The {@link restore_create_question_files}, executed in the final step + // step will be in charge of restoring all the question files + } + + protected function process_question_hint($data) { + global $DB; + + $data = (object)$data; + + // Check we have one mapping for this question + if (!$hintmapping = $this->get_mapping('question_hint', $oldid)) { + return; // No mapping = this question doesn't need to be created/mapped + } + + // Get the mapped category (cannot use get_new_parentid() because not + // all the categories have been created, so it is not always available + // Instead we get the mapping for the question->parentitemid because + // we have loaded qcatids there for all parsed questions + $data->questionid = $this->get_mappingid('question', $hintmapping->parentitemid); + + // In the past, there were some very sloppy values of penalty. Fix them. + if ($data->penalty >= 0.33 && $question->penalty <= 0.34) { + $data->penalty = 0.3333333; + } + if ($data->penalty >= 0.66 && $question->penalty <= 0.67) { + $data->penalty = 0.6666667; + } + if ($data->penalty >= 1) { + $data->penalty = 1; + } + $data->timecreated = $this->apply_date_offset($data->timecreated); $data->timemodified = $this->apply_date_offset($data->timemodified); @@ -2447,6 +2514,8 @@ class restore_create_question_files extends restore_execution_step { $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true); restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'answerfeedback', $oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true); + restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'hint', + $oldctxid, $this->task->get_userid(), 'question_hint', null, $newctxid, true); // Add qtype dependent files $components = backup_qtype_plugin::get_components_and_fileareas($question->qtype); foreach ($components as $component => $fileareas) { diff --git a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php index c3e26eb5f82..74880b0435e 100644 --- a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php @@ -42,9 +42,12 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru // Define each element separated $quiz = new backup_nested_element('quiz', array('id'), array( 'name', 'intro', 'introformat', 'timeopen', - 'timeclose', 'optionflags', 'penaltyscheme', 'attempts_number', + 'timeclose', 'preferredbehaviour', 'attempts_number', 'attemptonlast', 'grademethod', 'decimalpoints', 'questiondecimalpoints', - 'review', 'questionsperpage', 'shufflequestions', 'shuffleanswers', + 'reviewattempt', 'reviewcorrectness', 'reviewmarks', + 'reviewspecificfeedback', 'reviewgeneralfeedback', + 'reviewrightanswer', 'reviewoverallfeedback', + 'questionsperpage', 'shufflequestions', 'shuffleanswers', 'questions', 'sumgrades', 'grade', 'timecreated', 'timemodified', 'timelimit', 'password', 'subnet', 'popup', 'delay1', 'delay2', 'showuserpicture', diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index cf93f8681a6..b59ef00deb9 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -57,7 +57,7 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st } protected function process_quiz($data) { - global $DB; + global $CFG, $DB; $data = (object)$data; $oldid = $data->id; @@ -77,6 +77,84 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st unset($data->attempts_number); } + // The old optionflags and penaltyscheme from 2.0 need to be mapped to + // the new preferredbehaviour. MDL-20636 + if (!isset($data->preferredbehaviour)) { + if (empty($data->optionflags)) { + $data->preferredbehaviour = 'deferredfeedback'; + } else if (empty($data->penaltyscheme)) { + $data->preferredbehaviour = 'adaptivenopenalty'; + } else { + $data->preferredbehaviour = 'adaptive'; + } + unset($data->optionflags); + unset($data->penaltyscheme); + } + + // The old review column from 2.0 need to be split into the seven new + // review columns. MDL-20636 + if (isset($data->review)) { + require_once($CFG->dirroot . '/mod/quiz/locallib.php'); + + if (!defined('QUIZ_OLD_IMMEDIATELY')) { + define('QUIZ_OLD_IMMEDIATELY', 0x3c003f); + define('QUIZ_OLD_OPEN', 0x3c00fc0); + define('QUIZ_OLD_CLOSED', 0x3c03f000); + + define('QUIZ_OLD_RESPONSES', 1*0x1041); // Show responses + define('QUIZ_OLD_SCORES', 2*0x1041); // Show scores + define('QUIZ_OLD_FEEDBACK', 4*0x1041); // Show question feedback + define('QUIZ_OLD_ANSWERS', 8*0x1041); // Show correct answers + define('QUIZ_OLD_SOLUTIONS', 16*0x1041); // Show solutions + define('QUIZ_OLD_GENERALFEEDBACK',32*0x1041); // Show question general feedback + define('QUIZ_OLD_OVERALLFEEDBACK', 1*0x4440000); // Show quiz overall feedback + } + + $oldreview = $data->review; + + $data->reviewattempt = + mod_quiz_display_options::DURING | + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_RESPONSES ? mod_quiz_display_options::IMMEDIATELY_AFTER : 0) | + ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_RESPONSES ? mod_quiz_display_options::LATER_WHILE_OPEN : 0) | + ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_RESPONSES ? mod_quiz_display_options::AFTER_CLOSE : 0); + + $data->reviewcorrectness = + mod_quiz_display_options::DURING | + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ? mod_quiz_display_options::IMMEDIATELY_AFTER : 0) | + ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ? mod_quiz_display_options::LATER_WHILE_OPEN : 0) | + ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ? mod_quiz_display_options::AFTER_CLOSE : 0); + + $data->reviewmarks = + mod_quiz_display_options::DURING | + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ? mod_quiz_display_options::IMMEDIATELY_AFTER : 0) | + ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ? mod_quiz_display_options::LATER_WHILE_OPEN : 0) | + ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ? mod_quiz_display_options::AFTER_CLOSE : 0); + + $data->reviewspecificfeedback = + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ? mod_quiz_display_options::DURING : 0) | + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ? mod_quiz_display_options::IMMEDIATELY_AFTER : 0) | + ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_FEEDBACK ? mod_quiz_display_options::LATER_WHILE_OPEN : 0) | + ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_FEEDBACK ? mod_quiz_display_options::AFTER_CLOSE : 0); + + $data->reviewgeneralfeedback = + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ? mod_quiz_display_options::DURING : 0) | + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ? mod_quiz_display_options::IMMEDIATELY_AFTER : 0) | + ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_GENERALFEEDBACK ? mod_quiz_display_options::LATER_WHILE_OPEN : 0) | + ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_GENERALFEEDBACK ? mod_quiz_display_options::AFTER_CLOSE : 0); + + $data->reviewrightanswer = + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ? mod_quiz_display_options::DURING : 0) | + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ? mod_quiz_display_options::IMMEDIATELY_AFTER : 0) | + ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_ANSWERS ? mod_quiz_display_options::LATER_WHILE_OPEN : 0) | + ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_ANSWERS ? mod_quiz_display_options::AFTER_CLOSE : 0); + + $data->reviewoverallfeedback = + 0 | + ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_OVERALLFEEDBACK ? mod_quiz_display_options::IMMEDIATELY_AFTER : 0) | + ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_OVERALLFEEDBACK ? mod_quiz_display_options::LATER_WHILE_OPEN : 0) | + ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_OVERALLFEEDBACK ? mod_quiz_display_options::AFTER_CLOSE : 0); + } + // insert the quiz record $newitemid = $DB->insert_record('quiz', $data); // immediately after inserting "activity" record, call this diff --git a/question/type/ddwtos/backup/moodle2/backup_qtype_ddwtos_plugin.class.php b/question/type/ddwtos/backup/moodle2/backup_qtype_ddwtos_plugin.class.php new file mode 100644 index 00000000000..a86b6b33655 --- /dev/null +++ b/question/type/ddwtos/backup/moodle2/backup_qtype_ddwtos_plugin.class.php @@ -0,0 +1,84 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Provides the information to backup ddwtos questions + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class backup_qtype_ddwtos_plugin extends backup_qtype_plugin { + + /** + * Returns the qtype information to attach to question element + */ + protected function define_question_plugin_structure() { + + // Define the virtual plugin element with the condition to fulfill + $plugin = $this->get_plugin_element(null, '../../qtype', 'ddwtos'); + + // 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); + + // Now create the qtype own structures + $ddwtos = new backup_nested_element('ddwtos', array('id'), array( + 'shuffleanswers', 'correctfeedback', 'correctfeedbackformat', + 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', + 'incorrectfeedback', 'incorrectfeedbackformat', 'shownumcorrect')); + + // Now the own qtype tree + $pluginwrapper->add_child($ddwtos); + + // set source to populate the data + $ddwtos->set_source_table('question_ddwtos', array('questionid' => backup::VAR_PARENTID)); + + // don't need to annotate ids nor files + + return $plugin; + } + + /** + * 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() { + return array( + 'correctfeedback' => 'question_created', + 'partiallycorrectfeedback' => 'question_created', + 'incorrectfeedback' => 'question_created'); + } +} diff --git a/question/type/ddwtos/backup/moodle2/restore_qtype_ddwtos_plugin.class.php b/question/type/ddwtos/backup/moodle2/restore_qtype_ddwtos_plugin.class.php new file mode 100644 index 00000000000..786ec45e5cd --- /dev/null +++ b/question/type/ddwtos/backup/moodle2/restore_qtype_ddwtos_plugin.class.php @@ -0,0 +1,136 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * restore plugin class that provides the necessary information + * needed to restore one ddwtos qtype plugin + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class restore_qtype_ddwtos_plugin extends restore_qtype_plugin { + + /** + * 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 + $elename = 'ddwtos'; + $elepath = $this->get_pathfor('/ddwtos'); // we used get_recommended_name() so this works + $paths[] = new restore_path_element($elename, $elepath); + + + return $paths; // And we return the interesting paths + } + + /** + * Process the qtype/ddwtos element + */ + public function process_ddwtos($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_ddwtos too + if ($questioncreated) { + // Adjust some columns + $data->questionid = $newquestionid; + // Insert record + $newitemid = $DB->insert_record('question_ddwtos', $data); + // Create mapping (needed for decoding links) + $this->set_mapping('question_ddwtos', $oldid, $newitemid); + } else { + // Nothing to remap if the question already existed + } + } + + /** + * Given one question_states record, return the answer + * recoded pointing to all the restored stuff for ddwtos questions + * + * answer are two (hypen speparated) lists of comma separated question_answers + * the first to specify the order of the answers and the second to specify the + * responses. Note the order list (the first one) can be optional + */ + public function recode_state_answer($state) { + $answer = $state->answer; + $orderarr = array(); + $responsesarr = array(); + $lists = explode(':', $answer); + // if only 1 list, answer is missing the order list, adjust + if (count($lists) == 1) { + $lists[1] = $lists[0]; // here we have the responses + $lists[0] = ''; // here we have the order + } + // Map order + foreach (explode(',', $lists[0]) as $id) { + if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) { + $orderarr[] = $newid; + } + } + // Map responses + foreach (explode(',', $lists[1]) as $id) { + if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) { + $responsesarr[] = $newid; + } + } + // Build the final answer, if not order, only responses + $result = ''; + if (empty($orderarr)) { + $result = implode(',', $responsesarr); + } else { + $result = implode(',', $orderarr) . ':' . implode(',', $responsesarr); + } + 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_ddwtos', $fields, 'question_ddwtos'); + + return $contents; + } +} diff --git a/question/type/gapselect/backup/moodle2/backup_qtype_gapselect_plugin.class.php b/question/type/gapselect/backup/moodle2/backup_qtype_gapselect_plugin.class.php new file mode 100644 index 00000000000..a1c542ce9c4 --- /dev/null +++ b/question/type/gapselect/backup/moodle2/backup_qtype_gapselect_plugin.class.php @@ -0,0 +1,84 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Provides the information to backup gapselect questions + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class backup_qtype_gapselect_plugin extends backup_qtype_plugin { + + /** + * Returns the qtype information to attach to question element + */ + protected function define_question_plugin_structure() { + + // Define the virtual plugin element with the condition to fulfill + $plugin = $this->get_plugin_element(null, '../../qtype', 'gapselect'); + + // 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); + + // Now create the qtype own structures + $gapselect = new backup_nested_element('gapselect', array('id'), array( + 'shuffleanswers', 'correctfeedback', 'correctfeedbackformat', + 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', + 'incorrectfeedback', 'incorrectfeedbackformat', 'shownumcorrect')); + + // Now the own qtype tree + $pluginwrapper->add_child($gapselect); + + // set source to populate the data + $gapselect->set_source_table('question_gapselect', array('questionid' => backup::VAR_PARENTID)); + + // don't need to annotate ids nor files + + return $plugin; + } + + /** + * 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() { + return array( + 'correctfeedback' => 'question_created', + 'partiallycorrectfeedback' => 'question_created', + 'incorrectfeedback' => 'question_created'); + } +} diff --git a/question/type/gapselect/backup/moodle2/restore_qtype_gapselect_plugin.class.php b/question/type/gapselect/backup/moodle2/restore_qtype_gapselect_plugin.class.php new file mode 100644 index 00000000000..34dbf812b2a --- /dev/null +++ b/question/type/gapselect/backup/moodle2/restore_qtype_gapselect_plugin.class.php @@ -0,0 +1,136 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * restore plugin class that provides the necessary information + * needed to restore one gapselect qtype plugin + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class restore_qtype_gapselect_plugin extends restore_qtype_plugin { + + /** + * 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 + $elename = 'gapselect'; + $elepath = $this->get_pathfor('/gapselect'); // we used get_recommended_name() so this works + $paths[] = new restore_path_element($elename, $elepath); + + + return $paths; // And we return the interesting paths + } + + /** + * Process the qtype/gapselect element + */ + public function process_gapselect($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_gapselect too + if ($questioncreated) { + // Adjust some columns + $data->questionid = $newquestionid; + // Insert record + $newitemid = $DB->insert_record('question_gapselect', $data); + // Create mapping (needed for decoding links) + $this->set_mapping('question_gapselect', $oldid, $newitemid); + } else { + // Nothing to remap if the question already existed + } + } + + /** + * Given one question_states record, return the answer + * recoded pointing to all the restored stuff for gapselect questions + * + * answer are two (hypen speparated) lists of comma separated question_answers + * the first to specify the order of the answers and the second to specify the + * responses. Note the order list (the first one) can be optional + */ + public function recode_state_answer($state) { + $answer = $state->answer; + $orderarr = array(); + $responsesarr = array(); + $lists = explode(':', $answer); + // if only 1 list, answer is missing the order list, adjust + if (count($lists) == 1) { + $lists[1] = $lists[0]; // here we have the responses + $lists[0] = ''; // here we have the order + } + // Map order + foreach (explode(',', $lists[0]) as $id) { + if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) { + $orderarr[] = $newid; + } + } + // Map responses + foreach (explode(',', $lists[1]) as $id) { + if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) { + $responsesarr[] = $newid; + } + } + // Build the final answer, if not order, only responses + $result = ''; + if (empty($orderarr)) { + $result = implode(',', $responsesarr); + } else { + $result = implode(',', $orderarr) . ':' . implode(',', $responsesarr); + } + 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_gapselect', $fields, 'question_gapselect'); + + return $contents; + } +} diff --git a/question/type/match/backup/moodle2/backup_qtype_match_plugin.class.php b/question/type/match/backup/moodle2/backup_qtype_match_plugin.class.php index e8ccc667f93..6b1f2877adc 100644 --- a/question/type/match/backup/moodle2/backup_qtype_match_plugin.class.php +++ b/question/type/match/backup/moodle2/backup_qtype_match_plugin.class.php @@ -50,7 +50,9 @@ class backup_qtype_match_plugin extends backup_qtype_plugin { // Now create the qtype own structures $matchoptions = new backup_nested_element('matchoptions', array('id'), array( - 'subquestions', 'shuffleanswers')); + 'subquestions', 'shuffleanswers', 'correctfeedback', 'correctfeedbackformat', + 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', + 'incorrectfeedback', 'incorrectfeedbackformat', 'shownumcorrect')); $matches = new backup_nested_element('matches'); 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 82ff810b13c..daac47a5793 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 @@ -183,6 +183,9 @@ class restore_qtype_match_plugin extends restore_qtype_plugin { $contents[] = new restore_decode_content('question_match_sub', array('questiontext'), 'question_match_sub'); + $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'); + $contents[] = new restore_decode_content('question_match', $fields, 'question_match'); + return $contents; } } diff --git a/question/type/multichoice/backup/moodle2/backup_qtype_multichoice_plugin.class.php b/question/type/multichoice/backup/moodle2/backup_qtype_multichoice_plugin.class.php index 0f6ba149c30..78fcb891029 100644 --- a/question/type/multichoice/backup/moodle2/backup_qtype_multichoice_plugin.class.php +++ b/question/type/multichoice/backup/moodle2/backup_qtype_multichoice_plugin.class.php @@ -56,7 +56,7 @@ class backup_qtype_multichoice_plugin extends backup_qtype_plugin { $multichoice = new backup_nested_element('multichoice', array('id'), array( 'layout', 'answers', 'single', 'shuffleanswers', 'correctfeedback', 'correctfeedbackformat', 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', - 'incorrectfeedback', 'incorrectfeedbackformat', 'answernumbering')); + 'incorrectfeedback', 'incorrectfeedbackformat', 'answernumbering', 'shownumcorrect')); // Now the own qtype tree $pluginwrapper->add_child($multichoice); diff --git a/question/type/oumultiresponse/backup/moodle2/backup_qtype_oumultiresponse_plugin.class.php b/question/type/oumultiresponse/backup/moodle2/backup_qtype_oumultiresponse_plugin.class.php new file mode 100644 index 00000000000..362a898989c --- /dev/null +++ b/question/type/oumultiresponse/backup/moodle2/backup_qtype_oumultiresponse_plugin.class.php @@ -0,0 +1,84 @@ +. + +/** + * @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 + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Provides the information to backup oumultiresponse questions + * + * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class backup_qtype_oumultiresponse_plugin extends backup_qtype_plugin { + + /** + * Returns the qtype information to attach to question element + */ + protected function define_question_plugin_structure() { + + // Define the virtual plugin element with the condition to fulfill + $plugin = $this->get_plugin_element(null, '../../qtype', 'oumultiresponse'); + + // 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); + + // Now create the qtype own structures + $oumultiresponse = new backup_nested_element('oumultiresponse', array('id'), array( + 'shuffleanswers', 'correctfeedback', 'correctfeedbackformat', + 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', + 'incorrectfeedback', 'incorrectfeedbackformat', 'answernumbering', 'shownumcorrect')); + + // Now the own qtype tree + $pluginwrapper->add_child($oumultiresponse); + + // set source to populate the data + $oumultiresponse->set_source_table('question_oumultiresponse', array('question' => backup::VAR_PARENTID)); + + // don't need to annotate ids nor files + + return $plugin; + } + + /** + * 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() { + return array( + 'correctfeedback' => 'question_created', + 'partiallycorrectfeedback' => 'question_created', + 'incorrectfeedback' => 'question_created'); + } +} diff --git a/question/type/oumultiresponse/backup/moodle2/restore_qtype_oumultiresponse_plugin.class.php b/question/type/oumultiresponse/backup/moodle2/restore_qtype_oumultiresponse_plugin.class.php new file mode 100644 index 00000000000..6680d9297d8 --- /dev/null +++ b/question/type/oumultiresponse/backup/moodle2/restore_qtype_oumultiresponse_plugin.class.php @@ -0,0 +1,136 @@ +. + +/** + * @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 + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * restore plugin class that provides the necessary information + * needed to restore one oumultiresponse qtype plugin + * + * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class restore_qtype_oumultiresponse_plugin extends restore_qtype_plugin { + + /** + * 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 + $elename = 'oumultiresponse'; + $elepath = $this->get_pathfor('/oumultiresponse'); // we used get_recommended_name() so this works + $paths[] = new restore_path_element($elename, $elepath); + + + return $paths; // And we return the interesting paths + } + + /** + * Process the qtype/oumultiresponse element + */ + public function process_oumultiresponse($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_oumultiresponse too + if ($questioncreated) { + // Adjust some columns + $data->question = $newquestionid; + // Insert record + $newitemid = $DB->insert_record('question_oumultiresponse', $data); + // Create mapping (needed for decoding links) + $this->set_mapping('question_oumultiresponse', $oldid, $newitemid); + } else { + // Nothing to remap if the question already existed + } + } + + /** + * Given one question_states record, return the answer + * recoded pointing to all the restored stuff for oumultiresponse questions + * + * answer are two (hypen speparated) lists of comma separated question_answers + * the first to specify the order of the answers and the second to specify the + * responses. Note the order list (the first one) can be optional + */ + public function recode_state_answer($state) { + $answer = $state->answer; + $orderarr = array(); + $responsesarr = array(); + $lists = explode(':', $answer); + // if only 1 list, answer is missing the order list, adjust + if (count($lists) == 1) { + $lists[1] = $lists[0]; // here we have the responses + $lists[0] = ''; // here we have the order + } + // Map order + foreach (explode(',', $lists[0]) as $id) { + if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) { + $orderarr[] = $newid; + } + } + // Map responses + foreach (explode(',', $lists[1]) as $id) { + if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) { + $responsesarr[] = $newid; + } + } + // Build the final answer, if not order, only responses + $result = ''; + if (empty($orderarr)) { + $result = implode(',', $responsesarr); + } else { + $result = implode(',', $orderarr) . ':' . implode(',', $responsesarr); + } + 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_oumultiresponse', $fields, 'question_oumultiresponse'); + + return $contents; + } +} diff --git a/question/type/questiontype.php b/question/type/questiontype.php index 048ca224f81..00efdab2b12 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -499,7 +499,7 @@ class question_type { continue; } - // Update an existing answer if possible. + // Update an existing hint if possible. $hint = array_shift($oldhints); if (!$hint) { $hint = new stdClass();