You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
371 lines
13 KiB
371 lines
13 KiB
<?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/>.
|
|
|
|
/**
|
|
* Helper functions for asynchronous backups and restores.
|
|
*
|
|
* @package core
|
|
* @copyright 2019 Matt Porritt <mattp@catalyst-au.net>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
require_once($CFG->dirroot . '/user/lib.php');
|
|
|
|
/**
|
|
* Helper functions for asynchronous backups and restores.
|
|
*
|
|
* @package core
|
|
* @copyright 2019 Matt Porritt <mattp@catalyst-au.net>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class async_helper {
|
|
|
|
/**
|
|
* @var string $type The type of async operation.
|
|
*/
|
|
protected $type = 'backup';
|
|
|
|
/**
|
|
* @var string $backupid The id of the backup or restore.
|
|
*/
|
|
protected $backupid;
|
|
|
|
/**
|
|
* @var object $user The user who created the backup record.
|
|
*/
|
|
protected $user;
|
|
|
|
/**
|
|
* @var object $backuprec The backup controller record from the database.
|
|
*/
|
|
protected $backuprec;
|
|
|
|
/**
|
|
* Class constructor.
|
|
*
|
|
* @param string $type The type of async operation.
|
|
* @param string $id The id of the backup or restore.
|
|
*/
|
|
public function __construct($type, $id) {
|
|
$this->type = $type;
|
|
$this->backupid = $id;
|
|
$this->backuprec = $this->get_backup_record($id);
|
|
$this->user = $this->get_user();
|
|
}
|
|
|
|
/**
|
|
* Given a backup id return a the record from the database.
|
|
* We use this method rather than 'load_controller' as the controller may
|
|
* not exist if this backup/restore has completed.
|
|
*
|
|
* @param int $id The backup id to get.
|
|
* @return object $backuprec The backup controller record.
|
|
*/
|
|
private function get_backup_record($id) {
|
|
global $DB;
|
|
|
|
$backuprec = $DB->get_record('backup_controllers', array('backupid' => $id), '*', MUST_EXIST);
|
|
|
|
return $backuprec;
|
|
}
|
|
|
|
/**
|
|
* Given a user id return a user object.
|
|
*
|
|
* @return object $user The limited user record.
|
|
*/
|
|
private function get_user() {
|
|
$userid = $this->backuprec->userid;
|
|
$user = core_user::get_user($userid, '*', MUST_EXIST);
|
|
|
|
return $user;
|
|
}
|
|
|
|
/**
|
|
* Callback for preg_replace_callback.
|
|
* Replaces message placeholders with real values.
|
|
*
|
|
* @param array $matches The match array from from preg_replace_callback.
|
|
* @return string $match The replaced string.
|
|
*/
|
|
private function lookup_message_variables($matches) {
|
|
$options = array(
|
|
'operation' => $this->type,
|
|
'backupid' => $this->backupid,
|
|
'user_username' => $this->user->username,
|
|
'user_email' => $this->user->email,
|
|
'user_firstname' => $this->user->firstname,
|
|
'user_lastname' => $this->user->lastname,
|
|
'link' => $this->get_resource_link(),
|
|
);
|
|
|
|
$match = $options[$matches[1]] ?? $matches[1];
|
|
|
|
return $match;
|
|
}
|
|
|
|
/**
|
|
* Get the link to the resource that is being backuped or restored.
|
|
*
|
|
* @return moodle_url $url The link to the resource.
|
|
*/
|
|
private function get_resource_link() {
|
|
// Get activity context only for backups.
|
|
if ($this->backuprec->type == 'activity' && $this->type == 'backup') {
|
|
$context = context_module::instance($this->backuprec->itemid);
|
|
} else { // Course or Section which have the same context getter.
|
|
$context = context_course::instance($this->backuprec->itemid);
|
|
}
|
|
|
|
// Generate link based on operation type.
|
|
if ($this->type == 'backup') {
|
|
// For backups simply generate link to restore file area UI.
|
|
$url = new moodle_url('/backup/restorefile.php', array('contextid' => $context->id));
|
|
} else {
|
|
// For restore generate link to the item itself.
|
|
$url = $context->get_url();
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Sends a confirmation message for an aynchronous process.
|
|
*
|
|
* @return int $messageid The id of the sent message.
|
|
*/
|
|
public function send_message() {
|
|
global $USER;
|
|
|
|
$subjectraw = get_config('backup', 'backup_async_message_subject');
|
|
$subjecttext = preg_replace_callback(
|
|
'/\{([-_A-Za-z0-9]+)\}/u',
|
|
array('async_helper', 'lookup_message_variables'),
|
|
$subjectraw);
|
|
|
|
$messageraw = get_config('backup', 'backup_async_message');
|
|
$messagehtml = preg_replace_callback(
|
|
'/\{([-_A-Za-z0-9]+)\}/u',
|
|
array('async_helper', 'lookup_message_variables'),
|
|
$messageraw);
|
|
$messagetext = html_to_text($messagehtml);
|
|
|
|
$message = new \core\message\message();
|
|
$message->component = 'moodle';
|
|
$message->name = 'asyncbackupnotification';
|
|
$message->userfrom = $USER;
|
|
$message->userto = $this->user;
|
|
$message->subject = $subjecttext;
|
|
$message->fullmessage = $messagetext;
|
|
$message->fullmessageformat = FORMAT_HTML;
|
|
$message->fullmessagehtml = $messagehtml;
|
|
$message->smallmessage = '';
|
|
$message->notification = '1';
|
|
|
|
$messageid = message_send($message);
|
|
|
|
return $messageid;
|
|
}
|
|
|
|
/**
|
|
* Check if asynchronous backup and restore mode is
|
|
* enabled at system level.
|
|
*
|
|
* @return bool $async True if async mode enabled false otherwise.
|
|
*/
|
|
static public function is_async_enabled() {
|
|
global $CFG;
|
|
|
|
$async = false;
|
|
if (!empty($CFG->enableasyncbackup)) {
|
|
$async = true;
|
|
}
|
|
|
|
return $async;
|
|
}
|
|
|
|
/**
|
|
* Check if there is a pending async operation for given details.
|
|
*
|
|
* @param int $id The item id to check in the backup record.
|
|
* @param string $type The type of operation: course, activity or section.
|
|
* @param string $operation Operation backup or restore.
|
|
* @return boolean $asyncpedning Is there a pending async operation.
|
|
*/
|
|
public static function is_async_pending($id, $type, $operation) {
|
|
global $DB, $USER, $CFG;
|
|
$asyncpending = false;
|
|
|
|
// Only check for pending async operations if async mode is enabled.
|
|
require_once($CFG->dirroot . '/backup/util/interfaces/checksumable.class.php');
|
|
require_once($CFG->dirroot . '/backup/backup.class.php');
|
|
|
|
if (self::is_async_enabled()) {
|
|
$select = 'userid = ? AND itemid = ? AND type = ? AND operation = ? AND execution = ? AND status < ? AND status > ?';
|
|
$params = array(
|
|
$USER->id,
|
|
$id,
|
|
$type,
|
|
$operation,
|
|
backup::EXECUTION_DELAYED,
|
|
backup::STATUS_FINISHED_ERR,
|
|
backup::STATUS_NEED_PRECHECK
|
|
);
|
|
$asyncpending = $DB->record_exists_select('backup_controllers', $select, $params);
|
|
}
|
|
return $asyncpending;
|
|
}
|
|
|
|
/**
|
|
* Get the size, url and restore url for a backup file.
|
|
*
|
|
* @param string $filename The name of the file to get info for.
|
|
* @param string $filearea The file area for the file.
|
|
* @param int $contextid The context ID of the file.
|
|
* @return array $results The result array containing the size, url and restore url of the file.
|
|
*/
|
|
public static function get_backup_file_info($filename, $filearea, $contextid) {
|
|
$fs = get_file_storage();
|
|
$file = $fs->get_file($contextid, 'backup', $filearea, 0, '/', $filename);
|
|
$filesize = display_size ($file->get_filesize());
|
|
$fileurl = moodle_url::make_pluginfile_url(
|
|
$file->get_contextid(),
|
|
$file->get_component(),
|
|
$file->get_filearea(),
|
|
null,
|
|
$file->get_filepath(),
|
|
$file->get_filename(),
|
|
true
|
|
);
|
|
|
|
$params = array();
|
|
$params['action'] = 'choosebackupfile';
|
|
$params['filename'] = $file->get_filename();
|
|
$params['filepath'] = $file->get_filepath();
|
|
$params['component'] = $file->get_component();
|
|
$params['filearea'] = $file->get_filearea();
|
|
$params['filecontextid'] = $file->get_contextid();
|
|
$params['contextid'] = $contextid;
|
|
$params['itemid'] = $file->get_itemid();
|
|
$restoreurl = new moodle_url('/backup/restorefile.php', $params);
|
|
$filesize = display_size ($file->get_filesize());
|
|
|
|
$results = array(
|
|
'filesize' => $filesize,
|
|
'fileurl' => $fileurl->out(false),
|
|
'restoreurl' => $restoreurl->out(false));
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Get the url of a restored backup item based on the backup ID.
|
|
*
|
|
* @param string $backupid The backup ID to get the restore location url.
|
|
* @return array $urlarray The restored item URL as an array.
|
|
*/
|
|
public static function get_restore_url($backupid) {
|
|
global $DB;
|
|
|
|
$backupitemid = $DB->get_field('backup_controllers', 'itemid', array('backupid' => $backupid), MUST_EXIST);
|
|
$newcontext = context_course::instance($backupitemid);
|
|
|
|
$restoreurl = $newcontext->get_url()->out();
|
|
$urlarray = array('restoreurl' => $restoreurl);
|
|
|
|
return $urlarray;
|
|
}
|
|
|
|
/**
|
|
* Get markup for in progress async backups,
|
|
* to use in backup table UI.
|
|
*
|
|
* @param \core_backup_renderer $renderer The backup renderer object.
|
|
* @param integer $instanceid The context id to get backup data for.
|
|
* @return array $tabledata the rows of table data.
|
|
*/
|
|
public static function get_async_backups($renderer, $instanceid) {
|
|
global $DB;
|
|
|
|
$tabledata = array();
|
|
|
|
// Get relevant backup ids based on context instance id.
|
|
$select = 'itemid = ? AND execution = ? AND status < ? AND status > ?';
|
|
$params = array($instanceid, backup::EXECUTION_DELAYED, backup::STATUS_FINISHED_ERR, backup::STATUS_NEED_PRECHECK);
|
|
$backups = $DB->get_records_select('backup_controllers', $select, $params, 'timecreated DESC', 'id, backupid, timecreated');
|
|
|
|
foreach ($backups as $backup) {
|
|
$bc = \backup_controller::load_controller($backup->backupid); // Get the backup controller.
|
|
$filename = $bc->get_plan()->get_setting('filename')->get_value();
|
|
$timecreated = $backup->timecreated;
|
|
$status = $renderer->get_status_display($bc->get_status(), $bc->get_backupid());
|
|
$bc->destroy();
|
|
|
|
$tablerow = array($filename, userdate($timecreated), '-', '-', '-', $status);
|
|
$tabledata[] = $tablerow;
|
|
}
|
|
|
|
return $tabledata;
|
|
}
|
|
|
|
/**
|
|
* Get the course name of the resource being restored.
|
|
*
|
|
* @param \context $context The Moodle context for the restores.
|
|
* @return string $coursename The full name of the course.
|
|
*/
|
|
public static function get_restore_name(\context $context) {
|
|
global $DB;
|
|
$instanceid = $context->instanceid;
|
|
|
|
if ($context->contextlevel == CONTEXT_MODULE) {
|
|
// For modules get the course name and module name.
|
|
$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
|
|
$coursename = $DB->get_field('course', 'fullname', array('id' => $cm->course));
|
|
$itemname = $coursename . ' - ' . $cm->name;
|
|
} else {
|
|
$itemname = $DB->get_field('course', 'fullname', array('id' => $context->instanceid));
|
|
|
|
}
|
|
|
|
return $itemname;
|
|
}
|
|
|
|
/**
|
|
* Get all the current in progress async restores for a user.
|
|
*
|
|
* @param int $userid Moodle user id.
|
|
* @return array $restores List of current restores in progress.
|
|
*/
|
|
public static function get_async_restores($userid) {
|
|
global $DB;
|
|
|
|
$select = 'userid = ? AND execution = ? AND status < ? AND status > ? AND operation = ?';
|
|
$params = array($userid, backup::EXECUTION_DELAYED, backup::STATUS_FINISHED_ERR, backup::STATUS_NEED_PRECHECK, 'restore');
|
|
$restores = $DB->get_records_select(
|
|
'backup_controllers',
|
|
$select,
|
|
$params,
|
|
'timecreated DESC',
|
|
'id, backupid, status, itemid, timecreated');
|
|
|
|
return $restores;
|
|
}
|
|
|
|
}
|
|
|
|
|