| 1 | efrain | 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 |  * This script allows to restore a course from CLI.
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * @package    core
 | 
        
           |  |  | 21 |  * @subpackage cli
 | 
        
           |  |  | 22 |  * @copyright  2020 Catalyst IT
 | 
        
           |  |  | 23 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 24 |  */
 | 
        
           |  |  | 25 |   | 
        
           |  |  | 26 | define('CLI_SCRIPT', 1);
 | 
        
           |  |  | 27 |   | 
        
           |  |  | 28 | require(__DIR__ . '/../../config.php');
 | 
        
           |  |  | 29 | require_once($CFG->libdir . '/clilib.php');
 | 
        
           |  |  | 30 | require_once($CFG->dirroot . "/backup/util/includes/restore_includes.php");
 | 
        
           |  |  | 31 |   | 
        
           |  |  | 32 | list($options, $unrecognized) = cli_get_params([
 | 
        
           |  |  | 33 |     'file' => '',
 | 
        
           |  |  | 34 |     'categoryid' => '',
 | 
        
           |  |  | 35 |     'courseid' => '',
 | 
        
           |  |  | 36 |     'showdebugging' => false,
 | 
        
           |  |  | 37 |     'help' => false,
 | 
        
           |  |  | 38 | ], [
 | 
        
           |  |  | 39 |     'f' => 'file',
 | 
        
           |  |  | 40 |     'c' => 'categoryid',
 | 
        
           |  |  | 41 |     'C' => 'courseid',
 | 
        
           |  |  | 42 |     's' => 'showdebugging',
 | 
        
           |  |  | 43 |     'h' => 'help',
 | 
        
           |  |  | 44 | ]);
 | 
        
           |  |  | 45 |   | 
        
           |  |  | 46 | if ($unrecognized) {
 | 
        
           |  |  | 47 |     $unrecognized = implode("\n  ", $unrecognized);
 | 
        
           |  |  | 48 |     cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
 | 
        
           |  |  | 49 | }
 | 
        
           |  |  | 50 |   | 
        
           |  |  | 51 | if ($options['help'] || !($options['file']) || !($options['categoryid'] || $options['courseid'])) {
 | 
        
           |  |  | 52 |     $help = <<<EOL
 | 
        
           |  |  | 53 | Restore backup into provided category or course.
 | 
        
           |  |  | 54 | If courseid is set, course module/s will be added into the course.
 | 
        
           |  |  | 55 |   | 
        
           |  |  | 56 | Options:
 | 
        
           |  |  | 57 | -f, --file=STRING       Path to the backup file.
 | 
        
           | 1441 | ariadna | 58 | -c, --categoryid=INT    ID of the course category to restore to. This option is ignored when restoring an activity and courseid is set.
 | 
        
           |  |  | 59 | -C, --courseid=INT      ID of the course to restore to. This option is ignored when restoring a course and the categoryid is set.
 | 
        
           | 1 | efrain | 60 | -s, --showdebugging     Show developer level debugging information
 | 
        
           |  |  | 61 | -h, --help              Print out this help.
 | 
        
           |  |  | 62 |   | 
        
           |  |  | 63 | Example:
 | 
        
           | 1441 | ariadna | 64 | \$sudo -u www-data /usr/bin/php admin/cli/restore_backup.php --file=/path/to/backup/coursebackup.mbz --categoryid=1\n
 | 
        
           |  |  | 65 | \$sudo -u www-data /usr/bin/php admin/cli/restore_backup.php --file=/path/to/backup/activitybackup.mbz --courseid=1\n
 | 
        
           | 1 | efrain | 66 | EOL;
 | 
        
           |  |  | 67 |   | 
        
           |  |  | 68 |     echo $help;
 | 
        
           |  |  | 69 |     exit(0);
 | 
        
           |  |  | 70 | }
 | 
        
           |  |  | 71 |   | 
        
           |  |  | 72 | if ($options['showdebugging']) {
 | 
        
           |  |  | 73 |     set_debugging(DEBUG_DEVELOPER, true);
 | 
        
           |  |  | 74 | }
 | 
        
           |  |  | 75 |   | 
        
           |  |  | 76 | if (!$admin = get_admin()) {
 | 
        
           |  |  | 77 |     throw new \moodle_exception('noadmins');
 | 
        
           |  |  | 78 | }
 | 
        
           |  |  | 79 |   | 
        
           |  |  | 80 | if (!file_exists($options['file'])) {
 | 
        
           |  |  | 81 |     throw new \moodle_exception('filenotfound');
 | 
        
           |  |  | 82 | }
 | 
        
           |  |  | 83 |   | 
        
           |  |  | 84 | if ($options['categoryid']) {
 | 
        
           |  |  | 85 |     if (!$category = $DB->get_record('course_categories', ['id' => $options['categoryid']], 'id')) {
 | 
        
           |  |  | 86 |         throw new \moodle_exception('invalidcategoryid');
 | 
        
           |  |  | 87 |     }
 | 
        
           | 1441 | ariadna | 88 | }
 | 
        
           |  |  | 89 |   | 
        
           |  |  | 90 | if ($options['courseid']) {
 | 
        
           | 1 | efrain | 91 |     if (!$course = $DB->get_record('course', ['id' => $options['courseid']], 'id')) {
 | 
        
           |  |  | 92 |         throw new \moodle_exception('invalidcourseid');
 | 
        
           |  |  | 93 |     }
 | 
        
           | 1441 | ariadna | 94 | }
 | 
        
           |  |  | 95 |   | 
        
           |  |  | 96 | if (empty($category) && empty($course)) {
 | 
        
           | 1 | efrain | 97 |     throw new \moodle_exception('invalidoption');
 | 
        
           |  |  | 98 | }
 | 
        
           |  |  | 99 |   | 
        
           |  |  | 100 | $backupdir = restore_controller::get_tempdir_name(SITEID, $USER->id);
 | 
        
           |  |  | 101 | $path = make_backup_temp_directory($backupdir);
 | 
        
           |  |  | 102 |   | 
        
           |  |  | 103 | cli_heading(get_string('extractingbackupfileto', 'backup', $path));
 | 
        
           |  |  | 104 | $fp = get_file_packer('application/vnd.moodle.backup');
 | 
        
           |  |  | 105 | $fp->extract_to_pathname($options['file'], $path);
 | 
        
           |  |  | 106 |   | 
        
           |  |  | 107 | cli_heading(get_string('preprocessingbackupfile'));
 | 
        
           | 1441 | ariadna | 108 |   | 
        
           | 1 | efrain | 109 | try {
 | 
        
           | 1441 | ariadna | 110 |     // Create a temporary restore controller to determine the restore type.
 | 
        
           |  |  | 111 |     $tmprc = new restore_controller($backupdir, SITEID, backup::INTERACTIVE_NO,
 | 
        
           |  |  | 112 |         backup::MODE_GENERAL, $admin->id, backup::TARGET_EXISTING_ADDING);
 | 
        
           |  |  | 113 |     // Restore the backup into a new course if:
 | 
        
           |  |  | 114 |     // - It is a course backup and the category is set.
 | 
        
           |  |  | 115 |     // - It is an activity backup and the course is not set.
 | 
        
           |  |  | 116 |     $restoreasnewcourse = ($tmprc->get_type() === backup::TYPE_1COURSE && !empty($category)) ||
 | 
        
           |  |  | 117 |         ($tmprc->get_type() !== backup::TYPE_1COURSE && empty($course));
 | 
        
           |  |  | 118 |     // Make sure to clean up the temporary restore controller.
 | 
        
           |  |  | 119 |     $tmprc->destroy();
 | 
        
           | 1 | efrain | 120 |   | 
        
           | 1441 | ariadna | 121 |     if ($restoreasnewcourse) {
 | 
        
           |  |  | 122 |         list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoringcourse', 'backup'),
 | 
        
           |  |  | 123 |             get_string('restoringcourseshortname', 'backup'));
 | 
        
           |  |  | 124 |         $courseid = restore_dbops::create_new_course($fullname, $shortname, $category->id);
 | 
        
           |  |  | 125 |         $rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
 | 
        
           |  |  | 126 |             backup::MODE_GENERAL, $admin->id, backup::TARGET_NEW_COURSE);
 | 
        
           |  |  | 127 |     } else {
 | 
        
           | 1 | efrain | 128 |         $courseid = $course->id;
 | 
        
           |  |  | 129 |         $rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
 | 
        
           |  |  | 130 |             backup::MODE_GENERAL, $admin->id, backup::TARGET_EXISTING_ADDING);
 | 
        
           |  |  | 131 |     }
 | 
        
           |  |  | 132 |     $rc->execute_precheck();
 | 
        
           |  |  | 133 |     $rc->execute_plan();
 | 
        
           |  |  | 134 |     $rc->destroy();
 | 
        
           |  |  | 135 |   | 
        
           | 1441 | ariadna | 136 |     // Rename the course's full and short names with the backup file's original names if the backup file is an activity backup
 | 
        
           |  |  | 137 |     // that is restored to a new course.
 | 
        
           |  |  | 138 |     if ($restoreasnewcourse && $rc->get_type() !== backup::TYPE_1COURSE) {
 | 
        
           | 1 | efrain | 139 |         $course = get_course($courseid);
 | 
        
           | 1441 | ariadna | 140 |         $backupinfo = $rc->get_info();
 | 
        
           |  |  | 141 |         $tmpfullname = $backupinfo->original_course_fullname ?? get_string('restoretonewcourse', 'backup');
 | 
        
           |  |  | 142 |         $tmpshortname = $backupinfo->original_course_shortname ?? get_string('newcourse');
 | 
        
           |  |  | 143 |         list($fullname, $shortname) = restore_dbops::calculate_course_names(
 | 
        
           |  |  | 144 |             courseid: 0,
 | 
        
           |  |  | 145 |             fullname: $tmpfullname,
 | 
        
           |  |  | 146 |             shortname: $tmpshortname,
 | 
        
           |  |  | 147 |         );
 | 
        
           | 1 | efrain | 148 |         $course->fullname = $fullname;
 | 
        
           |  |  | 149 |         $course->shortname = $shortname;
 | 
        
           |  |  | 150 |         $course->visible = 1;
 | 
        
           |  |  | 151 |         $DB->update_record('course', $course);
 | 
        
           |  |  | 152 |     }
 | 
        
           |  |  | 153 | } catch (Exception $e) {
 | 
        
           |  |  | 154 |     cli_heading(get_string('cleaningtempdata'));
 | 
        
           |  |  | 155 |     fulldelete($path);
 | 
        
           |  |  | 156 |     throw new \moodle_exception('generalexceptionmessage', 'error', '', $e->getMessage());
 | 
        
           |  |  | 157 | }
 | 
        
           |  |  | 158 |   | 
        
           |  |  | 159 | cli_heading(get_string('restoredcourseid', 'backup', $courseid));
 | 
        
           |  |  | 160 | exit(0);
 |