Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Base class for unit tests for mod_assign.
  19   *
  20   * @package    mod_assign
  21   * @copyright  2018 Adrian Greeve <adrian@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace mod_assign\privacy;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  require_once($CFG->dirroot . '/mod/assign/locallib.php');
  31  
  32  use core_privacy\tests\provider_testcase;
  33  use core_privacy\local\request\writer;
  34  use core_privacy\local\request\approved_contextlist;
  35  use mod_assign\privacy\provider;
  36  
  37  /**
  38   * Unit tests for mod/assign/classes/privacy/
  39   *
  40   * @copyright  2018 Adrian Greeve <adrian@moodle.com>
  41   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42   */
  43  class provider_test extends provider_testcase {
  44  
  45      /**
  46       * Convenience method for creating a submission.
  47       *
  48       * @param  assign  $assign The assign object
  49       * @param  stdClass  $user The user object
  50       * @param  string  $submissiontext Submission text
  51       * @param  integer $attemptnumber The attempt number
  52       * @return object A submission object.
  53       */
  54      protected function create_submission($assign, $user, $submissiontext, $attemptnumber = 0) {
  55          $submission = $assign->get_user_submission($user->id, true, $attemptnumber);
  56          $submission->onlinetext_editor = ['text' => $submissiontext,
  57                                           'format' => FORMAT_MOODLE];
  58  
  59          $this->setUser($user);
  60          $notices = [];
  61          $assign->save_submission($submission, $notices);
  62          return $submission;
  63      }
  64  
  65      /**
  66       * Convenience function to create an instance of an assignment.
  67       *
  68       * @param array $params Array of parameters to pass to the generator
  69       * @return assign The assign class.
  70       */
  71      protected function create_instance($params = array()) {
  72          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
  73          $instance = $generator->create_instance($params);
  74          $cm = get_coursemodule_from_instance('assign', $instance->id);
  75          $context = \context_module::instance($cm->id);
  76          return new \assign($context, $cm, $params['course']);
  77      }
  78  
  79      /**
  80       * Test that getting the contexts for a user works.
  81       */
  82      public function test_get_contexts_for_userid() {
  83          global $DB;
  84          $this->resetAfterTest();
  85  
  86          $course1 = $this->getDataGenerator()->create_course();
  87          $course2 = $this->getDataGenerator()->create_course();
  88          $course3 = $this->getDataGenerator()->create_course();
  89  
  90          $user1 = $this->getDataGenerator()->create_user();
  91          $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
  92          $this->getDataGenerator()->enrol_user($user1->id, $course3->id, 'student');
  93          // Need a second user to create content in other assignments.
  94          $user2 = $this->getDataGenerator()->create_user();
  95          $this->getDataGenerator()->enrol_user($user2->id, $course2->id, 'student');
  96  
  97          // Create multiple assignments.
  98          // Assignment with a text submission.
  99          $assign1 = $this->create_instance(['course' => $course1]);
 100          // Assignment two in a different course that the user is not enrolled in.
 101          $assign2 = $this->create_instance(['course' => $course2]);
 102          // Assignment three has an entry in the override table.
 103          $assign3 = $this->create_instance(['course' => $course3, 'cutoffdate' => time()]);
 104          // Assignment four - blind marking.
 105          $assign4 = $this->create_instance(['course' => $course1, 'blindmarking' => 1]);
 106          // Assignment five - user flags.
 107          $assign5 = $this->create_instance(['course' => $course3]);
 108  
 109          // Override has to be manually inserted into the DB.
 110          $overridedata = new \stdClass();
 111          $overridedata->assignid = $assign3->get_instance()->id;
 112          $overridedata->userid = $user1->id;
 113          $overridedata->duedate = time();
 114          $DB->insert_record('assign_overrides', $overridedata);
 115          // Assign unique id for blind marking in assignment four for user 1.
 116          \assign::get_uniqueid_for_user_static($assign4->get_instance()->id, $user1->id);
 117          // Create an entry in the user flags table.
 118          $assign5->get_user_flags($user1->id, true);
 119  
 120          // The user will be in these contexts.
 121          $usercontextids = [
 122              $assign1->get_context()->id,
 123              $assign3->get_context()->id,
 124              $assign4->get_context()->id,
 125              $assign5->get_context()->id,
 126          ];
 127  
 128          $submission = new \stdClass();
 129          $submission->assignment = $assign1->get_instance()->id;
 130          $submission->userid = $user1->id;
 131          $submission->timecreated = time();
 132          $submission->onlinetext_editor = ['text' => 'Submission text',
 133                                           'format' => FORMAT_MOODLE];
 134  
 135          $this->setUser($user1);
 136          $notices = [];
 137          $assign1->save_submission($submission, $notices);
 138  
 139          // Create a submission for the second assignment.
 140          $submission->assignment = $assign2->get_instance()->id;
 141          $submission->userid = $user2->id;
 142          $this->setUser($user2);
 143          $assign2->save_submission($submission, $notices);
 144  
 145          $contextlist = provider::get_contexts_for_userid($user1->id);
 146          $this->assertEquals(count($usercontextids), count($contextlist->get_contextids()));
 147          // There should be no difference between the contexts.
 148          $this->assertEmpty(array_diff($usercontextids, $contextlist->get_contextids()));
 149      }
 150  
 151      /**
 152       * Test returning a list of user IDs related to a context (assign).
 153       */
 154      public function test_get_users_in_context() {
 155          global $DB;
 156  
 157          $this->resetAfterTest();
 158  
 159          $course = $this->getDataGenerator()->create_course();
 160  
 161          // Only made a comment on a submission.
 162          $user1 = $this->getDataGenerator()->create_user();
 163          // User 2 only has information about an activity override.
 164          $user2 = $this->getDataGenerator()->create_user();
 165          // User 3 made a submission.
 166          $user3 = $this->getDataGenerator()->create_user();
 167          // User 4 makes a submission and it is marked by the teacher.
 168          $user4 = $this->getDataGenerator()->create_user();
 169          // Grading and providing feedback as a teacher.
 170          $user5 = $this->getDataGenerator()->create_user();
 171          // This user has no entries and should not show up.
 172          $user6 = $this->getDataGenerator()->create_user();
 173  
 174          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 175          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
 176          $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student');
 177          $this->getDataGenerator()->enrol_user($user4->id, $course->id, 'student');
 178          $this->getDataGenerator()->enrol_user($user5->id, $course->id, 'editingteacher');
 179          $this->getDataGenerator()->enrol_user($user6->id, $course->id, 'student');
 180  
 181          $assign1 = $this->create_instance(['course' => $course,
 182                  'assignsubmission_onlinetext_enabled' => true,
 183                  'assignfeedback_comments_enabled' => true]);
 184          $assign2 = $this->create_instance(['course' => $course]);
 185  
 186          $context = $assign1->get_context();
 187  
 188          // Jam an entry in the comments table for user 1.
 189          $comment = (object) [
 190              'contextid' => $context->id,
 191              'component' => 'assignsubmission_comments',
 192              'commentarea' => 'submission_comments',
 193              'itemid' => 5,
 194              'content' => 'A comment by user 1',
 195              'format' => 0,
 196              'userid' => $user1->id,
 197              'timecreated' => time()
 198          ];
 199          $DB->insert_record('comments', $comment);
 200  
 201          $this->setUser($user5); // Set the user to the teacher.
 202  
 203          $overridedata = new \stdClass();
 204          $overridedata->assignid = $assign1->get_instance()->id;
 205          $overridedata->userid = $user2->id;
 206          $overridedata->duedate = time();
 207          $overridedata->allowsubmissionsfromdate = time();
 208          $overridedata->cutoffdate = time();
 209          $DB->insert_record('assign_overrides', $overridedata);
 210  
 211          $submissiontext = 'My first submission';
 212          $submission = $this->create_submission($assign1, $user3, $submissiontext);
 213  
 214          $submissiontext = 'My first submission';
 215          $submission = $this->create_submission($assign1, $user4, $submissiontext);
 216  
 217          $this->setUser($user5);
 218  
 219          $grade = '72.00';
 220          $teachercommenttext = 'This is better. Thanks.';
 221          $data = new \stdClass();
 222          $data->attemptnumber = 1;
 223          $data->grade = $grade;
 224          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
 225  
 226          // Give the submission a grade.
 227          $assign1->save_grade($user4->id, $data);
 228  
 229          $userlist = new \core_privacy\local\request\userlist($context, 'assign');
 230          provider::get_users_in_context($userlist);
 231          $userids = $userlist->get_userids();
 232          $this->assertTrue(in_array($user1->id, $userids));
 233          $this->assertTrue(in_array($user2->id, $userids));
 234          $this->assertTrue(in_array($user3->id, $userids));
 235          $this->assertTrue(in_array($user4->id, $userids));
 236          $this->assertTrue(in_array($user5->id, $userids));
 237          $this->assertFalse(in_array($user6->id, $userids));
 238      }
 239  
 240      /**
 241       * Test that a student with multiple submissions and grades is returned with the correct data.
 242       */
 243      public function test_export_user_data_student() {
 244          global $DB;
 245          $this->resetAfterTest();
 246          $course = $this->getDataGenerator()->create_course();
 247          $coursecontext = \context_course::instance($course->id);
 248  
 249          $user = $this->getDataGenerator()->create_user();
 250          $teacher = $this->getDataGenerator()->create_user();
 251          $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
 252          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
 253          $assign = $this->create_instance([
 254                  'course' => $course,
 255                  'name' => 'Assign 1',
 256                  'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
 257                  'maxattempts' => 3,
 258                  'assignsubmission_onlinetext_enabled' => true,
 259                  'assignfeedback_comments_enabled' => true
 260              ]);
 261  
 262          $context = $assign->get_context();
 263          // Create some submissions (multiple attempts) for a student.
 264          $submissiontext = 'My first submission';
 265          $submission = $this->create_submission($assign, $user, $submissiontext);
 266  
 267          $this->setUser($teacher);
 268  
 269          $overridedata = new \stdClass();
 270          $overridedata->assignid = $assign->get_instance()->id;
 271          $overridedata->userid = $user->id;
 272          $overridedata->duedate = time();
 273          $overridedata->allowsubmissionsfromdate = time();
 274          $overridedata->cutoffdate = time();
 275          $DB->insert_record('assign_overrides', $overridedata);
 276  
 277          $grade1 = '67.00';
 278          $teachercommenttext = 'Please try again.';
 279          $data = new \stdClass();
 280          $data->attemptnumber = 0;
 281          $data->grade = $grade1;
 282          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
 283  
 284          // Give the submission a grade.
 285          $assign->save_grade($user->id, $data);
 286  
 287          $submissiontext2 = 'My second submission';
 288          $submission = $this->create_submission($assign, $user, $submissiontext2, 1);
 289  
 290          $this->setUser($teacher);
 291  
 292          $grade2 = '72.00';
 293          $teachercommenttext2 = 'This is better. Thanks.';
 294          $data = new \stdClass();
 295          $data->attemptnumber = 1;
 296          $data->grade = $grade2;
 297          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext2, 'format' => FORMAT_MOODLE];
 298  
 299          // Give the submission a grade.
 300          $assign->save_grade($user->id, $data);
 301  
 302          $writer = writer::with_context($context);
 303          $this->assertFalse($writer->has_any_data());
 304  
 305          // The student should have some text submitted.
 306          // Add the course context as well to make sure there is no error.
 307          $approvedlist = new approved_contextlist($user, 'mod_assign', [$context->id, $coursecontext->id]);
 308          provider::export_user_data($approvedlist);
 309  
 310          // Check that we have general details about the assignment.
 311          $this->assertEquals('Assign 1', $writer->get_data()->name);
 312          // Check Submissions.
 313          $this->assertEquals($submissiontext, $writer->get_data(['attempt 1', 'Submission Text'])->text);
 314          $this->assertEquals($submissiontext2, $writer->get_data(['attempt 2', 'Submission Text'])->text);
 315          $this->assertEquals(1, $writer->get_data(['attempt 1', 'submission'])->attemptnumber);
 316          $this->assertEquals(2, $writer->get_data(['attempt 2', 'submission'])->attemptnumber);
 317          // Check grades.
 318          $this->assertEquals((float)$grade1, $writer->get_data(['attempt 1', 'grade'])->grade);
 319          $this->assertEquals((float)$grade2, $writer->get_data(['attempt 2', 'grade'])->grade);
 320          // Check feedback.
 321          $this->assertStringContainsString($teachercommenttext, $writer->get_data(['attempt 1', 'Feedback comments'])->commenttext);
 322          $this->assertStringContainsString($teachercommenttext2, $writer->get_data(['attempt 2', 'Feedback comments'])->commenttext);
 323  
 324          // Check override data was exported correctly.
 325          $overrideexport = $writer->get_data(['Overrides']);
 326          $this->assertEquals(\core_privacy\local\request\transform::datetime($overridedata->duedate),
 327                  $overrideexport->duedate);
 328          $this->assertEquals(\core_privacy\local\request\transform::datetime($overridedata->cutoffdate),
 329                  $overrideexport->cutoffdate);
 330          $this->assertEquals(\core_privacy\local\request\transform::datetime($overridedata->allowsubmissionsfromdate),
 331                  $overrideexport->allowsubmissionsfromdate);
 332      }
 333  
 334      /**
 335       * Tests the data returned for a teacher.
 336       */
 337      public function test_export_user_data_teacher() {
 338          $this->resetAfterTest();
 339          $course = $this->getDataGenerator()->create_course();
 340          $coursecontext = \context_course::instance($course->id);
 341  
 342          $user1 = $this->getDataGenerator()->create_user();
 343          $user2 = $this->getDataGenerator()->create_user();
 344          $teacher = $this->getDataGenerator()->create_user();
 345          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 346          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
 347          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
 348          $assign = $this->create_instance([
 349                  'course' => $course,
 350                  'name' => 'Assign 1',
 351                  'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
 352                  'maxattempts' => 3,
 353                  'assignsubmission_onlinetext_enabled' => true,
 354                  'assignfeedback_comments_enabled' => true
 355              ]);
 356  
 357          $context = $assign->get_context();
 358  
 359          // Create and grade some submissions from the students.
 360          $submissiontext = 'My first submission';
 361          $submission = $this->create_submission($assign, $user1, $submissiontext);
 362  
 363          $this->setUser($teacher);
 364  
 365          $grade1 = '54.00';
 366          $teachercommenttext = 'Comment on user 1 attempt 1.';
 367          $data = new \stdClass();
 368          $data->attemptnumber = 0;
 369          $data->grade = $grade1;
 370          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
 371  
 372          // Give the submission a grade.
 373          $assign->save_grade($user1->id, $data);
 374  
 375          // Create and grade some submissions from the students.
 376          $submissiontext2 = 'My first submission for user 2';
 377          $submission = $this->create_submission($assign, $user2, $submissiontext2);
 378  
 379          $this->setUser($teacher);
 380  
 381          $grade2 = '56.00';
 382          $teachercommenttext2 = 'Comment on user 2 first attempt.';
 383          $data = new \stdClass();
 384          $data->attemptnumber = 0;
 385          $data->grade = $grade2;
 386          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext2, 'format' => FORMAT_MOODLE];
 387  
 388          // Give the submission a grade.
 389          $assign->save_grade($user2->id, $data);
 390  
 391          // Create and grade some submissions from the students.
 392          $submissiontext3 = 'My second submission for user 2';
 393          $submission = $this->create_submission($assign, $user2, $submissiontext3, 1);
 394  
 395          $this->setUser($teacher);
 396  
 397          $grade3 = '83.00';
 398          $teachercommenttext3 = 'Comment on user 2 another attempt.';
 399          $data = new \stdClass();
 400          $data->attemptnumber = 1;
 401          $data->grade = $grade3;
 402          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext3, 'format' => FORMAT_MOODLE];
 403  
 404          // Give the submission a grade.
 405          $assign->save_grade($user2->id, $data);
 406  
 407          // Set up some flags.
 408          $duedate = time();
 409          $flagdata = $assign->get_user_flags($teacher->id, true);
 410          $flagdata->mailed = 1;
 411          $flagdata->extensionduedate = $duedate;
 412          $assign->update_user_flags($flagdata);
 413  
 414          $writer = writer::with_context($context);
 415          $this->assertFalse($writer->has_any_data());
 416  
 417          // The student should have some text submitted.
 418          $approvedlist = new approved_contextlist($teacher, 'mod_assign', [$context->id, $coursecontext->id]);
 419          provider::export_user_data($approvedlist);
 420  
 421          // Check flag metadata.
 422          $metadata = $writer->get_all_metadata();
 423          $this->assertEquals(\core_privacy\local\request\transform::yesno(1), $metadata['mailed']->value);
 424          $this->assertEquals(\core_privacy\local\request\transform::datetime($duedate), $metadata['extensionduedate']->value);
 425  
 426          // Check for student grades given.
 427          $student1grade = $writer->get_data(['studentsubmissions', $user1->id, 'attempt 1', 'grade']);
 428          $this->assertEquals((float)$grade1, $student1grade->grade);
 429          $student2grade1 = $writer->get_data(['studentsubmissions', $user2->id, 'attempt 1', 'grade']);
 430          $this->assertEquals((float)$grade2, $student2grade1->grade);
 431          $student2grade2 = $writer->get_data(['studentsubmissions', $user2->id, 'attempt 2', 'grade']);
 432          $this->assertEquals((float)$grade3, $student2grade2->grade);
 433          // Check for feedback given to students.
 434          $this->assertStringContainsString($teachercommenttext, $writer->get_data(['studentsubmissions', $user1->id, 'attempt 1',
 435                  'Feedback comments'])->commenttext);
 436          $this->assertStringContainsString($teachercommenttext2, $writer->get_data(['studentsubmissions', $user2->id, 'attempt 1',
 437                  'Feedback comments'])->commenttext);
 438          $this->assertStringContainsString($teachercommenttext3, $writer->get_data(['studentsubmissions', $user2->id, 'attempt 2',
 439                  'Feedback comments'])->commenttext);
 440      }
 441  
 442      /**
 443       * A test for deleting all user data for a given context.
 444       */
 445      public function test_delete_data_for_all_users_in_context() {
 446          global $DB;
 447          $this->resetAfterTest();
 448          $course = $this->getDataGenerator()->create_course();
 449  
 450          $user1 = $this->getDataGenerator()->create_user();
 451          $user2 = $this->getDataGenerator()->create_user();
 452          $teacher = $this->getDataGenerator()->create_user();
 453          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 454          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
 455          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
 456          $assign = $this->create_instance([
 457                  'course' => $course,
 458                  'name' => 'Assign 1',
 459                  'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
 460                  'maxattempts' => 3,
 461                  'assignsubmission_onlinetext_enabled' => true,
 462                  'assignfeedback_comments_enabled' => true
 463              ]);
 464  
 465          $context = $assign->get_context();
 466  
 467          // Create and grade some submissions from the students.
 468          $submissiontext = 'My first submission';
 469          $submission = $this->create_submission($assign, $user1, $submissiontext);
 470  
 471          $this->setUser($teacher);
 472  
 473          // Overrides for both students.
 474          $overridedata = new \stdClass();
 475          $overridedata->assignid = $assign->get_instance()->id;
 476          $overridedata->userid = $user1->id;
 477          $overridedata->duedate = time();
 478          $DB->insert_record('assign_overrides', $overridedata);
 479          $overridedata->userid = $user2->id;
 480          $DB->insert_record('assign_overrides', $overridedata);
 481          assign_update_events($assign);
 482  
 483          $grade1 = '54.00';
 484          $teachercommenttext = 'Comment on user 1 attempt 1.';
 485          $data = new \stdClass();
 486          $data->attemptnumber = 0;
 487          $data->grade = $grade1;
 488          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
 489  
 490          // Give the submission a grade.
 491          $assign->save_grade($user1->id, $data);
 492  
 493          // Create and grade some submissions from the students.
 494          $submissiontext2 = 'My first submission for user 2';
 495          $submission = $this->create_submission($assign, $user2, $submissiontext2);
 496  
 497          $this->setUser($teacher);
 498  
 499          $grade2 = '56.00';
 500          $teachercommenttext2 = 'Comment on user 2 first attempt.';
 501          $data = new \stdClass();
 502          $data->attemptnumber = 0;
 503          $data->grade = $grade2;
 504          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext2, 'format' => FORMAT_MOODLE];
 505  
 506          // Give the submission a grade.
 507          $assign->save_grade($user2->id, $data);
 508  
 509          // Create and grade some submissions from the students.
 510          $submissiontext3 = 'My second submission for user 2';
 511          $submission = $this->create_submission($assign, $user2, $submissiontext3, 1);
 512  
 513          $this->setUser($teacher);
 514  
 515          $grade3 = '83.00';
 516          $teachercommenttext3 = 'Comment on user 2 another attempt.';
 517          $data = new \stdClass();
 518          $data->attemptnumber = 1;
 519          $data->grade = $grade3;
 520          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext3, 'format' => FORMAT_MOODLE];
 521  
 522          // Give the submission a grade.
 523          $assign->save_grade($user2->id, $data);
 524  
 525          // Delete all user data for this assignment.
 526          provider::delete_data_for_all_users_in_context($context);
 527  
 528          // Check all relevant tables.
 529          $records = $DB->get_records('assign_submission');
 530          $this->assertEmpty($records);
 531          $records = $DB->get_records('assign_grades');
 532          $this->assertEmpty($records);
 533          $records = $DB->get_records('assignsubmission_onlinetext');
 534          $this->assertEmpty($records);
 535          $records = $DB->get_records('assignfeedback_comments');
 536          $this->assertEmpty($records);
 537  
 538          // Check that overrides and the calendar events are deleted.
 539          $records = $DB->get_records('event');
 540          $this->assertEmpty($records);
 541          $records = $DB->get_records('assign_overrides');
 542          $this->assertEmpty($records);
 543      }
 544  
 545      /**
 546       * A test for deleting all user data for one user.
 547       */
 548      public function test_delete_data_for_user() {
 549          global $DB;
 550          $this->resetAfterTest();
 551          $course = $this->getDataGenerator()->create_course();
 552  
 553          $coursecontext = \context_course::instance($course->id);
 554  
 555          $user1 = $this->getDataGenerator()->create_user();
 556          $user2 = $this->getDataGenerator()->create_user();
 557          $teacher = $this->getDataGenerator()->create_user();
 558          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 559          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
 560          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
 561          $assign = $this->create_instance([
 562                  'course' => $course,
 563                  'name' => 'Assign 1',
 564                  'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
 565                  'maxattempts' => 3,
 566                  'assignsubmission_onlinetext_enabled' => true,
 567                  'assignfeedback_comments_enabled' => true
 568              ]);
 569  
 570          $context = $assign->get_context();
 571  
 572          // Create and grade some submissions from the students.
 573          $submissiontext = 'My first submission';
 574          $submission1 = $this->create_submission($assign, $user1, $submissiontext);
 575  
 576          $this->setUser($teacher);
 577  
 578          // Overrides for both students.
 579          $overridedata = new \stdClass();
 580          $overridedata->assignid = $assign->get_instance()->id;
 581          $overridedata->userid = $user1->id;
 582          $overridedata->duedate = time();
 583          $DB->insert_record('assign_overrides', $overridedata);
 584          $overridedata->userid = $user2->id;
 585          $DB->insert_record('assign_overrides', $overridedata);
 586          assign_update_events($assign);
 587  
 588          $grade1 = '54.00';
 589          $teachercommenttext = 'Comment on user 1 attempt 1.';
 590          $data = new \stdClass();
 591          $data->attemptnumber = 0;
 592          $data->grade = $grade1;
 593          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
 594  
 595          // Give the submission a grade.
 596          $assign->save_grade($user1->id, $data);
 597  
 598          // Create and grade some submissions from the students.
 599          $submissiontext2 = 'My first submission for user 2';
 600          $submission2 = $this->create_submission($assign, $user2, $submissiontext2);
 601  
 602          $this->setUser($teacher);
 603  
 604          $grade2 = '56.00';
 605          $teachercommenttext2 = 'Comment on user 2 first attempt.';
 606          $data = new \stdClass();
 607          $data->attemptnumber = 0;
 608          $data->grade = $grade2;
 609          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext2, 'format' => FORMAT_MOODLE];
 610  
 611          // Give the submission a grade.
 612          $assign->save_grade($user2->id, $data);
 613  
 614          // Create and grade some submissions from the students.
 615          $submissiontext3 = 'My second submission for user 2';
 616          $submission3 = $this->create_submission($assign, $user2, $submissiontext3, 1);
 617  
 618          $this->setUser($teacher);
 619  
 620          $grade3 = '83.00';
 621          $teachercommenttext3 = 'Comment on user 2 another attempt.';
 622          $data = new \stdClass();
 623          $data->attemptnumber = 1;
 624          $data->grade = $grade3;
 625          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext3, 'format' => FORMAT_MOODLE];
 626  
 627          // Give the submission a grade.
 628          $assign->save_grade($user2->id, $data);
 629  
 630          // Delete user 2's data.
 631          $approvedlist = new approved_contextlist($user2, 'mod_assign', [$context->id, $coursecontext->id]);
 632          provider::delete_data_for_user($approvedlist);
 633  
 634          // Check all relevant tables.
 635          $records = $DB->get_records('assign_submission');
 636          foreach ($records as $record) {
 637              $this->assertEquals($user1->id, $record->userid);
 638              $this->assertNotEquals($user2->id, $record->userid);
 639          }
 640          $records = $DB->get_records('assign_grades');
 641          foreach ($records as $record) {
 642              $this->assertEquals($user1->id, $record->userid);
 643              $this->assertNotEquals($user2->id, $record->userid);
 644          }
 645          $records = $DB->get_records('assignsubmission_onlinetext');
 646          $this->assertCount(1, $records);
 647          $record = array_shift($records);
 648          // The only submission is for user 1.
 649          $this->assertEquals($submission1->id, $record->submission);
 650          $records = $DB->get_records('assignfeedback_comments');
 651          $this->assertCount(1, $records);
 652          $record = array_shift($records);
 653          // The only record is the feedback comment for user 1.
 654          $this->assertEquals($teachercommenttext, $record->commenttext);
 655  
 656          // Check calendar events as well as assign overrides.
 657          $records = $DB->get_records('event');
 658          $this->assertCount(1, $records);
 659          $record = array_shift($records);
 660          // The remaining event should be for user 1.
 661          $this->assertEquals($user1->id, $record->userid);
 662          // Now for assign_overrides
 663          $records = $DB->get_records('assign_overrides');
 664          $this->assertCount(1, $records);
 665          $record = array_shift($records);
 666          // The remaining event should be for user 1.
 667          $this->assertEquals($user1->id, $record->userid);
 668      }
 669  
 670      /**
 671       * A test for deleting all user data for a bunch of users.
 672       */
 673      public function test_delete_data_for_users() {
 674          global $DB;
 675  
 676          $this->resetAfterTest();
 677  
 678          $course = $this->getDataGenerator()->create_course();
 679  
 680          // Only made a comment on a submission.
 681          $user1 = $this->getDataGenerator()->create_user();
 682          // User 2 only has information about an activity override.
 683          $user2 = $this->getDataGenerator()->create_user();
 684          // User 3 made a submission.
 685          $user3 = $this->getDataGenerator()->create_user();
 686          // User 4 makes a submission and it is marked by the teacher.
 687          $user4 = $this->getDataGenerator()->create_user();
 688          // Grading and providing feedback as a teacher.
 689          $user5 = $this->getDataGenerator()->create_user();
 690          // This user has entries in assignment 2 and should not have their data deleted.
 691          $user6 = $this->getDataGenerator()->create_user();
 692  
 693          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 694          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
 695          $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student');
 696          $this->getDataGenerator()->enrol_user($user4->id, $course->id, 'student');
 697          $this->getDataGenerator()->enrol_user($user5->id, $course->id, 'editingteacher');
 698          $this->getDataGenerator()->enrol_user($user6->id, $course->id, 'student');
 699  
 700          $assign1 = $this->create_instance(['course' => $course,
 701                  'assignsubmission_onlinetext_enabled' => true,
 702                  'assignfeedback_comments_enabled' => true]);
 703          $assign2 = $this->create_instance(['course' => $course,
 704                  'assignsubmission_onlinetext_enabled' => true,
 705                  'assignfeedback_comments_enabled' => true]);
 706  
 707          $context = $assign1->get_context();
 708  
 709          // Jam an entry in the comments table for user 1.
 710          $comment = (object) [
 711              'contextid' => $context->id,
 712              'component' => 'assignsubmission_comments',
 713              'commentarea' => 'submission_comments',
 714              'itemid' => 5,
 715              'content' => 'A comment by user 1',
 716              'format' => 0,
 717              'userid' => $user1->id,
 718              'timecreated' => time()
 719          ];
 720          $DB->insert_record('comments', $comment);
 721  
 722          $this->setUser($user5); // Set the user to the teacher.
 723  
 724          $overridedata = new \stdClass();
 725          $overridedata->assignid = $assign1->get_instance()->id;
 726          $overridedata->userid = $user2->id;
 727          $overridedata->duedate = time();
 728          $overridedata->allowsubmissionsfromdate = time();
 729          $overridedata->cutoffdate = time();
 730          $DB->insert_record('assign_overrides', $overridedata);
 731  
 732          $submissiontext = 'My first submission';
 733          $submission = $this->create_submission($assign1, $user3, $submissiontext);
 734  
 735          $submissiontext = 'My first submission';
 736          $submission = $this->create_submission($assign1, $user4, $submissiontext);
 737  
 738          $submissiontext = 'My first submission';
 739          $submission = $this->create_submission($assign2, $user6, $submissiontext);
 740  
 741          $this->setUser($user5);
 742  
 743          $grade = '72.00';
 744          $teachercommenttext = 'This is better. Thanks.';
 745          $data = new \stdClass();
 746          $data->attemptnumber = 1;
 747          $data->grade = $grade;
 748          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
 749  
 750          // Give the submission a grade.
 751          $assign1->save_grade($user4->id, $data);
 752  
 753          $this->setUser($user5);
 754  
 755          $grade = '81.00';
 756          $teachercommenttext = 'This is nice.';
 757          $data = new \stdClass();
 758          $data->attemptnumber = 1;
 759          $data->grade = $grade;
 760          $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
 761  
 762          // Give the submission a grade.
 763          $assign2->save_grade($user6->id, $data);
 764  
 765          // Check data is in place.
 766          $data = $DB->get_records('assign_submission');
 767          // We should have one entry for user 3 and two entries each for user 4 and 6.
 768          $this->assertCount(5, $data);
 769          $usercounts = [
 770              $user3->id => 0,
 771              $user4->id => 0,
 772              $user6->id => 0
 773          ];
 774          foreach ($data as $datum) {
 775              $usercounts[$datum->userid]++;
 776          }
 777          $this->assertEquals(1, $usercounts[$user3->id]);
 778          $this->assertEquals(2, $usercounts[$user4->id]);
 779          $this->assertEquals(2, $usercounts[$user6->id]);
 780  
 781          $data = $DB->get_records('assign_grades');
 782          // Two entries in assign_grades, one for each grade given.
 783          $this->assertCount(2, $data);
 784  
 785          $data = $DB->get_records('assign_overrides');
 786          $this->assertCount(1, $data);
 787  
 788          $data = $DB->get_records('comments');
 789          $this->assertCount(1, $data);
 790  
 791          $userlist = new \core_privacy\local\request\approved_userlist($context, 'assign', [$user1->id, $user2->id]);
 792          provider::delete_data_for_users($userlist);
 793  
 794          $data = $DB->get_records('assign_overrides');
 795          $this->assertEmpty($data);
 796  
 797          $data = $DB->get_records('comments');
 798          $this->assertEmpty($data);
 799  
 800          $data = $DB->get_records('assign_submission');
 801          // No change here.
 802          $this->assertCount(5, $data);
 803  
 804          $userlist = new \core_privacy\local\request\approved_userlist($context, 'assign', [$user3->id, $user5->id]);
 805          provider::delete_data_for_users($userlist);
 806  
 807          $data = $DB->get_records('assign_submission');
 808          // Only the record for user 3 has been deleted.
 809          $this->assertCount(4, $data);
 810  
 811          $data = $DB->get_records('assign_grades');
 812          // Grades should be unchanged.
 813          $this->assertCount(2, $data);
 814      }
 815  }