Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 use mod_quiz\question\display_options; 18 19 /** 20 * Structure step to restore one quiz activity 21 * 22 * @package mod_quiz 23 * @subpackage backup-moodle2 24 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 class restore_quiz_activity_structure_step extends restore_questions_activity_structure_step { 28 29 /** 30 * @var bool tracks whether the quiz contains at least one section. Before 31 * Moodle 2.9 quiz sections did not exist, so if the file being restored 32 * did not contain any, we need to create one in {@link after_execute()}. 33 */ 34 protected $sectioncreated = false; 35 36 /** @var stdClass|null $currentquizattempt Track the current quiz attempt being restored. */ 37 protected $currentquizattempt = null; 38 39 /** 40 * @var bool when restoring old quizzes (2.8 or before) this records the 41 * shufflequestionsoption quiz option which has moved to the quiz_sections table. 42 */ 43 protected $legacyshufflequestionsoption = false; 44 45 /** @var stdClass */ 46 protected $oldquizlayout; 47 48 /** 49 * @var array Track old question ids that need to be removed at the end of the restore. 50 */ 51 protected $oldquestionids = []; 52 53 protected function define_structure() { 54 55 $paths = []; 56 $userinfo = $this->get_setting_value('userinfo'); 57 58 $quiz = new restore_path_element('quiz', '/activity/quiz'); 59 $paths[] = $quiz; 60 61 // A chance for access subplugings to set up their quiz data. 62 $this->add_subplugin_structure('quizaccess', $quiz); 63 64 $quizquestioninstance = new restore_path_element('quiz_question_instance', 65 '/activity/quiz/question_instances/question_instance'); 66 $paths[] = $quizquestioninstance; 67 if ($this->task->get_old_moduleversion() < 2021091700) { 68 $paths[] = new restore_path_element('quiz_slot_tags', 69 '/activity/quiz/question_instances/question_instance/tags/tag'); 70 } else { 71 $this->add_question_references($quizquestioninstance, $paths); 72 $this->add_question_set_references($quizquestioninstance, $paths); 73 } 74 $paths[] = new restore_path_element('quiz_section', '/activity/quiz/sections/section'); 75 $paths[] = new restore_path_element('quiz_feedback', '/activity/quiz/feedbacks/feedback'); 76 $paths[] = new restore_path_element('quiz_override', '/activity/quiz/overrides/override'); 77 78 if ($userinfo) { 79 $paths[] = new restore_path_element('quiz_grade', '/activity/quiz/grades/grade'); 80 81 if ($this->task->get_old_moduleversion() > 2011010100) { 82 // Restoring from a version 2.1 dev or later. 83 // Process the new-style attempt data. 84 $quizattempt = new restore_path_element('quiz_attempt', 85 '/activity/quiz/attempts/attempt'); 86 $paths[] = $quizattempt; 87 88 // Add states and sessions. 89 $this->add_question_usages($quizattempt, $paths); 90 91 // A chance for access subplugings to set up their attempt data. 92 $this->add_subplugin_structure('quizaccess', $quizattempt); 93 94 } else { 95 // Restoring from a version 2.0.x+ or earlier. 96 // Upgrade the legacy attempt data. 97 $quizattempt = new restore_path_element('quiz_attempt_legacy', 98 '/activity/quiz/attempts/attempt', 99 true); 100 $paths[] = $quizattempt; 101 $this->add_legacy_question_attempt_data($quizattempt, $paths); 102 } 103 } 104 105 // Return the paths wrapped into standard activity structure. 106 return $this->prepare_activity_structure($paths); 107 } 108 109 /** 110 * Process the quiz data. 111 * 112 * @param stdClass|array $data 113 */ 114 protected function process_quiz($data) { 115 global $CFG, $DB, $USER; 116 117 $data = (object)$data; 118 $oldid = $data->id; 119 $data->course = $this->get_courseid(); 120 121 // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset. 122 // See MDL-9367. 123 124 $data->timeopen = $this->apply_date_offset($data->timeopen); 125 $data->timeclose = $this->apply_date_offset($data->timeclose); 126 127 if (property_exists($data, 'questions')) { 128 // Needed by {@link process_quiz_attempt_legacy}, in which case it will be present. 129 $this->oldquizlayout = $data->questions; 130 } 131 132 // The setting quiz->attempts can come both in data->attempts and 133 // data->attempts_number, handle both. MDL-26229. 134 if (isset($data->attempts_number)) { 135 $data->attempts = $data->attempts_number; 136 unset($data->attempts_number); 137 } 138 139 // The old optionflags and penaltyscheme from 2.0 need to be mapped to 140 // the new preferredbehaviour. See MDL-20636. 141 if (!isset($data->preferredbehaviour)) { 142 if (empty($data->optionflags)) { 143 $data->preferredbehaviour = 'deferredfeedback'; 144 } else if (empty($data->penaltyscheme)) { 145 $data->preferredbehaviour = 'adaptivenopenalty'; 146 } else { 147 $data->preferredbehaviour = 'adaptive'; 148 } 149 unset($data->optionflags); 150 unset($data->penaltyscheme); 151 } 152 153 // The old review column from 2.0 need to be split into the seven new 154 // review columns. See MDL-20636. 155 if (isset($data->review)) { 156 require_once($CFG->dirroot . '/mod/quiz/locallib.php'); 157 158 if (!defined('QUIZ_OLD_IMMEDIATELY')) { 159 define('QUIZ_OLD_IMMEDIATELY', 0x3c003f); 160 define('QUIZ_OLD_OPEN', 0x3c00fc0); 161 define('QUIZ_OLD_CLOSED', 0x3c03f000); 162 163 define('QUIZ_OLD_RESPONSES', 1*0x1041); 164 define('QUIZ_OLD_SCORES', 2*0x1041); 165 define('QUIZ_OLD_FEEDBACK', 4*0x1041); 166 define('QUIZ_OLD_ANSWERS', 8*0x1041); 167 define('QUIZ_OLD_SOLUTIONS', 16*0x1041); 168 define('QUIZ_OLD_GENERALFEEDBACK', 32*0x1041); 169 define('QUIZ_OLD_OVERALLFEEDBACK', 1*0x4440000); 170 } 171 172 $oldreview = $data->review; 173 174 $data->reviewattempt = 175 display_options::DURING | 176 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_RESPONSES ? 177 display_options::IMMEDIATELY_AFTER : 0) | 178 ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_RESPONSES ? 179 display_options::LATER_WHILE_OPEN : 0) | 180 ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_RESPONSES ? 181 display_options::AFTER_CLOSE : 0); 182 183 $data->reviewcorrectness = 184 display_options::DURING | 185 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ? 186 display_options::IMMEDIATELY_AFTER : 0) | 187 ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ? 188 display_options::LATER_WHILE_OPEN : 0) | 189 ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ? 190 display_options::AFTER_CLOSE : 0); 191 192 if (!isset($data->reviewmaxmarks)) { 193 $data->reviewmaxmarks = 194 display_options::DURING | 195 display_options::IMMEDIATELY_AFTER | 196 display_options::LATER_WHILE_OPEN | 197 display_options::AFTER_CLOSE; 198 } 199 200 $data->reviewmarks = 201 display_options::DURING | 202 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ? 203 display_options::IMMEDIATELY_AFTER : 0) | 204 ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ? 205 display_options::LATER_WHILE_OPEN : 0) | 206 ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ? 207 display_options::AFTER_CLOSE : 0); 208 209 $data->reviewspecificfeedback = 210 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ? 211 display_options::DURING : 0) | 212 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ? 213 display_options::IMMEDIATELY_AFTER : 0) | 214 ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_FEEDBACK ? 215 display_options::LATER_WHILE_OPEN : 0) | 216 ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_FEEDBACK ? 217 display_options::AFTER_CLOSE : 0); 218 219 $data->reviewgeneralfeedback = 220 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ? 221 display_options::DURING : 0) | 222 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ? 223 display_options::IMMEDIATELY_AFTER : 0) | 224 ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_GENERALFEEDBACK ? 225 display_options::LATER_WHILE_OPEN : 0) | 226 ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_GENERALFEEDBACK ? 227 display_options::AFTER_CLOSE : 0); 228 229 $data->reviewrightanswer = 230 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ? 231 display_options::DURING : 0) | 232 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ? 233 display_options::IMMEDIATELY_AFTER : 0) | 234 ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_ANSWERS ? 235 display_options::LATER_WHILE_OPEN : 0) | 236 ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_ANSWERS ? 237 display_options::AFTER_CLOSE : 0); 238 239 $data->reviewoverallfeedback = 240 0 | 241 ($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_OVERALLFEEDBACK ? 242 display_options::IMMEDIATELY_AFTER : 0) | 243 ($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_OVERALLFEEDBACK ? 244 display_options::LATER_WHILE_OPEN : 0) | 245 ($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_OVERALLFEEDBACK ? 246 display_options::AFTER_CLOSE : 0); 247 } 248 249 // The old popup column from from <= 2.1 need to be mapped to 250 // the new browsersecurity. See MDL-29627. 251 if (!isset($data->browsersecurity)) { 252 if (empty($data->popup)) { 253 $data->browsersecurity = '-'; 254 } else if ($data->popup == 1) { 255 $data->browsersecurity = 'securewindow'; 256 } else if ($data->popup == 2) { 257 // Since 3.9 quizaccess_safebrowser replaced with a new quizaccess_seb. 258 $data->browsersecurity = '-'; 259 $addsebrule = true; 260 } else { 261 $data->preferredbehaviour = '-'; 262 } 263 unset($data->popup); 264 } else if ($data->browsersecurity == 'safebrowser') { 265 // Since 3.9 quizaccess_safebrowser replaced with a new quizaccess_seb. 266 $data->browsersecurity = '-'; 267 $addsebrule = true; 268 } 269 270 if (!isset($data->overduehandling)) { 271 $data->overduehandling = get_config('quiz', 'overduehandling'); 272 } 273 274 // Old shufflequestions setting is now stored in quiz sections, 275 // so save it here if necessary so it is available when we need it. 276 $this->legacyshufflequestionsoption = !empty($data->shufflequestions); 277 278 // Insert the quiz record. 279 $newitemid = $DB->insert_record('quiz', $data); 280 // Immediately after inserting "activity" record, call this. 281 $this->apply_activity_instance($newitemid); 282 283 // Process Safe Exam Browser settings for backups taken in Moodle < 3.9. 284 if (!empty($addsebrule)) { 285 $sebsettings = new stdClass(); 286 287 $sebsettings->quizid = $newitemid; 288 $sebsettings->cmid = $this->task->get_moduleid(); 289 $sebsettings->templateid = 0; 290 $sebsettings->requiresafeexambrowser = \quizaccess_seb\settings_provider::USE_SEB_CLIENT_CONFIG; 291 $sebsettings->showsebtaskbar = null; 292 $sebsettings->showwificontrol = null; 293 $sebsettings->showreloadbutton = null; 294 $sebsettings->showtime = null; 295 $sebsettings->showkeyboardlayout = null; 296 $sebsettings->allowuserquitseb = null; 297 $sebsettings->quitpassword = null; 298 $sebsettings->linkquitseb = null; 299 $sebsettings->userconfirmquit = null; 300 $sebsettings->enableaudiocontrol = null; 301 $sebsettings->muteonstartup = null; 302 $sebsettings->allowspellchecking = null; 303 $sebsettings->allowreloadinexam = null; 304 $sebsettings->activateurlfiltering = null; 305 $sebsettings->filterembeddedcontent = null; 306 $sebsettings->expressionsallowed = null; 307 $sebsettings->regexallowed = null; 308 $sebsettings->expressionsblocked = null; 309 $sebsettings->regexblocked = null; 310 $sebsettings->allowedbrowserexamkeys = null; 311 $sebsettings->showsebdownloadlink = 1; 312 $sebsettings->usermodified = $USER->id; 313 $sebsettings->timecreated = time(); 314 $sebsettings->timemodified = time(); 315 316 $DB->insert_record('quizaccess_seb_quizsettings', $sebsettings); 317 } 318 319 // If we are dealing with a backup from < 4.0 then we need to move completionpass to core. 320 if (!empty($data->completionpass)) { 321 $params = ['id' => $this->task->get_moduleid()]; 322 $DB->set_field('course_modules', 'completionpassgrade', $data->completionpass, $params); 323 } 324 } 325 326 /** 327 * Process the data for pre 4.0 quiz data where the question_references and question_set_references table introduced. 328 * 329 * @param stdClass|array $data 330 */ 331 protected function process_quiz_question_legacy_instance($data) { 332 global $DB; 333 334 $questionid = $this->get_mappingid('question', $data->questionid); 335 $sql = 'SELECT qbe.id as questionbankentryid, 336 qc.contextid as questioncontextid, 337 qc.id as category, 338 qv.version, 339 q.qtype, 340 q.id as questionid 341 FROM {question} q 342 JOIN {question_versions} qv ON qv.questionid = q.id 343 JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid 344 JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid 345 WHERE q.id = ?'; 346 $question = $DB->get_record_sql($sql, [$questionid]); 347 $module = $DB->get_record('quiz', ['id' => $data->quizid]); 348 349 if ($question->qtype === 'random') { 350 // Set reference data. 351 $questionsetreference = new \stdClass(); 352 $questionsetreference->usingcontextid = context_module::instance(get_coursemodule_from_instance( 353 "quiz", $module->id, $module->course)->id)->id; 354 $questionsetreference->component = 'mod_quiz'; 355 $questionsetreference->questionarea = 'slot'; 356 $questionsetreference->itemid = $data->id; 357 $questionsetreference->questionscontextid = $question->questioncontextid; 358 $filtercondition = new stdClass(); 359 $filtercondition->questioncategoryid = $question->category; 360 $filtercondition->includingsubcategories = $data->includingsubcategories ?? false; 361 $questionsetreference->filtercondition = json_encode($filtercondition); 362 $DB->insert_record('question_set_references', $questionsetreference); 363 $this->oldquestionids[$question->questionid] = 1; 364 } else { 365 // Reference data. 366 $questionreference = new \stdClass(); 367 $questionreference->usingcontextid = context_module::instance(get_coursemodule_from_instance( 368 "quiz", $module->id, $module->course)->id)->id; 369 $questionreference->component = 'mod_quiz'; 370 $questionreference->questionarea = 'slot'; 371 $questionreference->itemid = $data->id; 372 $questionreference->questionbankentryid = $question->questionbankentryid; 373 $questionreference->version = null; // Default to Always latest. 374 $DB->insert_record('question_references', $questionreference); 375 } 376 } 377 378 /** 379 * Process quiz slots. 380 * 381 * @param stdClass|array $data 382 */ 383 protected function process_quiz_question_instance($data) { 384 global $CFG, $DB; 385 386 $data = (object)$data; 387 $oldid = $data->id; 388 389 // Backwards compatibility for old field names (MDL-43670). 390 if (!isset($data->questionid) && isset($data->question)) { 391 $data->questionid = $data->question; 392 } 393 if (!isset($data->maxmark) && isset($data->grade)) { 394 $data->maxmark = $data->grade; 395 } 396 397 if (!property_exists($data, 'slot')) { 398 $page = 1; 399 $slot = 1; 400 foreach (explode(',', $this->oldquizlayout) as $item) { 401 if ($item == 0) { 402 $page += 1; 403 continue; 404 } 405 if (isset($data->questionid) && $item == $data->questionid) { 406 $data->slot = $slot; 407 $data->page = $page; 408 break; 409 } 410 $slot += 1; 411 } 412 } 413 414 if (!property_exists($data, 'slot')) { 415 // There was a question_instance in the backup file for a question 416 // that was not actually in the quiz. Drop it. 417 $this->log('question ' . $data->questionid . ' was associated with quiz ' . 418 $this->get_new_parentid('quiz') . ' but not actually used. ' . 419 'The instance has been ignored.', backup::LOG_INFO); 420 return; 421 } 422 423 $data->quizid = $this->get_new_parentid('quiz'); 424 425 $newitemid = $DB->insert_record('quiz_slots', $data); 426 // Add mapping, restore of slot tags (for random questions) need it. 427 $this->set_mapping('quiz_question_instance', $oldid, $newitemid); 428 429 if ($this->task->get_old_moduleversion() < 2022020300) { 430 $data->id = $newitemid; 431 $this->process_quiz_question_legacy_instance($data); 432 } 433 } 434 435 /** 436 * Process a quiz_slot_tags to restore the tags to the new structure. 437 * 438 * @param stdClass|array $data The quiz_slot_tags data 439 */ 440 protected function process_quiz_slot_tags($data) { 441 global $DB; 442 443 $data = (object) $data; 444 $slotid = $this->get_new_parentid('quiz_question_instance'); 445 446 if ($this->task->is_samesite() && $tag = core_tag_tag::get($data->tagid, 'id, name')) { 447 $data->tagname = $tag->name; 448 } else if ($tag = core_tag_tag::get_by_name(0, $data->tagname, 'id, name')) { 449 $data->tagid = $tag->id; 450 } else { 451 $data->tagid = null; 452 $data->tagname = $tag->name; 453 } 454 455 $tagstring = "{$data->tagid},{$data->tagname}"; 456 $setreferencedata = $DB->get_record('question_set_references', 457 ['itemid' => $slotid, 'component' => 'mod_quiz', 'questionarea' => 'slot']); 458 $filtercondition = json_decode($setreferencedata->filtercondition); 459 $filtercondition->tags[] = $tagstring; 460 $setreferencedata->filtercondition = json_encode($filtercondition); 461 $DB->update_record('question_set_references', $setreferencedata); 462 } 463 464 protected function process_quiz_section($data) { 465 global $DB; 466 467 $data = (object) $data; 468 $data->quizid = $this->get_new_parentid('quiz'); 469 $oldid = $data->id; 470 $newitemid = $DB->insert_record('quiz_sections', $data); 471 $this->sectioncreated = true; 472 $this->set_mapping('quiz_section', $oldid, $newitemid, true); 473 } 474 475 protected function process_quiz_feedback($data) { 476 global $DB; 477 478 $data = (object)$data; 479 $oldid = $data->id; 480 481 $data->quizid = $this->get_new_parentid('quiz'); 482 483 $newitemid = $DB->insert_record('quiz_feedback', $data); 484 $this->set_mapping('quiz_feedback', $oldid, $newitemid, true); // Has related files. 485 } 486 487 protected function process_quiz_override($data) { 488 global $DB; 489 490 $data = (object)$data; 491 $oldid = $data->id; 492 493 // Based on userinfo, we'll restore user overides or no. 494 $userinfo = $this->get_setting_value('userinfo'); 495 496 // Skip user overrides if we are not restoring userinfo. 497 if (!$userinfo && !is_null($data->userid)) { 498 return; 499 } 500 501 $data->quiz = $this->get_new_parentid('quiz'); 502 503 if ($data->userid !== null) { 504 $data->userid = $this->get_mappingid('user', $data->userid); 505 } 506 507 if ($data->groupid !== null) { 508 $data->groupid = $this->get_mappingid('group', $data->groupid); 509 } 510 511 // Skip if there is no user and no group data. 512 if (empty($data->userid) && empty($data->groupid)) { 513 return; 514 } 515 516 $data->timeopen = $this->apply_date_offset($data->timeopen); 517 $data->timeclose = $this->apply_date_offset($data->timeclose); 518 519 $newitemid = $DB->insert_record('quiz_overrides', $data); 520 521 // Add mapping, restore of logs needs it. 522 $this->set_mapping('quiz_override', $oldid, $newitemid); 523 } 524 525 protected function process_quiz_grade($data) { 526 global $DB; 527 528 $data = (object)$data; 529 $oldid = $data->id; 530 531 $data->quiz = $this->get_new_parentid('quiz'); 532 533 $data->userid = $this->get_mappingid('user', $data->userid); 534 $data->grade = $data->gradeval; 535 536 $DB->insert_record('quiz_grades', $data); 537 } 538 539 protected function process_quiz_attempt($data) { 540 $data = (object)$data; 541 542 $data->quiz = $this->get_new_parentid('quiz'); 543 $data->attempt = $data->attemptnum; 544 545 // Get user mapping, return early if no mapping found for the quiz attempt. 546 $olduserid = $data->userid; 547 $data->userid = $this->get_mappingid('user', $olduserid, 0); 548 if ($data->userid === 0) { 549 $this->log('Mapped user ID not found for user ' . $olduserid . ', quiz ' . $this->get_new_parentid('quiz') . 550 ', attempt ' . $data->attempt . '. Skipping quiz attempt', backup::LOG_INFO); 551 552 $this->currentquizattempt = null; 553 return; 554 } 555 556 if (!empty($data->timecheckstate)) { 557 $data->timecheckstate = $this->apply_date_offset($data->timecheckstate); 558 } else { 559 $data->timecheckstate = 0; 560 } 561 562 if (!isset($data->gradednotificationsenttime)) { 563 // For attempts restored from old Moodle sites before this field 564 // existed, we never want to send emails. 565 $data->gradednotificationsenttime = $data->timefinish; 566 } 567 568 // Deals with up-grading pre-2.3 back-ups to 2.3+. 569 if (!isset($data->state)) { 570 if ($data->timefinish > 0) { 571 $data->state = 'finished'; 572 } else { 573 $data->state = 'inprogress'; 574 } 575 } 576 577 // The data is actually inserted into the database later in inform_new_usage_id. 578 $this->currentquizattempt = clone($data); 579 } 580 581 protected function process_quiz_attempt_legacy($data) { 582 global $DB; 583 584 $this->process_quiz_attempt($data); 585 586 $quiz = $DB->get_record('quiz', ['id' => $this->get_new_parentid('quiz')]); 587 $quiz->oldquestions = $this->oldquizlayout; 588 $this->process_legacy_quiz_attempt_data($data, $quiz); 589 } 590 591 protected function inform_new_usage_id($newusageid) { 592 global $DB; 593 594 $data = $this->currentquizattempt; 595 if ($data === null) { 596 return; 597 } 598 599 $oldid = $data->id; 600 $data->uniqueid = $newusageid; 601 602 $newitemid = $DB->insert_record('quiz_attempts', $data); 603 604 // Save quiz_attempt->id mapping, because logs use it. 605 $this->set_mapping('quiz_attempt', $oldid, $newitemid, false); 606 } 607 608 protected function after_execute() { 609 global $DB; 610 611 parent::after_execute(); 612 // Add quiz related files, no need to match by itemname (just internally handled context). 613 $this->add_related_files('mod_quiz', 'intro', null); 614 // Add feedback related files, matching by itemname = 'quiz_feedback'. 615 $this->add_related_files('mod_quiz', 'feedback', 'quiz_feedback'); 616 617 if (!$this->sectioncreated) { 618 $DB->insert_record('quiz_sections', [ 619 'quizid' => $this->get_new_parentid('quiz'), 620 'firstslot' => 1, 'heading' => '', 621 'shufflequestions' => $this->legacyshufflequestionsoption]); 622 } 623 } 624 625 protected function after_restore() { 626 parent::after_restore(); 627 // Delete old random questions that have been converted to set references. 628 foreach (array_keys($this->oldquestionids) as $oldquestionid) { 629 question_delete_question($oldquestionid); 630 } 631 } 632 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body