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 311 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  /**
  18   * Steps definitions related with permissions.
  19   *
  20   * @package   core
  21   * @category  test
  22   * @copyright 2013 David MonllaĆ³
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
  27  
  28  require_once (__DIR__ . '/../../behat/behat_base.php');
  29  
  30  use Behat\Mink\Exception\ExpectationException as ExpectationException,
  31      Behat\Gherkin\Node\TableNode as TableNode;
  32  
  33  /**
  34   * Steps definitions to set up permissions to capabilities.
  35   *
  36   * @package   core
  37   * @category  test
  38   * @copyright 2013 David MonllaĆ³
  39   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  class behat_permissions extends behat_base {
  42  
  43      /**
  44       * Set system level permissions to the specified role. Expects a table with capability name and permission (Inherit/Allow/Prevent/Prohibit) columns.
  45       * @Given /^I set the following system permissions of "(?P<rolefullname_string>(?:[^"]|\\")*)" role:$/
  46       * @param string $rolename
  47       * @param TableNode $table
  48       */
  49      public function i_set_the_following_system_permissions_of_role($rolename, $table) {
  50          // Applied in the System context.
  51          $context = \context_system::instance();
  52  
  53          // Translate the specified rolename into a role.
  54          $rolenames = role_get_names($context);
  55          $matched = array_filter($rolenames, function($role) use ($rolename) {
  56              return ($role->localname === $rolename) || ($role->shortname === $rolename) || ($role->description === $rolename);
  57          });
  58  
  59          if (count($matched) === 0) {
  60              throw new ExpectationException("Unable to find a role with name '{$rolename}'", $this->getSession());
  61          } else if (count($matched) > 1) {
  62              throw new ExpectationException("Multiple roles matched '{$rolename}'", $this->getSession());
  63          }
  64  
  65          $role = reset($matched);
  66  
  67          $permissionmap = [
  68              get_string('inherit', 'role') => 'inherit',
  69              get_string('allow', 'role') => 'allow',
  70              get_string('prevent', 'role') => 'prevent',
  71              get_string('prohibit', 'role') => 'prohibit',
  72          ];
  73  
  74          $columns = ['role'];
  75          $newtabledata = [$role->shortname];
  76          foreach ($table as $data) {
  77              $columns[] = $data['capability'];
  78              $newtabledata[] = $permissionmap[$data['permission']];
  79          }
  80  
  81          $this->execute(
  82              'behat_data_generators::the_following_entities_exist',
  83              [
  84                  'role capabilities',
  85                  new TableNode([
  86                      0 => $columns,
  87                      1 => $newtabledata,
  88                  ])
  89              ]
  90          );
  91      }
  92  
  93      /**
  94       * Overrides system capabilities at category, course and module levels. This step begins after clicking 'Permissions' link. Expects a table with capability name and permission (Inherit/Allow/Prevent/Prohibit) columns.
  95       * @Given /^I override the system permissions of "(?P<rolefullname_string>(?:[^"]|\\")*)" role with:$/
  96       * @param string $rolename
  97       * @param TableNode $table
  98       */
  99      public function i_override_the_system_permissions_of_role_with($rolename, $table) {
 100  
 101          // We don't know the number of overrides so we have to get it to match the option contents.
 102          $roleoption = $this->find('xpath', '//select[@name="roleid"]/option[contains(.,"' . $this->escape($rolename) . '")]');
 103  
 104          $this->execute('behat_forms::i_set_the_field_to',
 105              array(get_string('advancedoverride', 'role'), $this->escape($roleoption->getText()))
 106          );
 107  
 108          if (!$this->running_javascript()) {
 109              $xpath = "//div[@class='advancedoverride']/div/form/noscript";
 110              $this->execute("behat_general::i_click_on_in_the", [
 111                  get_string('go'), 'button',
 112                  $this->escape($xpath),
 113                  'xpath_element']
 114              );
 115          }
 116  
 117          $this->execute("behat_permissions::i_fill_the_capabilities_form_with_the_following_permissions", $table);
 118  
 119          $this->execute('behat_forms::press_button', get_string('savechanges'));
 120      }
 121  
 122      /**
 123       * Fills the advanced permissions form with the provided data. Expects a table with capability name and permission (Inherit/Allow/Prevent/Prohibit) columns.
 124       * @Given /^I fill the capabilities form with the following permissions:$/
 125       * @param TableNode $table
 126       * @return void
 127       */
 128      public function i_fill_the_capabilities_form_with_the_following_permissions($table) {
 129  
 130          // Ensure we are using the advanced view.
 131          // Wrapped in a try/catch to capture the exception and continue execution, we don't know if advanced mode was already enabled.
 132          try {
 133              $advancedtoggle = $this->find_button(get_string('showadvanced', 'form'));
 134              if ($advancedtoggle) {
 135                  $advancedtoggle->click();
 136  
 137                  // Wait for the page to load.
 138                  $this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
 139              }
 140          } catch (Exception $e) {
 141              // We already are in advanced mode.
 142          }
 143  
 144          // Using getRows() as we are not sure if tests writers will add the header.
 145          foreach ($table->getRows() as $key => $row) {
 146  
 147              if (count($row) !== 2) {
 148                  throw new ExpectationException('You should specify a table with capability/permission columns', $this->getSession());
 149              }
 150  
 151              list($capability, $permission) = $row;
 152  
 153              // Skip the headers row if it was provided
 154              if (strtolower($capability) == 'capability' || strtolower($capability) == 'capabilities') {
 155                  continue;
 156              }
 157  
 158              // Checking the permission value.
 159              $permissionconstant = 'CAP_'. strtoupper($permission);
 160              if (!defined($permissionconstant)) {
 161                  throw new ExpectationException(
 162                      'The provided permission value "' . $permission . '" is not valid. Use Inherit, Allow, Prevent or Prohibited',
 163                      $this->getSession()
 164                  );
 165              }
 166  
 167              // Converting from permission to constant value.
 168              $permissionvalue = constant($permissionconstant);
 169  
 170              // Here we wait for the element to appear and exception if it does not exist.
 171              $radio = $this->find('xpath', '//input[@name="' . $capability . '" and @value="' . $permissionvalue . '"]');
 172              $field = behat_field_manager::get_field_instance('radio', $radio, $this->getSession());
 173              $field->set_value(1);
 174          }
 175      }
 176  
 177      /**
 178       * Checks if the capability has the specified permission. Works in the role definition advanced page.
 179       *
 180       * @Then /^"(?P<capability_string>(?:[^"]|\\")*)" capability has "(?P<permission_string>Not set|Allow|Prevent|Prohibit)" permission$/
 181       * @throws ExpectationException
 182       * @param string $capabilityname
 183       * @param string $permission
 184       * @return void
 185       */
 186      public function capability_has_permission($capabilityname, $permission) {
 187  
 188          // We already know the name, so we just need the value.
 189          $radioxpath = "//table[contains(concat(' ',
 190   normalize-space(@class), ' '), ' rolecap ')]/descendant::input[@type='radio']" .
 191              "[@name='" . $capabilityname . "'][@checked]";
 192  
 193          $checkedradio = $this->find('xpath', $radioxpath);
 194  
 195          switch ($permission) {
 196              case get_string('notset', 'role'):
 197                  $perm = CAP_INHERIT;
 198                  break;
 199              case get_string('allow', 'role'):
 200                  $perm = CAP_ALLOW;
 201                  break;
 202              case get_string('prevent', 'role'):
 203                  $perm = CAP_PREVENT;
 204                  break;
 205              case get_string('prohibit', 'role'):
 206                  $perm = CAP_PROHIBIT;
 207                  break;
 208              default:
 209                  throw new ExpectationException('"' . $permission . '" permission does not exist', $this->getSession());
 210                  break;
 211          }
 212  
 213          if ($checkedradio->getAttribute('value') != $perm) {
 214              throw new ExpectationException('"' . $capabilityname . '" permission is not "' . $permission . '"', $this->getSession());
 215          }
 216      }
 217  
 218      /**
 219       * Set the allowed role assignments for the specified role.
 220       *
 221       * @Given /^I define the allowed role assignments for the "(?P<rolefullname_string>(?:[^"]|\\")*)" role as:$/
 222       * @param string $rolename
 223       * @param TableNode $table
 224       * @return void Executes other steps
 225       */
 226      public function i_define_the_allowed_role_assignments_for_a_role_as($rolename, $table) {
 227          $parentnodes = get_string('users', 'admin') . ' > ' .
 228              get_string('permissions', 'role');
 229  
 230          // Go to home page.
 231          $this->execute("behat_general::i_am_on_homepage");
 232  
 233          // Navigate to Define roles page via site administration menu.
 234          $this->execute("behat_navigation::i_navigate_to_in_site_administration",
 235                  $parentnodes .' > '. get_string('defineroles', 'role')
 236          );
 237  
 238          $this->execute("behat_general::click_link", "Allow role assignments");
 239          $this->execute("behat_permissions::i_fill_in_the_allowed_role_assignments_form_for_a_role_with",
 240              array($rolename, $table)
 241          );
 242  
 243          $this->execute('behat_forms::press_button', get_string('savechanges'));
 244      }
 245  
 246      /**
 247       * Fill in the allowed role assignments form for the specied role.
 248       *
 249       * Takes a table with two columns. Each row should contain the target
 250       * role, and either "Assignable" or "Not assignable".
 251       *
 252       * @Given /^I fill in the allowed role assignments form for the "(?P<rolefullname_string>(?:[^"]|\\")*)" role with:$/
 253       * @param String $sourcerole
 254       * @param TableNode $table
 255       * @return void
 256       */
 257      public function i_fill_in_the_allowed_role_assignments_form_for_a_role_with($sourcerole, $table) {
 258          foreach ($table->getRows() as $key => $row) {
 259              list($targetrole, $allowed) = $row;
 260  
 261              $node = $this->find('xpath', '//input[@title="Allow users with role ' .
 262                  $sourcerole .
 263                  ' to assign the role ' .
 264                  $targetrole . '"]');
 265  
 266              if ($allowed == 'Assignable') {
 267                  if (!$node->isChecked()) {
 268                      $node->check();
 269                  }
 270              } else if ($allowed == 'Not assignable') {
 271                  if ($node->isChecked()) {
 272                      $node->uncheck();
 273                  }
 274              } else {
 275                  throw new ExpectationException(
 276                      'The provided permission value "' . $allowed . '" is not valid. Use Assignable, or Not assignable',
 277                      $this->getSession()
 278                  );
 279              }
 280          }
 281      }
 282  
 283      /**
 284       * Mark context as frozen.
 285       *
 286       * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" is context frozen$/
 287       * @throws ExpectationException if the context cannot be frozen or found
 288       * @param string $element Element we look on
 289       * @param string $selector The type of where we look (activity, course)
 290       */
 291      public function the_context_is_context_frozen(string $element, string $selector) {
 292  
 293          // Enable context freeze if it is not done yet.
 294          set_config('contextlocking', 1);
 295  
 296          // Find context.
 297          $context = self::get_context($selector, $element);
 298  
 299          // Freeze context.
 300          $context->set_locked(true);
 301      }
 302  
 303      /**
 304       * Unmark context as frozen.
 305       *
 306       * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" is not context frozen$/
 307       * @throws ExpectationException if the context cannot be frozen or found
 308       * @param string $element Element we look on
 309       * @param string $selector The type of where we look (activity, course)
 310       */
 311      public function the_context_is_not_context_frozen(string $element, string $selector) {
 312  
 313          // Enable context freeze if it is not done yet.
 314          set_config('contextlocking', 1);
 315  
 316          // Find context.
 317          $context = self::get_context($selector, $element);
 318  
 319          // Freeze context.
 320          $context->set_locked(false);
 321      }
 322  }