Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.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   * Data provider tests.
  19   *
  20   * @package    core_grades
  21   * @category   test
  22   * @copyright  2018 Frédéric Massart
  23   * @author     Frédéric Massart <fred@branchup.tech>
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  namespace core_grades\privacy;
  27  
  28  defined('MOODLE_INTERNAL') || die();
  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_grades\privacy\provider;
  36  
  37  require_once($CFG->libdir . '/gradelib.php');
  38  
  39  /**
  40   * Data provider testcase class.
  41   *
  42   * @package    core_grades
  43   * @category   test
  44   * @copyright  2018 Frédéric Massart
  45   * @author     Frédéric Massart <fred@branchup.tech>
  46   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  47   */
  48  class provider_test extends provider_testcase {
  49  
  50      public function setUp(): void {
  51          global $PAGE;
  52          $this->resetAfterTest();
  53          $PAGE->get_renderer('core');
  54      }
  55  
  56      public function test_get_contexts_for_userid_gradebook_edits() {
  57          $dg = $this->getDataGenerator();
  58  
  59          $c1 = $dg->create_course();
  60          $c2 = $dg->create_course();
  61  
  62          $u1 = $dg->create_user();
  63          $u2 = $dg->create_user();
  64          $u3 = $dg->create_user();
  65          $u4 = $dg->create_user();
  66          $u5 = $dg->create_user();
  67          $u6 = $dg->create_user();
  68          $u7 = $dg->create_user();
  69          $u8 = $dg->create_user();
  70          $u9 = $dg->create_user();
  71          $u10 = $dg->create_user();
  72          $u11 = $dg->create_user();
  73  
  74          $sysctx = \context_system::instance();
  75          $c1ctx = \context_course::instance($c1->id);
  76          $c2ctx = \context_course::instance($c2->id);
  77  
  78          // Create some stuff.
  79          $gi1a = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
  80          $gi1b = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
  81          $gi2a = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
  82          $gc1a = new \grade_category($dg->create_grade_category(['courseid' => $c1->id]), false);
  83          $gc1b = new \grade_category($dg->create_grade_category(['courseid' => $c1->id]), false);
  84          $gc2a = new \grade_category($dg->create_grade_category(['courseid' => $c2->id]), false);
  85          $go2 = new \grade_outcome($dg->create_grade_outcome(['courseid' => $c2->id, 'shortname' => 'go2',
  86              'fullname' => 'go2']), false);
  87  
  88          // Nothing as of now.
  89          foreach ([$u1, $u2, $u3, $u4, $u5, $u6, $u7, $u8, $u9, $u10, $u11] as $u) {
  90              $contexts = array_flip(provider::get_contexts_for_userid($u->id)->get_contextids());
  91              $this->assertEmpty($contexts);
  92          }
  93  
  94          $go0 = new \grade_outcome(['shortname' => 'go0', 'fullname' => 'go0', 'usermodified' => $u1->id]);
  95          $go0->insert();
  96          $go1 = new \grade_outcome(['shortname' => 'go1', 'fullname' => 'go1', 'courseid' => $c1->id, 'usermodified' => $u11->id]);
  97          $go1->insert();
  98  
  99          // Create scales.
 100          $s1 = new \grade_scale(['name' => 's1', 'scale' => 'a,b', 'userid' => $u7->id, 'courseid' => 0, 'description' => '']);
 101          $s1->insert();
 102          $s2 = new \grade_scale(['name' => 's2', 'scale' => 'a,b', 'userid' => $u8->id, 'courseid' => $c1->id, 'description' => '']);
 103          $s2->insert();
 104  
 105          // User 2 creates history.
 106          $this->setUser($u2);
 107          $go0->shortname .= ' edited';
 108          $go0->update();
 109          $gc1a->fullname .= ' edited';
 110          $gc1a->update();
 111  
 112          // User 3 creates history.
 113          $this->setUser($u3);
 114          $go1->shortname .= ' edited';
 115          $go1->update();
 116          $gc2a->fullname .= ' a';
 117          $gc2a->update();
 118  
 119          // User 4 updates an outcome in course (creates history).
 120          $this->setUser($u4);
 121          $go2->shortname .= ' edited';
 122          $go2->update();
 123  
 124          // User 5 updates an item.
 125          $this->setUser($u5);
 126          $gi1a->itemname .= ' edited';
 127          $gi1a->update();
 128  
 129          // User 6 creates history.
 130          $this->setUser($u6);
 131          $gi2a->delete();
 132  
 133          // User 9 creates history.
 134          $this->setUser($u9);
 135          $s1->name .= ' edited';
 136          $s1->update();
 137  
 138          // Assert contexts.
 139          $contexts = array_flip(provider::get_contexts_for_userid($u1->id)->get_contextids());
 140          $this->assertCount(1, $contexts);
 141          $this->assertArrayHasKey($sysctx->id, $contexts);
 142          $contexts = array_flip(provider::get_contexts_for_userid($u11->id)->get_contextids());
 143          $this->assertCount(1, $contexts);
 144          $this->assertArrayHasKey($c1ctx->id, $contexts);
 145          $contexts = array_flip(provider::get_contexts_for_userid($u2->id)->get_contextids());
 146          $this->assertCount(2, $contexts);
 147          $this->assertArrayHasKey($sysctx->id, $contexts);
 148          $this->assertArrayHasKey($c1ctx->id, $contexts);
 149          $contexts = array_flip(provider::get_contexts_for_userid($u3->id)->get_contextids());
 150          $this->assertCount(2, $contexts);
 151          $this->assertArrayHasKey($c1ctx->id, $contexts);
 152          $this->assertArrayHasKey($c2ctx->id, $contexts);
 153          $contexts = array_flip(provider::get_contexts_for_userid($u4->id)->get_contextids());
 154          $this->assertCount(1, $contexts);
 155          $this->assertArrayHasKey($c2ctx->id, $contexts);
 156          $contexts = array_flip(provider::get_contexts_for_userid($u5->id)->get_contextids());
 157          $this->assertCount(1, $contexts);
 158          $this->assertArrayHasKey($c1ctx->id, $contexts);
 159          $contexts = array_flip(provider::get_contexts_for_userid($u6->id)->get_contextids());
 160          $this->assertCount(1, $contexts);
 161          $this->assertArrayHasKey($c2ctx->id, $contexts);
 162          $contexts = array_flip(provider::get_contexts_for_userid($u7->id)->get_contextids());
 163          $this->assertCount(1, $contexts);
 164          $this->assertArrayHasKey($sysctx->id, $contexts);
 165          $contexts = array_flip(provider::get_contexts_for_userid($u8->id)->get_contextids());
 166          $this->assertCount(1, $contexts);
 167          $this->assertArrayHasKey($c1ctx->id, $contexts);
 168          $contexts = array_flip(provider::get_contexts_for_userid($u9->id)->get_contextids());
 169          $this->assertCount(1, $contexts);
 170          $this->assertArrayHasKey($sysctx->id, $contexts);
 171  
 172          // User 10 creates history.
 173          $this->setUser($u10);
 174          $s2->delete();
 175  
 176          $contexts = array_flip(provider::get_contexts_for_userid($u10->id)->get_contextids());
 177          $this->assertCount(1, $contexts);
 178          $this->assertArrayHasKey($c1ctx->id, $contexts);
 179      }
 180  
 181      public function test_get_contexts_for_userid_grades_and_history() {
 182          $dg = $this->getDataGenerator();
 183  
 184          $c1 = $dg->create_course();
 185          $c2 = $dg->create_course();
 186  
 187          $u1 = $dg->create_user();
 188          $u2 = $dg->create_user();
 189          $u3 = $dg->create_user();
 190          $u4 = $dg->create_user();
 191          $u5 = $dg->create_user();
 192          $u6 = $dg->create_user();
 193  
 194          $sysctx = \context_system::instance();
 195          $c1ctx = \context_course::instance($c1->id);
 196          $c2ctx = \context_course::instance($c2->id);
 197  
 198          // Create some stuff.
 199          $gi1a = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
 200          $gi1b = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
 201          $gi2a = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
 202          $gi2b = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
 203  
 204          // Nothing as of now.
 205          foreach ([$u1, $u2, $u3, $u4, $u5, $u6] as $u) {
 206              $contexts = array_flip(provider::get_contexts_for_userid($u->id)->get_contextids());
 207              $this->assertEmpty($contexts);
 208          }
 209  
 210          // User 1 is graded in course 1.
 211          $gi1a->update_final_grade($u1->id, 1, 'test');
 212  
 213          // User 2 is graded in course 2.
 214          $gi2a->update_final_grade($u2->id, 10, 'test');
 215  
 216          // User 3 is set as modifier.
 217          $gi1a->update_final_grade($u1->id, 1, 'test', '', FORMAT_MOODLE, $u3->id);
 218  
 219          // User 4 is set as modifier, and creates history..
 220          $this->setUser($u4);
 221          $gi1a->update_final_grade($u2->id, 1, 'test');
 222  
 223          // User 5 creates history, user 6 is the known modifier, and we delete the item.
 224          $this->setUser($u5);
 225          $gi2b->update_final_grade($u2->id, 1, 'test', '', FORMAT_PLAIN, $u6->id);
 226          $gi2b->delete();
 227  
 228          // Assert contexts.
 229          $contexts = array_flip(provider::get_contexts_for_userid($u1->id)->get_contextids());
 230          $this->assertCount(1, $contexts);
 231          $this->assertArrayHasKey($c1ctx->id, $contexts);
 232          $contexts = array_flip(provider::get_contexts_for_userid($u2->id)->get_contextids());
 233          $this->assertCount(3, $contexts);
 234          $this->assertArrayHasKey($c1ctx->id, $contexts);
 235          $this->assertArrayHasKey($c2ctx->id, $contexts);
 236          $this->assertArrayHasKey(\context_user::instance($u2->id)->id, $contexts);
 237          $contexts = array_flip(provider::get_contexts_for_userid($u3->id)->get_contextids());
 238          $this->assertCount(1, $contexts);
 239          $this->assertArrayHasKey($c1ctx->id, $contexts);
 240          $contexts = array_flip(provider::get_contexts_for_userid($u4->id)->get_contextids());
 241          $this->assertCount(1, $contexts);
 242          $this->assertArrayHasKey($c1ctx->id, $contexts);
 243          $contexts = array_flip(provider::get_contexts_for_userid($u5->id)->get_contextids());
 244          $this->assertCount(2, $contexts);
 245          $this->assertArrayHasKey($c2ctx->id, $contexts);
 246          $this->assertArrayHasKey(\context_user::instance($u2->id)->id, $contexts);
 247          $contexts = array_flip(provider::get_contexts_for_userid($u6->id)->get_contextids());
 248          $this->assertCount(1, $contexts);
 249          $this->assertArrayHasKey(\context_user::instance($u2->id)->id, $contexts);
 250      }
 251  
 252      /**
 253       * Test that the appropriate user IDs are returned for a given context.
 254       */
 255      public function test_get_users_in_context_gradebook_edits() {
 256          $dg = $this->getDataGenerator();
 257  
 258          $c1 = $dg->create_course();
 259          $c2 = $dg->create_course();
 260  
 261          $u1 = $dg->create_user();
 262          $u2 = $dg->create_user();
 263          $u3 = $dg->create_user();
 264          $u4 = $dg->create_user();
 265          $u5 = $dg->create_user();
 266          $u6 = $dg->create_user();
 267          $u7 = $dg->create_user();
 268          $u8 = $dg->create_user();
 269          $u9 = $dg->create_user();
 270          $u10 = $dg->create_user();
 271          $u11 = $dg->create_user();
 272  
 273          $sysctx = \context_system::instance();
 274          $c1ctx = \context_course::instance($c1->id);
 275          $c2ctx = \context_course::instance($c2->id);
 276  
 277          // Create some stuff.
 278          $gi1a = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
 279          $gi1b = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
 280          $gi2a = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
 281          $gc1a = new \grade_category($dg->create_grade_category(['courseid' => $c1->id]), false);
 282          $gc1b = new \grade_category($dg->create_grade_category(['courseid' => $c1->id]), false);
 283          $gc2a = new \grade_category($dg->create_grade_category(['courseid' => $c2->id]), false);
 284          $go2 = new \grade_outcome($dg->create_grade_outcome(['courseid' => $c2->id, 'shortname' => 'go2',
 285              'fullname' => 'go2']), false);
 286  
 287          $go0 = new \grade_outcome(['shortname' => 'go0', 'fullname' => 'go0', 'usermodified' => $u1->id]);
 288          $go0->insert();
 289          $go1 = new \grade_outcome(['shortname' => 'go1', 'fullname' => 'go1', 'courseid' => $c1->id, 'usermodified' => $u11->id]);
 290          $go1->insert();
 291  
 292          // Create scales.
 293          $s1 = new \grade_scale(['name' => 's1', 'scale' => 'a,b', 'userid' => $u7->id, 'courseid' => 0, 'description' => '']);
 294          $s1->insert();
 295          $s2 = new \grade_scale(['name' => 's2', 'scale' => 'a,b', 'userid' => $u8->id, 'courseid' => $c1->id, 'description' => '']);
 296          $s2->insert();
 297  
 298          // User 2 creates history.
 299          $this->setUser($u2);
 300          $go0->shortname .= ' edited';
 301          $go0->update();
 302          $gc1a->fullname .= ' edited';
 303          $gc1a->update();
 304  
 305          // User 3 creates history.
 306          $this->setUser($u3);
 307          $go1->shortname .= ' edited';
 308          $go1->update();
 309          $gc2a->fullname .= ' a';
 310          $gc2a->update();
 311  
 312          // User 4 updates an outcome in course (creates history).
 313          $this->setUser($u4);
 314          $go2->shortname .= ' edited';
 315          $go2->update();
 316  
 317          // User 5 updates an item.
 318          $this->setUser($u5);
 319          $gi1a->itemname .= ' edited';
 320          $gi1a->update();
 321  
 322          // User 6 creates history.
 323          $this->setUser($u6);
 324          $gi2a->delete();
 325  
 326          // User 9 creates history.
 327          $this->setUser($u9);
 328          $s1->name .= ' edited';
 329          $s1->update();
 330  
 331          $userlist = new \core_privacy\local\request\userlist($sysctx, 'core_grades');
 332          provider::get_users_in_context($userlist);
 333          $systemcontextuserids = [$u1->id, $u2->id, $u7->id, $u9->id];
 334          $this->assertEquals($systemcontextuserids, $userlist->get_userids());
 335  
 336          $userlist = new \core_privacy\local\request\userlist($c1ctx, 'core_grades');
 337          provider::get_users_in_context($userlist);
 338          $course1userids = [$u11->id, $u3->id, $u8->id, $u5->id, $u2->id];
 339          $this->assertEquals($course1userids, $userlist->get_userids());
 340  
 341          $userlist = new \core_privacy\local\request\userlist($c2ctx, 'core_grades');
 342          provider::get_users_in_context($userlist);
 343          $course2userids = [$u4->id, $u6->id, $u3->id];
 344          $this->assertEquals($course2userids, $userlist->get_userids());
 345      }
 346  
 347      /**
 348       * Test that the appropriate user IDs are returned for a given context.
 349       */
 350      public function test_get_users_in_context_grades_and_history() {
 351          $dg = $this->getDataGenerator();
 352  
 353          $c1 = $dg->create_course();
 354          $c2 = $dg->create_course();
 355  
 356          $u1 = $dg->create_user();
 357          $u2 = $dg->create_user();
 358          $u3 = $dg->create_user();
 359          $u4 = $dg->create_user();
 360          $u5 = $dg->create_user();
 361          $u6 = $dg->create_user();
 362  
 363          $c1ctx = \context_course::instance($c1->id);
 364          $c2ctx = \context_course::instance($c2->id);
 365          $u2ctx = \context_user::instance($u2->id);
 366  
 367          // Create some stuff.
 368          $gi1a = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
 369          $gi1b = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
 370          $gi2a = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
 371          $gi2b = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
 372  
 373          // User 1 is graded in course 1.
 374          $gi1a->update_final_grade($u1->id, 1, 'test');
 375  
 376          // User 2 is graded in course 2.
 377          $gi2a->update_final_grade($u2->id, 10, 'test');
 378  
 379          // User 3 is set as modifier.
 380          $gi1a->update_final_grade($u1->id, 1, 'test', '', FORMAT_MOODLE, $u3->id);
 381  
 382          // User 4 is set as modifier, and creates history..
 383          $this->setUser($u4);
 384          $gi1a->update_final_grade($u2->id, 1, 'test');
 385  
 386          // User 5 creates history, user 6 is the known modifier, and we delete the item.
 387          $this->setUser($u5);
 388          $gi2b->update_final_grade($u2->id, 1, 'test', '', FORMAT_PLAIN, $u6->id);
 389          $gi2b->delete();
 390  
 391          $userlist = new \core_privacy\local\request\userlist($c1ctx, 'core_grades');
 392          provider::get_users_in_context($userlist);
 393          $course1userids = [$u1->id, $u2->id, $u3->id, $u4->id];
 394          $this->assertEqualsCanonicalizing($course1userids, $userlist->get_userids());
 395  
 396          $userlist = new \core_privacy\local\request\userlist($c2ctx, 'core_grades');
 397          provider::get_users_in_context($userlist);
 398          $course2userids = [$u5->id, $u2->id];
 399          $this->assertEqualsCanonicalizing($course2userids, $userlist->get_userids());
 400  
 401          $userlist = new \core_privacy\local\request\userlist($u2ctx, 'core_grades');
 402          provider::get_users_in_context($userlist);
 403          $this->assertEquals([$u2->id], $userlist->get_userids());
 404      }
 405  
 406      public function test_delete_data_for_all_users_in_context() {
 407          global $DB;
 408  
 409          $fs = new \file_storage();
 410  
 411          $dg = $this->getDataGenerator();
 412  
 413          $c1 = $dg->create_course();
 414          $c2 = $dg->create_course();
 415          $u1 = $dg->create_user();
 416          $u2 = $dg->create_user();
 417          $u1ctx = \context_user::instance($u1->id);
 418          $c1ctx = \context_course::instance($c1->id);
 419          $c2ctx = \context_course::instance($c2->id);
 420  
 421          $a1 = $dg->create_module('assign', ['course' => $c1->id]);
 422          $a2 = $dg->create_module('assign', ['course' => $c1->id]);
 423          $a3 = $dg->create_module('assign', ['course' => $c2->id]);
 424          $a4 = $dg->create_module('assign', ['course' => $c2->id]);
 425  
 426          $a1context = \context_module::instance($a1->cmid);
 427          $a2context = \context_module::instance($a2->cmid);
 428          $a3context = \context_module::instance($a3->cmid);
 429          $a4context = \context_module::instance($a4->cmid);
 430  
 431          // Create some stuff.
 432          $gi1a = new \grade_item($dg->create_grade_item(
 433              [
 434                  'courseid' => $c1->id,
 435                  'itemtype' => 'mod',
 436                  'itemmodule' => 'assign',
 437                  'iteminstance' => $a1->id
 438              ]
 439          ), false);
 440          $gi1b = new \grade_item($dg->create_grade_item(
 441              [
 442                  'courseid' => $c1->id,
 443                  'itemtype' => 'mod',
 444                  'itemmodule' => 'assign',
 445                  'iteminstance' => $a2->id
 446              ]
 447          ), false);
 448          $gi2a = new \grade_item($dg->create_grade_item(
 449              [
 450                  'courseid' => $c2->id,
 451                  'itemtype' => 'mod',
 452                  'itemmodule' => 'assign',
 453                  'iteminstance' => $a3->id
 454              ]
 455          ), false);
 456          $gi2b = new \grade_item($dg->create_grade_item(
 457              [
 458                  'courseid' => $c2->id,
 459                  'itemtype' => 'mod',
 460                  'itemmodule' => 'assign',
 461                  'iteminstance' => $a4->id
 462              ]
 463          ), false);
 464  
 465          $this->add_feedback_file_to_copy();
 466  
 467          $grades['feedback'] = 'Nice feedback!';
 468          $grades['feedbackformat'] = FORMAT_MOODLE;
 469          $grades['feedbackfiles'] = [
 470              'contextid' => 1,
 471              'component' => 'test',
 472              'filearea' => 'testarea',
 473              'itemid' => 1
 474          ];
 475  
 476          $grades['userid'] = $u1->id;
 477          grade_update('mod/assign', $gi1a->courseid, $gi1a->itemtype, $gi1a->itemmodule, $gi1a->iteminstance,
 478              $gi1a->itemnumber, $grades);
 479  
 480          $grades['userid'] = $u2->id;
 481          grade_update('mod/assign', $gi1a->courseid, $gi1a->itemtype, $gi1a->itemmodule, $gi1a->iteminstance,
 482              $gi1a->itemnumber, $grades);
 483  
 484          $grades['userid'] = $u1->id;
 485          grade_update('mod/assign', $gi1b->courseid, $gi1b->itemtype, $gi1b->itemmodule, $gi1b->iteminstance,
 486              $gi1b->itemnumber, $grades);
 487  
 488          $grades['userid'] = $u1->id;
 489          grade_update('mod/assign', $gi2a->courseid, $gi2a->itemtype, $gi2a->itemmodule, $gi2a->iteminstance,
 490              $gi2a->itemnumber, $grades);
 491  
 492          $grades['userid'] = $u2->id;
 493          grade_update('mod/assign', $gi2a->courseid, $gi2a->itemtype, $gi2a->itemmodule, $gi2a->iteminstance,
 494              $gi2a->itemnumber, $grades);
 495  
 496          $grades['userid'] = $u1->id;
 497          grade_update('mod/assign', $gi2b->courseid, $gi2b->itemtype, $gi2b->itemmodule, $gi2b->iteminstance,
 498              $gi2b->itemnumber, $grades);
 499  
 500          $grades['userid'] = $u2->id;
 501          grade_update('mod/assign', $gi2b->courseid, $gi2b->itemtype, $gi2b->itemmodule, $gi2b->iteminstance,
 502              $gi2b->itemnumber, $grades);
 503          $gi2b->delete();
 504  
 505          // Feedback file area.
 506          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 507          $this->assertEquals(4, count($files));
 508  
 509          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 510          $this->assertEquals(2, count($files));
 511  
 512          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 513          $this->assertEquals(4, count($files));
 514  
 515          // Grade item 2 was deleted, so the associated files were as well.
 516          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 517          $this->assertEquals(0, count($files));
 518  
 519          // History file area.
 520          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 521          $this->assertEquals(4, count($files));
 522  
 523          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 524          $this->assertEquals(2, count($files));
 525  
 526          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 527          $this->assertEquals(4, count($files));
 528  
 529          // Grade item 2 was deleted, so the associated files were as well.
 530          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 531          $this->assertEquals(0, count($files));
 532  
 533          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 534          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 535          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 536          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 537          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 538          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
 539          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 540  
 541          provider::delete_data_for_all_users_in_context($c1ctx);
 542          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 543          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 544          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 545          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 546          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 547          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
 548          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 549  
 550          // Feedback file area.
 551          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 552          $this->assertEquals(0, count($files));
 553  
 554          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 555          $this->assertEquals(0, count($files));
 556  
 557          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 558          $this->assertEquals(4, count($files));
 559  
 560          // Grade item 2 was deleted, so the associated files were as well.
 561          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 562          $this->assertEquals(0, count($files));
 563  
 564          // History file area.
 565          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 566          $this->assertEquals(0, count($files));
 567  
 568          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 569          $this->assertEquals(0, count($files));
 570  
 571          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 572          $this->assertEquals(4, count($files));
 573  
 574          // Grade item 2 was deleted, so the associated files were as well.
 575          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 576          $this->assertEquals(0, count($files));
 577  
 578          provider::delete_data_for_all_users_in_context($u1ctx);
 579          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 580          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 581          $this->assertFalse($DB->record_exists('grade_grades_history', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
 582          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 583  
 584          // Feedback file area.
 585          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 586          $this->assertEquals(0, count($files));
 587  
 588          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 589          $this->assertEquals(0, count($files));
 590  
 591          // The user context is only reported when there are orphan historical grades, so we only delete those files.
 592          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 593          $this->assertEquals(4, count($files));
 594  
 595          // Grade item 2 was deleted, so the associated files were as well.
 596          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 597          $this->assertEquals(0, count($files));
 598  
 599          // History file area.
 600          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 601          $this->assertEquals(0, count($files));
 602  
 603          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 604          $this->assertEquals(0, count($files));
 605  
 606          // User 2 still has historical files.
 607          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 608          $this->assertEquals(2, count($files));
 609  
 610          // Grade item 2 was deleted, so the associated files were as well.
 611          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 612          $this->assertEquals(0, count($files));
 613  
 614          provider::delete_data_for_all_users_in_context($c2ctx);
 615          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 616          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 617          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 618  
 619          // Feedback file area.
 620          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 621          $this->assertEquals(0, count($files));
 622  
 623          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 624          $this->assertEquals(0, count($files));
 625  
 626          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 627          $this->assertEquals(0, count($files));
 628  
 629          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 630          $this->assertEquals(0, count($files));
 631  
 632          // History file area.
 633          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 634          $this->assertEquals(0, count($files));
 635  
 636          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 637          $this->assertEquals(0, count($files));
 638  
 639          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 640          $this->assertEquals(0, count($files));
 641  
 642          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 643          $this->assertEquals(0, count($files));
 644      }
 645  
 646      public function test_delete_data_for_user() {
 647          global $DB;
 648  
 649          $fs = new \file_storage();
 650  
 651          $dg = $this->getDataGenerator();
 652  
 653          $c1 = $dg->create_course();
 654          $c2 = $dg->create_course();
 655          $u1 = $dg->create_user();
 656          $u2 = $dg->create_user();
 657          $u1ctx = \context_user::instance($u1->id);
 658          $u2ctx = \context_user::instance($u2->id);
 659          $c1ctx = \context_course::instance($c1->id);
 660          $c2ctx = \context_course::instance($c2->id);
 661  
 662          $a1 = $dg->create_module('assign', ['course' => $c1->id]);
 663          $a2 = $dg->create_module('assign', ['course' => $c1->id]);
 664          $a3 = $dg->create_module('assign', ['course' => $c2->id]);
 665          $a4 = $dg->create_module('assign', ['course' => $c2->id]);
 666  
 667          $a1context = \context_module::instance($a1->cmid);
 668          $a2context = \context_module::instance($a2->cmid);
 669          $a3context = \context_module::instance($a3->cmid);
 670          $a4context = \context_module::instance($a4->cmid);
 671  
 672          // Create some stuff.
 673          $gi1a = new \grade_item($dg->create_grade_item(
 674              [
 675                  'courseid' => $c1->id,
 676                  'itemtype' => 'mod',
 677                  'itemmodule' => 'assign',
 678                  'iteminstance' => $a1->id
 679              ]
 680          ), false);
 681          $gi1b = new \grade_item($dg->create_grade_item(
 682              [
 683                  'courseid' => $c1->id,
 684                  'itemtype' => 'mod',
 685                  'itemmodule' => 'assign',
 686                  'iteminstance' => $a2->id
 687              ]
 688          ), false);
 689          $gi2a = new \grade_item($dg->create_grade_item(
 690              [
 691                  'courseid' => $c2->id,
 692                  'itemtype' => 'mod',
 693                  'itemmodule' => 'assign',
 694                  'iteminstance' => $a3->id
 695              ]
 696          ), false);
 697          $gi2b = new \grade_item($dg->create_grade_item(
 698              [
 699                  'courseid' => $c2->id,
 700                  'itemtype' => 'mod',
 701                  'itemmodule' => 'assign',
 702                  'iteminstance' => $a4->id
 703              ]
 704          ), false);
 705  
 706          $this->add_feedback_file_to_copy();
 707  
 708          $grades['feedback'] = 'Nice feedback!';
 709          $grades['feedbackfiles'] = [
 710              'contextid' => 1,
 711              'component' => 'test',
 712              'filearea' => 'testarea',
 713              'itemid' => 1
 714          ];
 715  
 716          $grades['userid'] = $u1->id;
 717          grade_update('mod/assign', $gi1a->courseid, $gi1a->itemtype, $gi1a->itemmodule, $gi1a->iteminstance,
 718              $gi1a->itemnumber, $grades);
 719  
 720          $grades['userid'] = $u2->id;
 721          grade_update('mod/assign', $gi1a->courseid, $gi1a->itemtype, $gi1a->itemmodule, $gi1a->iteminstance,
 722              $gi1a->itemnumber, $grades);
 723  
 724          $grades['userid'] = $u1->id;
 725          grade_update('mod/assign', $gi1b->courseid, $gi1b->itemtype, $gi1b->itemmodule, $gi1b->iteminstance,
 726              $gi1b->itemnumber, $grades);
 727  
 728          $grades['userid'] = $u1->id;
 729          grade_update('mod/assign', $gi2a->courseid, $gi2a->itemtype, $gi2a->itemmodule, $gi2a->iteminstance,
 730              $gi2a->itemnumber, $grades);
 731  
 732          $grades['userid'] = $u2->id;
 733          grade_update('mod/assign', $gi2a->courseid, $gi2a->itemtype, $gi2a->itemmodule, $gi2a->iteminstance,
 734              $gi2a->itemnumber, $grades);
 735  
 736          $grades['userid'] = $u1->id;
 737          grade_update('mod/assign', $gi2b->courseid, $gi2b->itemtype, $gi2b->itemmodule, $gi2b->iteminstance,
 738              $gi2b->itemnumber, $grades);
 739  
 740          $grades['userid'] = $u2->id;
 741          grade_update('mod/assign', $gi2b->courseid, $gi2b->itemtype, $gi2b->itemmodule, $gi2b->iteminstance,
 742              $gi2b->itemnumber, $grades);
 743  
 744          $gi2b->delete();
 745  
 746          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 747          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 748          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 749          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 750          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 751          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
 752          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 753  
 754          // Feedback file area.
 755          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 756          $this->assertEquals(4, count($files));
 757  
 758          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 759          $this->assertEquals(2, count($files));
 760  
 761          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 762          $this->assertEquals(4, count($files));
 763  
 764          // Grade item 2 was deleted, so the associated files were as well.
 765          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 766          $this->assertEquals(0, count($files));
 767  
 768          // History file area.
 769          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 770          $this->assertEquals(4, count($files));
 771  
 772          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 773          $this->assertEquals(2, count($files));
 774  
 775          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 776          $this->assertEquals(4, count($files));
 777  
 778          // Grade item 2 was deleted, so the associated files were as well.
 779          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 780          $this->assertEquals(0, count($files));
 781  
 782          provider::delete_data_for_user(new approved_contextlist($u1, 'core_grades', [$c1ctx->id]));
 783          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 784          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 785          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 786          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 787          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 788          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
 789          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 790  
 791          // Feedback file area.
 792          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 793          $this->assertEquals(2, count($files));
 794  
 795          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 796          $this->assertEquals(0, count($files));
 797  
 798          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 799          $this->assertEquals(4, count($files));
 800  
 801          // Grade item 2 was deleted, so the associated files were as well.
 802          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 803          $this->assertEquals(0, count($files));
 804  
 805          // History file area.
 806          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 807          $this->assertEquals(2, count($files));
 808  
 809          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 810          $this->assertEquals(0, count($files));
 811  
 812          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 813          $this->assertEquals(4, count($files));
 814  
 815          // Grade item 2 was deleted, so the associated files were as well.
 816          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 817          $this->assertEquals(0, count($files));
 818  
 819          provider::delete_data_for_user(new approved_contextlist($u1, 'core_grades', [$u1ctx->id]));
 820          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 821          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 822          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 823          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 824          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 825          $this->assertFalse($DB->record_exists('grade_grades_history', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
 826          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 827  
 828          // Feedback file area.
 829          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 830          $this->assertEquals(2, count($files));
 831  
 832          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 833          $this->assertEquals(0, count($files));
 834  
 835          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 836          $this->assertEquals(4, count($files));
 837  
 838          // Grade item 2 was deleted, so the associated files were as well.
 839          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 840          $this->assertEquals(0, count($files));
 841  
 842          // History file area.
 843          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 844          $this->assertEquals(2, count($files));
 845  
 846          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 847          $this->assertEquals(0, count($files));
 848  
 849          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 850          $this->assertEquals(2, count($files));
 851  
 852          // Grade item 2 was deleted, so the associated files were as well.
 853          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 854          $this->assertEquals(0, count($files));
 855  
 856          provider::delete_data_for_user(new approved_contextlist($u1, 'core_grades', [$u2ctx->id, $c2ctx->id]));
 857          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 858          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 859          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 860          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 861          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 862          $this->assertFalse($DB->record_exists('grade_grades_history', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
 863          $this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 864  
 865          // Feedback file area.
 866          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 867          $this->assertEquals(2, count($files));
 868  
 869          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 870          $this->assertEquals(0, count($files));
 871  
 872          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 873          $this->assertEquals(2, count($files));
 874  
 875          // Grade item 2 was deleted, so the associated files were as well.
 876          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
 877          $this->assertEquals(0, count($files));
 878  
 879          // History file area.
 880          $files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 881          $this->assertEquals(2, count($files));
 882  
 883          $files = $fs->get_area_files($a2context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 884          $this->assertEquals(0, count($files));
 885  
 886          $files = $fs->get_area_files($a3context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 887          $this->assertEquals(2, count($files));
 888  
 889          // Grade item 2 was deleted, so the associated files were as well.
 890          $files = $fs->get_area_files($a4context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
 891          $this->assertEquals(0, count($files));
 892      }
 893  
 894      /**
 895       * Test deleting multiple users for a context works.
 896       */
 897      public function test_delete_data_for_users() {
 898          global $DB;
 899          $dg = $this->getDataGenerator();
 900  
 901          $c1 = $dg->create_course();
 902          $c2 = $dg->create_course();
 903          $u1 = $dg->create_user();
 904          $u2 = $dg->create_user();
 905          $u3 = $dg->create_user();
 906          $u4 = $dg->create_user();
 907          $u1ctx = \context_user::instance($u1->id);
 908          $u2ctx = \context_user::instance($u2->id);
 909          $c1ctx = \context_course::instance($c1->id);
 910          $c2ctx = \context_course::instance($c2->id);
 911  
 912          // Create some stuff.
 913          $gi1a = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
 914          $gi1b = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
 915          $gi2a = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
 916          $gi2b = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
 917  
 918          $gi1a->update_final_grade($u1->id, 1, 'test');
 919          $gi1a->update_final_grade($u2->id, 1, 'test');
 920          $gi1a->update_final_grade($u3->id, 1, 'test');
 921          $gi1b->update_final_grade($u1->id, 1, 'test');
 922          $gi1b->update_final_grade($u4->id, 1, 'test');
 923          $gi2a->update_final_grade($u1->id, 1, 'test');
 924          $gi2a->update_final_grade($u2->id, 1, 'test');
 925          $gi2a->update_final_grade($u4->id, 1, 'test');
 926          $gi2b->update_final_grade($u1->id, 1, 'test');
 927          $gi2b->update_final_grade($u2->id, 1, 'test');
 928          $gi2b->update_final_grade($u3->id, 1, 'test');
 929          $gi2b->delete();
 930  
 931          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 932          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 933          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u3->id, 'itemid' => $gi1a->id]));
 934          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 935          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi1b->id]));
 936          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 937          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 938          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi2a->id]));
 939          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
 940          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
 941          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u3->id, 'itemid' => $gi2b->id]));
 942  
 943          $userlist = new \core_privacy\local\request\approved_userlist($c1ctx, 'core_grades', [$u1->id, $u2->id]);
 944          provider::delete_data_for_users($userlist);
 945  
 946          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 947          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 948          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u3->id, 'itemid' => $gi1a->id]));
 949          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 950          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi1b->id]));
 951          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 952          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 953          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi2a->id]));
 954  
 955          $userlist = new \core_privacy\local\request\approved_userlist($c2ctx, 'core_grades', [$u2->id, $u4->id]);
 956          provider::delete_data_for_users($userlist);
 957  
 958          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
 959          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
 960          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u3->id, 'itemid' => $gi1a->id]));
 961          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
 962          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi1b->id]));
 963          $this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
 964          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
 965          $this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi2a->id]));
 966      }
 967  
 968      public function test_export_data_for_user_about_grades_and_history() {
 969          $dg = $this->getDataGenerator();
 970  
 971          $c1 = $dg->create_course();
 972          $c2 = $dg->create_course();
 973  
 974          // Users being graded.
 975          $ug1 = $dg->create_user();
 976          $ug2 = $dg->create_user();
 977          $ug3 = $dg->create_user();
 978          // Users performing actions.
 979          $ua1 = $dg->create_user();
 980          $ua2 = $dg->create_user();
 981          $ua3 = $dg->create_user();
 982  
 983          $ug1ctx = \context_user::instance($ug1->id);
 984          $ug2ctx = \context_user::instance($ug2->id);
 985          $c1ctx = \context_course::instance($c1->id);
 986          $c2ctx = \context_course::instance($c2->id);
 987  
 988          $rootpath = [get_string('grades', 'core_grades')];
 989          $relatedtomepath = array_merge($rootpath, [get_string('privacy:path:relatedtome', 'core_grades')]);
 990  
 991          // Create the course minimal stuff.
 992          \grade_category::fetch_course_category($c1->id);
 993          $ci1 = \grade_item::fetch_course_item($c1->id);
 994          \grade_category::fetch_course_category($c2->id);
 995          $ci2 = \grade_item::fetch_course_item($c2->id);
 996  
 997          $this->add_feedback_file_to_copy();
 998  
 999          $grades['feedbackfiles'] = [
1000              'contextid' => 1,
1001              'component' => 'test',
1002              'filearea' => 'testarea',
1003              'itemid' => 1
1004          ];
1005  
1006          $a1 = $dg->create_module('assign', ['course' => $c1->id]);
1007  
1008          // Create data that will sit in the user context because we will delete the grate item.
1009          $gi1 = new \grade_item($dg->create_grade_item(
1010              [
1011                  'courseid' => $c1->id,
1012                  'itemtype' => 'mod',
1013                  'itemmodule' => 'assign',
1014                  'iteminstance' => $a1->id,
1015                  'aggregationcoef2' => 1
1016              ]
1017          ), false);
1018  
1019          $grades['feedback'] = 'Well done!';
1020          $grades['feedbackformat'] = FORMAT_PLAIN;
1021          $grades['userid'] = $ug1->id;
1022          $grades['usermodified'] = $ua2->id;
1023          $grades['rawgrade'] = 100;
1024          grade_update('mod/assign', $gi1->courseid, $gi1->itemtype, $gi1->itemmodule, $gi1->iteminstance,
1025              $gi1->itemnumber, $grades);
1026  
1027          $grades['feedback'] = 'Hi';
1028          $grades['userid'] = $ug1->id;
1029          $grades['usermodified'] = $ua2->id;
1030          $grades['rawgrade'] = 1;
1031          grade_update('mod/assign', $gi1->courseid, $gi1->itemtype, $gi1->itemmodule, $gi1->iteminstance,
1032              $gi1->itemnumber, $grades);
1033  
1034          $grades['feedback'] = 'Hello';
1035          $grades['userid'] = $ug3->id;
1036          $grades['usermodified'] = $ua2->id;
1037          $grades['rawgrade'] = 12;
1038          grade_update('mod/assign', $gi1->courseid, $gi1->itemtype, $gi1->itemmodule, $gi1->iteminstance,
1039              $gi1->itemnumber, $grades);
1040  
1041          // Create another set for another user.
1042          $a2 = $dg->create_module('assign', ['course' => $c2->id]);
1043          $a3 = $dg->create_module('assign', ['course' => $c2->id]);
1044          $gi2a = new \grade_item($dg->create_grade_item(
1045              [
1046                  'courseid' => $c2->id,
1047                  'itemtype' => 'mod',
1048                  'itemmodule' => 'assign',
1049                  'iteminstance' => $a2->id
1050              ]
1051          ), false);
1052          $gi2b = new \grade_item($dg->create_grade_item(
1053              [
1054                  'courseid' => $c2->id,
1055                  'itemtype' => 'mod',
1056                  'itemmodule' => 'assign',
1057                  'iteminstance' => $a3->id
1058              ]
1059          ), false);
1060  
1061          $grades['feedback'] = '';
1062          $grades['userid'] = $ug1->id;
1063          $grades['usermodified'] = $ua2->id;
1064          $grades['rawgrade'] = 15;
1065          grade_update('mod/assign', $gi2a->courseid, $gi2a->itemtype, $gi2a->itemmodule, $gi2a->iteminstance,
1066              $gi2a->itemnumber, $grades);
1067  
1068          $grades['feedback'] = 'Well played!';
1069          $grades['userid'] = $ug1->id;
1070          $grades['usermodified'] = $ua2->id;
1071          $grades['rawgrade'] = 30;
1072          grade_update('mod/assign', $gi2b->courseid, $gi2b->itemtype, $gi2b->itemmodule, $gi2b->iteminstance,
1073              $gi2b->itemnumber, $grades);
1074  
1075          // Export action user 1 everywhere.
1076          provider::export_user_data(new approved_contextlist($ua1, 'core_grades', [$ug1ctx->id, $ug2ctx->id,
1077              $c1ctx->id, $c2ctx->id]));
1078          $this->assert_context_has_no_data($ug1ctx);
1079          $this->assert_context_has_no_data($ug2ctx);
1080          $this->assert_context_has_no_data($c1ctx);
1081          $this->assert_context_has_no_data($c2ctx);
1082  
1083          // Export action user 2 in course 1.
1084          writer::reset();
1085          provider::export_user_data(new approved_contextlist($ua2, 'core_grades', [$c1ctx->id]));
1086          $this->assert_context_has_no_data($ug1ctx);
1087          $this->assert_context_has_no_data($ug2ctx);
1088          $this->assert_context_has_no_data($c2ctx);
1089          $data = writer::with_context($c1ctx)->get_data($rootpath);
1090          $this->assertEmpty($data);
1091  
1092          // Here we are testing the export of grades that we've changed.
1093          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'grades');
1094          $this->assertCount(2, $data->grades);
1095          $this->assertEquals($gi1->get_name(), $data->grades[0]['item']);
1096          $this->assertEquals(1, $data->grades[0]['grade']);
1097          $this->assertEquals('Hi', $data->grades[0]['feedback']);
1098          $this->assertEquals(transform::yesno(true), $data->grades[0]['created_or_modified_by_you']);
1099          $this->assertEquals($gi1->get_name(), $data->grades[1]['item']);
1100          $this->assertEquals(12, $data->grades[1]['grade']);
1101          $this->assertEquals('Hello', $data->grades[1]['feedback']);
1102          $this->assertEquals(transform::yesno(true), $data->grades[1]['created_or_modified_by_you']);
1103  
1104          $pathtofiles = [
1105              get_string('grades', 'core_grades'),
1106              get_string('feedbackfiles', 'core_grades')
1107          ];
1108          $file = writer::with_context($gi1->get_context())->get_files($pathtofiles)['feedback1.txt'];
1109  
1110          $this->assertInstanceOf('stored_file', $file);
1111          $this->assertEquals('feedback1.txt', $file->get_filename());
1112  
1113          $relatedtomepath = array_merge($rootpath, [get_string('privacy:path:relatedtome', 'core_grades')]);
1114  
1115          // Here we are testing the export of history of grades that we've changed.
1116          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'grades_history');
1117          $this->assertCount(3, $data->modified_records);
1118          $grade = $data->modified_records[0];
1119          $this->assertEquals($ug1->id, $grade['userid']);
1120          $this->assertEquals($gi1->get_name(), $grade['item']);
1121          $this->assertEquals(100, $grade['grade']);
1122          $this->assertEquals('Well done!', $grade['feedback']);
1123          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1124          $this->assertEquals(transform::yesno(true), $grade['author_of_change_was_you']);
1125          $grade = $data->modified_records[1];
1126          $this->assertEquals($ug1->id, $grade['userid']);
1127          $this->assertEquals($gi1->get_name(), $grade['item']);
1128          $this->assertEquals(1, $grade['grade']);
1129          $this->assertEquals('Hi', $grade['feedback']);
1130          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1131          $this->assertEquals(transform::yesno(true), $grade['author_of_change_was_you']);
1132          $grade = $data->modified_records[2];
1133          $this->assertEquals($ug3->id, $grade['userid']);
1134          $this->assertEquals($gi1->get_name(), $grade['item']);
1135          $this->assertEquals(12, $grade['grade']);
1136          $this->assertEquals('Hello', $grade['feedback']);
1137          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1138          $this->assertEquals(transform::yesno(true), $grade['author_of_change_was_you']);
1139  
1140          $pathtofiles = [
1141              get_string('grades', 'core_grades'),
1142              get_string('feedbackhistoryfiles', 'core_grades')
1143          ];
1144          $file = writer::with_context($gi1->get_context())->get_files($pathtofiles)['feedback1.txt'];
1145  
1146          $this->assertInstanceOf('stored_file', $file);
1147          $this->assertEquals('feedback1.txt', $file->get_filename());
1148  
1149          // Create a history record with logged user.
1150          $this->setUser($ua3);
1151          $gi1->update_final_grade($ug3->id, 50, 'test', '...', FORMAT_PLAIN, $ua2->id);
1152          writer::reset();
1153          provider::export_user_data(new approved_contextlist($ua3, 'core_grades', [$c1ctx->id]));
1154          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'grades_history');
1155          $this->assertCount(1, $data->modified_records);
1156          $grade = $data->modified_records[0];
1157          $this->assertEquals($ug3->id, $grade['userid']);
1158          $this->assertEquals($gi1->get_name(), $grade['item']);
1159          $this->assertEquals(50, $grade['grade']);
1160          $this->assertEquals('...', $grade['feedback']);
1161          $this->assertEquals(transform::yesno(true), $grade['logged_in_user_was_you']);
1162          $this->assertEquals(transform::yesno(false), $grade['author_of_change_was_you']);
1163  
1164          // Test that we export our own grades.
1165          writer::reset();
1166          provider::export_user_data(new approved_contextlist($ug1, 'core_grades', [$c1ctx->id]));
1167          $data = writer::with_context($c1ctx)->get_data($rootpath);
1168          $this->assert_context_has_no_data($c2ctx);
1169          $this->assertCount(3, $data->grades);
1170          $grade = $data->grades[0];
1171          $this->assertEquals($ci1->get_name(), $grade['item']);
1172          $this->assertEquals(1, $grade['grade']);
1173          $grade = $data->grades[2];
1174          $this->assertEquals($gi1->get_name(), $grade['item']);
1175          $this->assertEquals(1, $grade['grade']);
1176          $this->assertEquals('Hi', $grade['feedback']);
1177  
1178          // Test that we export our own grades in two courses.
1179          writer::reset();
1180          provider::export_user_data(new approved_contextlist($ug1, 'core_grades', [$ug1ctx->id, $c1ctx->id, $c2ctx->id]));
1181          $this->assert_context_has_no_data($ug1ctx);
1182          $data = writer::with_context($c1ctx)->get_data($rootpath);
1183          $this->assertCount(3, $data->grades);
1184          $grade = $data->grades[0];
1185          $this->assertEquals($ci1->get_name(), $grade['item']);
1186          $this->assertEquals(1, $grade['grade']);
1187          $grade = $data->grades[2];
1188          $this->assertEquals($gi1->get_name(), $grade['item']);
1189          $this->assertEquals(1, $grade['grade']);
1190          $this->assertEquals('Hi', $grade['feedback']);
1191  
1192          $data = writer::with_context($c2ctx)->get_data($rootpath);
1193          $this->assertCount(5, $data->grades);
1194          $grade = $data->grades[0];
1195          $this->assertEquals($ci2->get_name(), $grade['item']);
1196          $grade = $data->grades[3];
1197          $this->assertEquals($gi2a->get_name(), $grade['item']);
1198          $this->assertEquals(15, $grade['grade']);
1199          $this->assertEquals('', $grade['feedback']);
1200          $grade = $data->grades[4];
1201          $this->assertEquals($gi2b->get_name(), $grade['item']);
1202          $this->assertEquals(30, $grade['grade']);
1203          $this->assertEquals('Well played!', $grade['feedback']);
1204  
1205          // Delete a grade item.
1206          $this->setUser($ua3);
1207          $gi1->delete();
1208  
1209          // Now, we should find history of grades in our own context.
1210          writer::reset();
1211          provider::export_user_data(new approved_contextlist($ug1, 'core_grades', [$ug1ctx->id, $c1ctx->id, $c2ctx->id]));
1212          $data = writer::with_context($c1ctx)->get_data($rootpath);
1213          $this->assertCount(2, $data->grades);
1214          $this->assertEquals($ci1->get_name(), $data->grades[0]['item']);
1215          $data = writer::with_context($c2ctx)->get_data($rootpath);
1216          $this->assertCount(5, $data->grades);
1217          $data = writer::with_context($ug1ctx)->get_related_data($rootpath, 'history');
1218          $this->assertCount(3, $data->grades);
1219          $grade = $data->grades[0];
1220          $this->assertEquals(get_string('privacy:request:unknowndeletedgradeitem', 'core_grades'), $grade['name']);
1221          $this->assertEquals(100, $grade['grade']);
1222          $this->assertEquals('Well done!', $grade['feedback']);
1223          $this->assertEquals(transform::yesno(true), $grade['graded_user_was_you']);
1224          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1225          $this->assertEquals(transform::yesno(false), $grade['author_of_change_was_you']);
1226          $this->assertEquals(get_string('privacy:request:historyactioninsert', 'core_grades'), $grade['action']);
1227          $grade = $data->grades[1];
1228          $this->assertEquals(get_string('privacy:request:unknowndeletedgradeitem', 'core_grades'), $grade['name']);
1229          $this->assertEquals(1, $grade['grade']);
1230          $this->assertEquals('Hi', $grade['feedback']);
1231          $this->assertEquals(transform::yesno(true), $grade['graded_user_was_you']);
1232          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1233          $this->assertEquals(transform::yesno(false), $grade['author_of_change_was_you']);
1234          $this->assertEquals(get_string('privacy:request:historyactionupdate', 'core_grades'), $grade['action']);
1235          $grade = $data->grades[2];
1236          $this->assertEquals(get_string('privacy:request:unknowndeletedgradeitem', 'core_grades'), $grade['name']);
1237          $this->assertEquals(1, $grade['grade']);
1238          $this->assertEquals('Hi', $grade['feedback']);
1239          $this->assertEquals(transform::yesno(true), $grade['graded_user_was_you']);
1240          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1241          $this->assertEquals(transform::yesno(false), $grade['author_of_change_was_you']);
1242          $this->assertEquals(get_string('privacy:request:historyactiondelete', 'core_grades'), $grade['action']);
1243  
1244          // The action user 3 should have a record of the deletion in the user's context.
1245          writer::reset();
1246          provider::export_user_data(new approved_contextlist($ua3, 'core_grades', [$ug1ctx->id]));
1247          $data = writer::with_context($ug1ctx)->get_related_data($rootpath, 'history');
1248          $this->assertCount(1, $data->grades);
1249          $grade = $data->grades[0];
1250          $this->assertEquals(get_string('privacy:request:unknowndeletedgradeitem', 'core_grades'), $grade['name']);
1251          $this->assertEquals(1, $grade['grade']);
1252          $this->assertEquals('Hi', $grade['feedback']);
1253          $this->assertEquals(transform::yesno(true), $grade['logged_in_user_was_you']);
1254          $this->assertEquals(transform::yesno(false), $grade['author_of_change_was_you']);
1255          $this->assertEquals(get_string('privacy:request:historyactiondelete', 'core_grades'), $grade['action']);
1256  
1257          // The action user 2 should have a record of their edits in the user's context.
1258          writer::reset();
1259          provider::export_user_data(new approved_contextlist($ua2, 'core_grades', [$ug1ctx->id]));
1260          $data = writer::with_context($ug1ctx)->get_related_data($rootpath, 'history');
1261          $this->assertCount(3, $data->grades);
1262          $grade = $data->grades[0];
1263          $this->assertEquals(get_string('privacy:request:unknowndeletedgradeitem', 'core_grades'), $grade['name']);
1264          $this->assertEquals(100, $grade['grade']);
1265          $this->assertEquals('Well done!', $grade['feedback']);
1266          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1267          $this->assertEquals(transform::yesno(true), $grade['author_of_change_was_you']);
1268          $this->assertEquals(get_string('privacy:request:historyactioninsert', 'core_grades'), $grade['action']);
1269          $grade = $data->grades[1];
1270          $this->assertEquals(get_string('privacy:request:unknowndeletedgradeitem', 'core_grades'), $grade['name']);
1271          $this->assertEquals(1, $grade['grade']);
1272          $this->assertEquals('Hi', $grade['feedback']);
1273          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1274          $this->assertEquals(transform::yesno(true), $grade['author_of_change_was_you']);
1275          $this->assertEquals(get_string('privacy:request:historyactionupdate', 'core_grades'), $grade['action']);
1276          $grade = $data->grades[2];
1277          $this->assertEquals(get_string('privacy:request:unknowndeletedgradeitem', 'core_grades'), $grade['name']);
1278          $this->assertEquals(1, $grade['grade']);
1279          $this->assertEquals('Hi', $grade['feedback']);
1280          $this->assertEquals(transform::yesno(false), $grade['logged_in_user_was_you']);
1281          $this->assertEquals(transform::yesno(true), $grade['author_of_change_was_you']);
1282          $this->assertEquals(get_string('privacy:request:historyactiondelete', 'core_grades'), $grade['action']);
1283      }
1284  
1285      public function test_export_data_for_user_with_scale() {
1286          global $DB;
1287          $dg = $this->getDataGenerator();
1288          $c1 = $dg->create_course();
1289          $scale = $dg->create_scale(['scale' => 'Awesome,OK,Reasonable,Bad']);
1290          $u1 = $dg->create_user();
1291          $u2 = $dg->create_user();
1292  
1293          $u1ctx = \context_user::instance($u1->id);
1294          $c1ctx = \context_course::instance($c1->id);
1295  
1296          $rootpath = [get_string('grades', 'core_grades')];
1297  
1298          // Create another set for another user.
1299          $gi1 = new \grade_item($dg->create_grade_item(['courseid' => $c1->id, 'scaleid' => $scale->id]), false);
1300          $gi1->update_final_grade($u1->id, 1, 'test', '', FORMAT_PLAIN, $u2->id);
1301          $gi2 = new \grade_item($dg->create_grade_item(['courseid' => $c1->id, 'scaleid' => $scale->id]), false);
1302          $gi2->update_final_grade($u1->id, 3, 'test', '', FORMAT_PLAIN, $u2->id);
1303  
1304          // Export user's data.
1305          writer::reset();
1306          provider::export_user_data(new approved_contextlist($u1, 'core_grades', [$c1ctx->id]));
1307          $data = writer::with_context($c1ctx)->get_data($rootpath);
1308          $this->assertCount(3, $data->grades);
1309          $this->assertEquals(\grade_item::fetch_course_item($c1->id)->get_name(), $data->grades[0]['item']);
1310          $this->assertEquals($gi1->get_name(), $data->grades[1]['item']);
1311          $this->assertEquals(1, $data->grades[1]['grade']);
1312          $this->assertEquals('Awesome', $data->grades[1]['grade_formatted']);
1313          $this->assertEquals($gi2->get_name(), $data->grades[2]['item']);
1314          $this->assertEquals(3, $data->grades[2]['grade']);
1315          $this->assertEquals('Reasonable', $data->grades[2]['grade_formatted']);
1316      }
1317  
1318      public function test_export_data_for_user_about_gradebook_edits() {
1319          global $DB;
1320          $dg = $this->getDataGenerator();
1321          $c1 = $dg->create_course();
1322          $c2 = $dg->create_course();
1323          $u1 = $dg->create_user();
1324          $u2 = $dg->create_user();
1325          $u3 = $dg->create_user();
1326          $u4 = $dg->create_user();
1327          $u5 = $dg->create_user();
1328          $u6 = $dg->create_user();
1329          $u7 = $dg->create_user();
1330          $u8 = $dg->create_user();
1331          $u9 = $dg->create_user();
1332          $u10 = $dg->create_user();
1333  
1334          $sysctx = \context_system::instance();
1335          $u1ctx = \context_user::instance($u1->id);
1336          $u2ctx = \context_user::instance($u2->id);
1337          $u3ctx = \context_user::instance($u3->id);
1338          $u4ctx = \context_user::instance($u4->id);
1339          $u5ctx = \context_user::instance($u5->id);
1340          $u6ctx = \context_user::instance($u6->id);
1341          $c1ctx = \context_course::instance($c1->id);
1342          $c2ctx = \context_course::instance($c2->id);
1343  
1344          $rootpath = [get_string('grades', 'core_grades')];
1345          $relatedtomepath = array_merge($rootpath, [get_string('privacy:path:relatedtome', 'core_grades')]);
1346          $allcontexts = [$sysctx->id, $c1ctx->id, $c2ctx->id, $u1ctx->id, $u2ctx->id, $u3ctx->id, $u4ctx->id,
1347              $u5ctx->id, $u6ctx->id];
1348          $updateactionstr = get_string('privacy:request:historyactionupdate', 'core_grades');
1349  
1350          // Create some stuff.
1351          $gi1a = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
1352          $gi1b = new \grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
1353          $gi2a = new \grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
1354          $gc1a = new \grade_category($dg->create_grade_category(['courseid' => $c1->id]), false);
1355          $gc1b = new \grade_category($dg->create_grade_category(['courseid' => $c1->id]), false);
1356          $gc2a = new \grade_category($dg->create_grade_category(['courseid' => $c2->id]), false);
1357          $go2 = new \grade_outcome($dg->create_grade_outcome(['courseid' => $c2->id, 'shortname' => 'go2',
1358              'fullname' => 'go2']), false);
1359  
1360          $go0 = new \grade_outcome(['shortname' => 'go0', 'fullname' => 'go0', 'usermodified' => $u1->id]);
1361          $go0->insert();
1362          $go1 = new \grade_outcome(['shortname' => 'go1', 'fullname' => 'go1', 'courseid' => $c1->id, 'usermodified' => $u1->id]);
1363          $go1->insert();
1364  
1365          // Create scales.
1366          $s1 = new \grade_scale(['name' => 's1', 'scale' => 'a,b', 'userid' => $u7->id, 'courseid' => 0, 'description' => '']);
1367          $s1->insert();
1368          $s2 = new \grade_scale(['name' => 's2', 'scale' => 'a,b', 'userid' => $u8->id, 'courseid' => $c1->id, 'description' => '']);
1369          $s2->insert();
1370          $s3 = new \grade_scale(['name' => 's3', 'scale' => 'a,b', 'userid' => $u8->id, 'courseid' => $c2->id, 'description' => '']);
1371          $s3->insert();
1372  
1373          // User 2 creates history.
1374          $this->setUser($u2);
1375          $go0->shortname .= ' edited';
1376          $go0->update();
1377          $gc1a->fullname .= ' edited';
1378          $gc1a->update();
1379  
1380          // User 3 creates history.
1381          $this->setUser($u3);
1382          $go1->shortname .= ' edited';
1383          $go1->update();
1384          $gc2a->fullname .= ' a';
1385          $gc2a->update();
1386  
1387          // User 4 updates an outcome in course (creates history).
1388          $this->setUser($u4);
1389          $go2->shortname .= ' edited';
1390          $go2->update();
1391  
1392          // User 5 updates an item.
1393          $this->setUser($u5);
1394          $gi1a->itemname .= ' edited';
1395          $gi1a->update();
1396  
1397          // User 6 creates history.
1398          $this->setUser($u6);
1399          $gi2a->delete();
1400  
1401          // User 9 creates history.
1402          $this->setUser($u9);
1403          $s1->name .= ' edited';
1404          $s1->update();
1405  
1406          // User 10 creates history.
1407          $this->setUser($u10);
1408          $s3->delete();
1409  
1410          $this->setAdminUser();
1411  
1412          // Export data for u1.
1413          writer::reset();
1414          provider::export_user_data(new approved_contextlist($u1, 'core_grades', $allcontexts));
1415          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'outcomes');
1416          $this->assertCount(1, $data->outcomes);
1417          $this->assertEquals($go0->shortname, $data->outcomes[0]['shortname']);
1418          $this->assertEquals($go0->fullname, $data->outcomes[0]['fullname']);
1419          $this->assertEquals(transform::yesno(true), $data->outcomes[0]['created_or_modified_by_you']);
1420          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'outcomes');
1421          $this->assertCount(1, $data->outcomes);
1422          $this->assertEquals($go1->shortname, $data->outcomes[0]['shortname']);
1423          $this->assertEquals($go1->fullname, $data->outcomes[0]['fullname']);
1424          $this->assertEquals(transform::yesno(true), $data->outcomes[0]['created_or_modified_by_you']);
1425          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'outcomes_history');
1426          $this->assertEmpty($data);
1427          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'outcomes_history');
1428          $this->assertEmpty($data);
1429  
1430          // Export data for u2.
1431          writer::reset();
1432          provider::export_user_data(new approved_contextlist($u2, 'core_grades', $allcontexts));
1433          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'outcomes');
1434          $this->assertEmpty($data);
1435          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'outcomes');
1436          $this->assertEmpty($data);
1437          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'outcomes_history');
1438          $this->assertCount(1, $data->modified_records);
1439          $this->assertEquals($go0->shortname, $data->modified_records[0]['shortname']);
1440          $this->assertEquals($go0->fullname, $data->modified_records[0]['fullname']);
1441          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['logged_in_user_was_you']);
1442          $this->assertEquals($updateactionstr, $data->modified_records[0]['action']);
1443  
1444          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'categories_history');
1445          $this->assertCount(1, $data->modified_records);
1446          $this->assertEquals($gc1a->fullname, $data->modified_records[0]['name']);
1447          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['logged_in_user_was_you']);
1448          $this->assertEquals($updateactionstr, $data->modified_records[0]['action']);
1449  
1450          // Export data for u3.
1451          writer::reset();
1452          provider::export_user_data(new approved_contextlist($u3, 'core_grades', $allcontexts));
1453          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'outcomes_history');
1454          $this->assertCount(1, $data->modified_records);
1455          $this->assertEquals($go1->shortname, $data->modified_records[0]['shortname']);
1456          $this->assertEquals($go1->fullname, $data->modified_records[0]['fullname']);
1457          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['logged_in_user_was_you']);
1458          $this->assertEquals($updateactionstr, $data->modified_records[0]['action']);
1459  
1460          $data = writer::with_context($c2ctx)->get_related_data($relatedtomepath, 'categories_history');
1461          $this->assertCount(1, $data->modified_records);
1462          $this->assertEquals($gc2a->fullname, $data->modified_records[0]['name']);
1463          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['logged_in_user_was_you']);
1464          $this->assertEquals($updateactionstr, $data->modified_records[0]['action']);
1465  
1466          // Export data for u4.
1467          writer::reset();
1468          provider::export_user_data(new approved_contextlist($u4, 'core_grades', $allcontexts));
1469          $data = writer::with_context($c2ctx)->get_related_data($relatedtomepath, 'outcomes_history');
1470          $this->assertCount(1, $data->modified_records);
1471          $this->assertEquals($go2->shortname, $data->modified_records[0]['shortname']);
1472          $this->assertEquals($go2->fullname, $data->modified_records[0]['fullname']);
1473          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['logged_in_user_was_you']);
1474          $this->assertEquals($updateactionstr, $data->modified_records[0]['action']);
1475  
1476          // Export data for u5.
1477          writer::reset();
1478          provider::export_user_data(new approved_contextlist($u5, 'core_grades', $allcontexts));
1479          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'items_history');
1480          $this->assertCount(1, $data->modified_records);
1481          $this->assertEquals($gi1a->itemname, $data->modified_records[0]['name']);
1482          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['logged_in_user_was_you']);
1483          $this->assertEquals($updateactionstr, $data->modified_records[0]['action']);
1484  
1485          // Export data for u6.
1486          writer::reset();
1487          provider::export_user_data(new approved_contextlist($u6, 'core_grades', $allcontexts));
1488          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'items_history');
1489          $this->assertEmpty($data);
1490          $data = writer::with_context($c2ctx)->get_related_data($relatedtomepath, 'items_history');
1491          $this->assertCount(1, $data->modified_records);
1492          $this->assertEquals($gi2a->itemname, $data->modified_records[0]['name']);
1493          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['logged_in_user_was_you']);
1494          $this->assertEquals(get_string('privacy:request:historyactiondelete', 'core_grades'),
1495              $data->modified_records[0]['action']);
1496  
1497          // Export data for u7.
1498          writer::reset();
1499          provider::export_user_data(new approved_contextlist($u7, 'core_grades', $allcontexts));
1500          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'scales');
1501          $this->assertEmpty($data);
1502          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'scales');
1503          $this->assertCount(1, $data->scales);
1504          $this->assertEquals($s1->name, $data->scales[0]['name']);
1505          $this->assertEquals(transform::yesno(true), $data->scales[0]['created_or_modified_by_you']);
1506  
1507          // Export data for u8.
1508          writer::reset();
1509          provider::export_user_data(new approved_contextlist($u8, 'core_grades', $allcontexts));
1510          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'scales');
1511          $this->assertEmpty($data);
1512          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'scales');
1513          $this->assertCount(1, $data->scales);
1514          $this->assertEquals($s2->name, $data->scales[0]['name']);
1515          $this->assertEquals(transform::yesno(true), $data->scales[0]['created_or_modified_by_you']);
1516          $data = writer::with_context($c2ctx)->get_related_data($relatedtomepath, 'scales_history');
1517          $this->assertCount(2, $data->modified_records);
1518          $this->assertEquals($s3->name, $data->modified_records[0]['name']);
1519          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['author_of_change_was_you']);
1520          $this->assertEquals(transform::yesno(false), $data->modified_records[0]['author_of_action_was_you']);
1521          $this->assertEquals(get_string('privacy:request:historyactioninsert', 'core_grades'),
1522              $data->modified_records[0]['action']);
1523          $this->assertEquals($s3->name, $data->modified_records[1]['name']);
1524          $this->assertEquals(transform::yesno(true), $data->modified_records[1]['author_of_change_was_you']);
1525          $this->assertEquals(transform::yesno(false), $data->modified_records[1]['author_of_action_was_you']);
1526          $this->assertEquals(get_string('privacy:request:historyactiondelete', 'core_grades'),
1527              $data->modified_records[1]['action']);
1528  
1529          // Export data for u9.
1530          writer::reset();
1531          provider::export_user_data(new approved_contextlist($u9, 'core_grades', $allcontexts));
1532          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'scales');
1533          $this->assertEmpty($data);
1534          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'scales');
1535          $this->assertEmpty($data);
1536          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'scales_history');
1537          $this->assertCount(1, $data->modified_records);
1538          $this->assertEquals($s1->name, $data->modified_records[0]['name']);
1539          $this->assertEquals(transform::yesno(false), $data->modified_records[0]['author_of_change_was_you']);
1540          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['author_of_action_was_you']);
1541          $this->assertEquals(get_string('privacy:request:historyactionupdate', 'core_grades'),
1542              $data->modified_records[0]['action']);
1543  
1544          // Export data for u10.
1545          writer::reset();
1546          provider::export_user_data(new approved_contextlist($u10, 'core_grades', $allcontexts));
1547          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'scales');
1548          $this->assertEmpty($data);
1549          $data = writer::with_context($c2ctx)->get_related_data($relatedtomepath, 'scales');
1550          $this->assertEmpty($data);
1551          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'scales');
1552          $this->assertEmpty($data);
1553          $data = writer::with_context($c1ctx)->get_related_data($relatedtomepath, 'scales_history');
1554          $this->assertEmpty($data);
1555          $data = writer::with_context($sysctx)->get_related_data($relatedtomepath, 'scales_history');
1556          $this->assertEmpty($data);
1557          $data = writer::with_context($c2ctx)->get_related_data($relatedtomepath, 'scales_history');
1558          $this->assertCount(1, $data->modified_records);
1559          $this->assertEquals($s3->name, $data->modified_records[0]['name']);
1560          $this->assertEquals(transform::yesno(false), $data->modified_records[0]['author_of_change_was_you']);
1561          $this->assertEquals(transform::yesno(true), $data->modified_records[0]['author_of_action_was_you']);
1562          $this->assertEquals(get_string('privacy:request:historyactiondelete', 'core_grades'),
1563              $data->modified_records[0]['action']);
1564      }
1565  
1566      /**
1567       * Assert there is no grade data in the context.
1568       *
1569       * @param context $context The context.
1570       * @return void
1571       */
1572      protected function assert_context_has_no_data(\context $context) {
1573          $rootpath = [get_string('grades', 'core_grades')];
1574          $relatedtomepath = array_merge($rootpath, [get_string('privacy:path:relatedtome', 'core_grades')]);
1575  
1576          $data = writer::with_context($context)->get_data($rootpath);
1577          $this->assertEmpty($data);
1578  
1579          $data = writer::with_context($context)->get_related_data($rootpath, 'history');
1580          $this->assertEmpty($data);
1581  
1582          $files = ['categories_history', 'items_history', 'outcomes', 'outcomes_history', 'grades', 'grades_history', 'history'];
1583          foreach ($files as $file) {
1584              $data = writer::with_context($context)->get_related_data($relatedtomepath, $file);
1585              $this->assertEmpty($data);
1586          }
1587      }
1588  
1589      /**
1590       * Creates a feedback file to copy to the gradebook area.
1591       */
1592      private function add_feedback_file_to_copy() {
1593          $dummy = array(
1594              'contextid' => 1,
1595              'component' => 'test',
1596              'filearea' => 'testarea',
1597              'itemid' => 1,
1598              'filepath' => '/',
1599              'filename' => 'feedback1.txt'
1600          );
1601  
1602          $fs = get_file_storage();
1603          $fs->create_file_from_string($dummy, '');
1604      }
1605  }