Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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      }
 195  
 196      /**
 197       * Tests that user data is restored when course is restored.
 198       *
 199       * @dataProvider recycle_bin_settings_provider
 200       * @param array $settings array of plugin, name, value stdClass().
 201       */
 202      public function test_course_restore_with_userdata($settings) {
 203          global $DB;
 204  
 205          // Force configuration changes from provider.
 206          foreach ($settings as $setting) {
 207              // Need to create a directory for backup_auto_destination.
 208              if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
 209                  $setting->value = make_request_directory();
 210              }
 211              set_config($setting->name, $setting->value, $setting->plugin);
 212          }
 213  
 214          // We want user data to be included for this test.
 215          set_config('backup_auto_users', true, 'backup');
 216  
 217          $student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
 218  
 219          // Delete course.
 220          delete_course($this->course, false);
 221          $this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
 222  
 223          // Verify there is now a backup @ cat recycle bin file area.
 224          $recyclebin = new \tool_recyclebin\category_bin($this->course->category);
 225          $this->assertEquals(1, count($recyclebin->get_items()));
 226  
 227          // Restore the recycle bin item.
 228          $recyclebin->restore_item(current($recyclebin->get_items()));
 229  
 230          // Get the new course.
 231          $newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
 232  
 233          // Check that it was removed from the recycle bin.
 234          $this->assertEquals(0, count($recyclebin->get_items()));
 235  
 236          // Verify that student DOES continue enrolled.
 237          $this->assertTrue(is_enrolled(\context_course::instance($newcourse->id), $student->id));
 238      }
 239  
 240      /**
 241       * Tests that user data is not restored when course is restored.
 242       *
 243       * @dataProvider recycle_bin_settings_provider
 244       * @param array $settings array of plugin, name, value stdClass().
 245       */
 246      public function test_course_restore_without_userdata($settings) {
 247          global $DB;
 248  
 249          // Force configuration changes from provider.
 250          foreach ($settings as $setting) {
 251              // Need to create a directory for backup_auto_destination.
 252              if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
 253                  $setting->value = make_request_directory();
 254              }
 255              set_config($setting->name, $setting->value, $setting->plugin);
 256          }
 257  
 258          // We want user data to be included for this test.
 259          set_config('backup_auto_users', false, 'backup');
 260  
 261          $student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
 262  
 263          // Delete course.
 264          delete_course($this->course, false);
 265          $this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
 266  
 267          // Verify there is now a backup @ cat recycle bin file area.
 268          $recyclebin = new \tool_recyclebin\category_bin($this->course->category);
 269          $this->assertEquals(1, count($recyclebin->get_items()));
 270  
 271          // Restore the recycle bin item.
 272          $recyclebin->restore_item(current($recyclebin->get_items()));
 273  
 274          // Get the new course.
 275          $newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
 276  
 277          // Check that it was removed from the recycle bin.
 278          $this->assertEquals(0, count($recyclebin->get_items()));
 279  
 280          // Verify that student DOES NOT continue enrolled.
 281          $this->assertFalse(is_enrolled(\context_course::instance($newcourse->id), $student->id));
 282      }
 283  }