Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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   * Recycle bin tests.
  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  defined('MOODLE_INTERNAL') || die();
  26  
  27  /**
  28   * Recycle bin category tests.
  29   *
  30   * @package    tool_recyclebin
  31   * @copyright  2015 University of Kent
  32   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   */
  34  class tool_recyclebin_category_bin_tests extends advanced_testcase {
  35  
  36      /**
  37       * @var stdClass $course
  38       */
  39      protected $course;
  40  
  41      /**
  42       * @var stdClass $coursebeingrestored
  43       */
  44      protected $coursebeingrestored;
  45  
  46      /**
  47       * Setup for each test.
  48       */
  49      protected function setUp(): void {
  50          $this->resetAfterTest();
  51          $this->setAdminUser();
  52  
  53          // We want the category bin to be enabled.
  54          set_config('categorybinenable', 1, 'tool_recyclebin');
  55  
  56          $this->course = $this->getDataGenerator()->create_course();
  57      }
  58  
  59      /**
  60       * Check that our hook is called when a course is deleted.
  61       */
  62      public function test_pre_course_delete_hook() {
  63          global $DB;
  64  
  65          // This simulates a temporary course being cleaned up by a course restore.
  66          $this->coursebeingrestored = $this->getDataGenerator()->create_course();
  67          $this->coursebeingrestored->deletesource = 'restore';
  68  
  69          // Should have nothing in the recycle bin.
  70          $this->assertEquals(0, $DB->count_records('tool_recyclebin_category'));
  71  
  72          delete_course($this->course, false);
  73          // This should not be added to the recycle bin.
  74          delete_course($this->coursebeingrestored, false);
  75  
  76          // Check the course is now in the recycle bin.
  77          $this->assertEquals(1, $DB->count_records('tool_recyclebin_category'));
  78  
  79          // Try with the API.
  80          $recyclebin = new \tool_recyclebin\category_bin($this->course->category);
  81          $this->assertEquals(1, count($recyclebin->get_items()));
  82      }
  83  
  84      /**
  85       * Check that our hook is called when a course is deleted.
  86       */
  87      public function test_pre_course_category_delete_hook() {
  88          global $DB;
  89  
  90          // Should have nothing in the recycle bin.
  91          $this->assertEquals(0, $DB->count_records('tool_recyclebin_category'));
  92  
  93          delete_course($this->course, false);
  94  
  95          // Check the course is now in the recycle bin.
  96          $this->assertEquals(1, $DB->count_records('tool_recyclebin_category'));
  97  
  98          // Now let's delete the course category.
  99          $category = core_course_category::get($this->course->category);
 100          $category->delete_full(false);
 101  
 102          // Check that the course was deleted from the category recycle bin.
 103          $this->assertEquals(0, $DB->count_records('tool_recyclebin_category'));
 104      }
 105  
 106      /**
 107       * Test that we can restore recycle bin items.
 108       */
 109      public function test_restore() {
 110          global $DB;
 111  
 112          delete_course($this->course, false);
 113  
 114          $recyclebin = new \tool_recyclebin\category_bin($this->course->category);
 115          foreach ($recyclebin->get_items() as $item) {
 116              $recyclebin->restore_item($item);
 117          }
 118  
 119          // Check that it was restored and removed from the recycle bin.
 120          $this->assertEquals(2, $DB->count_records('course')); // Site course and the course we restored.
 121          $this->assertEquals(0, count($recyclebin->get_items()));
 122      }
 123  
 124      /**
 125       * Test that we can delete recycle bin items.
 126       */
 127      public function test_delete() {
 128          global $DB;
 129  
 130          delete_course($this->course, false);
 131  
 132          $recyclebin = new \tool_recyclebin\category_bin($this->course->category);
 133          foreach ($recyclebin->get_items() as $item) {
 134              $recyclebin->delete_item($item);
 135          }
 136  
 137          // Item was deleted, so no course was restored.
 138          $this->assertEquals(1, $DB->count_records('course')); // Just the site course.
 139          $this->assertEquals(0, count($recyclebin->get_items()));
 140      }
 141  
 142      /**
 143       * Test the cleanup task.
 144       */
 145      public function test_cleanup_task() {
 146          global $DB;
 147  
 148          // Set the expiry to 1 week.
 149          set_config('categorybinexpiry', WEEKSECS, 'tool_recyclebin');
 150  
 151          delete_course($this->course, false);
 152  
 153          $recyclebin = new \tool_recyclebin\category_bin($this->course->category);
 154  
 155          // Set deleted date to the distant past.
 156          foreach ($recyclebin->get_items() as $item) {
 157              $item->timecreated = time() - WEEKSECS;
 158              $DB->update_record('tool_recyclebin_category', $item);
 159          }
 160  
 161          // Create another course to delete.
 162          $course = $this->getDataGenerator()->create_course();
 163          delete_course($course, false);
 164  
 165          // Should now be two courses in the recycle bin.
 166          $this->assertEquals(2, count($recyclebin->get_items()));
 167  
 168          // Execute cleanup task.
 169          $this->expectOutputRegex("/\[tool_recyclebin\] Deleting item '\d+' from the category recycle bin/");
 170          $task = new \tool_recyclebin\task\cleanup_category_bin();
 171          $task->execute();
 172  
 173          // Task should only have deleted the course where we updated the time.
 174          $courses = $recyclebin->get_items();
 175          $this->assertEquals(1, count($courses));
 176          $course = reset($courses);
 177          $this->assertEquals('Test course 2', $course->fullname);
 178      }
 179  
 180      /**
 181       * Provider for test_course_restore_with_userdata() and test_course_restore_without_userdata()
 182       *
 183       * Used to verify that recycle bin is immune to various settings. Provides plugin, name, value for
 184       * direct usage with set_config()
 185       */
 186      public function recycle_bin_settings_provider() {
 187          return [
 188              'backup/backup_auto_storage moodle' => [[
 189                  (object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 0],
 190              ]],
 191  
 192              'backup/backup_auto_storage external' => [[
 193                  (object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 1],
 194                  (object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
 195              ]],
 196  
 197              'backup/backup_auto_storage mixed' => [[
 198                  (object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
 199                  (object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
 200              ]],
 201          ];
 202      }
 203  
 204      /**
 205       * Tests that user data is restored when course is restored.
 206       *
 207       * @dataProvider recycle_bin_settings_provider
 208       * @param array $settings array of plugin, name, value stdClass().
 209       */
 210      public function test_course_restore_with_userdata($settings) {
 211          global $DB;
 212  
 213          // Force configuration changes from provider.
 214          foreach ($settings as $setting) {
 215              // Need to create a directory for backup_auto_destination.
 216              if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
 217                  $setting->value = make_request_directory();
 218              }
 219              set_config($setting->name, $setting->value, $setting->plugin);
 220          }
 221  
 222          // We want user data to be included for this test.
 223          set_config('backup_auto_users', true, 'backup');
 224  
 225          $student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
 226  
 227          // Delete course.
 228          delete_course($this->course, false);
 229          $this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
 230  
 231          // Verify there is now a backup @ cat recycle bin file area.
 232          $recyclebin = new \tool_recyclebin\category_bin($this->course->category);
 233          $this->assertEquals(1, count($recyclebin->get_items()));
 234  
 235          // Restore the recycle bin item.
 236          $recyclebin->restore_item(current($recyclebin->get_items()));
 237  
 238          // Get the new course.
 239          $newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
 240  
 241          // Check that it was removed from the recycle bin.
 242          $this->assertEquals(0, count($recyclebin->get_items()));
 243  
 244          // Verify that student DOES continue enrolled.
 245          $this->assertTrue(is_enrolled(context_course::instance($newcourse->id), $student->id));
 246      }
 247  
 248      /**
 249       * Tests that user data is not restored when course is restored.
 250       *
 251       * @dataProvider recycle_bin_settings_provider
 252       * @param array $settings array of plugin, name, value stdClass().
 253       */
 254      public function test_course_restore_without_userdata($settings) {
 255          global $DB;
 256  
 257          // Force configuration changes from provider.
 258          foreach ($settings as $setting) {
 259              // Need to create a directory for backup_auto_destination.
 260              if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
 261                  $setting->value = make_request_directory();
 262              }
 263              set_config($setting->name, $setting->value, $setting->plugin);
 264          }
 265  
 266          // We want user data to be included for this test.
 267          set_config('backup_auto_users', false, 'backup');
 268  
 269          $student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
 270  
 271          // Delete course.
 272          delete_course($this->course, false);
 273          $this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
 274  
 275          // Verify there is now a backup @ cat recycle bin file area.
 276          $recyclebin = new \tool_recyclebin\category_bin($this->course->category);
 277          $this->assertEquals(1, count($recyclebin->get_items()));
 278  
 279          // Restore the recycle bin item.
 280          $recyclebin->restore_item(current($recyclebin->get_items()));
 281  
 282          // Get the new course.
 283          $newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
 284  
 285          // Check that it was removed from the recycle bin.
 286          $this->assertEquals(0, count($recyclebin->get_items()));
 287  
 288          // Verify that student DOES NOT continue enrolled.
 289          $this->assertFalse(is_enrolled(context_course::instance($newcourse->id), $student->id));
 290      }
 291  }