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.
   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   * Privacy tests for core_grading.
  19   *
  20   * @package    core_grading
  21   * @category   test
  22   * @copyright  2018 Sara Arjona <sara@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  namespace core_grading\privacy;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  
  31  use core_privacy\tests\provider_testcase;
  32  use core_privacy\local\request\approved_contextlist;
  33  use core_privacy\local\request\transform;
  34  use core_privacy\local\request\writer;
  35  use core_grading\privacy\provider;
  36  
  37  /**
  38   * Privacy tests for core_grading.
  39   *
  40   * @copyright  2018 Sara Arjona <sara@moodle.com>
  41   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42   */
  43  class provider_test extends provider_testcase {
  44  
  45      /** @var stdClass User without data. */
  46      protected $user0;
  47  
  48      /** @var stdClass User with data. */
  49      protected $user1;
  50  
  51      /** @var stdClass User with data. */
  52      protected $user2;
  53  
  54      /** @var context context_module of an activity without grading definitions. */
  55      protected $instancecontext0;
  56  
  57      /** @var context context_module of the activity where the grading definitions are. */
  58      protected $instancecontext1;
  59  
  60      /** @var context context_module of the activity where the grading definitions are. */
  61      protected $instancecontext2;
  62  
  63      /**
  64       * Test getting the context for the user ID related to this plugin.
  65       */
  66      public function test_get_contexts_for_userid() {
  67          global $DB;
  68  
  69          $this->resetAfterTest();
  70          $this->grading_setup_test_scenario_data();
  71          $this->assertCount(2, $DB->get_records('grading_definitions'));
  72  
  73          // User1 has created grading definitions for instance1 and instance2.
  74          $contextlist = provider::get_contexts_for_userid($this->user1->id);
  75          $this->assertCount(2, $contextlist);
  76          $this->assertContainsEquals($this->instancecontext1->id, $contextlist->get_contextids());
  77          $this->assertContainsEquals($this->instancecontext2->id, $contextlist->get_contextids());
  78          $this->assertNotContainsEquals($this->instancecontext0->id, $contextlist->get_contextids());
  79  
  80          // User2 has only modified grading definitions for instance2.
  81          $contextlist = provider::get_contexts_for_userid($this->user2->id);
  82          $this->assertCount(1, $contextlist);
  83          $this->assertContainsEquals($this->instancecontext2->id, $contextlist->get_contextids());
  84  
  85          // User0 hasn't created or modified any grading definition.
  86          $contextlist = provider::get_contexts_for_userid($this->user0->id);
  87          $this->assertCount(0, $contextlist);
  88      }
  89  
  90      /**
  91       * Test retrieval of user ids in a given context.
  92       */
  93      public function test_get_users_in_context() {
  94          $this->resetAfterTest();
  95          $this->grading_setup_test_scenario_data();
  96          // Instance two has one user who created the definitions and another who modified it.
  97          $userlist = new \core_privacy\local\request\userlist($this->instancecontext2, 'core_grading');
  98          provider::get_users_in_context($userlist);
  99          // Check that we get both.
 100          $this->assertCount(2, $userlist->get_userids());
 101      }
 102  
 103      /**
 104       * Export for a user with no grading definitions created or modified will not have any data exported.
 105       */
 106      public function test_export_user_data_no_content() {
 107          $this->resetAfterTest();
 108  
 109          $user = $this->getDataGenerator()->create_user();
 110          $this->setUser($user);
 111          $context = \context_system::instance();
 112  
 113          $writer = writer::with_context($context);
 114          $this->assertFalse($writer->has_any_data());
 115          $this->export_context_data_for_user($user->id, $context, 'core_grading');
 116          $this->assertFalse(writer::with_context($context)->has_any_data());
 117      }
 118  
 119      /**
 120       * Test that data is exported correctly for this plugin.
 121       */
 122      public function test_export_user_data() {
 123          global $DB;
 124  
 125          $this->resetAfterTest();
 126          $now = time();
 127          $defnameprefix = 'fakename';
 128          $this->grading_setup_test_scenario_data($defnameprefix, $now);
 129          $this->assertCount(2, $DB->get_records('grading_definitions'));
 130  
 131          // Validate exported data: instance1 - user0 has NO data.
 132          $this->setUser($this->user0);
 133          writer::reset();
 134          $writer = writer::with_context($this->instancecontext1);
 135          $this->assertFalse($writer->has_any_data());
 136          $this->export_context_data_for_user($this->user0->id, $this->instancecontext1, 'core_grading');
 137          $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
 138          $this->assertEmpty($data);
 139  
 140          // Validate exported data: instance0 - user1 has NO data.
 141          $this->setUser($this->user1);
 142          writer::reset();
 143          $writer = writer::with_context($this->instancecontext0);
 144          $this->assertFalse($writer->has_any_data());
 145          $this->export_context_data_for_user($this->user1->id, $this->instancecontext0, 'core_grading');
 146          $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
 147          $this->assertEmpty($data);
 148  
 149          // Validate exported data: instance1 - user1 has data (user has created and modified it).
 150          writer::reset();
 151          $writer = writer::with_context($this->instancecontext1);
 152          $this->assertFalse($writer->has_any_data());
 153          $this->export_context_data_for_user($this->user1->id, $this->instancecontext1, 'core_grading');
 154          $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
 155          $this->assertCount(1, $data->definitions);
 156  
 157          $firstkey = reset($data->definitions);
 158          $this->assertNotEmpty($firstkey->name);
 159          $this->assertEquals('test_method', $firstkey->method);
 160          $this->assertEquals(transform::datetime($now), $firstkey->timecreated);
 161          $this->assertEquals($this->user1->id, $firstkey->usercreated);
 162          $this->assertEquals($defnameprefix.'1', $firstkey->name);
 163  
 164          // Validate exported data: instance2 - user1 has data (user has created it).
 165          writer::reset();
 166          $writer = writer::with_context($this->instancecontext2);
 167          $this->assertFalse($writer->has_any_data());
 168          $this->export_context_data_for_user($this->user1->id, $this->instancecontext2, 'core_grading');
 169          $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
 170          $this->assertCount(1, $data->definitions);
 171  
 172          $firstkey = reset($data->definitions);
 173          $this->assertNotEmpty($firstkey->name);
 174          $this->assertEquals('test_method', $firstkey->method);
 175          $this->assertEquals(transform::datetime($now), $firstkey->timecreated);
 176          $this->assertEquals($this->user1->id, $firstkey->usercreated);
 177          $this->assertEquals($defnameprefix.'2', $firstkey->name);
 178  
 179          // Validate exported data: instance1 - user2 has NO data.
 180          $this->setUser($this->user2);
 181          writer::reset();
 182          $writer = writer::with_context($this->instancecontext1);
 183          $this->assertFalse($writer->has_any_data());
 184          $this->export_context_data_for_user($this->user2->id, $this->instancecontext1, 'core_grading');
 185          $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
 186          $this->assertEmpty($data);
 187  
 188          // Validate exported data: instance2 - user2 has data (user has modified it).
 189          $this->setUser($this->user2);
 190          writer::reset();
 191          $writer = writer::with_context($this->instancecontext2);
 192          $this->assertFalse($writer->has_any_data());
 193          $this->export_context_data_for_user($this->user2->id, $this->instancecontext2, 'core_grading');
 194          $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
 195          $this->assertCount(1, $data->definitions);
 196      }
 197  
 198      /**
 199       * Test for provider::delete_data_for_all_users_in_context().
 200       */
 201      public function test_delete_data_for_all_users_in_context() {
 202          global $DB;
 203  
 204          $this->resetAfterTest();
 205          $this->grading_setup_test_scenario_data();
 206  
 207          // Before deletion, we should have 2 grading_definitions.
 208          $this->assertCount(2, $DB->get_records('grading_definitions'));
 209  
 210          // Delete data.
 211          provider::delete_data_for_all_users_in_context($this->instancecontext0);
 212          provider::delete_data_for_all_users_in_context($this->instancecontext1);
 213          provider::delete_data_for_all_users_in_context($this->instancecontext2);
 214  
 215          // Before deletion, we should have same grading_definitions (nothing was deleted).
 216          $this->assertCount(2, $DB->get_records('grading_definitions'));
 217      }
 218  
 219      /**
 220       * Test for provider::delete_data_for_user().
 221       */
 222      public function test_delete_data_for_user() {
 223          global $DB;
 224  
 225          $this->resetAfterTest();
 226          $this->grading_setup_test_scenario_data();
 227  
 228          // Before deletion, we should have 2 grading_definitions.
 229          $this->assertCount(2, $DB->get_records('grading_definitions'));
 230  
 231          // Delete data for $user0.
 232          $contextlist = provider::get_contexts_for_userid($this->user0->id);
 233          $approvedcontextlist = new approved_contextlist(
 234              $this->user0,
 235              'core_grading',
 236              $contextlist->get_contextids()
 237          );
 238          provider::delete_data_for_user($approvedcontextlist);
 239  
 240          // Delete data for $user1.
 241          $contextlist = provider::get_contexts_for_userid($this->user1->id);
 242          $approvedcontextlist = new approved_contextlist(
 243              $this->user1,
 244              'core_grading',
 245              $contextlist->get_contextids()
 246          );
 247          provider::delete_data_for_user($approvedcontextlist);
 248  
 249          // Delete data for $user2.
 250          $contextlist = provider::get_contexts_for_userid($this->user2->id);
 251          $approvedcontextlist = new approved_contextlist(
 252              $this->user2,
 253              'core_grading',
 254              $contextlist->get_contextids()
 255          );
 256          provider::delete_data_for_user($approvedcontextlist);
 257  
 258          // Before deletion, we should have same grading_definitions (nothing was deleted).
 259          $this->assertCount(2, $DB->get_records('grading_definitions'));
 260      }
 261  
 262      /**
 263       * Test exporting user data relating to an item ID.
 264       */
 265      public function test_export_item_data() {
 266          global $DB;
 267          $this->resetAfterTest();
 268          $course = $this->getDataGenerator()->create_course();
 269          $module = $this->getDataGenerator()->create_module('assign', ['course' => $course]);
 270          $user = $this->getDataGenerator()->create_user();
 271          $guidegenerator = \testing_util::get_data_generator()->get_plugin_generator('gradingform_guide');
 272  
 273          $this->setUser($user);
 274  
 275          $modulecontext = \context_module::instance($module->cmid);
 276          $controller = $guidegenerator->get_test_guide($modulecontext);
 277  
 278          // In the situation of mod_assign this would be the id from assign_grades.
 279          $itemid = 1;
 280          $instance = $controller->create_instance($user->id, $itemid);
 281          $data = $guidegenerator->get_test_form_data(
 282              $controller,
 283              $itemid,
 284              5, 'This user made several mistakes.',
 285              10, 'This user has two pictures.'
 286          );
 287  
 288          $instance->update($data);
 289          $instanceid = $instance->get_data('id');
 290  
 291          provider::export_item_data($modulecontext, $itemid, ['Test']);
 292          $data = (array) writer::with_context($modulecontext)->get_data(['Test', 'Marking guide', $instance->get_data('id')]);
 293          $this->assertCount(2, $data);
 294          $this->assertEquals('This user made several mistakes.', $data['Spelling mistakes']->remark);
 295          $this->assertEquals(5, $data['Spelling mistakes']->score);
 296          $this->assertEquals('This user has two pictures.', $data['Pictures']->remark);
 297          $this->assertEquals(10, $data['Pictures']->score);
 298      }
 299  
 300      /**
 301       * Test deleting user data related to a context and item ID.
 302       */
 303      public function test_delete_instance_data() {
 304          global $DB;
 305          $this->resetAfterTest();
 306          $course = $this->getDataGenerator()->create_course();
 307          $module = $this->getDataGenerator()->create_module('assign', ['course' => $course]);
 308          $user = $this->getDataGenerator()->create_user();
 309          $guidegenerator = \testing_util::get_data_generator()->get_plugin_generator('gradingform_guide');
 310  
 311          $this->setUser($user);
 312  
 313          $modulecontext = \context_module::instance($module->cmid);
 314          $controller = $guidegenerator->get_test_guide($modulecontext);
 315  
 316          // In the situation of mod_assign this would be the id from assign_grades.
 317          $itemid = 1;
 318          $instance = $controller->create_instance($user->id, $itemid);
 319          $data = $guidegenerator->get_test_form_data(
 320              $controller,
 321              $itemid,
 322              5, 'This user made several mistakes.',
 323              10, 'This user has two pictures.'
 324          );
 325          $instance->update($data);
 326  
 327          $itemid = 2;
 328          $instance = $controller->create_instance($user->id, $itemid);
 329          $data = $guidegenerator->get_test_form_data(
 330              $controller,
 331              $itemid,
 332              25, 'This user made no mistakes.',
 333              5, 'This user has one picture.'
 334          );
 335          $instance->update($data);
 336  
 337          // Check how many records we have in the fillings table.
 338          $records = $DB->get_records('gradingform_guide_fillings');
 339          $this->assertCount(4, $records);
 340          // Let's delete one of the instances (the last one would be the easiest).
 341          provider::delete_instance_data($modulecontext, $itemid);
 342          $records = $DB->get_records('gradingform_guide_fillings');
 343          $this->assertCount(2, $records);
 344          foreach ($records as $record) {
 345              $this->assertNotEquals($instance->get_id(), $record->instanceid);
 346          }
 347          // This will delete all the rest of the instances for this context.
 348          provider::delete_instance_data($modulecontext);
 349          $records = $DB->get_records('gradingform_guide_fillings');
 350          $this->assertEmpty($records);
 351      }
 352  
 353      /**
 354       * Test the deletion of multiple instances at once.
 355       */
 356      public function test_delete_data_for_instances() {
 357          global $DB;
 358          $this->resetAfterTest();
 359          $course = $this->getDataGenerator()->create_course();
 360          $module = $this->getDataGenerator()->create_module('assign', ['course' => $course]);
 361          $user1 = $this->getDataGenerator()->create_user();
 362          $user2 = $this->getDataGenerator()->create_user();
 363          $user3 = $this->getDataGenerator()->create_user();
 364          $guidegenerator = \testing_util::get_data_generator()->get_plugin_generator('gradingform_guide');
 365  
 366          $this->setUser($user1);
 367  
 368          $modulecontext = \context_module::instance($module->cmid);
 369          $controller = $guidegenerator->get_test_guide($modulecontext);
 370  
 371          // In the situation of mod_assign this would be the id from assign_grades.
 372          $itemid1 = 1;
 373          $instance1 = $controller->create_instance($user1->id, $itemid1);
 374          $data = $guidegenerator->get_test_form_data(
 375              $controller,
 376              $itemid1,
 377              5, 'This user made several mistakes.',
 378              10, 'This user has two pictures.'
 379          );
 380          $instance1->update($data);
 381  
 382          $itemid2 = 2;
 383          $instance2 = $controller->create_instance($user2->id, $itemid2);
 384          $data = $guidegenerator->get_test_form_data(
 385              $controller,
 386              $itemid2,
 387              15, 'This user made a couple of mistakes.',
 388              10, 'This user has one picture.'
 389          );
 390          $instance2->update($data);
 391  
 392          $itemid3 = 3;
 393          $instance3 = $controller->create_instance($user3->id, $itemid3);
 394          $data = $guidegenerator->get_test_form_data(
 395              $controller,
 396              $itemid3,
 397              20, 'This user made one mistakes.',
 398              10, 'This user has one picture.'
 399          );
 400          $instance3->update($data);
 401  
 402          $records = $DB->get_records('gradingform_guide_fillings');
 403          $this->assertCount(6, $records);
 404  
 405          // Delete all user data for items 1 and 3.
 406          provider::delete_data_for_instances($modulecontext, [$itemid1, $itemid3]);
 407  
 408          $records = $DB->get_records('gradingform_guide_fillings');
 409          $this->assertCount(2, $records);
 410          $instanceid = $instance2->get_data('id');
 411          // The instance id should match for all remaining records.
 412          foreach ($records as $record) {
 413              $this->assertEquals($instanceid, $record->instanceid);
 414          }
 415      }
 416  
 417      /**
 418       * Helper function to setup the environment.
 419       *
 420       * course
 421       *  |
 422       *  +--instance0 (assignment)
 423       *  |   |
 424       *  +--instance1 (assignment)
 425       *  |   |
 426       *  |   +--grading_definition1 (created and modified by user1)
 427       *  |   |
 428       *  +--instance2 (assignment)
 429       *  |   |
 430       *  |   +--grading_definition2 (created by user1 and modified by user2)
 431       *
 432       *
 433       * user0 hasn't any data.
 434       *
 435       * @param string $defnameprefix
 436       * @param timestamp $now
 437       */
 438      protected function grading_setup_test_scenario_data($defnameprefix = null, $now = null) {
 439          global $DB;
 440  
 441          $this->user0 = $this->getDataGenerator()->create_user();
 442          $this->user1 = $this->getDataGenerator()->create_user();
 443          $this->user2 = $this->getDataGenerator()->create_user();
 444  
 445          // Create a course.
 446          $course = $this->getDataGenerator()->create_course();
 447          $coursecontext = \context_course::instance($course->id);
 448  
 449          // Create some assignment instances.
 450          $params = (object)array(
 451              'course' => $course->id,
 452              'name'   => 'Testing instance'
 453          );
 454          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 455          $instance0 = $generator->create_instance($params);
 456          $cm0 = get_coursemodule_from_instance('assign', $instance0->id);
 457          $this->instancecontext0 = \context_module::instance($cm0->id);
 458          $instance1 = $generator->create_instance($params);
 459          $cm1 = get_coursemodule_from_instance('assign', $instance1->id);
 460          $this->instancecontext1 = \context_module::instance($cm1->id);
 461          $instance2 = $generator->create_instance($params);
 462          $cm2 = get_coursemodule_from_instance('assign', $instance2->id);
 463          $this->instancecontext2 = \context_module::instance($cm2->id);
 464  
 465          // Create fake grading areas.
 466          $fakearea1 = (object)array(
 467              'contextid'    => $this->instancecontext1->id,
 468              'component'    => 'mod_assign',
 469              'areaname'     => 'submissions',
 470              'activemethod' => 'test_method'
 471          );
 472          $fakeareaid1 = $DB->insert_record('grading_areas', $fakearea1);
 473          $fakearea2 = clone($fakearea1);
 474          $fakearea2->contextid = $this->instancecontext2->id;
 475          $fakeareaid2 = $DB->insert_record('grading_areas', $fakearea2);
 476  
 477          // Create fake grading definitions.
 478          if (empty($now)) {
 479              $now = time();
 480          }
 481          if (empty($defnameprefix)) {
 482              $defnameprefix = 'fakename';
 483          }
 484          $fakedefinition1 = (object)array(
 485              'areaid'       => $fakeareaid1,
 486              'method'       => 'test_method',
 487              'name'         => $defnameprefix.'1',
 488              'status'       => 0,
 489              'timecreated'  => $now,
 490              'usercreated'  => $this->user1->id,
 491              'timemodified' => $now + 1,
 492              'usermodified' => $this->user1->id,
 493          );
 494          $fakedefid1 = $DB->insert_record('grading_definitions', $fakedefinition1);
 495          $fakedefinition2 = clone($fakedefinition1);
 496          $fakedefinition2->areaid = $fakeareaid2;
 497          $fakedefinition2->name = $defnameprefix.'2';
 498          $fakedefinition2->usermodified = $this->user2->id;
 499          $fakedefid2 = $DB->insert_record('grading_definitions', $fakedefinition2);
 500      }
 501  }