Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310]

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