first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+199
View File
@@ -0,0 +1,199 @@
<?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/>.
/**
* Provides support for the conversion of moodle1 backup to the moodle2 format
* Based off of a template @ http://docs.moodle.org/dev/Backup_1.9_conversion_for_developers
*
* @package mod_quiz
* @copyright 2011 Aparup Banerjee <aparup@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Quiz conversion handler
*/
class moodle1_mod_quiz_handler extends moodle1_mod_handler {
/** @var moodle1_file_manager */
protected $fileman = null;
/** @var int cmid */
protected $moduleid = null;
/**
* Declare the paths in moodle.xml we are able to convert
*
* The method returns list of {@link convert_path} instances.
* For each path returned, the corresponding conversion method must be
* defined.
*
* Note that the path /MOODLE_BACKUP/COURSE/MODULES/MOD/QUIZ does not
* actually exist in the file. The last element with the module name was
* appended by the moodle1_converter class.
*
* @return array of {@link convert_path} instances
*/
public function get_paths() {
return [
new convert_path(
'quiz', '/MOODLE_BACKUP/COURSE/MODULES/MOD/QUIZ',
[
'newfields' => [
'showuserpicture' => 0,
'questiondecimalpoints' => -1,
'introformat' => 0,
'showblocks' => 0,
],
]
),
new convert_path('quiz_question_instances',
'/MOODLE_BACKUP/COURSE/MODULES/MOD/QUIZ/QUESTION_INSTANCES'),
new convert_path('quiz_question_instance',
'/MOODLE_BACKUP/COURSE/MODULES/MOD/QUIZ/QUESTION_INSTANCES/QUESTION_INSTANCE',
[
'renamefields' => [
'question' => 'questionid',
'grade' => 'maxmark',
],
]
),
new convert_path('quiz_feedbacks',
'/MOODLE_BACKUP/COURSE/MODULES/MOD/QUIZ/FEEDBACKS'),
new convert_path('quiz_feedback',
'/MOODLE_BACKUP/COURSE/MODULES/MOD/QUIZ/FEEDBACKS/FEEDBACK',
[
'newfields' => [
'feedbacktextformat' => FORMAT_HTML,
]
]
)
];
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/QUIZ
* data available
*/
public function process_quiz($data) {
global $CFG;
// Replay the upgrade step 2008081501.
if (is_null($data['sumgrades'])) {
$data['sumgrades'] = 0;
// TODO for user data: quiz_attempts SET sumgrades=0 WHERE sumgrades IS NULL.
// TODO for user data: quiz_grades.grade should be not be null , convert to default 0.
}
// Replay the upgrade step 2009042000.
if ($CFG->texteditors !== 'textarea') {
$data['intro'] = text_to_html($data['intro'], false, false, true);
$data['introformat'] = FORMAT_HTML;
}
// Replay the upgrade step 2009031001.
$data['timelimit'] *= 60;
// Get the course module id and context id.
$instanceid = $data['id'];
$cminfo = $this->get_cminfo($instanceid);
$this->moduleid = $cminfo['id'];
$contextid = $this->converter->get_contextid(CONTEXT_MODULE, $this->moduleid);
// Get a fresh new file manager for this instance.
$this->fileman = $this->converter->get_file_manager($contextid, 'mod_quiz');
// Convert course files embedded into the intro.
$this->fileman->filearea = 'intro';
$this->fileman->itemid = 0;
$data['intro'] = moodle1_converter::migrate_referenced_files(
$data['intro'], $this->fileman);
// Start writing quiz.xml.
$this->open_xml_writer("activities/quiz_{$this->moduleid}/quiz.xml");
$this->xmlwriter->begin_tag('activity', ['id' => $instanceid,
'moduleid' => $this->moduleid, 'modulename' => 'quiz',
'contextid' => $contextid]);
$this->xmlwriter->begin_tag('quiz', ['id' => $instanceid]);
foreach ($data as $field => $value) {
if ($field <> 'id') {
$this->xmlwriter->full_tag($field, $value);
}
}
return $data;
}
public function on_quiz_question_instances_start() {
$this->xmlwriter->begin_tag('question_instances');
}
public function on_quiz_question_instances_end() {
$this->xmlwriter->end_tag('question_instances');
}
public function process_quiz_question_instance($data) {
$this->write_xml('question_instance', $data, ['/question_instance/id']);
}
public function on_quiz_feedbacks_start() {
$this->xmlwriter->begin_tag('feedbacks');
}
public function on_quiz_feedbacks_end() {
$this->xmlwriter->end_tag('feedbacks');
}
public function process_quiz_feedback($data) {
// Replay the upgrade step 2010122302.
if (is_null($data['mingrade'])) {
$data['mingrade'] = 0;
}
if (is_null($data['maxgrade'])) {
$data['maxgrade'] = 0;
}
$this->write_xml('feedback', $data, ['/feedback/id']);
}
/**
* This is executed when we reach the closing </MOD> tag of our 'quiz' path
*/
public function on_quiz_end() {
// Append empty <overrides> subpath element.
$this->write_xml('overrides', []);
// Finish writing quiz.xml.
$this->xmlwriter->end_tag('quiz');
$this->xmlwriter->end_tag('activity');
$this->close_xml_writer();
// Write inforef.xml.
$this->open_xml_writer("activities/quiz_{$this->moduleid}/inforef.xml");
$this->xmlwriter->begin_tag('inforef');
$this->xmlwriter->begin_tag('fileref');
foreach ($this->fileman->get_fileids() as $fileid) {
$this->write_xml('file', ['id' => $fileid]);
}
$this->xmlwriter->end_tag('fileref');
$this->xmlwriter->end_tag('inforef');
$this->close_xml_writer();
}
}
@@ -0,0 +1,55 @@
<?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/>.
/**
* Defines the base class for quiz access plugins backup code.
*
* @package mod_quiz
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Base class for backing up all the quiz settings and attempt data for an
* access rule quiz sub-plugin.
*
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_mod_quiz_access_subplugin extends backup_subplugin {
/**
* Use this method to describe the XML structure required to store your
* sub-plugin's settings for a particular quiz, and how that data is stored
* in the database.
*/
protected function define_quiz_subplugin_structure() {
// Do nothing by default.
}
/**
* Use this method to describe the XML structure required to store your
* sub-plugin's settings for a particular quiz attempt, and how that data
* is stored in the database.
*/
protected function define_attempt_subplugin_structure() {
// Do nothing by default.
}
}
@@ -0,0 +1,92 @@
<?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/>.
/**
* Defines backup_quiz_activity_task class
*
* @package mod_quiz
* @category backup
* @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();
require_once($CFG->dirroot . '/mod/quiz/backup/moodle2/backup_quiz_stepslib.php');
/**
* Provides the steps to perform one complete backup of the Quiz instance
*
* @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_quiz_activity_task extends backup_activity_task {
/**
* No specific settings for this activity
*/
protected function define_my_settings() {
}
/**
* Defines backup steps to store the instance data and required questions
*/
protected function define_my_steps() {
// Generate the quiz.xml file containing all the quiz information
// and annotating used questions.
$this->add_step(new backup_quiz_activity_structure_step('quiz_structure', 'quiz.xml'));
// Note: Following steps must be present
// in all the activities using question banks (only quiz for now)
// TODO: Specialise these step to a new subclass of backup_activity_task.
// Process all the annotated questions to calculate the question
// categories needing to be included in backup for this activity
// plus the categories belonging to the activity context itself.
$this->add_step(new backup_calculate_question_categories('activity_question_categories'));
// Clean backup_temp_ids table from questions. We already
// have used them to detect question_categories and aren't
// needed anymore.
$this->add_step(new backup_delete_temp_questions('clean_temp_questions'));
}
/**
* Encodes URLs to the index.php and view.php scripts
*
* @param string $content some HTML text that eventually contains URLs to the activity instance scripts
* @return string the content with the URLs encoded
*/
public static function encode_content_links($content) {
global $CFG;
$base = preg_quote($CFG->wwwroot, '/');
// Link to the list of quizzes.
$search="/(".$base."\/mod\/quiz\/index.php\?id\=)([0-9]+)/";
$content= preg_replace($search, '$@QUIZINDEX*$2@$', $content);
// Link to quiz view by moduleid.
$search="/(".$base."\/mod\/quiz\/view.php\?id\=)([0-9]+)/";
$content= preg_replace($search, '$@QUIZVIEWBYID*$2@$', $content);
// Link to quiz view by quizid.
$search="/(".$base."\/mod\/quiz\/view.php\?q\=)([0-9]+)/";
$content= preg_replace($search, '$@QUIZVIEWBYQ*$2@$', $content);
return $content;
}
}
@@ -0,0 +1,169 @@
<?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/>.
/**
* Define all the backup steps that will be used by the backup_quiz_activity_task.
*
* @package mod_quiz
* @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 backup_quiz_activity_structure_step extends backup_questions_activity_structure_step {
protected function define_structure() {
// To know if we are including userinfo.
$userinfo = $this->get_setting_value('userinfo');
// Define each element separated.
$quiz = new backup_nested_element('quiz', ['id'], [
'name', 'intro', 'introformat', 'timeopen', 'timeclose', 'timelimit',
'overduehandling', 'graceperiod', 'preferredbehaviour', 'canredoquestions', 'attempts_number',
'attemptonlast', 'grademethod', 'decimalpoints', 'questiondecimalpoints',
'reviewattempt', 'reviewcorrectness', 'reviewmaxmarks', 'reviewmarks',
'reviewspecificfeedback', 'reviewgeneralfeedback',
'reviewrightanswer', 'reviewoverallfeedback',
'questionsperpage', 'navmethod', 'shuffleanswers',
'sumgrades', 'grade', 'timecreated',
'timemodified', 'password', 'subnet', 'browsersecurity',
'delay1', 'delay2', 'showuserpicture', 'showblocks', 'completionattemptsexhausted',
'completionminattempts', 'allowofflineattempts']);
// Define elements for access rule subplugin settings.
$this->add_subplugin_structure('quizaccess', $quiz, true);
$quizgradeitems = new backup_nested_element('quiz_grade_items');
$quizgradeitem = new backup_nested_element('quiz_grade_item', ['id'], ['sortorder', 'name']);
$qinstances = new backup_nested_element('question_instances');
$qinstance = new backup_nested_element('question_instance', ['id'],
['quizid', 'slot', 'page', 'displaynumber', 'requireprevious', 'maxmark', 'quizgradeitemid']);
$this->add_question_references($qinstance, 'mod_quiz', 'slot');
$this->add_question_set_references($qinstance, 'mod_quiz', 'slot');
$sections = new backup_nested_element('sections');
$section = new backup_nested_element('section', ['id'], ['firstslot', 'heading', 'shufflequestions']);
$feedbacks = new backup_nested_element('feedbacks');
$feedback = new backup_nested_element('feedback', ['id'], ['feedbacktext', 'feedbacktextformat', 'mingrade', 'maxgrade']);
$overrides = new backup_nested_element('overrides');
$override = new backup_nested_element('override', ['id'], [
'userid', 'groupid', 'timeopen', 'timeclose',
'timelimit', 'attempts', 'password']);
$grades = new backup_nested_element('grades');
$grade = new backup_nested_element('grade', ['id'], ['userid', 'gradeval', 'timemodified']);
$attempts = new backup_nested_element('attempts');
$attempt = new backup_nested_element('attempt', ['id'], [
'userid', 'attemptnum', 'uniqueid', 'layout', 'currentpage', 'preview',
'state', 'timestart', 'timefinish', 'timemodified', 'timemodifiedoffline',
'timecheckstate', 'sumgrades', 'gradednotificationsenttime']);
// This module is using questions, so produce the related question states and sessions
// attaching them to the $attempt element based in 'uniqueid' matching.
$this->add_question_usages($attempt, 'uniqueid');
// Define elements for access rule subplugin attempt data.
$this->add_subplugin_structure('quizaccess', $attempt, true);
// Build the tree.
$quiz->add_child($quizgradeitems);
$quizgradeitems->add_child($quizgradeitem);
$quiz->add_child($qinstances);
$qinstances->add_child($qinstance);
$quiz->add_child($sections);
$sections->add_child($section);
$quiz->add_child($feedbacks);
$feedbacks->add_child($feedback);
$quiz->add_child($overrides);
$overrides->add_child($override);
$quiz->add_child($grades);
$grades->add_child($grade);
$quiz->add_child($attempts);
$attempts->add_child($attempt);
// Define sources.
$quiz->set_source_table('quiz', ['id' => backup::VAR_ACTIVITYID]);
$quizgradeitem->set_source_table('quiz_grade_items', ['quizid' => backup::VAR_PARENTID]);
$qinstance->set_source_table('quiz_slots', ['quizid' => backup::VAR_PARENTID]);
$section->set_source_table('quiz_sections', ['quizid' => backup::VAR_PARENTID]);
$feedback->set_source_table('quiz_feedback', ['quizid' => backup::VAR_PARENTID]);
// Quiz overrides to backup are different depending of user info.
$overrideparams = ['quiz' => backup::VAR_PARENTID];
if (!$userinfo) { // Without userinfo, skip user overrides.
$overrideparams['userid'] = backup_helper::is_sqlparam(null);
}
// Skip group overrides if not including groups.
$groupinfo = $this->get_setting_value('groups');
if (!$groupinfo) {
$overrideparams['groupid'] = backup_helper::is_sqlparam(null);
}
$override->set_source_table('quiz_overrides', $overrideparams);
// All the rest of elements only happen if we are including user info.
if ($userinfo) {
$grade->set_source_table('quiz_grades', ['quiz' => backup::VAR_PARENTID]);
$attempt->set_source_sql('
SELECT *
FROM {quiz_attempts}
WHERE quiz = :quiz AND preview = 0', ['quiz' => backup::VAR_PARENTID]);
}
// Define source alias.
$quiz->set_source_alias('attempts', 'attempts_number');
$grade->set_source_alias('grade', 'gradeval');
$attempt->set_source_alias('attempt', 'attemptnum');
// Define id annotations.
$override->annotate_ids('user', 'userid');
$override->annotate_ids('group', 'groupid');
$grade->annotate_ids('user', 'userid');
$attempt->annotate_ids('user', 'userid');
// Define file annotations.
$quiz->annotate_files('mod_quiz', 'intro', null); // This file area hasn't itemid.
$feedback->annotate_files('mod_quiz', 'feedback', 'id');
// Return the root element (quiz), wrapped into standard activity structure.
return $this->prepare_activity_structure($quiz);
}
}
@@ -0,0 +1,53 @@
<?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/>.
/**
* Restore code for the quizaccess_honestycheck plugin.
*
* @package mod_quiz
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Base class for restoring up all the quiz settings and attempt data for an
* access rule quiz sub-plugin.
*
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_mod_quiz_access_subplugin extends restore_subplugin {
/**
* Use this method to describe the XML paths that store your sub-plugin's
* settings for a particular quiz.
*/
protected function define_quiz_subplugin_structure() {
// Do nothing by default.
}
/**
* Use this method to describe the XML paths that store your sub-plugin's
* settings for a particular quiz attempt.
*/
protected function define_attempt_subplugin_structure() {
// Do nothing by default.
}
}
@@ -0,0 +1,190 @@
<?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 mod_quiz
* @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();
require_once($CFG->dirroot . '/mod/quiz/backup/moodle2/restore_quiz_stepslib.php');
/**
* quiz restore task that provides all the settings and steps to perform one
* complete restore of the activity
*
* @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_quiz_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() {
// Quiz only has one structure step.
$this->add_step(new restore_quiz_activity_structure_step('quiz_structure', 'quiz.xml'));
}
/**
* Define the contents in the activity that must be
* processed by the link decoder
*/
public static function define_decode_contents() {
$contents = [];
$contents[] = new restore_decode_content('quiz', ['intro'], 'quiz');
$contents[] = new restore_decode_content('quiz_feedback',
['feedbacktext'], 'quiz_feedback');
return $contents;
}
/**
* Define the decoding rules for links belonging
* to the activity to be executed by the link decoder
*/
public static function define_decode_rules() {
$rules = [];
$rules[] = new restore_decode_rule('QUIZVIEWBYID',
'/mod/quiz/view.php?id=$1', 'course_module');
$rules[] = new restore_decode_rule('QUIZVIEWBYQ',
'/mod/quiz/view.php?q=$1', 'quiz');
$rules[] = new restore_decode_rule('QUIZINDEX',
'/mod/quiz/index.php?id=$1', 'course');
return $rules;
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* quiz logs. It must return one array
* of {@link restore_log_rule} objects
*/
public static function define_restore_log_rules() {
$rules = [];
$rules[] = new restore_log_rule('quiz', 'add',
'view.php?id={course_module}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'update',
'view.php?id={course_module}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'view',
'view.php?id={course_module}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'preview',
'view.php?id={course_module}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'report',
'report.php?id={course_module}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'editquestions',
'view.php?id={course_module}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'delete attempt',
'report.php?id={course_module}', '[oldattempt]');
$rules[] = new restore_log_rule('quiz', 'edit override',
'overrideedit.php?id={quiz_override}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'delete override',
'overrides.php.php?cmid={course_module}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'addcategory',
'view.php?id={course_module}', '{question_category}');
$rules[] = new restore_log_rule('quiz', 'view summary',
'summary.php?attempt={quiz_attempt}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'manualgrade',
'comment.php?attempt={quiz_attempt}&question={question}', '{quiz}');
$rules[] = new restore_log_rule('quiz', 'manualgrading',
'report.php?mode=grading&q={quiz}', '{quiz}');
// All the ones calling to review.php have two rules to handle both old and new urls
// in any case they are always converted to new urls on restore.
// TODO: In Moodle 2.x (x >= 5) kill the old rules.
// Note we are using the 'quiz_attempt' mapping because that is the
// one containing the quiz_attempt->ids old an new for quiz-attempt.
$rules[] = new restore_log_rule('quiz', 'attempt',
'review.php?id={course_module}&attempt={quiz_attempt}', '{quiz}',
null, null, 'review.php?attempt={quiz_attempt}');
$rules[] = new restore_log_rule('quiz', 'attempt',
'review.php?attempt={quiz_attempt}', '{quiz}',
null, null, 'review.php?attempt={quiz_attempt}');
// Old an new for quiz-submit.
$rules[] = new restore_log_rule('quiz', 'submit',
'review.php?id={course_module}&attempt={quiz_attempt}', '{quiz}',
null, null, 'review.php?attempt={quiz_attempt}');
$rules[] = new restore_log_rule('quiz', 'submit',
'review.php?attempt={quiz_attempt}', '{quiz}');
// Old an new for quiz-review.
$rules[] = new restore_log_rule('quiz', 'review',
'review.php?id={course_module}&attempt={quiz_attempt}', '{quiz}',
null, null, 'review.php?attempt={quiz_attempt}');
$rules[] = new restore_log_rule('quiz', 'review',
'review.php?attempt={quiz_attempt}', '{quiz}');
// Old an new for quiz-start attemp.
$rules[] = new restore_log_rule('quiz', 'start attempt',
'review.php?id={course_module}&attempt={quiz_attempt}', '{quiz}',
null, null, 'review.php?attempt={quiz_attempt}');
$rules[] = new restore_log_rule('quiz', 'start attempt',
'review.php?attempt={quiz_attempt}', '{quiz}');
// Old an new for quiz-close attemp.
$rules[] = new restore_log_rule('quiz', 'close attempt',
'review.php?id={course_module}&attempt={quiz_attempt}', '{quiz}',
null, null, 'review.php?attempt={quiz_attempt}');
$rules[] = new restore_log_rule('quiz', 'close attempt',
'review.php?attempt={quiz_attempt}', '{quiz}');
// Old an new for quiz-continue attempt.
$rules[] = new restore_log_rule('quiz', 'continue attempt',
'review.php?id={course_module}&attempt={quiz_attempt}', '{quiz}',
null, null, 'review.php?attempt={quiz_attempt}');
$rules[] = new restore_log_rule('quiz', 'continue attempt',
'review.php?attempt={quiz_attempt}', '{quiz}');
// Old an new for quiz-continue attemp.
$rules[] = new restore_log_rule('quiz', 'continue attemp',
'review.php?id={course_module}&attempt={quiz_attempt}', '{quiz}',
null, 'continue attempt', 'review.php?attempt={quiz_attempt}');
$rules[] = new restore_log_rule('quiz', 'continue attemp',
'review.php?attempt={quiz_attempt}', '{quiz}',
null, 'continue attempt');
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)
*/
public static function define_restore_log_rules_for_course() {
$rules = [];
$rules[] = new restore_log_rule('quiz', 'view all', 'index.php?id={course}', null);
return $rules;
}
}
@@ -0,0 +1,658 @@
<?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/>.
use mod_quiz\question\display_options;
/**
* Structure step to restore one quiz activity
*
* @package mod_quiz
* @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 restore_quiz_activity_structure_step extends restore_questions_activity_structure_step {
/**
* @var bool tracks whether the quiz contains at least one section. Before
* Moodle 2.9 quiz sections did not exist, so if the file being restored
* did not contain any, we need to create one in {@link after_execute()}.
*/
protected $sectioncreated = false;
/** @var stdClass|null $currentquizattempt Track the current quiz attempt being restored. */
protected $currentquizattempt = null;
/**
* @var bool when restoring old quizzes (2.8 or before) this records the
* shufflequestionsoption quiz option which has moved to the quiz_sections table.
*/
protected $legacyshufflequestionsoption = false;
/** @var stdClass */
protected $oldquizlayout;
/**
* @var array Track old question ids that need to be removed at the end of the restore.
*/
protected $oldquestionids = [];
protected function define_structure() {
$paths = [];
$userinfo = $this->get_setting_value('userinfo');
$quiz = new restore_path_element('quiz', '/activity/quiz');
$paths[] = $quiz;
// A chance for access subplugings to set up their quiz data.
$this->add_subplugin_structure('quizaccess', $quiz);
$paths[] = new restore_path_element('quiz_grade_item', '/activity/quiz/quiz_grade_items/quiz_grade_item');
$quizquestioninstance = new restore_path_element('quiz_question_instance',
'/activity/quiz/question_instances/question_instance');
$paths[] = $quizquestioninstance;
if ($this->task->get_old_moduleversion() < 2021091700) {
$paths[] = new restore_path_element('quiz_slot_tags',
'/activity/quiz/question_instances/question_instance/tags/tag');
} else {
$this->add_question_references($quizquestioninstance, $paths);
$this->add_question_set_references($quizquestioninstance, $paths);
}
$paths[] = new restore_path_element('quiz_section', '/activity/quiz/sections/section');
$paths[] = new restore_path_element('quiz_feedback', '/activity/quiz/feedbacks/feedback');
$paths[] = new restore_path_element('quiz_override', '/activity/quiz/overrides/override');
if ($userinfo) {
$paths[] = new restore_path_element('quiz_grade', '/activity/quiz/grades/grade');
if ($this->task->get_old_moduleversion() > 2011010100) {
// Restoring from a version 2.1 dev or later.
// Process the new-style attempt data.
$quizattempt = new restore_path_element('quiz_attempt',
'/activity/quiz/attempts/attempt');
$paths[] = $quizattempt;
// Add states and sessions.
$this->add_question_usages($quizattempt, $paths);
// A chance for access subplugings to set up their attempt data.
$this->add_subplugin_structure('quizaccess', $quizattempt);
} else {
// Restoring from a version 2.0.x+ or earlier.
// Upgrade the legacy attempt data.
$quizattempt = new restore_path_element('quiz_attempt_legacy',
'/activity/quiz/attempts/attempt',
true);
$paths[] = $quizattempt;
$this->add_legacy_question_attempt_data($quizattempt, $paths);
}
}
// Return the paths wrapped into standard activity structure.
return $this->prepare_activity_structure($paths);
}
/**
* Process the quiz data.
*
* @param stdClass|array $data
*/
protected function process_quiz($data) {
global $CFG, $DB, $USER;
$data = (object)$data;
$oldid = $data->id;
$data->course = $this->get_courseid();
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
// See MDL-9367.
$data->timeopen = $this->apply_date_offset($data->timeopen);
$data->timeclose = $this->apply_date_offset($data->timeclose);
if (property_exists($data, 'questions')) {
// Needed by {@link process_quiz_attempt_legacy}, in which case it will be present.
$this->oldquizlayout = $data->questions;
}
// The setting quiz->attempts can come both in data->attempts and
// data->attempts_number, handle both. MDL-26229.
if (isset($data->attempts_number)) {
$data->attempts = $data->attempts_number;
unset($data->attempts_number);
}
// The old optionflags and penaltyscheme from 2.0 need to be mapped to
// the new preferredbehaviour. See 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. See 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);
define('QUIZ_OLD_SCORES', 2*0x1041);
define('QUIZ_OLD_FEEDBACK', 4*0x1041);
define('QUIZ_OLD_ANSWERS', 8*0x1041);
define('QUIZ_OLD_SOLUTIONS', 16*0x1041);
define('QUIZ_OLD_GENERALFEEDBACK', 32*0x1041);
define('QUIZ_OLD_OVERALLFEEDBACK', 1*0x4440000);
}
$oldreview = $data->review;
$data->reviewattempt =
display_options::DURING |
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_RESPONSES ?
display_options::IMMEDIATELY_AFTER : 0) |
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_RESPONSES ?
display_options::LATER_WHILE_OPEN : 0) |
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_RESPONSES ?
display_options::AFTER_CLOSE : 0);
$data->reviewcorrectness =
display_options::DURING |
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ?
display_options::IMMEDIATELY_AFTER : 0) |
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ?
display_options::LATER_WHILE_OPEN : 0) |
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ?
display_options::AFTER_CLOSE : 0);
$data->reviewmarks =
display_options::DURING |
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ?
display_options::IMMEDIATELY_AFTER : 0) |
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ?
display_options::LATER_WHILE_OPEN : 0) |
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ?
display_options::AFTER_CLOSE : 0);
$data->reviewspecificfeedback =
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ?
display_options::DURING : 0) |
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ?
display_options::IMMEDIATELY_AFTER : 0) |
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_FEEDBACK ?
display_options::LATER_WHILE_OPEN : 0) |
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_FEEDBACK ?
display_options::AFTER_CLOSE : 0);
$data->reviewgeneralfeedback =
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ?
display_options::DURING : 0) |
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ?
display_options::IMMEDIATELY_AFTER : 0) |
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_GENERALFEEDBACK ?
display_options::LATER_WHILE_OPEN : 0) |
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_GENERALFEEDBACK ?
display_options::AFTER_CLOSE : 0);
$data->reviewrightanswer =
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ?
display_options::DURING : 0) |
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ?
display_options::IMMEDIATELY_AFTER : 0) |
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_ANSWERS ?
display_options::LATER_WHILE_OPEN : 0) |
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_ANSWERS ?
display_options::AFTER_CLOSE : 0);
$data->reviewoverallfeedback =
0 |
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_OVERALLFEEDBACK ?
display_options::IMMEDIATELY_AFTER : 0) |
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_OVERALLFEEDBACK ?
display_options::LATER_WHILE_OPEN : 0) |
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_OVERALLFEEDBACK ?
display_options::AFTER_CLOSE : 0);
}
// New setting in 4.3 needs to be set if not in the backup.
if (!isset($data->reviewmaxmarks)) {
$data->reviewmaxmarks =
display_options::DURING |
display_options::IMMEDIATELY_AFTER |
display_options::LATER_WHILE_OPEN |
display_options::AFTER_CLOSE;
}
// The old popup column from from <= 2.1 need to be mapped to
// the new browsersecurity. See MDL-29627.
if (!isset($data->browsersecurity)) {
if (empty($data->popup)) {
$data->browsersecurity = '-';
} else if ($data->popup == 1) {
$data->browsersecurity = 'securewindow';
} else if ($data->popup == 2) {
// Since 3.9 quizaccess_safebrowser replaced with a new quizaccess_seb.
$data->browsersecurity = '-';
$addsebrule = true;
} else {
$data->preferredbehaviour = '-';
}
unset($data->popup);
} else if ($data->browsersecurity == 'safebrowser') {
// Since 3.9 quizaccess_safebrowser replaced with a new quizaccess_seb.
$data->browsersecurity = '-';
$addsebrule = true;
}
if (!isset($data->overduehandling)) {
$data->overduehandling = get_config('quiz', 'overduehandling');
}
// Old shufflequestions setting is now stored in quiz sections,
// so save it here if necessary so it is available when we need it.
$this->legacyshufflequestionsoption = !empty($data->shufflequestions);
// Insert the quiz record.
$newitemid = $DB->insert_record('quiz', $data);
// Immediately after inserting "activity" record, call this.
$this->apply_activity_instance($newitemid);
// Process Safe Exam Browser settings for backups taken in Moodle < 3.9.
if (!empty($addsebrule)) {
$sebsettings = new stdClass();
$sebsettings->quizid = $newitemid;
$sebsettings->cmid = $this->task->get_moduleid();
$sebsettings->templateid = 0;
$sebsettings->requiresafeexambrowser = \quizaccess_seb\settings_provider::USE_SEB_CLIENT_CONFIG;
$sebsettings->showsebtaskbar = null;
$sebsettings->showwificontrol = null;
$sebsettings->showreloadbutton = null;
$sebsettings->showtime = null;
$sebsettings->showkeyboardlayout = null;
$sebsettings->allowuserquitseb = null;
$sebsettings->quitpassword = null;
$sebsettings->linkquitseb = null;
$sebsettings->userconfirmquit = null;
$sebsettings->enableaudiocontrol = null;
$sebsettings->muteonstartup = null;
$sebsettings->allowspellchecking = null;
$sebsettings->allowreloadinexam = null;
$sebsettings->activateurlfiltering = null;
$sebsettings->filterembeddedcontent = null;
$sebsettings->expressionsallowed = null;
$sebsettings->regexallowed = null;
$sebsettings->expressionsblocked = null;
$sebsettings->regexblocked = null;
$sebsettings->allowedbrowserexamkeys = null;
$sebsettings->showsebdownloadlink = 1;
$sebsettings->usermodified = $USER->id;
$sebsettings->timecreated = time();
$sebsettings->timemodified = time();
$DB->insert_record('quizaccess_seb_quizsettings', $sebsettings);
}
// If we are dealing with a backup from < 4.0 then we need to move completionpass to core.
if (!empty($data->completionpass)) {
$params = ['id' => $this->task->get_moduleid()];
$DB->set_field('course_modules', 'completionpassgrade', $data->completionpass, $params);
}
}
/**
* Process a quiz grade items.
*
* @param stdClass|array $data
*/
protected function process_quiz_grade_item($data): void {
global $DB;
$data = (object) $data;
$data->quizid = $this->get_new_parentid('quiz');
$oldid = $data->id;
$newitemid = $DB->insert_record('quiz_grade_items', $data);
$this->set_mapping('quiz_grade_item', $oldid, $newitemid, true);
}
/**
* Process the data for pre 4.0 quiz data where the question_references and question_set_references table introduced.
*
* @param stdClass|array $data
*/
protected function process_quiz_question_legacy_instance($data) {
global $DB;
$questionid = $this->get_mappingid('question', $data->questionid);
$sql = 'SELECT qbe.id as questionbankentryid,
qc.contextid as questioncontextid,
qc.id as category,
qv.version,
q.qtype,
q.id as questionid
FROM {question} q
JOIN {question_versions} qv ON qv.questionid = q.id
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid
WHERE q.id = ?';
$question = $DB->get_record_sql($sql, [$questionid]);
$module = $DB->get_record('quiz', ['id' => $data->quizid]);
if ($question->qtype === 'random') {
// Set reference data.
$questionsetreference = new stdClass();
$questionsetreference->usingcontextid = context_module::instance(get_coursemodule_from_instance(
"quiz", $module->id, $module->course)->id)->id;
$questionsetreference->component = 'mod_quiz';
$questionsetreference->questionarea = 'slot';
$questionsetreference->itemid = $data->id;
// If, in the orginal quiz that was backed up, this random question was pointing to a
// category in the quiz question bank, then (for reasons explained in {@see restore_move_module_questions_categories})
// right now, $question->questioncontextid will incorrectly point to the course contextid.
// This will get fixed up later in restore_move_module_questions_categories
// as part of moving the question categories to the right place.
$questionsetreference->questionscontextid = $question->questioncontextid;
$filtercondition = new stdClass();
$filtercondition->questioncategoryid = $question->category;
$filtercondition->includingsubcategories = $data->includingsubcategories ?? false;
$questionsetreference->filtercondition = json_encode($filtercondition);
$DB->insert_record('question_set_references', $questionsetreference);
$this->oldquestionids[$question->questionid] = 1;
} else {
// Reference data.
$questionreference = new \stdClass();
$questionreference->usingcontextid = context_module::instance(get_coursemodule_from_instance(
"quiz", $module->id, $module->course)->id)->id;
$questionreference->component = 'mod_quiz';
$questionreference->questionarea = 'slot';
$questionreference->itemid = $data->id;
$questionreference->questionbankentryid = $question->questionbankentryid;
$questionreference->version = null; // Default to Always latest.
$DB->insert_record('question_references', $questionreference);
}
}
/**
* Process quiz slots.
*
* @param stdClass|array $data
*/
protected function process_quiz_question_instance($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
// Backwards compatibility for old field names (MDL-43670).
if (!isset($data->questionid) && isset($data->question)) {
$data->questionid = $data->question;
}
if (!isset($data->maxmark) && isset($data->grade)) {
$data->maxmark = $data->grade;
}
if (!property_exists($data, 'slot')) {
$page = 1;
$slot = 1;
foreach (explode(',', $this->oldquizlayout) as $item) {
if ($item == 0) {
$page += 1;
continue;
}
if (isset($data->questionid) && $item == $data->questionid) {
$data->slot = $slot;
$data->page = $page;
break;
}
$slot += 1;
}
}
if (!property_exists($data, 'slot')) {
// There was a question_instance in the backup file for a question
// that was not actually in the quiz. Drop it.
$this->log('question ' . $data->questionid . ' was associated with quiz ' .
$this->get_new_parentid('quiz') . ' but not actually used. ' .
'The instance has been ignored.', backup::LOG_INFO);
return;
}
if (isset($data->quizgradeitemid)) {
$data->quizgradeitemid = $this->get_mappingid('quiz_grade_item', $data->quizgradeitemid);
}
$data->quizid = $this->get_new_parentid('quiz');
$newitemid = $DB->insert_record('quiz_slots', $data);
// Add mapping, restore of slot tags (for random questions) need it.
$this->set_mapping('quiz_question_instance', $oldid, $newitemid);
if ($this->task->get_old_moduleversion() < 2022020300) {
$data->id = $newitemid;
$this->process_quiz_question_legacy_instance($data);
}
}
/**
* Process a quiz_slot_tags to restore the tags to the new structure.
*
* @param stdClass|array $data The quiz_slot_tags data
*/
protected function process_quiz_slot_tags($data) {
global $DB;
$data = (object) $data;
$slotid = $this->get_new_parentid('quiz_question_instance');
if ($this->task->is_samesite() && $tag = core_tag_tag::get($data->tagid, 'id, name')) {
$data->tagname = $tag->name;
} else if ($tag = core_tag_tag::get_by_name(0, $data->tagname, 'id, name')) {
$data->tagid = $tag->id;
} else {
$data->tagid = null;
$data->tagname = $tag->name;
}
$tagstring = "{$data->tagid},{$data->tagname}";
$setreferencedata = $DB->get_record('question_set_references',
['itemid' => $slotid, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
$filtercondition = json_decode($setreferencedata->filtercondition);
$filtercondition->tags[] = $tagstring;
$setreferencedata->filtercondition = json_encode($filtercondition);
$DB->update_record('question_set_references', $setreferencedata);
}
protected function process_quiz_section($data) {
global $DB;
$data = (object) $data;
$data->quizid = $this->get_new_parentid('quiz');
$oldid = $data->id;
$newitemid = $DB->insert_record('quiz_sections', $data);
$this->sectioncreated = true;
$this->set_mapping('quiz_section', $oldid, $newitemid, true);
}
protected function process_quiz_feedback($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->quizid = $this->get_new_parentid('quiz');
$newitemid = $DB->insert_record('quiz_feedback', $data);
$this->set_mapping('quiz_feedback', $oldid, $newitemid, true); // Has related files.
}
protected function process_quiz_override($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
// Based on userinfo, we'll restore user overides or no.
$userinfo = $this->get_setting_value('userinfo');
// Skip user overrides if we are not restoring userinfo.
if (!$userinfo && !is_null($data->userid)) {
return;
}
$data->quiz = $this->get_new_parentid('quiz');
if ($data->userid !== null) {
$data->userid = $this->get_mappingid('user', $data->userid);
}
if ($data->groupid !== null) {
$data->groupid = $this->get_mappingid('group', $data->groupid);
}
// Skip if there is no user and no group data.
if (empty($data->userid) && empty($data->groupid)) {
return;
}
$data->timeopen = $this->apply_date_offset($data->timeopen);
$data->timeclose = $this->apply_date_offset($data->timeclose);
$newitemid = $DB->insert_record('quiz_overrides', $data);
// Add mapping, restore of logs needs it.
$this->set_mapping('quiz_override', $oldid, $newitemid);
}
protected function process_quiz_grade($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->quiz = $this->get_new_parentid('quiz');
$data->userid = $this->get_mappingid('user', $data->userid);
$data->grade = $data->gradeval;
$DB->insert_record('quiz_grades', $data);
}
protected function process_quiz_attempt($data) {
$data = (object)$data;
$data->quiz = $this->get_new_parentid('quiz');
$data->attempt = $data->attemptnum;
// Get user mapping, return early if no mapping found for the quiz attempt.
$olduserid = $data->userid;
$data->userid = $this->get_mappingid('user', $olduserid, 0);
if ($data->userid === 0) {
$this->log('Mapped user ID not found for user ' . $olduserid . ', quiz ' . $this->get_new_parentid('quiz') .
', attempt ' . $data->attempt . '. Skipping quiz attempt', backup::LOG_INFO);
$this->currentquizattempt = null;
return;
}
if (!empty($data->timecheckstate)) {
$data->timecheckstate = $this->apply_date_offset($data->timecheckstate);
} else {
$data->timecheckstate = 0;
}
if (!isset($data->gradednotificationsenttime)) {
// For attempts restored from old Moodle sites before this field
// existed, we never want to send emails.
$data->gradednotificationsenttime = $data->timefinish;
}
// Deals with up-grading pre-2.3 back-ups to 2.3+.
if (!isset($data->state)) {
if ($data->timefinish > 0) {
$data->state = 'finished';
} else {
$data->state = 'inprogress';
}
}
// The data is actually inserted into the database later in inform_new_usage_id.
$this->currentquizattempt = clone($data);
}
protected function process_quiz_attempt_legacy($data) {
global $DB;
$this->process_quiz_attempt($data);
$quiz = $DB->get_record('quiz', ['id' => $this->get_new_parentid('quiz')]);
$quiz->oldquestions = $this->oldquizlayout;
$this->process_legacy_quiz_attempt_data($data, $quiz);
}
protected function inform_new_usage_id($newusageid) {
global $DB;
$data = $this->currentquizattempt;
if ($data === null) {
return;
}
$oldid = $data->id;
$data->uniqueid = $newusageid;
$newitemid = $DB->insert_record('quiz_attempts', $data);
// Save quiz_attempt->id mapping, because logs use it.
$this->set_mapping('quiz_attempt', $oldid, $newitemid, false);
}
protected function after_execute() {
global $DB;
parent::after_execute();
// Add quiz related files, no need to match by itemname (just internally handled context).
$this->add_related_files('mod_quiz', 'intro', null);
// Add feedback related files, matching by itemname = 'quiz_feedback'.
$this->add_related_files('mod_quiz', 'feedback', 'quiz_feedback');
if (!$this->sectioncreated) {
$DB->insert_record('quiz_sections', [
'quizid' => $this->get_new_parentid('quiz'),
'firstslot' => 1, 'heading' => '',
'shufflequestions' => $this->legacyshufflequestionsoption]);
}
}
protected function after_restore() {
parent::after_restore();
// Delete old random questions that have been converted to set references.
foreach (array_keys($this->oldquestionids) as $oldquestionid) {
question_delete_question($oldquestionid);
}
}
}