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