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 * Contains the import_backup_helper class. 18 * 19 * @package tool_moodlenet 20 * @copyright 2020 Adrian Greeve <adrian@moodle.com> 21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 */ 23 namespace tool_moodlenet\local; 24 25 /** 26 * The import_backup_helper class. 27 * 28 * The import_backup_helper objects provide a means to prepare a backup for for restoration of a course or activity backup file. 29 * 30 * @copyright 2020 Adrian Greeve <adrian@moodle.com> 31 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 32 */ 33 class import_backup_helper { 34 35 /** @var remote_resource $remoteresource A file resource to be restored. */ 36 protected $remoteresource; 37 38 /** @var user $user The user trying to restore a file. */ 39 protected $user; 40 41 /** @var context $context The context we are trying to restore this file into. */ 42 protected $context; 43 44 /** @var int $useruploadlimit The size limit that this user can upload in this context. */ 45 protected $useruploadlimit; 46 47 /** 48 * Constructor for the import backup helper. 49 * 50 * @param remote_resource $remoteresource A remote file resource 51 * @param \stdClass $user The user importing a file. 52 * @param \context $context Context to restore into. 53 */ 54 public function __construct(remote_resource $remoteresource, \stdClass $user, \context $context) { 55 $this->remoteresource = $remoteresource; 56 $this->user = $user; 57 $this->context = $context; 58 59 $maxbytes = 0; 60 if ($this->context->contextlevel == CONTEXT_COURSE) { 61 $course = get_course($this->context->instanceid); 62 $maxbytes = $course->maxbytes; 63 } 64 $this->useruploadlimit = get_user_max_upload_file_size($this->context, get_config('core', 'maxbytes'), 65 $maxbytes, 0, $this->user); 66 } 67 68 /** 69 * Return a stored user draft file for processing. 70 * 71 * @return \stored_file The imported file to ultimately be restored. 72 */ 73 public function get_stored_file(): \stored_file { 74 75 // Check if the user can upload a backup to this context. 76 require_capability('moodle/restore:uploadfile', $this->context, $this->user->id); 77 78 // Before starting a potentially lengthy download, try to ensure the file size does not exceed the upload size restrictions 79 // for the user. This is a time saving measure. 80 // This is a naive check, that serves only to catch files if they provide the content length header. 81 // Because of potential content encoding (compression), the stored file will be checked again after download as well. 82 $size = $this->remoteresource->get_download_size() ?? -1; 83 if ($this->size_exceeds_upload_limit($size)) { 84 throw new \moodle_exception('uploadlimitexceeded', 'tool_moodlenet', '', ['filesize' => $size, 85 'uploadlimit' => $this->useruploadlimit]); 86 } 87 88 [$filepath, $filename] = $this->remoteresource->download_to_requestdir(); 89 \core\antivirus\manager::scan_file($filepath, $filename, true); 90 91 // Check the final size of file against the user upload limits. 92 $localsize = filesize(sprintf('%s/%s', $filepath, $filename)); 93 if ($this->size_exceeds_upload_limit($localsize)) { 94 throw new \moodle_exception('uploadlimitexceeded', 'tool_moodlenet', '', ['filesize' => $localsize, 95 'uploadlimit' => $this->useruploadlimit]); 96 } 97 98 return $this->create_user_draft_stored_file($filename, $filepath); 99 } 100 101 /** 102 * Does the size exceed the upload limit for the current import, taking into account user and core settings. 103 * 104 * @param int $sizeinbytes 105 * @return bool true if exceeded, false otherwise. 106 */ 107 protected function size_exceeds_upload_limit(int $sizeinbytes): bool { 108 $maxbytes = 0; 109 if ($this->context->contextlevel == CONTEXT_COURSE) { 110 $course = get_course($this->context->instanceid); 111 $maxbytes = $course->maxbytes; 112 } 113 $maxbytes = get_user_max_upload_file_size($this->context, get_config('core', 'maxbytes'), $maxbytes, 0, 114 $this->user); 115 if ($maxbytes != USER_CAN_IGNORE_FILE_SIZE_LIMITS && $sizeinbytes > $maxbytes) { 116 return true; 117 } 118 return false; 119 } 120 121 /** 122 * Create a file in the user drafts ready for use by plugins implementing dndupload_handle(). 123 * 124 * @param string $filename the name of the file on disk 125 * @param string $path the path where the file is stored on disk 126 * @return \stored_file 127 */ 128 protected function create_user_draft_stored_file(string $filename, string $path): \stored_file { 129 global $CFG; 130 131 $record = new \stdClass(); 132 $record->filearea = 'draft'; 133 $record->component = 'user'; 134 $record->filepath = '/'; 135 $record->itemid = file_get_unused_draft_itemid(); 136 $record->license = $CFG->sitedefaultlicense; 137 $record->author = ''; 138 $record->filename = clean_param($filename, PARAM_FILE); 139 $record->contextid = \context_user::instance($this->user->id)->id; 140 $record->userid = $this->user->id; 141 142 $fullpathwithname = sprintf('%s/%s', $path, $filename); 143 144 $fs = get_file_storage(); 145 146 return $fs->create_file_from_pathname($record, $fullpathwithname); 147 } 148 149 /** 150 * Looks for a context that this user has permission to upload backup files to. 151 * This gets a list of roles that the user has, checks for the restore:uploadfile capability and then sends back a context 152 * that has this permission if available. 153 * 154 * This starts with the highest context level and moves down i.e. system -> category -> course. 155 * 156 * @param int $userid The user ID that we are looking for a working context for. 157 * @return \context A context that allows the upload of backup files. 158 */ 159 public static function get_context_for_user(int $userid): ?\context { 160 global $DB; 161 162 if (is_siteadmin()) { 163 return \context_system::instance(); 164 } 165 166 $sql = "SELECT ctx.id, ctx.contextlevel, ctx.instanceid, ctx.path, ctx.depth, ctx.locked 167 FROM {context} ctx 168 JOIN {role_assignments} r ON ctx.id = r.contextid 169 WHERE r.userid = :userid AND ctx.contextlevel IN (:contextsystem, :contextcategory, :contextcourse) 170 ORDER BY ctx.contextlevel ASC"; 171 172 $params = [ 173 'userid' => $userid, 174 'contextsystem' => CONTEXT_SYSTEM, 175 'contextcategory' => CONTEXT_COURSECAT, 176 'contextcourse' => CONTEXT_COURSE 177 ]; 178 $records = $DB->get_records_sql($sql, $params); 179 foreach ($records as $record) { 180 \context_helper::preload_from_record($record); 181 if ($record->contextlevel == CONTEXT_COURSECAT) { 182 $context = \context_coursecat::instance($record->instanceid); 183 } else if ($record->contextlevel == CONTEXT_COURSE) { 184 $context = \context_course::instance($record->instanceid); 185 } else { 186 $context = \context_system::instance(); 187 } 188 if (has_capability('moodle/restore:uploadfile', $context, $userid)) { 189 return $context; 190 } 191 } 192 return null; 193 } 194 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body