Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 39 and 403]

   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  defined('MOODLE_INTERNAL') || die();
  18  
  19  // Include all the needed stuff.
  20  global $CFG;
  21  require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
  22  require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
  23  
  24  
  25  /**
  26   * Unit tests for how backup and restore handles role-related things.
  27   *
  28   * @package   core_backup
  29   * @copyright 2021 The Open University
  30   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   */
  32  class roles_backup_restore_test extends advanced_testcase {
  33  
  34      /**
  35       * Create a course where the (non-editing) Teacher role is overridden
  36       * to have 'moodle/user:loginas' and 'moodle/site:accessallgroups'.
  37       *
  38       * @return stdClass the new course.
  39       */
  40      protected function create_course_with_role_overrides(): stdClass {
  41          $generator = $this->getDataGenerator();
  42          $course = $generator->create_course();
  43          $teacher = $generator->create_user();
  44  
  45          $context = context_course::instance($course->id);
  46          $generator->enrol_user($teacher->id, $course->id, 'teacher');
  47  
  48          $editingteacherrole = $this->get_role('teacher');
  49          role_change_permission($editingteacherrole->id, $context, 'moodle/user:loginas', CAP_ALLOW);
  50          role_change_permission($editingteacherrole->id, $context, 'moodle/site:accessallgroups', CAP_ALLOW);
  51  
  52          return $course;
  53      }
  54  
  55      /**
  56       * Get the role id from a shortname.
  57       *
  58       * @param string $shortname the role shortname.
  59       * @return stdClass the role from the DB.
  60       */
  61      protected function get_role(string $shortname): stdClass {
  62          global $DB;
  63          return $DB->get_record('role', ['shortname' => $shortname]);
  64      }
  65  
  66      /**
  67       * Get an array capability => CAP_... constant for all the orverrides set for a given role on a given context.
  68       *
  69       * @param string $shortname role shortname.
  70       * @param context $context context.
  71       * @return array the overrides set here.
  72       */
  73      protected function get_overrides_for_role_on_context(string $shortname, context $context): array {
  74          $overridedata = get_capabilities_from_role_on_context($this->get_role($shortname), $context);
  75          $overrides = [];
  76          foreach ($overridedata as $override) {
  77              $overrides[$override->capability] = $override->permission;
  78          }
  79          return $overrides;
  80      }
  81  
  82      /**
  83       * Makes a backup of the course.
  84       *
  85       * @param stdClass $course The course object.
  86       * @return string Unique identifier for this backup.
  87       */
  88      protected function backup_course(\stdClass $course): string {
  89          global $CFG, $USER;
  90  
  91          // Turn off file logging, otherwise it can't delete the file (Windows).
  92          $CFG->backup_file_logger_level = backup::LOG_NONE;
  93  
  94          // Do backup with default settings. MODE_IMPORT means it will just
  95          // create the directory and not zip it.
  96          $bc = new \backup_controller(backup::TYPE_1COURSE, $course->id,
  97                  backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT,
  98                  $USER->id);
  99          $backupid = $bc->get_backupid();
 100          $bc->execute_plan();
 101          $bc->destroy();
 102  
 103          return $backupid;
 104      }
 105  
 106      /**
 107       * Restores a backup that has been made earlier.
 108       *
 109       * @param string $backupid The unique identifier of the backup.
 110       * @param string $asroleshortname Which role in the new cousre the restorer should have.
 111       * @return int The new course id.
 112       */
 113      protected function restore_adding_to_course(string $backupid, string $asroleshortname): int {
 114          global $CFG, $USER;
 115  
 116          // Create course to restore into, and a user to do the restore.
 117          $generator = $this->getDataGenerator();
 118          $course = $generator->create_course();
 119          $restorer = $generator->create_user();
 120  
 121          $generator->enrol_user($restorer->id, $course->id, $asroleshortname);
 122          $this->setUser($restorer);
 123  
 124          // Turn off file logging, otherwise it can't delete the file (Windows).
 125          $CFG->backup_file_logger_level = backup::LOG_NONE;
 126  
 127          // Do restore to new course with default settings.
 128          $rc = new \restore_controller($backupid, $course->id,
 129                  backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id,
 130                  backup::TARGET_CURRENT_ADDING);
 131  
 132          $precheck = $rc->execute_precheck();
 133          $this->assertTrue($precheck);
 134          $rc->get_plan()->get_setting('role_assignments')->set_value(true);
 135          $rc->get_plan()->get_setting('permissions')->set_value(true);
 136          $rc->execute_plan();
 137          $rc->destroy();
 138  
 139          return $course->id;
 140      }
 141  
 142      public function test_restore_role_overrides_as_manager(): void {
 143          $this->resetAfterTest();
 144          $this->setAdminUser();
 145  
 146          // Create a course and back it up.
 147          $course = $this->create_course_with_role_overrides();
 148          $backupid = $this->backup_course($course);
 149  
 150          // When manager restores, both role overrides should be restored.
 151          $newcourseid = $this->restore_adding_to_course($backupid, 'manager');
 152  
 153          // Verify.
 154          $overrides = $this->get_overrides_for_role_on_context('teacher',
 155                  context_course::instance($newcourseid));
 156          $this->assertArrayHasKey('moodle/user:loginas', $overrides);
 157          $this->assertEquals(CAP_ALLOW, $overrides['moodle/user:loginas']);
 158          $this->assertArrayHasKey('moodle/site:accessallgroups', $overrides);
 159          $this->assertEquals(CAP_ALLOW, $overrides['moodle/site:accessallgroups']);
 160      }
 161  
 162      public function test_restore_role_overrides_as_teacher(): void {
 163          $this->resetAfterTest();
 164          $this->setAdminUser();
 165  
 166          // Create a course and back it up.
 167          $course = $this->create_course_with_role_overrides();
 168          $backupid = $this->backup_course($course);
 169  
 170          // When teacher restores, only the safe override should be restored.
 171          $newcourseid = $this->restore_adding_to_course($backupid, 'editingteacher');
 172  
 173          // Verify.
 174          $overrides = $this->get_overrides_for_role_on_context('teacher',
 175                  context_course::instance($newcourseid));
 176          $this->assertArrayNotHasKey('moodle/user:loginas', $overrides);
 177          $this->assertArrayHasKey('moodle/site:accessallgroups', $overrides);
 178          $this->assertEquals(CAP_ALLOW, $overrides['moodle/site:accessallgroups']);
 179      }
 180  }