See Release Notes
Long Term Support Release
Differences Between: [Versions 310 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 * Adhoc task that performs asynchronous course copies. 19 * 20 * @package core 21 * @copyright 2020 onward The Moodle Users Association <https://moodleassociation.org/> 22 * @author Matt Porritt <mattp@catalyst-au.net> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 namespace core\task; 27 28 use async_helper; 29 30 defined('MOODLE_INTERNAL') || die(); 31 32 global $CFG; 33 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 34 require_once($CFG->dirroot . '/backup/moodle2/backup_plan_builder.class.php'); 35 require_once($CFG->libdir . '/externallib.php'); 36 37 /** 38 * Adhoc task that performs asynchronous course copies. 39 * 40 * @package core 41 * @copyright 2020 onward The Moodle Users Association <https://moodleassociation.org/> 42 * @author Matt Porritt <mattp@catalyst-au.net> 43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 44 */ 45 class asynchronous_copy_task extends adhoc_task { 46 47 /** 48 * Run the adhoc task and preform the backup. 49 */ 50 public function execute() { 51 global $CFG, $DB; 52 $started = time(); 53 54 $backupid = $this->get_custom_data()->backupid; 55 $restoreid = $this->get_custom_data()->restoreid; 56 $backuprecord = $DB->get_record('backup_controllers', array('backupid' => $backupid), 'id, itemid', MUST_EXIST); 57 $restorerecord = $DB->get_record('backup_controllers', array('backupid' => $restoreid), 'id, itemid', MUST_EXIST); 58 59 // First backup the course. 60 mtrace('Course copy: Processing asynchronous course copy for course id: ' . $backuprecord->itemid); 61 try { 62 $bc = \backup_controller::load_controller($backupid); // Get the backup controller by backup id. 63 } catch (\backup_dbops_exception $e) { 64 mtrace('Course copy: Can not load backup controller for copy, marking job as failed'); 65 delete_course($restorerecord->itemid, false); // Clean up partially created destination course. 66 return; // Return early as we can't continue. 67 } 68 69 $rc = \restore_controller::load_controller($restoreid); // Get the restore controller by restore id. 70 $bc->set_progress(new \core\progress\db_updater($backuprecord->id, 'backup_controllers', 'progress')); 71 $copyinfo = $rc->get_copy(); 72 $backupplan = $bc->get_plan(); 73 74 $keepuserdata = (bool)$copyinfo->userdata; 75 $keptroles = $copyinfo->keptroles; 76 77 $bc->set_kept_roles($keptroles); 78 79 // If we are not keeping user data don't include users or data in the backup. 80 // In this case we'll add the user enrolments at the end. 81 // Also if we have no roles to keep don't backup users. 82 if (empty($keptroles) || !$keepuserdata) { 83 $backupplan->get_setting('users')->set_status(\backup_setting::NOT_LOCKED); 84 $backupplan->get_setting('users')->set_value('0'); 85 } else { 86 $backupplan->get_setting('users')->set_value('1'); 87 } 88 89 // Do some preflight checks on the backup. 90 $status = $bc->get_status(); 91 $execution = $bc->get_execution(); 92 // Check that the backup is in the correct status and 93 // that is set for asynchronous execution. 94 if ($status == \backup::STATUS_AWAITING && $execution == \backup::EXECUTION_DELAYED) { 95 // Execute the backup. 96 mtrace('Course copy: Backing up course, id: ' . $backuprecord->itemid); 97 $bc->execute_plan(); 98 99 } else { 100 // If status isn't 700, it means the process has failed. 101 // Retrying isn't going to fix it, so marked operation as failed. 102 mtrace('Course copy: Bad backup controller status, is: ' . $status . ' should be 700, marking job as failed.'); 103 $bc->set_status(\backup::STATUS_FINISHED_ERR); 104 delete_course($restorerecord->itemid, false); // Clean up partially created destination course. 105 $bc->destroy(); 106 return; // Return early as we can't continue. 107 108 } 109 110 $results = $bc->get_results(); 111 $backupbasepath = $backupplan->get_basepath(); 112 $file = $results['backup_destination']; 113 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath); 114 // Start the restore process. 115 $rc->set_progress(new \core\progress\db_updater($restorerecord->id, 'backup_controllers', 'progress')); 116 $rc->prepare_copy(); 117 118 // Set the course settings we can do now (the remaining settings will be done after restore completes). 119 $plan = $rc->get_plan(); 120 121 $startdate = $plan->get_setting('course_startdate'); 122 $startdate->set_value($copyinfo->startdate); 123 $fullname = $plan->get_setting('course_fullname'); 124 $fullname->set_value($copyinfo->fullname); 125 $shortname = $plan->get_setting('course_shortname'); 126 $shortname->set_value($copyinfo->shortname); 127 128 // Do some preflight checks on the restore. 129 $rc->execute_precheck(); 130 $status = $rc->get_status(); 131 $execution = $rc->get_execution(); 132 133 // Check that the restore is in the correct status and 134 // that is set for asynchronous execution. 135 if ($status == \backup::STATUS_AWAITING && $execution == \backup::EXECUTION_DELAYED) { 136 // Execute the restore. 137 mtrace('Course copy: Restoring into course, id: ' . $restorerecord->itemid); 138 $rc->execute_plan(); 139 140 } else { 141 // If status isn't 700, it means the process has failed. 142 // Retrying isn't going to fix it, so marked operation as failed. 143 mtrace('Course copy: Bad backup controller status, is: ' . $status . ' should be 700, marking job as failed.'); 144 $rc->set_status(\backup::STATUS_FINISHED_ERR); 145 delete_course($restorerecord->itemid, false); // Clean up partially created destination course. 146 $file->delete(); 147 if (empty($CFG->keeptempdirectoriesonbackup)) { 148 fulldelete($backupbasepath); 149 } 150 $rc->destroy(); 151 return; // Return early as we can't continue. 152 153 } 154 155 // Copy user enrolments from source course to destination. 156 if (!empty($keptroles) && !$keepuserdata) { 157 mtrace('Course copy: Creating user enrolments in destination course.'); 158 $context = \context_course::instance($backuprecord->itemid); 159 160 $enrol = enrol_get_plugin('manual'); 161 $instance = null; 162 $enrolinstances = enrol_get_instances($restorerecord->itemid, true); 163 foreach ($enrolinstances as $courseenrolinstance) { 164 if ($courseenrolinstance->enrol == 'manual') { 165 $instance = $courseenrolinstance; 166 break; 167 } 168 } 169 170 // Abort if there enrolment plugin problems. 171 if (empty($enrol) || empty($instance)) { 172 mtrace('Course copy: Could not enrol users in course.');; 173 delete_course($restorerecord->itemid, false); 174 return; 175 } 176 177 // Enrol the users from the source course to the destination. 178 foreach ($keptroles as $roleid) { 179 $sourceusers = get_role_users($roleid, $context); 180 foreach ($sourceusers as $sourceuser) { 181 $enrol->enrol_user($instance, $sourceuser->id, $roleid); 182 } 183 } 184 } 185 186 // Set up remaining course settings. 187 $course = $DB->get_record('course', array('id' => $restorerecord->itemid), '*', MUST_EXIST); 188 $course->visible = $copyinfo->visible; 189 $course->idnumber = $copyinfo->idnumber; 190 $course->enddate = $copyinfo->enddate; 191 192 $DB->update_record('course', $course); 193 194 // Send message to user if enabled. 195 $messageenabled = (bool)get_config('backup', 'backup_async_message_users'); 196 if ($messageenabled && $rc->get_status() == \backup::STATUS_FINISHED_OK) { 197 mtrace('Course copy: Sending user notification.'); 198 $asynchelper = new async_helper('copy', $restoreid); 199 $messageid = $asynchelper->send_message(); 200 mtrace('Course copy: Sent message: ' . $messageid); 201 } 202 203 // Cleanup. 204 $bc->destroy(); 205 $rc->destroy(); 206 $file->delete(); 207 if (empty($CFG->keeptempdirectoriesonbackup)) { 208 fulldelete($backupbasepath); 209 } 210 211 $duration = time() - $started; 212 mtrace('Course copy: Copy completed in: ' . $duration . ' seconds'); 213 } 214 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body