Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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