Search moodle.org's
Developer Documentation

See Release Notes

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