From 31d286d5f1303179b0ae094cf06153cefa119fd8 Mon Sep 17 00:00:00 2001 From: Stephen Bourget Date: Tue, 30 Jan 2018 20:26:12 -0500 Subject: [PATCH 1/4] mod_quizgame: Add Backup / Restore support Fix issue #31 --- .../backup_quizgame_activity_task.class.php | 67 +++++++++++++ backup/moodle2/backup_quizgame_stepslib.php | 76 +++++++++++++++ .../restore_quizgame_activity_task.class.php | 95 +++++++++++++++++++ backup/moodle2/restore_quizgame_stepslib.php | 82 ++++++++++++++++ lib.php | 4 + 5 files changed, 324 insertions(+) create mode 100644 backup/moodle2/backup_quizgame_activity_task.class.php create mode 100644 backup/moodle2/backup_quizgame_stepslib.php create mode 100644 backup/moodle2/restore_quizgame_activity_task.class.php create mode 100644 backup/moodle2/restore_quizgame_stepslib.php diff --git a/backup/moodle2/backup_quizgame_activity_task.class.php b/backup/moodle2/backup_quizgame_activity_task.class.php new file mode 100644 index 0000000..822970f --- /dev/null +++ b/backup/moodle2/backup_quizgame_activity_task.class.php @@ -0,0 +1,67 @@ +. + +/** + * @package mod_quizgame + * @subpackage backup-moodle2 + * @copyright 2018 Stephen Bourget + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); +require_once($CFG->dirroot . '/mod/quizgame/backup/moodle2/backup_quizgame_stepslib.php'); // Because it exists (must). + +/** + * quizgame backup task that provides all the settings and steps to perform one + * complete backup of the activity + */ +class backup_quizgame_activity_task extends backup_activity_task { + + /** + * Define (add) particular settings this activity can have + */ + protected function define_my_settings() { + // No particular settings for this activity. + } + + /** + * Define (add) particular steps this activity can have + */ + protected function define_my_steps() { + // Quizgame only has one structure step. + $this->add_step(new backup_quizgame_activity_structure_step('quizgame_structure', 'quizgame.xml')); + } + + /** + * Code the transformations to perform in the activity in + * order to get transportable (encoded) links + */ + static public function encode_content_links($content) { + global $CFG; + + $base = preg_quote($CFG->wwwroot, "/"); + + // Link to the list of quizgames. + $search = "/(".$base."\/mod\/quizgame\/index.php\?id\=)([0-9]+)/"; + $content = preg_replace($search, '$@QUIZGAMEINDEX*$2@$', $content); + + // Link to quizgame view by moduleid. + $search = "/(".$base."\/mod\/quizgame\/view.php\?id\=)([0-9]+)/"; + $content = preg_replace($search, '$@QUIZGAMEVIEWBYID*$2@$', $content); + + return $content; + } +} diff --git a/backup/moodle2/backup_quizgame_stepslib.php b/backup/moodle2/backup_quizgame_stepslib.php new file mode 100644 index 0000000..684f173 --- /dev/null +++ b/backup/moodle2/backup_quizgame_stepslib.php @@ -0,0 +1,76 @@ +. + +/** + * @package mod_quizgame + * @subpackage backup-moodle2 + * @copyright 2018 Stephen Bourget + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Define the complete quizgame structure for backup, with file and id annotations + */ +class backup_quizgame_activity_structure_step extends backup_activity_structure_step { + + protected function define_structure() { + + // To know if we are including userinfo. + $userinfo = $this->get_setting_value('userinfo'); + + // Define each element separated. + $quizgame = new backup_nested_element('quizgame', array('id'), array( + 'course', 'name', 'intro', 'introformat', 'timecreated', + 'timemodified', 'questioncategory', 'grade')); + + $scores = new backup_nested_element('scores'); + + $score = new backup_nested_element('score', array('id'), array( + 'quizgameid', 'userid', 'score')); + // Build the tree. + + $quizgame->add_child($scores); + $scores->add_child($score); + + // Define sources. + + $quizgame->set_source_table('quizgame', array('id' => backup::VAR_ACTIVITYID)); + + // All the rest of elements only happen if we are including user info. + if ($userinfo) { + + $score->set_source_sql(' + SELECT * + FROM {quizgame_scores} + WHERE quizgameid = ?', + array(backup::VAR_PARENTID)); + + } + + // Define id annotations. + $score->annotate_ids('user', 'userid'); + + + // Define file annotations. + $quizgame->annotate_files('mod_quizgame', 'intro', null); // This file area hasn't itemid. + + // Return the root element (quizgame), wrapped into standard activity structure. + return $this->prepare_activity_structure($quizgame); + + } +} diff --git a/backup/moodle2/restore_quizgame_activity_task.class.php b/backup/moodle2/restore_quizgame_activity_task.class.php new file mode 100644 index 0000000..2612655 --- /dev/null +++ b/backup/moodle2/restore_quizgame_activity_task.class.php @@ -0,0 +1,95 @@ +. + +/** + * @package mod_quizgame + * @subpackage backup-moodle2 + * @copyright 2018 Stephen Bourget + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); +require_once($CFG->dirroot . '/mod/quizgame/backup/moodle2/restore_quizgame_stepslib.php'); // Because it exists (must). + +/** + * quizgame restore task that provides all the settings and steps to perform one + * complete restore of the activity + */ +class restore_quizgame_activity_task extends restore_activity_task { + + /** + * Define (add) particular settings this activity can have + */ + protected function define_my_settings() { + // No particular settings for this activity. + } + + /** + * Define (add) particular steps this activity can have. + */ + protected function define_my_steps() { + // Quizgame only has one structure step. + $this->add_step(new restore_quizgame_activity_structure_step('quizgame_structure', 'quizgame.xml')); + } + + /** + * Define the contents in the activity that must be + * processed by the link decoder + */ + static public function define_decode_contents() { + $contents = array(); + + $contents[] = new restore_decode_content('quizgame', array('intro'), 'quizgame'); + + return $contents; + } + + /** + * Define the decoding rules for links belonging + * to the activity to be executed by the link decoder + */ + static public function define_decode_rules() { + $rules = array(); + + $rules[] = new restore_decode_rule('QUIZVENTUREVIEWBYID', '/mod/quizgame/view.php?id=$1', 'course_module'); + $rules[] = new restore_decode_rule('QUIZVENTUREINDEX', '/mod/quizgame/index.php?id=$1', 'course'); + + return $rules; + + } + + /** + * 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 this rules are applied when restoring course logs + * by the restore final task, but are defined here at + * activity level. All them are rules not linked to any module instance (cmid = 0) + */ + static public function define_restore_log_rules_for_course() { + $rules = array(); + + // Fix old wrong uses (missing extension). + $rules[] = new restore_log_rule('quizgame', 'view all', 'index?id={course}', null, + null, null, 'index.php?id={course}'); + $rules[] = new restore_log_rule('quizgame', 'view all', 'index.php?id={course}', null); + + return $rules; + } + +} diff --git a/backup/moodle2/restore_quizgame_stepslib.php b/backup/moodle2/restore_quizgame_stepslib.php new file mode 100644 index 0000000..8cb826a --- /dev/null +++ b/backup/moodle2/restore_quizgame_stepslib.php @@ -0,0 +1,82 @@ +. + +/** + * @package mod_quizgame + * @subpackage backup-moodle2 + * @copyright 2018 Stephen Bourget + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Structure step to restore one quizgame activity. + */ +class restore_quizgame_activity_structure_step extends restore_activity_structure_step { + + protected function define_structure() { + + $paths = array(); + $userinfo = $this->get_setting_value('userinfo'); + + $paths[] = new restore_path_element('quizgame', '/activity/quizgame'); + if ($userinfo) { + $paths[] = new restore_path_element('quizgame_score', '/activity/quizgame/scores/score'); + } + + // Return the paths wrapped into standard activity structure. + return $this->prepare_activity_structure($paths); + } + + protected function process_quizgame($data) { + global $DB; + + $data = (object)$data; + $oldid = $data->id; + $data->course = $this->get_courseid(); + + $data->userid = $this->get_mappingid('user', $data->userid); + + // Map the category in the QB. + $data->questioncategory = $this->get_mappingid('question_category', $data->questioncategory); + // Insert the quizgame record. + $newitemid = $DB->insert_record('quizgame', $data); + // Immediately after inserting "activity" record, call this. + $this->apply_activity_instance($newitemid); + $this->set_mapping('quizgame', $oldid, $newitemid); + } + + protected function process_quizgame_score($data) { + global $DB; + + $data = (object)$data; + $oldid = $data->id; + + $data->quizgameid = $this->get_new_parentid('quizgame'); + $data->userid = $this->get_mappingid('user', $data->userid); + + $newitemid = $DB->insert_record('quizgame_scores', $data); + $this->set_mapping('quizgame_scores', $oldid, $newitemid, true); // Files by this itemname. + } + + + protected function after_execute() { + // Add quizgame related files, no need to match by itemname (just internally handled context). + $this->add_related_files('mod_quizgame', 'intro', null); + + } +} diff --git a/lib.php b/lib.php index 1614915..ce67f9b 100644 --- a/lib.php +++ b/lib.php @@ -43,6 +43,10 @@ function quizgame_supports($feature) { return true; case FEATURE_SHOW_DESCRIPTION: return true; + case FEATURE_USES_QUESTIONS: + return true; + case FEATURE_BACKUP_MOODLE2: + return true; default: return null; } From 72b425dc9f5df299d5f039b0bc8b6ed896b6656e Mon Sep 17 00:00:00 2001 From: Stephen Bourget Date: Sat, 3 Feb 2018 11:24:58 -0500 Subject: [PATCH 2/4] mod_quizgame: Add Backup / Restore support Fix debugging notice --- backup/moodle2/restore_quizgame_stepslib.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/backup/moodle2/restore_quizgame_stepslib.php b/backup/moodle2/restore_quizgame_stepslib.php index 8cb826a..79e02a6 100644 --- a/backup/moodle2/restore_quizgame_stepslib.php +++ b/backup/moodle2/restore_quizgame_stepslib.php @@ -49,8 +49,6 @@ protected function process_quizgame($data) { $oldid = $data->id; $data->course = $this->get_courseid(); - $data->userid = $this->get_mappingid('user', $data->userid); - // Map the category in the QB. $data->questioncategory = $this->get_mappingid('question_category', $data->questioncategory); // Insert the quizgame record. From 6712bdd5792de6a430823ce0cf7d46e035ac0f4d Mon Sep 17 00:00:00 2001 From: Stephen Bourget Date: Mon, 12 Feb 2018 19:33:41 -0500 Subject: [PATCH 3/4] mod_quizgame: Backup / Restore support Improve logic for mapping to the question bank. --- backup/moodle2/restore_quizgame_stepslib.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/backup/moodle2/restore_quizgame_stepslib.php b/backup/moodle2/restore_quizgame_stepslib.php index 79e02a6..b96d445 100644 --- a/backup/moodle2/restore_quizgame_stepslib.php +++ b/backup/moodle2/restore_quizgame_stepslib.php @@ -50,7 +50,20 @@ protected function process_quizgame($data) { $data->course = $this->get_courseid(); // Map the category in the QB. - $data->questioncategory = $this->get_mappingid('question_category', $data->questioncategory); + // Data is stored either as id / context or just id. + $category = explode(",", $data->questioncategory); + if (count($category) > 1) { + // Question category here was stored as id,context. + // Get the new mapping to the category. + $newcat = $this->get_mappingid('question_category', $category[0]); + // Now get the context for this category. + $newcontext = $DB->get_field('question_categories', 'contextid', array('id' => $newcat)); + // Assemble the field data. + $data->questioncategory = implode(',', array($newcat, $newcontext)); + } else { + // The qustion category was just stored as an ID, so find the new mapping. + $data->questioncategory = $this->get_mappingid('question_category', $category); + } // Insert the quizgame record. $newitemid = $DB->insert_record('quizgame', $data); // Immediately after inserting "activity" record, call this. From 2be5c2beff3cddb50e475e6a7c958ab8d7b96e3f Mon Sep 17 00:00:00 2001 From: Stephen Bourget Date: Sat, 17 Feb 2018 20:59:59 -0500 Subject: [PATCH 4/4] mod_quizgame: improve handling of activity only restores. --- backup/moodle2/restore_quizgame_stepslib.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/backup/moodle2/restore_quizgame_stepslib.php b/backup/moodle2/restore_quizgame_stepslib.php index b96d445..c3930e2 100644 --- a/backup/moodle2/restore_quizgame_stepslib.php +++ b/backup/moodle2/restore_quizgame_stepslib.php @@ -47,6 +47,7 @@ protected function process_quizgame($data) { $data = (object)$data; $oldid = $data->id; + $oldcourse = $data->course; $data->course = $this->get_courseid(); // Map the category in the QB. @@ -59,7 +60,22 @@ protected function process_quizgame($data) { // Now get the context for this category. $newcontext = $DB->get_field('question_categories', 'contextid', array('id' => $newcat)); // Assemble the field data. - $data->questioncategory = implode(',', array($newcat, $newcontext)); + if (!empty($newcat)) { + $data->questioncategory = implode(',', array($newcat, $newcontext)); + } else { + if (!$this->task->is_samesite() || $data->course != $oldcourse) { + // We cannot map to the question category. + // They were not included in the backup since they were at a higher context. + // This can happen when we are backing up the activity alone and trying to restore it elsewhere. + $this->log('question category ' . $category[0] . ' was associated with the quizgame ' . + $data->id . ' but cannot actually be used as it is not available in this backup. ' . + 'The category needs to be re-selected.', backup::LOG_INFO); + + // Remove the old data. + $data->questioncategory = ""; + } + } + } else { // The qustion category was just stored as an ID, so find the new mapping. $data->questioncategory = $this->get_mappingid('question_category', $category);