See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]
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 * The main interface for recycle bin methods. 19 * 20 * @package tool_recyclebin 21 * @copyright 2015 University of Kent 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace tool_recyclebin; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 define('TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA', 'recyclebin_coursecat'); 30 31 /** 32 * Represents a category's recyclebin. 33 * 34 * @package tool_recyclebin 35 * @copyright 2015 University of Kent 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class category_bin extends base_bin { 39 40 /** 41 * @var int The category id. 42 */ 43 protected $_categoryid; 44 45 /** 46 * Constructor. 47 * 48 * @param int $categoryid The category id. 49 */ 50 public function __construct($categoryid) { 51 $this->_categoryid = $categoryid; 52 } 53 54 /** 55 * Is this recyclebin enabled? 56 * 57 * @return bool true if enabled, false if not. 58 */ 59 public static function is_enabled() { 60 return get_config('tool_recyclebin', 'categorybinenable'); 61 } 62 63 /** 64 * Returns an item from the recycle bin. 65 * 66 * @param int $itemid Item ID to retrieve. 67 * @return \stdClass the item. 68 */ 69 public function get_item($itemid) { 70 global $DB; 71 72 $item = $DB->get_record('tool_recyclebin_category', array( 73 'id' => $itemid 74 ), '*', MUST_EXIST); 75 76 $item->name = get_course_display_name_for_list($item); 77 78 return $item; 79 } 80 81 /** 82 * Returns a list of items in the recycle bin for this course. 83 * 84 * @return array the list of items. 85 */ 86 public function get_items() { 87 global $DB; 88 89 $items = $DB->get_records('tool_recyclebin_category', array( 90 'categoryid' => $this->_categoryid 91 )); 92 93 foreach ($items as $item) { 94 $item->name = get_course_display_name_for_list($item); 95 } 96 97 return $items; 98 } 99 100 /** 101 * Store a course in the recycle bin. 102 * 103 * @param \stdClass $course Course 104 * @throws \moodle_exception 105 */ 106 public function store_item($course) { 107 global $CFG, $DB; 108 109 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 110 111 // As far as recycle bin is using MODE_AUTOMATED, it observes the backup_auto_storage 112 // settings (storing backups @ real location and potentially not including files). 113 // For recycle bin we want to ensure that backup files are always stored in Moodle file 114 // area and always contain the users' files. In order to achieve that, we hack the 115 // setting here via $CFG->forced_plugin_settings, so it won't interfere other operations. 116 // See MDL-65218 and MDL-35773 for more information. 117 // This hack will be removed once recycle bin switches to use its own backup mode, with 118 // own preferences and 100% separate from MOODLE_AUTOMATED. 119 // TODO: Remove this as part of MDL-65228. 120 $CFG->forced_plugin_settings['backup'] = ['backup_auto_storage' => 0, 'backup_auto_files' => 1]; 121 122 // Backup the course. 123 $user = get_admin(); 124 $controller = new \backup_controller( 125 \backup::TYPE_1COURSE, 126 $course->id, 127 \backup::FORMAT_MOODLE, 128 \backup::INTERACTIVE_NO, 129 \backup::MODE_AUTOMATED, 130 $user->id 131 ); 132 $controller->execute_plan(); 133 134 // We don't need the forced setting anymore, hence unsetting it. 135 // TODO: Remove this as part of MDL-65228. 136 unset($CFG->forced_plugin_settings['backup']); 137 138 // Grab the result. 139 $result = $controller->get_results(); 140 if (!isset($result['backup_destination'])) { 141 throw new \moodle_exception('Failed to backup activity prior to deletion.'); 142 } 143 144 // Have finished with the controller, let's destroy it, freeing mem and resources. 145 $controller->destroy(); 146 147 // Grab the filename. 148 $file = $result['backup_destination']; 149 if (!$file->get_contenthash()) { 150 throw new \moodle_exception('Failed to backup activity prior to deletion (invalid file).'); 151 } 152 153 // Record the activity, get an ID. 154 $item = new \stdClass(); 155 $item->categoryid = $course->category; 156 $item->shortname = $course->shortname; 157 $item->fullname = $course->fullname; 158 $item->timecreated = time(); 159 $binid = $DB->insert_record('tool_recyclebin_category', $item); 160 161 // Create the location we want to copy this file to. 162 $filerecord = array( 163 'contextid' => \context_coursecat::instance($course->category)->id, 164 'component' => 'tool_recyclebin', 165 'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, 166 'itemid' => $binid, 167 'timemodified' => time() 168 ); 169 170 // Move the file to our own special little place. 171 $fs = get_file_storage(); 172 if (!$fs->create_file_from_storedfile($filerecord, $file)) { 173 // Failed, cleanup first. 174 $DB->delete_records('tool_recyclebin_category', array( 175 'id' => $binid 176 )); 177 178 throw new \moodle_exception("Failed to copy backup file to recyclebin."); 179 } 180 181 // Delete the old file. 182 $file->delete(); 183 184 // Fire event. 185 $event = \tool_recyclebin\event\category_bin_item_created::create(array( 186 'objectid' => $binid, 187 'context' => \context_coursecat::instance($course->category) 188 )); 189 $event->trigger(); 190 } 191 192 /** 193 * Restore an item from the recycle bin. 194 * 195 * @param \stdClass $item The item database record 196 * @throws \moodle_exception 197 */ 198 public function restore_item($item) { 199 global $CFG, $OUTPUT, $PAGE; 200 201 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 202 require_once($CFG->dirroot . '/course/lib.php'); 203 204 $user = get_admin(); 205 206 // Grab the course category context. 207 $context = \context_coursecat::instance($this->_categoryid); 208 209 // Get the backup file. 210 $fs = get_file_storage(); 211 $files = $fs->get_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id, 212 'itemid, filepath, filename', false); 213 214 if (empty($files)) { 215 throw new \moodle_exception('Invalid recycle bin item!'); 216 } 217 218 if (count($files) > 1) { 219 throw new \moodle_exception('Too many files found!'); 220 } 221 222 // Get the backup file. 223 $file = reset($files); 224 225 // Get a backup temp directory name and create it. 226 $tempdir = \restore_controller::get_tempdir_name($context->id, $user->id); 227 $fulltempdir = make_backup_temp_directory($tempdir); 228 229 // Extract the backup to tmpdir. 230 $fb = get_file_packer('application/vnd.moodle.backup'); 231 $fb->extract_to_pathname($file, $fulltempdir); 232 233 // Build a course. 234 $course = new \stdClass(); 235 $course->category = $this->_categoryid; 236 $course->shortname = $item->shortname; 237 $course->fullname = $item->fullname; 238 $course->summary = ''; 239 240 // Create a new course. 241 $course = create_course($course); 242 if (!$course) { 243 throw new \moodle_exception("Could not create course to restore into."); 244 } 245 246 // As far as recycle bin is using MODE_AUTOMATED, it observes the General restore settings. 247 // For recycle bin we want to ensure that backup files are always restore the users and groups information. 248 // In order to achieve that, we hack the setting here via $CFG->forced_plugin_settings, 249 // so it won't interfere other operations. 250 // See MDL-65218 and MDL-35773 for more information. 251 // This hack will be removed once recycle bin switches to use its own backup mode, with 252 // own preferences and 100% separate from MOODLE_AUTOMATED. 253 // TODO: Remove this as part of MDL-65228. 254 $CFG->forced_plugin_settings['restore'] = ['restore_general_users' => 1, 'restore_general_groups' => 1]; 255 256 // Define the import. 257 $controller = new \restore_controller( 258 $tempdir, 259 $course->id, 260 \backup::INTERACTIVE_NO, 261 \backup::MODE_AUTOMATED, 262 $user->id, 263 \backup::TARGET_NEW_COURSE 264 ); 265 266 // Prechecks. 267 if (!$controller->execute_precheck()) { 268 $results = $controller->get_precheck_results(); 269 270 // Check if errors have been found. 271 if (!empty($results['errors'])) { 272 // Delete the temporary file we created. 273 fulldelete($fulltempdir); 274 275 // Delete the course we created. 276 delete_course($course, false); 277 278 echo $OUTPUT->header(); 279 $backuprenderer = $PAGE->get_renderer('core', 'backup'); 280 echo $backuprenderer->precheck_notices($results); 281 echo $OUTPUT->continue_button(new \moodle_url('/course/index.php', array('categoryid' => $this->_categoryid))); 282 echo $OUTPUT->footer(); 283 exit(); 284 } 285 } 286 287 // Run the import. 288 $controller->execute_plan(); 289 290 // We don't need the forced setting anymore, hence unsetting it. 291 // TODO: Remove this as part of MDL-65228. 292 unset($CFG->forced_plugin_settings['restore']); 293 294 // Have finished with the controller, let's destroy it, freeing mem and resources. 295 $controller->destroy(); 296 297 // Fire event. 298 $event = \tool_recyclebin\event\category_bin_item_restored::create(array( 299 'objectid' => $item->id, 300 'context' => $context 301 )); 302 $event->add_record_snapshot('tool_recyclebin_category', $item); 303 $event->trigger(); 304 305 // Cleanup. 306 fulldelete($fulltempdir); 307 $this->delete_item($item); 308 } 309 310 /** 311 * Delete an item from the recycle bin. 312 * 313 * @param \stdClass $item The item database record 314 * @throws \coding_exception 315 */ 316 public function delete_item($item) { 317 global $DB; 318 319 // Grab the course category context. 320 $context = \context_coursecat::instance($this->_categoryid, IGNORE_MISSING); 321 if (!empty($context)) { 322 // Delete the files. 323 $fs = get_file_storage(); 324 $fs->delete_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id); 325 } else { 326 // Course category has been deleted. Find records using $item->id as this is unique for coursecat recylebin. 327 $files = $DB->get_recordset('files', [ 328 'component' => 'tool_recyclebin', 329 'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, 330 'itemid' => $item->id, 331 ]); 332 $fs = get_file_storage(); 333 foreach ($files as $filer) { 334 $file = $fs->get_file_instance($filer); 335 $file->delete(); 336 } 337 $files->close(); 338 } 339 340 // Delete the record. 341 $DB->delete_records('tool_recyclebin_category', array( 342 'id' => $item->id 343 )); 344 345 // The coursecat might have been deleted, check we have a context before triggering event. 346 if (!$context) { 347 return; 348 } 349 350 // Fire event. 351 $event = \tool_recyclebin\event\category_bin_item_deleted::create(array( 352 'objectid' => $item->id, 353 'context' => \context_coursecat::instance($item->categoryid) 354 )); 355 $event->add_record_snapshot('tool_recyclebin_category', $item); 356 $event->trigger(); 357 } 358 359 /** 360 * Can we view items in this recycle bin? 361 * 362 * @return bool returns true if they can view, false if not 363 */ 364 public function can_view() { 365 $context = \context_coursecat::instance($this->_categoryid); 366 return has_capability('tool/recyclebin:viewitems', $context); 367 } 368 369 /** 370 * Can we restore items in this recycle bin? 371 * 372 * @return bool returns true if they can restore, false if not 373 */ 374 public function can_restore() { 375 $context = \context_coursecat::instance($this->_categoryid); 376 return has_capability('tool/recyclebin:restoreitems', $context); 377 } 378 379 /** 380 * Can we delete items in this recycle bin? 381 * 382 * @return bool returns true if they can delete, false if not 383 */ 384 public function can_delete() { 385 $context = \context_coursecat::instance($this->_categoryid); 386 return has_capability('tool/recyclebin:deleteitems', $context); 387 } 388 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body