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