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   * Privacy test for the event monitor
  19   *
  20   * @package    mod_assignment
  21   * @category   test
  22   * @copyright  2018 Zig Tan <zig@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once (__DIR__ . '/../lib.php');
  29  
  30  use \mod_assignment\privacy\provider;
  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_privacy\tests\provider_testcase;
  35  
  36  /**
  37   * Privacy test for the event monitor
  38   *
  39   * @package    mod_assignment
  40   * @category   test
  41   * @copyright  2018 Zig Tan <zig@moodle.com>
  42   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   */
  44  class mod_assignment_privacy_testcase extends provider_testcase {
  45  
  46      /**
  47       * @var int array   Array of test student ids associated for Course 1.
  48       */
  49      private $course1students = [];
  50  
  51      /**
  52       * @var int array   Array of test student ids associated for Course 2.
  53       */
  54      private $course2students = [];
  55  
  56      /**
  57       * @var int array   Array of test assignments associated for Course 1.
  58       */
  59      private $course1assignments = [];
  60  
  61      /**
  62       * @var int array   Array of test assignments associated for Course 2.
  63       */
  64      private $course2assignments = [];
  65  
  66      /**
  67       * Test for provider::get_contexts_for_userid().
  68       *
  69       * @throws coding_exception
  70       */
  71      public function test_get_contexts_for_userid() {
  72          global $DB;
  73  
  74          $this->resetAfterTest(true);
  75          $this->create_courses_and_assignments();
  76  
  77          // Get Teacher 1 to test get_contexts_for_userid().
  78          $teacher1 = $DB->get_record('user', ['username' => 'teacher1']);
  79          $contextids = provider::get_contexts_for_userid($teacher1->id);
  80          // Verify there should be 4 contexts, as Teacher 1 has submitted tests and marked Assignments in Course 1 and 2.
  81          $this->assertEquals(4, count($contextids->get_contextids()));
  82  
  83          // Get Teacher 2 to test get_contexts_for_userid().
  84          $teacher2 = $DB->get_record('user', ['username' => 'teacher2']);
  85          $contextids = provider::get_contexts_for_userid($teacher2->id);
  86          // Verify there should be 0 contexts, as teacher 2 has not marked any Assignments.
  87          $this->assertEquals(0, count($contextids->get_contextids()));
  88  
  89          // Get Student 1 to test get_contexts_for_userid().
  90          $student1 = $DB->get_record('user', ['username' => 'student1']);
  91          $contextids = provider::get_contexts_for_userid($student1->id);
  92          // Verify there should be 2 contexts, as student 1 added submissions for both Assignments in Course 1.
  93          $this->assertEquals(2, count($contextids->get_contextids()));
  94  
  95          // Get Student 2 to test get_contexts_for_userid().
  96          $student2 = $DB->get_record('user', ['username' => 'student2']);
  97          $contextids = provider::get_contexts_for_userid($student2->id);
  98          // Verify there should be 2 context, as student 2 added submissions for both Assignments in Course 2.
  99          $this->assertEquals(2, count($contextids->get_contextids()));
 100      }
 101  
 102      /**
 103       * Test that the correct userids are returned for a specific context.
 104       */
 105      public function test_get_users_in_context() {
 106  
 107          $this->resetAfterTest(true);
 108  
 109          $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
 110          $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
 111          // Student 3 should not turn up in the results of this test.
 112          $student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);
 113          $teacher1 = $this->getDataGenerator()->create_user(['username' => 'teacher1']);
 114  
 115          $course = $this->getDataGenerator()->create_course();
 116          $course1assignment1 = $this->getDataGenerator()->create_module('assignment',
 117              [
 118                  'course' => $course->id,
 119                  'name' => 'Course 1 - Assignment 1 (onlinetext)',
 120                  'assignmenttype' => 'onlinetext',
 121              ]
 122          );
 123          // Create a second assignment in the same course.
 124          $course1assignment2 = $this->getDataGenerator()->create_module('assignment',
 125              [
 126                  'course' => $course->id,
 127                  'name' => 'Course 1 - Assignment 1 (onlinetext)',
 128                  'assignmenttype' => 'onlinetext',
 129              ]
 130          );
 131  
 132          $this->add_assignment_submission(
 133              $course1assignment1,
 134              $student1,
 135              "Course 1 - Ass 1: Student1 Test Submission"
 136          );
 137  
 138          $this->add_assignment_submission(
 139              $course1assignment1,
 140              $student2,
 141              "Course 1 - Ass 1: Student2 Test Submission"
 142          );
 143          // Add a submission for the second assignment.
 144          $this->add_assignment_submission(
 145              $course1assignment2,
 146              $student3,
 147              "Course 1 - Ass 2: Student3 Test Submission"
 148          );
 149  
 150          $submissions = $this->get_course_assignment_submissions($course->id);
 151          foreach ($submissions as $submission) {
 152              $this->mark_assignment_submission($submission->assignment, $submission->id, $teacher1, 50);
 153          }
 154  
 155          $c1ass1ctx = context_module::instance($course1assignment1->cmid);
 156          $userlist = new \core_privacy\local\request\userlist($c1ass1ctx, 'mod_assignment');
 157  
 158          provider::get_users_in_context($userlist);
 159          $userids = $userlist->get_userids();
 160  
 161          // This is both students and the teacher who marked both assignments.
 162          $this->assertCount(3, $userids);
 163          // Make sure that student 3 is not in the returned userids.
 164          $this->assertFalse(in_array($student3->id, $userids));
 165          // Try with the course context.
 166          $coursecontext = context_course::instance($course->id);
 167          $userlist = new \core_privacy\local\request\userlist($coursecontext, 'mod_assignment');
 168          provider::get_users_in_context($userlist);
 169          $this->assertEmpty($userlist->get_userids());
 170      }
 171  
 172      /**
 173       * Test for provider::export_user_data().
 174       *
 175       * @throws coding_exception
 176       */
 177      public function test_export_user_data_teacher() {
 178          global $DB;
 179  
 180          $this->resetAfterTest(true);
 181          $this->create_courses_and_assignments();
 182  
 183          // Test Teacher 1 export_data_for_user() - marking assignment submissions for both Course 1 and 2.
 184          $teacher1 = $DB->get_record('user', ['username' => 'teacher1']);
 185  
 186          $contextlist = provider::get_contexts_for_userid($teacher1->id);
 187          $approvedcontextlist = new approved_contextlist($teacher1, 'mod_assignment', $contextlist->get_contextids());
 188  
 189          // Verify Teacher 1 has four contexts.
 190          $this->assertCount(4, $contextlist->get_contextids());
 191  
 192          // Retrieve Assignment Submissions data for Teacher 1.
 193          provider::export_user_data($approvedcontextlist);
 194  
 195          $contexts = $contextlist->get_contexts();
 196  
 197          // Context 1 - Course 1's Assignment 1 -- (onlinetext).
 198          $context = context_module::instance($this->course1assignments['ass1']->cmid);
 199          $this->assertContains($context, $contexts);
 200  
 201          $writer = writer::with_context($context);
 202          $subcontexts = [
 203              get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 204              transform::user($teacher1->id)
 205          ];
 206          // Verify the test assignment submission from Teacher 1 exists.
 207          $submission = $writer->get_data($subcontexts);
 208          $this->assertEquals('<p>Course 1 - Ass 1: Teacher Test Submission</p>', $submission->data1);
 209  
 210          foreach ($this->course1students as $student) {
 211              $subcontexts = [
 212                  get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 213                  transform::user($student->id)
 214              ];
 215              // Verify the student assignment submissions exists.
 216              $submission = $writer->get_data($subcontexts);
 217              $this->assertEquals("<p>Course 1 - Ass 1: " . $student->id . "</p>", $submission->data1);
 218          }
 219  
 220          // Context 2 - Course 1's Assignment 2 -- (single file upload).
 221          $context = context_module::instance($this->course1assignments['ass2']->cmid);
 222          $this->assertContains($context, $contexts);
 223  
 224          $writer = writer::with_context($context);
 225          foreach ($this->course1students as $student) {
 226              $subcontexts = [
 227                  get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 228                  transform::user($student->id)
 229              ];
 230              // Verify the student assignment submissions exists.
 231              $submission = $writer->get_data($subcontexts);
 232              $this->assertEquals("<p>Course 1 - Ass 2: " . $student->id . "</p>", $submission->data1);
 233  
 234              // Verify the student assignment submission file upload exists.
 235              $submissionfiles = $writer->get_files($subcontexts);
 236              $this->assertTrue(array_key_exists('Student' . $student->id . '-Course1-Ass2-(File 1 of 1)', $submissionfiles));
 237          }
 238  
 239          // Context 3 - Course 2's Assignment 1 -- (offline).
 240          $context = context_module::instance($this->course2assignments['ass1']->cmid);
 241          $this->assertContains($context, $contexts);
 242  
 243          $writer = writer::with_context($context);
 244          foreach ($this->course2students as $student) {
 245              $subcontexts = [
 246                  get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 247                  transform::user($student->id)
 248              ];
 249              // Verify the student assignment submissions exists.
 250              $submission = $writer->get_data($subcontexts);
 251              $this->assertEquals("<p>Course 2 - Ass 1: " . $student->id . "</p>", $submission->data1);
 252          }
 253  
 254          // Context 4 - Course 2's Assignment 2 -- (multiple file upload).
 255          $context = context_module::instance($this->course2assignments['ass2']->cmid);
 256          $this->assertContains($context, $contexts);
 257  
 258          $writer = writer::with_context($context);
 259          foreach ($this->course2students as $student) {
 260              $subcontexts = [
 261                  get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 262                  transform::user($student->id)
 263              ];
 264              // Verify the student assignment submissions exists.
 265              $submission = $writer->get_data($subcontexts);
 266              $this->assertEquals("<p>Course 2 - Ass 2: " . $student->id . "</p>", $submission->data1);
 267  
 268              // Verify the student assignment submission file upload exists.
 269              $submissionfiles = $writer->get_files($subcontexts);
 270              $this->assertTrue(array_key_exists('Student' . $student->id . '-Course2-Ass2-(File 1 of 2)', $submissionfiles));
 271              $this->assertTrue(array_key_exists('Student' . $student->id . '-Course2-Ass2-(File 2 of 2)', $submissionfiles));
 272          }
 273      }
 274  
 275      /**
 276       * Test for provider::export_user_data().
 277       *
 278       * @throws dml_exception
 279       */
 280      public function test_export_user_data_student() {
 281          global $DB;
 282  
 283          $this->resetAfterTest(true);
 284          $this->create_courses_and_assignments();
 285  
 286          // Test Student 1 export_data_for_user() - added assignment submissions for both assignments in Course 1.
 287          $student1 = $DB->get_record('user', ['username' => 'student1']);
 288  
 289          $contextlist = provider::get_contexts_for_userid($student1->id);
 290          $approvedcontextlist = new approved_contextlist($student1, 'mod_assignment', $contextlist->get_contextids());
 291  
 292          // Retrieve Assignment Submissions data for Student 1.
 293          provider::export_user_data($approvedcontextlist);
 294          $contexts = $contextlist->get_contexts();
 295  
 296          // Context 1 - Course 1's Assignment 1 -- (onlinetext).
 297          $context = context_module::instance($this->course1assignments['ass1']->cmid);
 298          $this->assertContains($context, $contexts);
 299  
 300          $writer = writer::with_context($context);
 301          $subcontexts = [
 302              get_string('privacy:submissionpath', 'mod_assignment')
 303          ];
 304  
 305          // Verify the student assignment submissions exists.
 306          $submission = $writer->get_data($subcontexts);
 307          $this->assertEquals("<p>Course 1 - Ass 1: " . $student1->id . "</p>", $submission->data1);
 308  
 309          // Context 2 - Course 1's Assignment 2 -- (single file upload).
 310          $context = context_module::instance($this->course1assignments['ass2']->cmid);
 311          $this->assertContains($context, $contexts);
 312  
 313          $writer = writer::with_context($context);
 314          $subcontexts = [
 315              get_string('privacy:submissionpath', 'mod_assignment')
 316          ];
 317  
 318          // Verify the student assignment submission exists.
 319          $submission = $writer->get_data($subcontexts);
 320          $this->assertEquals("<p>Course 1 - Ass 2: " . $student1->id . "</p>", $submission->data1);
 321  
 322          // Verify the student assignment submission file upload exists.
 323          $submissionfiles = $writer->get_files($subcontexts);
 324          $this->assertTrue(array_key_exists('Student' . $student1->id . '-Course1-Ass2-(File 1 of 1)', $submissionfiles));
 325  
 326          // Test Student 2 export_data_for_user() - added assignment submissions for both assignments in Course 2.
 327          $student2 = $DB->get_record('user', ['username' => 'student2']);
 328  
 329          $contextlist = provider::get_contexts_for_userid($student2->id);
 330          $approvedcontextlist = new approved_contextlist($student2, 'mod_assignment', $contextlist->get_contextids());
 331  
 332          // Retrieve Assignment Submissions data for Student 2.
 333          provider::export_user_data($approvedcontextlist);
 334          $contexts = $contextlist->get_contexts();
 335  
 336          // Context 1 - Course 2's Assignment 1 -- (offline).
 337          $context = context_module::instance($this->course2assignments['ass1']->cmid);
 338          $this->assertContains($context, $contexts);
 339  
 340          $writer = writer::with_context($context);
 341          $subcontexts = [
 342              get_string('privacy:submissionpath', 'mod_assignment')
 343          ];
 344  
 345          // Verify the student assignment submissions exists.
 346          $submission = $writer->get_data($subcontexts);
 347          $this->assertEquals("<p>Course 2 - Ass 1: " . $student2->id . "</p>", $submission->data1);
 348  
 349          // Context 2 - Course 2's Assignment 2 -- (multiple file upload).
 350          $context = context_module::instance($this->course2assignments['ass2']->cmid);
 351          $this->assertContains($context, $contexts);
 352  
 353          $writer = writer::with_context($context);
 354          $subcontexts = [
 355              get_string('privacy:submissionpath', 'mod_assignment')
 356          ];
 357  
 358          // Verify the student assignment submission exists.
 359          $submission = $writer->get_data($subcontexts);
 360          $this->assertEquals("<p>Course 2 - Ass 2: " . $student2->id . "</p>", $submission->data1);
 361  
 362          // Verify the student assignment submission file upload exists.
 363          $submissionfiles = $writer->get_files($subcontexts);
 364          $this->assertTrue(array_key_exists('Student' . $student2->id . '-Course2-Ass2-(File 1 of 2)', $submissionfiles));
 365          $this->assertTrue(array_key_exists('Student' . $student2->id . '-Course2-Ass2-(File 2 of 2)', $submissionfiles));
 366      }
 367  
 368      /**
 369       * Test for provider::delete_data_for_all_users_in_context().
 370       *
 371       * @throws dml_exception
 372       */
 373      public function test_delete_data_for_all_users_in_context() {
 374          global $DB;
 375  
 376          $this->resetAfterTest(true);
 377          $this->create_courses_and_assignments();
 378  
 379          // Test teacher1 delete_data_for_all_users_in_context().
 380          $teacher1 = $DB->get_record('user', ['username' => 'teacher1']);
 381          $contextlist = provider::get_contexts_for_userid($teacher1->id);
 382  
 383          foreach ($contextlist as $context) {
 384              provider::delete_data_for_all_users_in_context($context);
 385  
 386              // Verify assignment submission(s) were deleted for the context.
 387              $deleted = $this->get_assignment_submissions($context->id);
 388              $this->assertCount(0, $deleted);
 389  
 390              // Verify all the file submissions associated with the context for all users were deleted.
 391              $files = $DB->get_records('files', ['component' => 'mod_assignment', 'filearea' => 'submission', 'contextid' => $context->id]);
 392              $this->assertEquals(0, count($files));
 393          }
 394      }
 395  
 396      /**
 397       * Test for provider::delete_data_for_user().
 398       *
 399       * @throws dml_exception
 400       */
 401      public function test_delete_data_for_user() {
 402          global $DB;
 403  
 404          $this->resetAfterTest(true);
 405          $this->create_courses_and_assignments();
 406  
 407          // Test Teacher 1 delete_data_for_user(), should only remove the 1 test submission added by Teacher 1.
 408          // Should not remove any assignment submission records marked by the teacher.
 409          $teacher1 = $DB->get_record('user', ['username' => 'teacher1']);
 410          $contextlist = provider::get_contexts_for_userid($teacher1->id);
 411          $approvedcontextlist = new approved_contextlist($teacher1, 'mod_assignment', $contextlist->get_contextids());
 412          provider::delete_data_for_user($approvedcontextlist);
 413  
 414          // Verify the submissions submitted by students still exists.
 415          $markedsubmissions = $DB->get_records('assignment_submissions', ['teacher' => $teacher1->id]);
 416          $this->assertCount(4, $markedsubmissions);
 417  
 418          // Test student 1 delete_data_for_user().
 419          $student1 = $DB->get_record('user', ['username' => 'student1']);
 420          $contextlist = provider::get_contexts_for_userid($student1->id);
 421          $approvedcontextlist = new approved_contextlist($student1, 'mod_assignment', $contextlist->get_contextids());
 422          provider::delete_data_for_user($approvedcontextlist);
 423  
 424          // Verify student 1's assignment submissions were deleted.
 425          $assignmentsubmissions = $DB->get_records('assignment_submissions', ['userid' => $student1->id]);
 426          $this->assertEquals(0, count($assignmentsubmissions));
 427  
 428          // Verify student 1's file submissions were deleted.
 429          foreach ($contextlist->get_contextids() as $contextid) {
 430              $files = $DB->get_records('files', ['component' => 'mod_assignment', 'filearea' => 'submission', 'contextid' => $contextid]);
 431              $this->assertEquals(0, count($files));
 432          }
 433      }
 434  
 435      /**
 436       * Test the deletion of a data for users.
 437       */
 438      public function test_delete_data_for_users() {
 439          global $DB;
 440          $this->resetAfterTest();
 441  
 442          $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
 443          $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
 444          $student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);
 445  
 446          $course = $this->getDataGenerator()->create_course();
 447          $course1assignment = $this->getDataGenerator()->create_module('assignment',
 448              [
 449                  'course' => $course->id,
 450                  'name' => 'Course 1 - Assignment 1 (single file upload)',
 451                  'assignmenttype' => 'uploadsingle'
 452              ]
 453          );
 454          $context = context_module::instance($course1assignment->cmid);
 455  
 456          // Student one submission.
 457          $this->add_file_assignment_submission(
 458              $course1assignment,
 459              $student1,
 460              "Course 1 - Ass 2: " . $student1->id,
 461              'Student' . $student1->id . '-Course1-Ass2'
 462          );
 463          // Student two submission.
 464          $this->add_file_assignment_submission(
 465              $course1assignment,
 466              $student2,
 467              "Course 1 - Ass 2: " . $student2->id,
 468              'Student' . $student2->id . '-Course1-Ass2'
 469          );
 470          // Student three submission to be retained.
 471          $this->add_file_assignment_submission(
 472              $course1assignment,
 473              $student3,
 474              "Course 1 - Ass 2: " . $student3->id,
 475              'Student' . $student3->id . '-Course1-Ass2'
 476          );
 477  
 478          $files = $DB->get_records('files', [
 479              'component' => 'mod_assignment',
 480              'filearea' => 'submission',
 481              'contextid' => $context->id
 482          ]);
 483          $this->assertCount(6, $files);
 484  
 485          $submissions = $this->get_assignment_submissions($context->id);
 486          $this->assertCount(3, $submissions);
 487  
 488          $userlist = new \core_privacy\local\request\approved_userlist($context, 'mod_assignment', [$student1->id, $student2->id]);
 489          provider::delete_data_for_users($userlist);
 490  
 491          $files = $DB->get_records('files', [
 492              'component' => 'mod_assignment',
 493              'filearea' => 'submission',
 494              'contextid' => $context->id
 495          ]);
 496          $this->assertCount(2, $files);
 497  
 498          $submissions = $this->get_assignment_submissions($context->id);
 499          $this->assertCount(1, $submissions);
 500      }
 501  
 502      // Start of helper functions.
 503  
 504      /**
 505       * Helper function to setup Course, users, and assignments for testing.
 506       */
 507      protected function create_courses_and_assignments() {
 508          // Create Courses, Users, and Assignments.
 509          $course1 = $this->getDataGenerator()->create_course(['shortname' => 'course1']);
 510          $course2 = $this->getDataGenerator()->create_course(['shortname' => 'course2']);
 511  
 512          $teacher1 = $this->getDataGenerator()->create_user(['username' => 'teacher1']);
 513          $teacher2 = $this->getDataGenerator()->create_user(['username' => 'teacher2']);
 514  
 515          $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
 516          $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
 517  
 518          $this->course1students = [
 519              $student1
 520          ];
 521  
 522          $this->course2students = [
 523              $student2
 524          ];
 525  
 526          $course1assignment1 = $this->getDataGenerator()->create_module('assignment',
 527              [
 528                  'course' => $course1->id,
 529                  'name' => 'Course 1 - Assignment 1 (onlinetext)',
 530                  'assignmenttype' => 'onlinetext',
 531              ]
 532          );
 533          $course1assignment2 = $this->getDataGenerator()->create_module('assignment',
 534              [
 535                  'course' => $course1->id,
 536                  'name' => 'Course 1 - Assignment 2 (single file upload)',
 537                  'assignmenttype' => 'uploadsingle',
 538              ]
 539          );
 540  
 541          $this->course1assignments = [
 542              'ass1' => $course1assignment1,
 543              'ass2' => $course1assignment2
 544          ];
 545  
 546          $course2assignment1 = $this->getDataGenerator()->create_module('assignment',
 547              [
 548                  'course' => $course2->id,
 549                  'name' => 'Course 2 - Assignment 1 (offline)',
 550                  'assignmenttype' => 'offline',
 551              ]
 552          );
 553          $course2assignment2 = $this->getDataGenerator()->create_module('assignment',
 554              [
 555                  'course' => $course2->id,
 556                  'name' => 'Course 2 - Assignment 2 (multiple file upload)',
 557                  'assignmenttype' => 'upload',
 558              ]
 559          );
 560  
 561          $this->course2assignments = [
 562              'ass1' => $course2assignment1,
 563              'ass2' => $course2assignment2
 564          ];
 565  
 566          // Teacher 1 add test assignment submission for Course 1 - Assignment 1.
 567          $this->add_assignment_submission(
 568              $course1assignment1,
 569              $teacher1,
 570              "Course 1 - Ass 1: Teacher Test Submission"
 571          );
 572  
 573          // Student 1 add assignment submissions for Course 1 - Assignment 1 and 2.
 574          $this->add_assignment_submission(
 575              $course1assignment1,
 576              $student1,
 577              "Course 1 - Ass 1: " . $student1->id
 578          );
 579          $this->add_file_assignment_submission(
 580              $course1assignment2,
 581              $student1,
 582              "Course 1 - Ass 2: " . $student1->id,
 583              'Student' . $student1->id . '-Course1-Ass2'
 584          );
 585  
 586          // Student 2 add assignment submissions for Course 2 - Assignment 1 and 2.
 587          $this->add_assignment_submission(
 588              $course2assignment1,
 589              $student2,
 590              "Course 2 - Ass 1: " . $student2->id
 591          );
 592          $this->add_file_assignment_submission(
 593              $course2assignment2,
 594              $student2,
 595              "Course 2 - Ass 2: " . $student2->id,
 596              'Student' . $student2->id . '-Course2-Ass2',
 597              2
 598          );
 599  
 600          // Teacher 1 to mark assignment submissions for Course 1's Assignment 1 and 2.
 601          $course1submissions = $this->get_course_assignment_submissions($course1->id);
 602          foreach ($course1submissions as $submission) {
 603              $this->mark_assignment_submission($submission->assignment, $submission->id, $teacher1, 49);
 604          }
 605  
 606          // Teacher 1 to mark assignment submissions for Course 2's Assignment 1 and 2.
 607          $course2submissions = $this->get_course_assignment_submissions($course2->id);
 608          foreach ($course2submissions as $submission) {
 609              $this->mark_assignment_submission($submission->assignment, $submission->id, $teacher1, 50);
 610          }
 611      }
 612  
 613      /**
 614       * Helper function to add an assignment submission for testing.
 615       *
 616       * @param object $assignment        Object containing assignment submission details to create for testing.
 617       * @param object $user              Object of the user making the assignment submission.
 618       * @param string $submissiondata    The onlintext string value of the assignment submission.
 619       * @throws dml_exception
 620       */
 621      protected function add_assignment_submission($assignment, $user, $submissiondata) {
 622          global $DB;
 623  
 624          $submission = (object) [
 625              'assignment' => $assignment->id,
 626              'userid' => $user->id,
 627              'timecreated' => date('U'),
 628              'data1' => '<p>' . $submissiondata . '</p>',
 629              'submissioncomment' => 'My submission by ' . $user->username
 630          ];
 631  
 632          return $DB->insert_record('assignment_submissions', $submission);
 633      }
 634  
 635      /**
 636       * Helper function to add an assignment submission with file submissions for testing.
 637       *
 638       * @param object $assignment        Object containing assignment submission details to create for testing.
 639       * @param object $user              Object of the user making the assignment submission.
 640       * @param string $submissiondata    The onlintext string value of the assignment submission.
 641       * @param string $filename          The filename of the file submission included with the assignment submission.
 642       * @param int $numfiles             The number of files included with the assignment submission.
 643       * @throws dml_exception
 644       * @throws file_exception
 645       * @throws stored_file_creation_exception
 646       */
 647      protected function add_file_assignment_submission($assignment, $user, $submissiondata, $filename, $numfiles = 1) {
 648          global $CFG, $DB;
 649  
 650          $submission = (object) [
 651              'assignment' => $assignment->id,
 652              'userid' => $user->id,
 653              'timecreated' => date('U'),
 654              'data1' => '<p>' . $submissiondata . '</p>',
 655              'numfiles' => $numfiles,
 656              'submissioncomment' => 'My submission by ' . $user->username
 657          ];
 658  
 659          $submissionid = $DB->insert_record('assignment_submissions', $submission);
 660  
 661          // Create a file submission with the test pdf.
 662          $this->setUser($user->id);
 663          $context = context_module::instance($assignment->cmid);
 664  
 665          $fs = get_file_storage();
 666          $sourcefile = $CFG->dirroot . '/mod/assign/feedback/editpdf/tests/fixtures/submission.pdf';
 667  
 668          for ($f = 1; $f <= $numfiles; $f++) {
 669              $pdfsubmission = (object)array(
 670                  'contextid' => $context->id,
 671                  'component' => 'mod_assignment',
 672                  'filearea' => 'submission',
 673                  'itemid' => $submissionid,
 674                  'filepath' => '/',
 675                  'filename' => $filename . "-(File $f of $numfiles)"
 676              );
 677              $fs->create_file_from_pathname($pdfsubmission, $sourcefile);
 678          }
 679      }
 680  
 681      /**
 682       * Helper function to retrieve the assignment submission records for a given course.
 683       *
 684       * @param int $courseid     The course ID to get assignment submissions by.
 685       * @return array            Array of assignment submission details.
 686       * @throws dml_exception
 687       */
 688      protected function get_course_assignment_submissions($courseid) {
 689          global $DB;
 690  
 691          $sql = "SELECT s.id,
 692                         s.assignment,
 693                         s.userid,
 694                         s.timecreated,
 695                         s.timemodified,
 696                         s.numfiles,
 697                         s.data1,
 698                         s.data2,
 699                         s.grade,
 700                         s.submissioncomment,
 701                         s.format,
 702                         s.teacher,
 703                         s.timemarked,
 704                         s.mailed
 705                    FROM {assignment} a
 706                    JOIN {assignment_submissions} s ON s.assignment = a.id
 707                   WHERE a.course = :courseid";
 708          $params = [
 709              'courseid' => $courseid
 710          ];
 711  
 712          return $DB->get_records_sql($sql, $params);
 713      }
 714  
 715      /**
 716       * Helper function to update an assignment submission with grading details for a teacher.
 717       *
 718       * @param int $assignmentid     The assignment ID to update assignment submissions with marking/graded details.
 719       * @param int $submissionid     The assignment submission ID to update with marking/grading details.
 720       * @param int $teacher          The teacher user ID to making the marking/grading details.
 721       * @param int $gradedata        The grade value set for the marking/grading details.
 722       */
 723      protected function mark_assignment_submission($assignmentid, $submissionid, $teacher, $gradedata) {
 724          global $DB;
 725  
 726          $submission = (object) [
 727              'id' => $submissionid,
 728              'assignment' => $assignmentid,
 729              'grade' => $gradedata,
 730              'teacher' => $teacher->id,
 731              'timemarked' => date('U')
 732          ];
 733  
 734          return $DB->update_record('assignment_submissions', $submission);
 735      }
 736  
 737      /**
 738       * Helper function to retrieve the assignment records for a given context.
 739       *
 740       * @param int $contextid    The context module ID value to retrieve assignment IDs by.
 741       * @return array            Array of assignment IDs.
 742       * @throws dml_exception
 743       */
 744      protected function get_assignments($contextid) {
 745          global $DB;
 746  
 747          $sql = "SELECT a.id
 748                    FROM {assignment} a
 749                    JOIN {course_modules} cm ON a.id = cm.instance
 750                    JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
 751                    JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextmodule
 752                   WHERE ctx.id = :contextid";
 753          $params = [
 754              'modulename' => 'assignment',
 755              'contextmodule' => CONTEXT_MODULE,
 756              'contextid' => $contextid
 757          ];
 758  
 759          return $DB->get_records_sql($sql, $params);
 760      }
 761  
 762      /**
 763       * Helper function to retrieve the assignment submission records for a given context.
 764       *
 765       * @param int $contextid    The context module ID value to retrieve assignment submission IDs by.
 766       * @return array            Array of assignment submission IDs.
 767       * @throws dml_exception
 768       */
 769      protected function get_assignment_submissions($contextid) {
 770          global $DB;
 771  
 772          $sql = "SELECT s.id
 773                    FROM {assignment_submissions} s
 774                    JOIN {assignment} a ON a.id = s.assignment
 775                    JOIN {course_modules} cm ON a.id = cm.instance
 776                    JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
 777                    JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextmodule
 778                   WHERE ctx.id = :contextid";
 779          $params = [
 780              'modulename' => 'assignment',
 781              'contextmodule' => CONTEXT_MODULE,
 782              'contextid' => $contextid
 783          ];
 784  
 785          return $DB->get_records_sql($sql, $params);
 786      }
 787  }