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] [Versions 400 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   * Step definitions to generate database fixtures for the data privacy tool.
  19   *
  20   * @package    tool_dataprivacy
  21   * @category   test
  22   * @copyright  2018 Jun Pataleta
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  require_once (__DIR__ . '/../../../../../lib/behat/behat_base.php');
  27  
  28  use Behat\Gherkin\Node\TableNode as TableNode;
  29  use Behat\Behat\Tester\Exception\PendingException as PendingException;
  30  use tool_dataprivacy\api;
  31  
  32  /**
  33   * Step definitions to generate database fixtures for the data privacy tool.
  34   *
  35   * @package    tool_dataprivacy
  36   * @category   test
  37   * @copyright  2018 Jun Pataleta
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class behat_tool_dataprivacy extends behat_base {
  41  
  42      /**
  43       * Each element specifies:
  44       * - The data generator suffix used.
  45       * - The required fields.
  46       * - The mapping between other elements references and database field names.
  47       * @var array
  48       */
  49      protected static $elements = array(
  50          'categories' => array(
  51              'datagenerator' => 'category',
  52              'required' => array()
  53          ),
  54          'purposes' => array(
  55              'datagenerator' => 'purpose',
  56              'required' => array()
  57          ),
  58      );
  59  
  60      /**
  61       * Creates the specified element. More info about available elements in https://moodledev.io/general/development/tools/behat.
  62       *
  63       * @Given /^the following data privacy "(?P<element_string>(?:[^"]|\\")*)" exist:$/
  64       *
  65       * @param string    $elementname The name of the entity to add
  66       * @param TableNode $data
  67       */
  68      public function the_following_data_categories_exist($elementname, TableNode $data) {
  69  
  70          // Now that we need them require the data generators.
  71          require_once (__DIR__.'/../../../../../lib/phpunit/classes/util.php');
  72  
  73          if (empty(self::$elements[$elementname])) {
  74              throw new PendingException($elementname . ' data generator is not implemented');
  75          }
  76  
  77          $datagenerator = testing_util::get_data_generator();
  78          $dataprivacygenerator = $datagenerator->get_plugin_generator('tool_dataprivacy');
  79  
  80          $elementdatagenerator = self::$elements[$elementname]['datagenerator'];
  81          $requiredfields = self::$elements[$elementname]['required'];
  82          if (!empty(self::$elements[$elementname]['switchids'])) {
  83              $switchids = self::$elements[$elementname]['switchids'];
  84          }
  85  
  86          foreach ($data->getHash() as $elementdata) {
  87  
  88              // Check if all the required fields are there.
  89              foreach ($requiredfields as $requiredfield) {
  90                  if (!isset($elementdata[$requiredfield])) {
  91                      throw new Exception($elementname . ' requires the field ' . $requiredfield . ' to be specified');
  92                  }
  93              }
  94  
  95              // Switch from human-friendly references to ids.
  96              if (isset($switchids)) {
  97                  foreach ($switchids as $element => $field) {
  98                      $methodname = 'get_' . $element . '_id';
  99  
 100                      // Not all the switch fields are required, default vars will be assigned by data generators.
 101                      if (isset($elementdata[$element])) {
 102                          // Temp $id var to avoid problems when $element == $field.
 103                          $id = $this->{$methodname}($elementdata[$element]);
 104                          unset($elementdata[$element]);
 105                          $elementdata[$field] = $id;
 106                      }
 107                  }
 108              }
 109  
 110              // Preprocess the entities that requires a special treatment.
 111              if (method_exists($this, 'preprocess_' . $elementdatagenerator)) {
 112                  $elementdata = $this->{'preprocess_' . $elementdatagenerator}($elementdata);
 113              }
 114  
 115              // Creates element.
 116              $methodname = 'create_' . $elementdatagenerator;
 117              if (method_exists($dataprivacygenerator, $methodname)) {
 118                  // Using data generators directly.
 119                  $dataprivacygenerator->{$methodname}($elementdata);
 120  
 121              } else if (method_exists($this, 'process_' . $elementdatagenerator)) {
 122                  // Using an alternative to the direct data generator call.
 123                  $this->{'process_' . $elementdatagenerator}($elementdata);
 124              } else {
 125                  throw new PendingException($elementname . ' data generator is not implemented');
 126              }
 127          }
 128      }
 129  
 130      /**
 131       * Sets the data category and data storage purpose for the site.
 132       *
 133       * @Given /^I set the site category and purpose to "(?P<category_string>(?:[^"]|\\")*)" and "(?P<purpose_string>(?:[^"]|\\")*)"$/
 134       *
 135       * @param string $category The ID of the category to be set for the instance.
 136       * @param string $purpose The ID of the purpose to be set for the instance.
 137       */
 138      public function i_set_the_site_category_and_purpose($category, $purpose) {
 139          $category = \tool_dataprivacy\category::get_record(['name' => $category]);
 140          $purpose = \tool_dataprivacy\purpose::get_record(['name' => $purpose]);
 141          $data = (object)[
 142              'contextlevel' => CONTEXT_SYSTEM,
 143              'categoryid' => $category->get('id'),
 144              'purposeid' => $purpose->get('id'),
 145          ];
 146          api::set_contextlevel($data);
 147      }
 148  
 149      /**
 150       * Sets the data category and data storage purpose for a course category instance.
 151       *
 152       * @Given /^I set the category and purpose for the course category "(?P<categoryname_string>(?:[^"]|\\")*)" to "(?P<category_string>(?:[^"]|\\")*)" and "(?P<purpose_string>(?:[^"]|\\")*)"$/
 153       *
 154       * @param string $name The instance name. It should match the name or the idnumber.
 155       * @param string $category The ID of the category to be set for the instance.
 156       * @param string $purpose The ID of the purpose to be set for the instance.
 157       */
 158      public function i_set_the_category_and_purpose_for_course_category($name, $category, $purpose) {
 159          global $DB;
 160  
 161          $params = [
 162              'name' => $name,
 163              'idnumber' => $name,
 164          ];
 165          $select = 'name = :name OR idnumber = :idnumber';
 166          $coursecatid = $DB->get_field_select('course_categories', 'id', $select, $params, MUST_EXIST);
 167          $context = context_coursecat::instance($coursecatid);
 168  
 169          $this->set_category_and_purpose($context->id, $category, $purpose);
 170      }
 171  
 172      /**
 173       * Sets the data category and data storage purpose for a course instance.
 174       *
 175       * @Given /^I set the category and purpose for the course "(?P<coursename_string>(?:[^"]|\\")*)" to "(?P<category_string>(?:[^"]|\\")*)" and "(?P<purpose_string>(?:[^"]|\\")*)"$/
 176       *
 177       * @param string $name The instance name. It should match the fullname or the shortname, or the idnumber.
 178       * @param string $category The ID of the category to be set for the instance.
 179       * @param string $purpose The ID of the purpose to be set for the instance.
 180       */
 181      public function i_set_the_category_and_purpose_for_course($name, $category, $purpose) {
 182          global $DB;
 183  
 184          $params = [
 185              'shortname' => $name,
 186              'fullname' => $name,
 187              'idnumber' => $name,
 188          ];
 189          $select = 'shortname = :shortname OR fullname = :fullname OR idnumber = :idnumber';
 190          $courseid = $DB->get_field_select('course', 'id', $select, $params, MUST_EXIST);
 191          $context = context_course::instance($courseid);
 192  
 193          $this->set_category_and_purpose($context->id, $category, $purpose);
 194      }
 195  
 196      /**
 197       * Sets the data category and data storage purpose for a course instance.
 198       *
 199       * @Given /^I set the category and purpose for the "(?P<activityname_string>(?:[^"]|\\")*)" "(?P<activitytype_string>(?:[^"]|\\")*)" in course "(?P<coursename_string>(?:[^"]|\\")*)" to "(?P<category_string>(?:[^"]|\\")*)" and "(?P<purpose_string>(?:[^"]|\\")*)"$/
 200       *
 201       * @param string $name The instance name. It should match the name of the activity.
 202       * @param string $type The activity type. E.g. assign, quiz, forum, etc.
 203       * @param string $coursename The course name. It should match the fullname or the shortname, or the idnumber.
 204       * @param string $category The ID of the category to be set for the instance.
 205       * @param string $purpose The ID of the purpose to be set for the instance.
 206       */
 207      public function i_set_the_category_and_purpose_for_activity($name, $type, $coursename, $category, $purpose) {
 208          global $DB;
 209  
 210          $params = [
 211              'shortname' => $coursename,
 212              'fullname' => $coursename,
 213              'idnumber' => $coursename,
 214          ];
 215          $select = 'shortname = :shortname OR fullname = :fullname OR idnumber = :idnumber';
 216          $courseid = $DB->get_field_select('course', 'id', $select, $params, MUST_EXIST);
 217  
 218          $cmid = null;
 219          $cms = get_coursemodules_in_course($type, $courseid);
 220          foreach ($cms as $cm) {
 221              if ($cm->name === $name || $cm->idnumber === $name) {
 222                  $cmid = $cm->id;
 223                  break;
 224              }
 225          }
 226          if ($cmid === null) {
 227              throw new coding_exception("Activity module '{$name}' of type '{$type}' not found!");
 228          }
 229          $context = context_module::instance($cmid);
 230  
 231          $this->set_category_and_purpose($context->id, $category, $purpose);
 232      }
 233  
 234      /**
 235       * Sets the data category and data storage purpose for a course instance.
 236       *
 237       * @Given /^I set the category and purpose for the "(?P<blockname_string>(?:[^"]|\\")*)" block in the "(?P<coursename_string>(?:[^"]|\\")*)" course to "(?P<category_string>(?:[^"]|\\")*)" and "(?P<purpose_string>(?:[^"]|\\")*)"$/
 238       *
 239       * @param string $name The instance name. It should match the name of the block. (e.g. online_users)
 240       * @param string $coursename The course name. It should match the fullname or the shortname, or the idnumber.
 241       * @param string $category The ID of the category to be set for the instance.
 242       * @param string $purpose The ID of the purpose to be set for the instance.
 243       */
 244      public function i_set_the_category_and_purpose_for_block($name, $coursename, $category, $purpose) {
 245          global $DB;
 246  
 247          $params = [
 248              'shortname' => $coursename,
 249              'fullname' => $coursename,
 250              'idnumber' => $coursename,
 251          ];
 252          $select = 'shortname = :shortname OR fullname = :fullname OR idnumber = :idnumber';
 253          $courseid = $DB->get_field_select('course', 'id', $select, $params, MUST_EXIST);
 254  
 255          // Fetch the course context.
 256          $coursecontext = context_course::instance($courseid);
 257  
 258          // Fetch the block record and context.
 259          $blockid = $DB->get_field('block_instances', 'id', ['blockname' => $name, 'parentcontextid' => $coursecontext->id]);
 260          $context = context_block::instance($blockid);
 261  
 262          // Set the category and purpose.
 263          $this->set_category_and_purpose($context->id, $category, $purpose);
 264      }
 265  
 266      /**
 267       * Sets the category and purpose for a context instance.
 268       *
 269       * @param int $contextid The context ID.
 270       * @param int $categoryname The category name.
 271       * @param int $purposename The purpose name.
 272       * @throws coding_exception
 273       */
 274      protected function set_category_and_purpose($contextid, $categoryname, $purposename) {
 275          $category = \tool_dataprivacy\category::get_record(['name' => $categoryname]);
 276          $purpose = \tool_dataprivacy\purpose::get_record(['name' => $purposename]);
 277  
 278          api::set_context_instance((object) [
 279              'contextid' => $contextid,
 280              'purposeid' => $purpose->get('id'),
 281              'categoryid' => $category->get('id'),
 282          ]);
 283      }
 284  
 285      /**
 286       * Create a dataprivacy request.
 287       *
 288       * @Given /^I create a dataprivacy "(?P<type_string>(?:[^"]|\\")*)" request for "(?P<user_string>(?:[^"]|\\")*)"$/
 289       *
 290       * @param string $type The type of request to create (delete, or export)
 291       * @param string $username The username to create for
 292       */
 293      public function i_create_a_dataprivacy_request_for($type, $username) {
 294          if ($type === 'delete') {
 295              $type = \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE;
 296          } else if ($type === 'export') {
 297              $type = \tool_dataprivacy\api::DATAREQUEST_TYPE_EXPORT;
 298          } else {
 299              throw new \Behat\Behat\Tester\Exception\ExpectationException("Unknown request type '{$type}'");
 300          }
 301  
 302          $user = \core_user::get_user_by_username($username);
 303  
 304          \tool_dataprivacy\api::create_data_request($user->id, $type);
 305      }
 306  
 307      /**
 308       * Approve a dataprivacy request.
 309       *
 310       * @Given /^I approve a dataprivacy "(?P<type_string>(?:[^"]|\\")*)" request for "(?P<user_string>(?:[^"]|\\")*)"$/
 311       *
 312       * @param string $type The type of request to create (delete, or export)
 313       * @param string $username The username to create for
 314       */
 315      public function i_approve_a_dataprivacy_request_for($type, $username) {
 316          if ($type === 'delete') {
 317              $type = \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE;
 318          } else if ($type === 'export') {
 319              $type = \tool_dataprivacy\api::DATAREQUEST_TYPE_EXPORT;
 320          } else {
 321              throw new \Behat\Behat\Tester\Exception\ExpectationException("Unknown request type '{$type}'");
 322          }
 323  
 324          $user = \core_user::get_user_by_username($username);
 325  
 326          $request = \tool_dataprivacy\data_request::get_record([
 327              'userid'    => $user->id,
 328              'type'      => $type,
 329              'status'    => \tool_dataprivacy\api::DATAREQUEST_STATUS_AWAITING_APPROVAL,
 330          ]);
 331  
 332          \tool_dataprivacy\api::approve_data_request($request->get('id'));
 333      }
 334  }