Search moodle.org's
Developer Documentation

Long Term Support Release

  • Bug fixes for general core bugs in 3.5.x ended May 2019 (12 months).
  • Bug fixes for security issues in 3.5.x will end 10 May 2021 (36 months) - Support has ended.
  • minimum PHP 7.0.0 Note: minimum PHP version has increased since Moodle 3.3. PHP 7.1.x and 7.2.x are supported too. PHP 7.x could have some engine limitations.
  • Differences Between: [Versions 35 and 310] [Versions 35 and 311] [Versions 35 and 36] [Versions 35 and 37] [Versions 35 and 38] [Versions 35 and 39]

       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  defined('MOODLE_INTERNAL') || die();
      18  
      19  global $CFG;
      20  
      21  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
      22  require_once($CFG->dirroot . '/mod/assign/externallib.php');
      23  require_once (__DIR__ . '/fixtures/testable_assign.php');
      24  
      25  /**
      26   * External mod assign functions unit tests
      27   *
      28   * @package mod_assign
      29   * @category external
      30   * @copyright 2012 Paul Charsley
      31   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      32   */
      33  class mod_assign_external_testcase extends externallib_advanced_testcase {
      34  
      35      /**
      36       * Test get_grades
      37       */
      38      public function test_get_grades() {
      39          global $DB, $USER;
      40  
      41          $this->resetAfterTest(true);
      42          // Create a course and assignment.
      43          $coursedata['idnumber'] = 'idnumbercourse';
      44          $coursedata['fullname'] = 'Lightwork Course';
      45          $coursedata['summary'] = 'Lightwork Course description';
      46          $coursedata['summaryformat'] = FORMAT_MOODLE;
      47          $course = self::getDataGenerator()->create_course($coursedata);
      48  
      49          $assigndata['course'] = $course->id;
      50          $assigndata['name'] = 'lightwork assignment';
      51  
      52          $assign = self::getDataGenerator()->create_module('assign', $assigndata);
      53  
      54          // Create a manual enrolment record.
      55          $manualenroldata['enrol'] = 'manual';
      56          $manualenroldata['status'] = 0;
      57          $manualenroldata['courseid'] = $course->id;
      58          $enrolid = $DB->insert_record('enrol', $manualenroldata);
      59  
      60          // Create a teacher and give them capabilities.
      61          $context = context_course::instance($course->id);
      62          $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
      63          $context = context_module::instance($assign->cmid);
      64          $this->assignUserCapability('mod/assign:viewgrades', $context->id, $roleid);
      65  
      66          // Create the teacher's enrolment record.
      67          $userenrolmentdata['status'] = 0;
      68          $userenrolmentdata['enrolid'] = $enrolid;
      69          $userenrolmentdata['userid'] = $USER->id;
      70          $DB->insert_record('user_enrolments', $userenrolmentdata);
      71  
      72          // Create a student and give them 2 grades (for 2 attempts).
      73          $student = self::getDataGenerator()->create_user();
      74  
      75          $submission = new stdClass();
      76          $submission->assignment = $assign->id;
      77          $submission->userid = $student->id;
      78          $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
      79          $submission->latest = 0;
      80          $submission->attemptnumber = 0;
      81          $submission->groupid = 0;
      82          $submission->timecreated = time();
      83          $submission->timemodified = time();
      84          $DB->insert_record('assign_submission', $submission);
      85  
      86          $grade = new stdClass();
      87          $grade->assignment = $assign->id;
      88          $grade->userid = $student->id;
      89          $grade->timecreated = time();
      90          $grade->timemodified = $grade->timecreated;
      91          $grade->grader = $USER->id;
      92          $grade->grade = 50;
      93          $grade->attemptnumber = 0;
      94          $DB->insert_record('assign_grades', $grade);
      95  
      96          $submission = new stdClass();
      97          $submission->assignment = $assign->id;
      98          $submission->userid = $student->id;
      99          $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
     100          $submission->latest = 1;
     101          $submission->attemptnumber = 1;
     102          $submission->groupid = 0;
     103          $submission->timecreated = time();
     104          $submission->timemodified = time();
     105          $DB->insert_record('assign_submission', $submission);
     106  
     107          $grade = new stdClass();
     108          $grade->assignment = $assign->id;
     109          $grade->userid = $student->id;
     110          $grade->timecreated = time();
     111          $grade->timemodified = $grade->timecreated;
     112          $grade->grader = $USER->id;
     113          $grade->grade = 75;
     114          $grade->attemptnumber = 1;
     115          $DB->insert_record('assign_grades', $grade);
     116  
     117          $assignmentids[] = $assign->id;
     118          $result = mod_assign_external::get_grades($assignmentids);
     119  
     120          // We need to execute the return values cleaning process to simulate the web service server.
     121          $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
     122  
     123          // Check that the correct grade information for the student is returned.
     124          $this->assertEquals(1, count($result['assignments']));
     125          $assignment = $result['assignments'][0];
     126          $this->assertEquals($assign->id, $assignment['assignmentid']);
     127          // Should only get the last grade for this student.
     128          $this->assertEquals(1, count($assignment['grades']));
     129          $grade = $assignment['grades'][0];
     130          $this->assertEquals($student->id, $grade['userid']);
     131          // Should be the last grade (not the first).
     132          $this->assertEquals(75, $grade['grade']);
     133      }
     134  
     135      /**
     136       * Test get_assignments
     137       */
     138      public function test_get_assignments() {
     139          global $DB, $USER, $CFG;
     140  
     141          $this->resetAfterTest(true);
     142  
     143          $category = self::getDataGenerator()->create_category(array(
     144              'name' => 'Test category'
     145          ));
     146  
     147          // Create a course.
     148          $course1 = self::getDataGenerator()->create_course(array(
     149              'idnumber' => 'idnumbercourse1',
     150              'fullname' => '<b>Lightwork Course 1</b>',      // Adding tags here to check that external_format_string works.
     151              'shortname' => '<b>Lightwork Course 1</b>',     // Adding tags here to check that external_format_string works.
     152              'summary' => 'Lightwork Course 1 description',
     153              'summaryformat' => FORMAT_MOODLE,
     154              'category' => $category->id
     155          ));
     156  
     157          // Create a second course, just for testing.
     158          $course2 = self::getDataGenerator()->create_course(array(
     159              'idnumber' => 'idnumbercourse2',
     160              'fullname' => 'Lightwork Course 2',
     161              'summary' => 'Lightwork Course 2 description',
     162              'summaryformat' => FORMAT_MOODLE,
     163              'category' => $category->id
     164          ));
     165  
     166          // Create the assignment module with links to a filerecord.
     167          $assign1 = self::getDataGenerator()->create_module('assign', array(
     168              'course' => $course1->id,
     169              'name' => 'lightwork assignment',
     170              'intro' => 'the assignment intro text here <a href="@@PLUGINFILE@@/intro.txt">link</a>',
     171              'introformat' => FORMAT_HTML,
     172              'markingworkflow' => 1,
     173              'markingallocation' => 1
     174          ));
     175  
     176          // Add a file as assignment attachment.
     177          $context = context_module::instance($assign1->cmid);
     178          $filerecord = array('component' => 'mod_assign', 'filearea' => 'intro', 'contextid' => $context->id, 'itemid' => 0,
     179                  'filename' => 'intro.txt', 'filepath' => '/');
     180          $fs = get_file_storage();
     181          $fs->create_file_from_string($filerecord, 'Test intro file');
     182  
     183          // Create manual enrolment record.
     184          $enrolid = $DB->insert_record('enrol', (object)array(
     185              'enrol' => 'manual',
     186              'status' => 0,
     187              'courseid' => $course1->id
     188          ));
     189  
     190          // Create the user and give them capabilities.
     191          $context = context_course::instance($course1->id);
     192          $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
     193          $context = context_module::instance($assign1->cmid);
     194          $this->assignUserCapability('mod/assign:view', $context->id, $roleid);
     195  
     196          // Create the user enrolment record.
     197          $DB->insert_record('user_enrolments', (object)array(
     198              'status' => 0,
     199              'enrolid' => $enrolid,
     200              'userid' => $USER->id
     201          ));
     202  
     203          // Add a file as assignment attachment.
     204          $filerecord = array('component' => 'mod_assign', 'filearea' => ASSIGN_INTROATTACHMENT_FILEAREA,
     205                  'contextid' => $context->id, 'itemid' => 0,
     206                  'filename' => 'introattachment.txt', 'filepath' => '/');
     207          $fs = get_file_storage();
     208          $fs->create_file_from_string($filerecord, 'Test intro attachment file');
     209  
     210          $result = mod_assign_external::get_assignments();
     211  
     212          // We need to execute the return values cleaning process to simulate the web service server.
     213          $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
     214  
     215          // Check the course and assignment are returned.
     216          $this->assertEquals(1, count($result['courses']));
     217          $course = $result['courses'][0];
     218          $this->assertEquals('Lightwork Course 1', $course['fullname']);
     219          $this->assertEquals('Lightwork Course 1', $course['shortname']);
     220          $this->assertEquals(1, count($course['assignments']));
     221          $assignment = $course['assignments'][0];
     222          $this->assertEquals($assign1->id, $assignment['id']);
     223          $this->assertEquals($course1->id, $assignment['course']);
     224          $this->assertEquals('lightwork assignment', $assignment['name']);
     225          $this->assertContains('the assignment intro text here', $assignment['intro']);
     226          $this->assertNotEmpty($assignment['configs']);
     227          // Check the url of the file attatched.
     228          $this->assertRegExp('@"' . $CFG->wwwroot . '/webservice/pluginfile.php/\d+/mod_assign/intro/intro\.txt"@', $assignment['intro']);
     229          $this->assertEquals(1, $assignment['markingworkflow']);
     230          $this->assertEquals(1, $assignment['markingallocation']);
     231          $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
     232  
     233          $this->assertCount(1, $assignment['introattachments']);
     234          $this->assertEquals('introattachment.txt', $assignment['introattachments'][0]['filename']);
     235  
     236          // Now, hide the descritption until the submission from date.
     237          $DB->set_field('assign', 'alwaysshowdescription', 0, array('id' => $assign1->id));
     238          $DB->set_field('assign', 'allowsubmissionsfromdate', time() + DAYSECS, array('id' => $assign1->id));
     239  
     240          $result = mod_assign_external::get_assignments(array($course1->id));
     241  
     242          // We need to execute the return values cleaning process to simulate the web service server.
     243          $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
     244  
     245          $this->assertEquals(1, count($result['courses']));
     246          $course = $result['courses'][0];
     247          $this->assertEquals('Lightwork Course 1', $course['fullname']);
     248          $this->assertEquals(1, count($course['assignments']));
     249          $assignment = $course['assignments'][0];
     250          $this->assertEquals($assign1->id, $assignment['id']);
     251          $this->assertEquals($course1->id, $assignment['course']);
     252          $this->assertEquals('lightwork assignment', $assignment['name']);
     253          $this->assertArrayNotHasKey('intro', $assignment);
     254          $this->assertArrayNotHasKey('introattachments', $assignment);
     255          $this->assertEquals(1, $assignment['markingworkflow']);
     256          $this->assertEquals(1, $assignment['markingallocation']);
     257          $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
     258  
     259          $result = mod_assign_external::get_assignments(array($course2->id));
     260  
     261          // We need to execute the return values cleaning process to simulate the web service server.
     262          $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
     263  
     264          $this->assertEquals(0, count($result['courses']));
     265          $this->assertEquals(1, count($result['warnings']));
     266  
     267          // Test with non-enrolled user, but with view capabilities.
     268          $this->setAdminUser();
     269          $result = mod_assign_external::get_assignments();
     270          $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
     271          $this->assertEquals(0, count($result['courses']));
     272          $this->assertEquals(0, count($result['warnings']));
     273  
     274          // Expect no courses, because we are not using the special flag.
     275          $result = mod_assign_external::get_assignments(array($course1->id));
     276          $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
     277          $this->assertCount(0, $result['courses']);
     278  
     279          // Now use the special flag to return courses where you are not enroled in.
     280          $result = mod_assign_external::get_assignments(array($course1->id), array(), true);
     281          $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
     282          $this->assertCount(1, $result['courses']);
     283  
     284          $course = $result['courses'][0];
     285          $this->assertEquals('Lightwork Course 1', $course['fullname']);
     286          $this->assertEquals(1, count($course['assignments']));
     287          $assignment = $course['assignments'][0];
     288          $this->assertEquals($assign1->id, $assignment['id']);
     289          $this->assertEquals($course1->id, $assignment['course']);
     290          $this->assertEquals('lightwork assignment', $assignment['name']);
     291          $this->assertArrayNotHasKey('intro', $assignment);
     292          $this->assertArrayNotHasKey('introattachments', $assignment);
     293          $this->assertEquals(1, $assignment['markingworkflow']);
     294          $this->assertEquals(1, $assignment['markingallocation']);
     295          $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
     296      }
     297  
     298      /**
     299       * Test get_assignments with submissionstatement.
     300       */
     301      public function test_get_assignments_with_submissionstatement() {
     302          global $DB, $USER, $CFG;
     303  
     304          $this->resetAfterTest(true);
     305  
     306          // Setup test data. Create 2 assigns, one with requiresubmissionstatement and the other without it.
     307          $course = $this->getDataGenerator()->create_course();
     308          $assign = $this->getDataGenerator()->create_module('assign', array(
     309              'course' => $course->id,
     310              'requiresubmissionstatement' => 1
     311          ));
     312          $assign2 = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
     313  
     314          // Create student.
     315          $student = self::getDataGenerator()->create_user();
     316  
     317          // Users enrolments.
     318          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
     319          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
     320  
     321          // Update the submissionstatement.
     322          $submissionstatement = 'This is a fake submission statement.';
     323          set_config('submissionstatement', $submissionstatement, 'assign');
     324  
     325          $this->setUser($student);
     326  
     327          $result = mod_assign_external::get_assignments();
     328          // We need to execute the return values cleaning process to simulate the web service server.
     329          $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
     330  
     331          // Check that the amount of courses and assignments is right.
     332          $this->assertCount(1, $result['courses']);
     333          $assignmentsret = $result['courses'][0]['assignments'];
     334          $this->assertCount(2, $assignmentsret);
     335  
     336          // Order the returned assignments by ID.
     337          usort($assignmentsret, function($a, $b) {
     338              return strcmp($a['id'], $b['id']);
     339          });
     340  
     341          // Check that the first assign contains the submission statement.
     342          $assignmentret = $assignmentsret[0];
     343          $this->assertEquals($assign->id, $assignmentret['id']);
     344          $this->assertEquals(1, $assignmentret['requiresubmissionstatement']);
     345          $this->assertEquals($submissionstatement, $assignmentret['submissionstatement']);
     346  
     347          // Check that the second assign does NOT contain the submission statement.
     348          $assignmentret = $assignmentsret[1];
     349          $this->assertEquals($assign2->id, $assignmentret['id']);
     350          $this->assertEquals(0, $assignmentret['requiresubmissionstatement']);
     351          $this->assertArrayNotHasKey('submissionstatement', $assignmentret);
     352      }
     353  
     354      /**
     355       * Test get_submissions
     356       */
     357      public function test_get_submissions() {
     358          global $DB, $USER;
     359  
     360          $this->resetAfterTest(true);
     361          // Create a course and assignment.
     362          $coursedata['idnumber'] = 'idnumbercourse1';
     363          $coursedata['fullname'] = 'Lightwork Course 1';
     364          $coursedata['summary'] = 'Lightwork Course 1 description';
     365          $coursedata['summaryformat'] = FORMAT_MOODLE;
     366          $course1 = self::getDataGenerator()->create_course($coursedata);
     367  
     368          $assigndata['course'] = $course1->id;
     369          $assigndata['name'] = 'lightwork assignment';
     370  
     371          $assign1 = self::getDataGenerator()->create_module('assign', $assigndata);
     372  
     373          // Create a student with an online text submission.
     374          // First attempt.
     375          $student = self::getDataGenerator()->create_user();
     376          $teacher = self::getDataGenerator()->create_user();
     377          $submission = new stdClass();
     378          $submission->assignment = $assign1->id;
     379          $submission->userid = $student->id;
     380          $submission->timecreated = time();
     381          $submission->timemodified = $submission->timecreated;
     382          $submission->status = 'draft';
     383          $submission->attemptnumber = 0;
     384          $submission->latest = 0;
     385          $sid = $DB->insert_record('assign_submission', $submission);
     386  
     387          // Second attempt.
     388          $submission = new stdClass();
     389          $submission->assignment = $assign1->id;
     390          $submission->userid = $student->id;
     391          $submission->timecreated = time();
     392          $submission->timemodified = $submission->timecreated;
     393          $submission->status = 'submitted';
     394          $submission->attemptnumber = 1;
     395          $submission->latest = 1;
     396          $sid = $DB->insert_record('assign_submission', $submission);
     397          $submission->id = $sid;
     398  
     399          $onlinetextsubmission = new stdClass();
     400          $onlinetextsubmission->onlinetext = "<p>online test text</p>";
     401          $onlinetextsubmission->onlineformat = 1;
     402          $onlinetextsubmission->submission = $submission->id;
     403          $onlinetextsubmission->assignment = $assign1->id;
     404          $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
     405  
     406          // Enrol the teacher in the course.
     407          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
     408          $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id);
     409          $this->setUser($teacher);
     410  
     411          $assignmentids[] = $assign1->id;
     412          $result = mod_assign_external::get_submissions($assignmentids);
     413          $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
     414  
     415          // Check the online text submission is NOT returned because the student is not yet enrolled in the course.
     416          $this->assertEquals(1, count($result['assignments']));
     417          $assignment = $result['assignments'][0];
     418          $this->assertEquals($assign1->id, $assignment['assignmentid']);
     419          $this->assertEquals(0, count($assignment['submissions']));
     420  
     421          // Enrol the student in the course.
     422          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
     423          $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id);
     424  
     425          $result = mod_assign_external::get_submissions($assignmentids);
     426          $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
     427  
     428          $this->assertEquals(1, count($result['assignments']));
     429          $assignment = $result['assignments'][0];
     430          $this->assertEquals($assign1->id, $assignment['assignmentid']);
     431          // Now, we get the submission because the user is enrolled.
     432          $this->assertEquals(1, count($assignment['submissions']));
     433          $submission = $assignment['submissions'][0];
     434          $this->assertEquals($sid, $submission['id']);
     435          $this->assertCount(1, $submission['plugins']);
     436          $this->assertEquals('notgraded', $submission['gradingstatus']);
     437      }
     438  
     439      /**
     440       * Test get_user_flags
     441       */
     442      public function test_get_user_flags() {
     443          global $DB, $USER;
     444  
     445          $this->resetAfterTest(true);
     446          // Create a course and assignment.
     447          $coursedata['idnumber'] = 'idnumbercourse';
     448          $coursedata['fullname'] = 'Lightwork Course';
     449          $coursedata['summary'] = 'Lightwork Course description';
     450          $coursedata['summaryformat'] = FORMAT_MOODLE;
     451          $course = self::getDataGenerator()->create_course($coursedata);
     452  
     453          $assigndata['course'] = $course->id;
     454          $assigndata['name'] = 'lightwork assignment';
     455  
     456          $assign = self::getDataGenerator()->create_module('assign', $assigndata);
     457  
     458          // Create a manual enrolment record.
     459          $manualenroldata['enrol'] = 'manual';
     460          $manualenroldata['status'] = 0;
     461          $manualenroldata['courseid'] = $course->id;
     462          $enrolid = $DB->insert_record('enrol', $manualenroldata);
     463  
     464          // Create a teacher and give them capabilities.
     465          $context = context_course::instance($course->id);
     466          $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
     467          $context = context_module::instance($assign->cmid);
     468          $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
     469  
     470          // Create the teacher's enrolment record.
     471          $userenrolmentdata['status'] = 0;
     472          $userenrolmentdata['enrolid'] = $enrolid;
     473          $userenrolmentdata['userid'] = $USER->id;
     474          $DB->insert_record('user_enrolments', $userenrolmentdata);
     475  
     476          // Create a student and give them a user flag record.
     477          $student = self::getDataGenerator()->create_user();
     478          $userflag = new stdClass();
     479          $userflag->assignment = $assign->id;
     480          $userflag->userid = $student->id;
     481          $userflag->locked = 0;
     482          $userflag->mailed = 0;
     483          $userflag->extensionduedate = 0;
     484          $userflag->workflowstate = 'inmarking';
     485          $userflag->allocatedmarker = $USER->id;
     486  
     487          $DB->insert_record('assign_user_flags', $userflag);
     488  
     489          $assignmentids[] = $assign->id;
     490          $result = mod_assign_external::get_user_flags($assignmentids);
     491  
     492          // We need to execute the return values cleaning process to simulate the web service server.
     493          $result = external_api::clean_returnvalue(mod_assign_external::get_user_flags_returns(), $result);
     494  
     495          // Check that the correct user flag information for the student is returned.
     496          $this->assertEquals(1, count($result['assignments']));
     497          $assignment = $result['assignments'][0];
     498          $this->assertEquals($assign->id, $assignment['assignmentid']);
     499          // Should be one user flag record.
     500          $this->assertEquals(1, count($assignment['userflags']));
     501          $userflag = $assignment['userflags'][0];
     502          $this->assertEquals($student->id, $userflag['userid']);
     503          $this->assertEquals(0, $userflag['locked']);
     504          $this->assertEquals(0, $userflag['mailed']);
     505          $this->assertEquals(0, $userflag['extensionduedate']);
     506          $this->assertEquals('inmarking', $userflag['workflowstate']);
     507          $this->assertEquals($USER->id, $userflag['allocatedmarker']);
     508      }
     509  
     510      /**
     511       * Test get_user_mappings
     512       */
     513      public function test_get_user_mappings() {
     514          global $DB, $USER;
     515  
     516          $this->resetAfterTest(true);
     517          // Create a course and assignment.
     518          $coursedata['idnumber'] = 'idnumbercourse';
     519          $coursedata['fullname'] = 'Lightwork Course';
     520          $coursedata['summary'] = 'Lightwork Course description';
     521          $coursedata['summaryformat'] = FORMAT_MOODLE;
     522          $course = self::getDataGenerator()->create_course($coursedata);
     523  
     524          $assigndata['course'] = $course->id;
     525          $assigndata['name'] = 'lightwork assignment';
     526  
     527          $assign = self::getDataGenerator()->create_module('assign', $assigndata);
     528  
     529          // Create a manual enrolment record.
     530          $manualenroldata['enrol'] = 'manual';
     531          $manualenroldata['status'] = 0;
     532          $manualenroldata['courseid'] = $course->id;
     533          $enrolid = $DB->insert_record('enrol', $manualenroldata);
     534  
     535          // Create a teacher and give them capabilities.
     536          $context = context_course::instance($course->id);
     537          $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
     538          $context = context_module::instance($assign->cmid);
     539          $this->assignUserCapability('mod/assign:revealidentities', $context->id, $roleid);
     540  
     541          // Create the teacher's enrolment record.
     542          $userenrolmentdata['status'] = 0;
     543          $userenrolmentdata['enrolid'] = $enrolid;
     544          $userenrolmentdata['userid'] = $USER->id;
     545          $DB->insert_record('user_enrolments', $userenrolmentdata);
     546  
     547          // Create a student and give them a user mapping record.
     548          $student = self::getDataGenerator()->create_user();
     549          $mapping = new stdClass();
     550          $mapping->assignment = $assign->id;
     551          $mapping->userid = $student->id;
     552  
     553          $DB->insert_record('assign_user_mapping', $mapping);
     554  
     555          $assignmentids[] = $assign->id;
     556          $result = mod_assign_external::get_user_mappings($assignmentids);
     557  
     558          // We need to execute the return values cleaning process to simulate the web service server.
     559          $result = external_api::clean_returnvalue(mod_assign_external::get_user_mappings_returns(), $result);
     560  
     561          // Check that the correct user mapping information for the student is returned.
     562          $this->assertEquals(1, count($result['assignments']));
     563          $assignment = $result['assignments'][0];
     564          $this->assertEquals($assign->id, $assignment['assignmentid']);
     565          // Should be one user mapping record.
     566          $this->assertEquals(1, count($assignment['mappings']));
     567          $mapping = $assignment['mappings'][0];
     568          $this->assertEquals($student->id, $mapping['userid']);
     569      }
     570  
     571      /**
     572       * Test lock_submissions
     573       *
     574       * @expectedException moodle_exception
     575       */
     576      public function test_lock_submissions() {
     577          global $DB, $USER;
     578  
     579          $this->resetAfterTest(true);
     580          // Create a course and assignment and users.
     581          $course = self::getDataGenerator()->create_course();
     582  
     583          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     584          $params['course'] = $course->id;
     585          $params['assignsubmission_onlinetext_enabled'] = 1;
     586          $instance = $generator->create_instance($params);
     587          $cm = get_coursemodule_from_instance('assign', $instance->id);
     588          $context = context_module::instance($cm->id);
     589  
     590          $assign = new assign($context, $cm, $course);
     591  
     592          $student1 = self::getDataGenerator()->create_user();
     593          $student2 = self::getDataGenerator()->create_user();
     594          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
     595          $this->getDataGenerator()->enrol_user($student1->id,
     596                                                $course->id,
     597                                                $studentrole->id);
     598          $this->getDataGenerator()->enrol_user($student2->id,
     599                                                $course->id,
     600                                                $studentrole->id);
     601          $teacher = self::getDataGenerator()->create_user();
     602          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
     603          $this->getDataGenerator()->enrol_user($teacher->id,
     604                                                $course->id,
     605                                                $teacherrole->id);
     606  
     607          // Create a student1 with an online text submission.
     608          // Simulate a submission.
     609          $this->setUser($student1);
     610          $submission = $assign->get_user_submission($student1->id, true);
     611          $data = new stdClass();
     612          $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
     613                                           'text'=>'Submission text',
     614                                           'format'=>FORMAT_MOODLE);
     615          $plugin = $assign->get_submission_plugin_by_type('onlinetext');
     616          $plugin->save($submission, $data);
     617  
     618          // Ready to test.
     619          $this->setUser($teacher);
     620          $students = array($student1->id, $student2->id);
     621          $result = mod_assign_external::lock_submissions($instance->id, $students);
     622          $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
     623  
     624          // Check for 0 warnings.
     625          $this->assertEquals(0, count($result));
     626  
     627          $this->setUser($student2);
     628          $submission = $assign->get_user_submission($student2->id, true);
     629          $data = new stdClass();
     630          $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
     631                                           'text'=>'Submission text',
     632                                           'format'=>FORMAT_MOODLE);
     633          $notices = array();
     634          $assign->save_submission($data, $notices);
     635      }
     636  
     637      /**
     638       * Test unlock_submissions
     639       */
     640      public function test_unlock_submissions() {
     641          global $DB, $USER;
     642  
     643          $this->resetAfterTest(true);
     644          // Create a course and assignment and users.
     645          $course = self::getDataGenerator()->create_course();
     646  
     647          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     648          $params['course'] = $course->id;
     649          $params['assignsubmission_onlinetext_enabled'] = 1;
     650          $instance = $generator->create_instance($params);
     651          $cm = get_coursemodule_from_instance('assign', $instance->id);
     652          $context = context_module::instance($cm->id);
     653  
     654          $assign = new assign($context, $cm, $course);
     655  
     656          $student1 = self::getDataGenerator()->create_user();
     657          $student2 = self::getDataGenerator()->create_user();
     658          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
     659          $this->getDataGenerator()->enrol_user($student1->id,
     660                                                $course->id,
     661                                                $studentrole->id);
     662          $this->getDataGenerator()->enrol_user($student2->id,
     663                                                $course->id,
     664                                                $studentrole->id);
     665          $teacher = self::getDataGenerator()->create_user();
     666          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
     667          $this->getDataGenerator()->enrol_user($teacher->id,
     668                                                $course->id,
     669                                                $teacherrole->id);
     670  
     671          // Create a student1 with an online text submission.
     672          // Simulate a submission.
     673          $this->setUser($student1);
     674          $submission = $assign->get_user_submission($student1->id, true);
     675          $data = new stdClass();
     676          $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
     677                                           'text'=>'Submission text',
     678                                           'format'=>FORMAT_MOODLE);
     679          $plugin = $assign->get_submission_plugin_by_type('onlinetext');
     680          $plugin->save($submission, $data);
     681  
     682          // Ready to test.
     683          $this->setUser($teacher);
     684          $students = array($student1->id, $student2->id);
     685          $result = mod_assign_external::lock_submissions($instance->id, $students);
     686          $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
     687  
     688          // Check for 0 warnings.
     689          $this->assertEquals(0, count($result));
     690  
     691          $result = mod_assign_external::unlock_submissions($instance->id, $students);
     692          $result = external_api::clean_returnvalue(mod_assign_external::unlock_submissions_returns(), $result);
     693  
     694          // Check for 0 warnings.
     695          $this->assertEquals(0, count($result));
     696  
     697          $this->setUser($student2);
     698          $submission = $assign->get_user_submission($student2->id, true);
     699          $data = new stdClass();
     700          $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
     701                                           'text'=>'Submission text',
     702                                           'format'=>FORMAT_MOODLE);
     703          $notices = array();
     704          $assign->save_submission($data, $notices);
     705      }
     706  
     707      /**
     708       * Test submit_for_grading
     709       */
     710      public function test_submit_for_grading() {
     711          global $DB, $USER;
     712  
     713          $this->resetAfterTest(true);
     714          // Create a course and assignment and users.
     715          $course = self::getDataGenerator()->create_course();
     716  
     717          set_config('submissionreceipts', 0, 'assign');
     718          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     719          $params['course'] = $course->id;
     720          $params['assignsubmission_onlinetext_enabled'] = 1;
     721          $params['submissiondrafts'] = 1;
     722          $params['sendnotifications'] = 0;
     723          $params['requiresubmissionstatement'] = 1;
     724          $instance = $generator->create_instance($params);
     725          $cm = get_coursemodule_from_instance('assign', $instance->id);
     726          $context = context_module::instance($cm->id);
     727  
     728          $assign = new assign($context, $cm, $course);
     729  
     730          $student1 = self::getDataGenerator()->create_user();
     731          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
     732          $this->getDataGenerator()->enrol_user($student1->id,
     733                                                $course->id,
     734                                                $studentrole->id);
     735  
     736          // Create a student1 with an online text submission.
     737          // Simulate a submission.
     738          $this->setUser($student1);
     739          $submission = $assign->get_user_submission($student1->id, true);
     740          $data = new stdClass();
     741          $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
     742                                           'text'=>'Submission text',
     743                                           'format'=>FORMAT_MOODLE);
     744          $plugin = $assign->get_submission_plugin_by_type('onlinetext');
     745          $plugin->save($submission, $data);
     746  
     747          $result = mod_assign_external::submit_for_grading($instance->id, false);
     748          $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
     749  
     750          // Should be 1 fail because the submission statement was not aceptted.
     751          $this->assertEquals(1, count($result));
     752  
     753          $result = mod_assign_external::submit_for_grading($instance->id, true);
     754          $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
     755  
     756          // Check for 0 warnings.
     757          $this->assertEquals(0, count($result));
     758  
     759          $submission = $assign->get_user_submission($student1->id, false);
     760  
     761          $this->assertEquals(ASSIGN_SUBMISSION_STATUS_SUBMITTED, $submission->status);
     762      }
     763  
     764      /**
     765       * Test save_user_extensions
     766       */
     767      public function test_save_user_extensions() {
     768          global $DB, $USER;
     769  
     770          $this->resetAfterTest(true);
     771          // Create a course and assignment and users.
     772          $course = self::getDataGenerator()->create_course();
     773  
     774          $teacher = self::getDataGenerator()->create_user();
     775          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
     776          $this->getDataGenerator()->enrol_user($teacher->id,
     777                                                $course->id,
     778                                                $teacherrole->id);
     779          $this->setUser($teacher);
     780  
     781          $now = time();
     782          $yesterday = $now - 24*60*60;
     783          $tomorrow = $now + 24*60*60;
     784          set_config('submissionreceipts', 0, 'assign');
     785          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     786          $params['course'] = $course->id;
     787          $params['submissiondrafts'] = 1;
     788          $params['sendnotifications'] = 0;
     789          $params['duedate'] = $yesterday;
     790          $params['cutoffdate'] = $now - 10;
     791          $instance = $generator->create_instance($params);
     792          $cm = get_coursemodule_from_instance('assign', $instance->id);
     793          $context = context_module::instance($cm->id);
     794  
     795          $assign = new assign($context, $cm, $course);
     796  
     797          $student1 = self::getDataGenerator()->create_user();
     798          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
     799          $this->getDataGenerator()->enrol_user($student1->id,
     800                                                $course->id,
     801                                                $studentrole->id);
     802  
     803          $this->setUser($student1);
     804          $result = mod_assign_external::submit_for_grading($instance->id, true);
     805          $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
     806  
     807          // Check for 0 warnings.
     808          $this->assertEquals(1, count($result));
     809  
     810          $this->setUser($teacher);
     811          $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
     812          $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
     813          $this->assertEquals(1, count($result));
     814  
     815          $this->setUser($teacher);
     816          $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($yesterday - 10));
     817          $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
     818          $this->assertEquals(1, count($result));
     819  
     820          $this->setUser($teacher);
     821          $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($tomorrow));
     822          $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
     823          $this->assertEquals(0, count($result));
     824  
     825          $this->setUser($student1);
     826          $result = mod_assign_external::submit_for_grading($instance->id, true);
     827          $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
     828          $this->assertEquals(0, count($result));
     829  
     830          $this->setUser($student1);
     831          $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
     832          $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
     833  
     834      }
     835  
     836      /**
     837       * Test reveal_identities
     838       *
     839       * @expectedException required_capability_exception
     840       */
     841      public function test_reveal_identities() {
     842          global $DB, $USER;
     843  
     844          $this->resetAfterTest(true);
     845          // Create a course and assignment and users.
     846          $course = self::getDataGenerator()->create_course();
     847  
     848          $teacher = self::getDataGenerator()->create_user();
     849          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
     850          $this->getDataGenerator()->enrol_user($teacher->id,
     851                                                $course->id,
     852                                                $teacherrole->id);
     853          $this->setUser($teacher);
     854  
     855          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     856          $params['course'] = $course->id;
     857          $params['submissiondrafts'] = 1;
     858          $params['sendnotifications'] = 0;
     859          $params['blindmarking'] = 1;
     860          $instance = $generator->create_instance($params);
     861          $cm = get_coursemodule_from_instance('assign', $instance->id);
     862          $context = context_module::instance($cm->id);
     863  
     864          $assign = new assign($context, $cm, $course);
     865  
     866          $student1 = self::getDataGenerator()->create_user();
     867          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
     868          $this->getDataGenerator()->enrol_user($student1->id,
     869                                                $course->id,
     870                                                $studentrole->id);
     871  
     872          $this->setUser($student1);
     873          $result = mod_assign_external::reveal_identities($instance->id);
     874          $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
     875          $this->assertEquals(1, count($result));
     876          $this->assertEquals(true, $assign->is_blind_marking());
     877  
     878          $this->setUser($teacher);
     879          $result = mod_assign_external::reveal_identities($instance->id);
     880          $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
     881          $this->assertEquals(0, count($result));
     882          $this->assertEquals(false, $assign->is_blind_marking());
     883  
     884          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     885          $params['course'] = $course->id;
     886          $params['submissiondrafts'] = 1;
     887          $params['sendnotifications'] = 0;
     888          $params['blindmarking'] = 0;
     889          $instance = $generator->create_instance($params);
     890          $cm = get_coursemodule_from_instance('assign', $instance->id);
     891          $context = context_module::instance($cm->id);
     892  
     893          $assign = new assign($context, $cm, $course);
     894          $result = mod_assign_external::reveal_identities($instance->id);
     895          $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
     896          $this->assertEquals(1, count($result));
     897          $this->assertEquals(false, $assign->is_blind_marking());
     898  
     899      }
     900  
     901      /**
     902       * Test revert_submissions_to_draft
     903       */
     904      public function test_revert_submissions_to_draft() {
     905          global $DB, $USER;
     906  
     907          $this->resetAfterTest(true);
     908          set_config('submissionreceipts', 0, 'assign');
     909          // Create a course and assignment and users.
     910          $course = self::getDataGenerator()->create_course();
     911  
     912          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     913          $params['course'] = $course->id;
     914          $params['sendnotifications'] = 0;
     915          $params['submissiondrafts'] = 1;
     916          $instance = $generator->create_instance($params);
     917          $cm = get_coursemodule_from_instance('assign', $instance->id);
     918          $context = context_module::instance($cm->id);
     919  
     920          $assign = new assign($context, $cm, $course);
     921  
     922          $student1 = self::getDataGenerator()->create_user();
     923          $student2 = self::getDataGenerator()->create_user();
     924          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
     925          $this->getDataGenerator()->enrol_user($student1->id,
     926                                                $course->id,
     927                                                $studentrole->id);
     928          $this->getDataGenerator()->enrol_user($student2->id,
     929                                                $course->id,
     930                                                $studentrole->id);
     931          $teacher = self::getDataGenerator()->create_user();
     932          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
     933          $this->getDataGenerator()->enrol_user($teacher->id,
     934                                                $course->id,
     935                                                $teacherrole->id);
     936  
     937          // Create a student1 with an online text submission.
     938          // Simulate a submission.
     939          $this->setUser($student1);
     940          $result = mod_assign_external::submit_for_grading($instance->id, true);
     941          $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
     942          $this->assertEquals(0, count($result));
     943  
     944          // Ready to test.
     945          $this->setUser($teacher);
     946          $students = array($student1->id, $student2->id);
     947          $result = mod_assign_external::revert_submissions_to_draft($instance->id, array($student1->id));
     948          $result = external_api::clean_returnvalue(mod_assign_external::revert_submissions_to_draft_returns(), $result);
     949  
     950          // Check for 0 warnings.
     951          $this->assertEquals(0, count($result));
     952  
     953      }
     954  
     955      /**
     956       * Test save_submission
     957       */
     958      public function test_save_submission() {
     959          global $DB, $USER;
     960  
     961          $this->resetAfterTest(true);
     962          // Create a course and assignment and users.
     963          $course = self::getDataGenerator()->create_course();
     964  
     965          $teacher = self::getDataGenerator()->create_user();
     966          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
     967          $this->getDataGenerator()->enrol_user($teacher->id,
     968                                                $course->id,
     969                                                $teacherrole->id);
     970          $this->setUser($teacher);
     971  
     972          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     973          $params['course'] = $course->id;
     974          $params['assignsubmission_onlinetext_enabled'] = 1;
     975          $params['assignsubmission_file_enabled'] = 1;
     976          $params['assignsubmission_file_maxfiles'] = 5;
     977          $params['assignsubmission_file_maxsizebytes'] = 1024*1024;
     978          $instance = $generator->create_instance($params);
     979          $cm = get_coursemodule_from_instance('assign', $instance->id);
     980          $context = context_module::instance($cm->id);
     981  
     982          $assign = new assign($context, $cm, $course);
     983  
     984          $student1 = self::getDataGenerator()->create_user();
     985          $student2 = self::getDataGenerator()->create_user();
     986          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
     987          $this->getDataGenerator()->enrol_user($student1->id,
     988                                                $course->id,
     989                                                $studentrole->id);
     990          $this->getDataGenerator()->enrol_user($student2->id,
     991                                                $course->id,
     992                                                $studentrole->id);
     993          // Create a student1 with an online text submission.
     994          // Simulate a submission.
     995          $this->setUser($student1);
     996  
     997          // Create a file in a draft area.
     998          $draftidfile = file_get_unused_draft_itemid();
     999  
    1000          $usercontext = context_user::instance($student1->id);
    1001          $filerecord = array(
    1002              'contextid' => $usercontext->id,
    1003              'component' => 'user',
    1004              'filearea'  => 'draft',
    1005              'itemid'    => $draftidfile,
    1006              'filepath'  => '/',
    1007              'filename'  => 'testtext.txt',
    1008          );
    1009  
    1010          $fs = get_file_storage();
    1011          $fs->create_file_from_string($filerecord, 'text contents');
    1012  
    1013          // Create another file in a different draft area.
    1014          $draftidonlinetext = file_get_unused_draft_itemid();
    1015  
    1016          $filerecord = array(
    1017              'contextid' => $usercontext->id,
    1018              'component' => 'user',
    1019              'filearea'  => 'draft',
    1020              'itemid'    => $draftidonlinetext,
    1021              'filepath'  => '/',
    1022              'filename'  => 'shouldbeanimage.txt',
    1023          );
    1024  
    1025          $fs->create_file_from_string($filerecord, 'image contents (not really)');
    1026  
    1027          // Now try a submission.
    1028          $submissionpluginparams = array();
    1029          $submissionpluginparams['files_filemanager'] = $draftidfile;
    1030          $onlinetexteditorparams = array('text' => '<p>Yeeha!</p>',
    1031                                          'format'=>1,
    1032                                          'itemid'=>$draftidonlinetext);
    1033          $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
    1034          $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
    1035          $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
    1036  
    1037          $this->assertEquals(0, count($result));
    1038  
    1039          // Set up a due and cutoff passed date.
    1040          $instance->duedate = time() - WEEKSECS;
    1041          $instance->cutoffdate = time() - WEEKSECS;
    1042          $DB->update_record('assign', $instance);
    1043  
    1044          $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
    1045          $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
    1046  
    1047          $this->assertCount(1, $result);
    1048          $this->assertEquals(get_string('duedatereached', 'assign'), $result[0]['item']);
    1049      }
    1050  
    1051      /**
    1052       * Test save_grade
    1053       */
    1054      public function test_save_grade() {
    1055          global $DB, $USER;
    1056  
    1057          $this->resetAfterTest(true);
    1058          // Create a course and assignment and users.
    1059          $course = self::getDataGenerator()->create_course();
    1060  
    1061          $teacher = self::getDataGenerator()->create_user();
    1062          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
    1063          $this->getDataGenerator()->enrol_user($teacher->id,
    1064                                                $course->id,
    1065                                                $teacherrole->id);
    1066          $this->setUser($teacher);
    1067  
    1068          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1069          $params['course'] = $course->id;
    1070          $params['assignfeedback_file_enabled'] = 1;
    1071          $params['assignfeedback_comments_enabled'] = 1;
    1072          $instance = $generator->create_instance($params);
    1073          $cm = get_coursemodule_from_instance('assign', $instance->id);
    1074          $context = context_module::instance($cm->id);
    1075  
    1076          $assign = new assign($context, $cm, $course);
    1077  
    1078          $student1 = self::getDataGenerator()->create_user();
    1079          $student2 = self::getDataGenerator()->create_user();
    1080          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
    1081          $this->getDataGenerator()->enrol_user($student1->id,
    1082                                                $course->id,
    1083                                                $studentrole->id);
    1084          $this->getDataGenerator()->enrol_user($student2->id,
    1085                                                $course->id,
    1086                                                $studentrole->id);
    1087          // Simulate a grade.
    1088          $this->setUser($teacher);
    1089  
    1090          // Create a file in a draft area.
    1091          $draftidfile = file_get_unused_draft_itemid();
    1092  
    1093          $usercontext = context_user::instance($teacher->id);
    1094          $filerecord = array(
    1095              'contextid' => $usercontext->id,
    1096              'component' => 'user',
    1097              'filearea'  => 'draft',
    1098              'itemid'    => $draftidfile,
    1099              'filepath'  => '/',
    1100              'filename'  => 'testtext.txt',
    1101          );
    1102  
    1103          $fs = get_file_storage();
    1104          $fs->create_file_from_string($filerecord, 'text contents');
    1105  
    1106          // Now try a grade.
    1107          $feedbackpluginparams = array();
    1108          $feedbackpluginparams['files_filemanager'] = $draftidfile;
    1109          $feedbackeditorparams = array('text' => 'Yeeha!',
    1110                                          'format' => 1);
    1111          $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
    1112          $result = mod_assign_external::save_grade($instance->id,
    1113                                                    $student1->id,
    1114                                                    50.0,
    1115                                                    -1,
    1116                                                    true,
    1117                                                    'released',
    1118                                                    false,
    1119                                                    $feedbackpluginparams);
    1120          // No warnings.
    1121          $this->assertNull($result);
    1122  
    1123          $result = mod_assign_external::get_grades(array($instance->id));
    1124          $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
    1125  
    1126          $this->assertEquals($result['assignments'][0]['grades'][0]['grade'], '50.0');
    1127      }
    1128  
    1129      /**
    1130       * Test save grades with advanced grading data
    1131       */
    1132      public function test_save_grades_with_advanced_grading() {
    1133          global $DB, $USER;
    1134  
    1135          $this->resetAfterTest(true);
    1136          // Create a course and assignment and users.
    1137          $course = self::getDataGenerator()->create_course();
    1138  
    1139          $teacher = self::getDataGenerator()->create_user();
    1140          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    1141          $this->getDataGenerator()->enrol_user($teacher->id,
    1142                                                $course->id,
    1143                                                $teacherrole->id);
    1144  
    1145          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1146          $params['course'] = $course->id;
    1147          $params['assignfeedback_file_enabled'] = 0;
    1148          $params['assignfeedback_comments_enabled'] = 0;
    1149          $instance = $generator->create_instance($params);
    1150          $cm = get_coursemodule_from_instance('assign', $instance->id);
    1151          $context = context_module::instance($cm->id);
    1152  
    1153          $assign = new assign($context, $cm, $course);
    1154  
    1155          $student1 = self::getDataGenerator()->create_user();
    1156          $student2 = self::getDataGenerator()->create_user();
    1157          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
    1158          $this->getDataGenerator()->enrol_user($student1->id,
    1159                                                $course->id,
    1160                                                $studentrole->id);
    1161          $this->getDataGenerator()->enrol_user($student2->id,
    1162                                                $course->id,
    1163                                                $studentrole->id);
    1164  
    1165          $this->setUser($teacher);
    1166  
    1167          $feedbackpluginparams = array();
    1168          $feedbackpluginparams['files_filemanager'] = 0;
    1169          $feedbackeditorparams = array('text' => '', 'format' => 1);
    1170          $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
    1171  
    1172          // Create advanced grading data.
    1173          // Create grading area.
    1174          $gradingarea = array(
    1175              'contextid' => $context->id,
    1176              'component' => 'mod_assign',
    1177              'areaname' => 'submissions',
    1178              'activemethod' => 'rubric'
    1179          );
    1180          $areaid = $DB->insert_record('grading_areas', $gradingarea);
    1181  
    1182          // Create a rubric grading definition.
    1183          $rubricdefinition = array (
    1184              'areaid' => $areaid,
    1185              'method' => 'rubric',
    1186              'name' => 'test',
    1187              'status' => 20,
    1188              'copiedfromid' => 1,
    1189              'timecreated' => 1,
    1190              'usercreated' => $teacher->id,
    1191              'timemodified' => 1,
    1192              'usermodified' => $teacher->id,
    1193              'timecopied' => 0
    1194          );
    1195          $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
    1196  
    1197          // Create a criterion with a level.
    1198          $rubriccriteria = array (
    1199               'definitionid' => $definitionid,
    1200               'sortorder' => 1,
    1201               'description' => 'Demonstrate an understanding of disease control',
    1202               'descriptionformat' => 0
    1203          );
    1204          $criterionid = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria);
    1205          $rubriclevel1 = array (
    1206              'criterionid' => $criterionid,
    1207              'score' => 50,
    1208              'definition' => 'pass',
    1209              'definitionformat' => 0
    1210          );
    1211          $rubriclevel2 = array (
    1212              'criterionid' => $criterionid,
    1213              'score' => 100,
    1214              'definition' => 'excellent',
    1215              'definitionformat' => 0
    1216          );
    1217          $rubriclevel3 = array (
    1218              'criterionid' => $criterionid,
    1219              'score' => 0,
    1220              'definition' => 'fail',
    1221              'definitionformat' => 0
    1222          );
    1223          $levelid1 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
    1224          $levelid2 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
    1225          $levelid3 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel3);
    1226  
    1227          // Create the filling.
    1228          $student1filling = array (
    1229              'criterionid' => $criterionid,
    1230              'levelid' => $levelid1,
    1231              'remark' => 'well done you passed',
    1232              'remarkformat' => 0
    1233          );
    1234  
    1235          $student2filling = array (
    1236              'criterionid' => $criterionid,
    1237              'levelid' => $levelid2,
    1238              'remark' => 'Excellent work',
    1239              'remarkformat' => 0
    1240          );
    1241  
    1242          $student1criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student1filling)));
    1243          $student1advancedgradingdata = array('rubric' => array('criteria' => $student1criteria));
    1244  
    1245          $student2criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student2filling)));
    1246          $student2advancedgradingdata = array('rubric' => array('criteria' => $student2criteria));
    1247  
    1248          $grades = array();
    1249          $student1gradeinfo = array();
    1250          $student1gradeinfo['userid'] = $student1->id;
    1251          $student1gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
    1252          $student1gradeinfo['attemptnumber'] = -1;
    1253          $student1gradeinfo['addattempt'] = true;
    1254          $student1gradeinfo['workflowstate'] = 'released';
    1255          $student1gradeinfo['plugindata'] = $feedbackpluginparams;
    1256          $student1gradeinfo['advancedgradingdata'] = $student1advancedgradingdata;
    1257          $grades[] = $student1gradeinfo;
    1258  
    1259          $student2gradeinfo = array();
    1260          $student2gradeinfo['userid'] = $student2->id;
    1261          $student2gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
    1262          $student2gradeinfo['attemptnumber'] = -1;
    1263          $student2gradeinfo['addattempt'] = true;
    1264          $student2gradeinfo['workflowstate'] = 'released';
    1265          $student2gradeinfo['plugindata'] = $feedbackpluginparams;
    1266          $student2gradeinfo['advancedgradingdata'] = $student2advancedgradingdata;
    1267          $grades[] = $student2gradeinfo;
    1268  
    1269          $result = mod_assign_external::save_grades($instance->id, false, $grades);
    1270          $this->assertNull($result);
    1271  
    1272          $student1grade = $DB->get_record('assign_grades',
    1273                                           array('userid' => $student1->id, 'assignment' => $instance->id),
    1274                                           '*',
    1275                                           MUST_EXIST);
    1276          $this->assertEquals($student1grade->grade, '50.0');
    1277  
    1278          $student2grade = $DB->get_record('assign_grades',
    1279                                           array('userid' => $student2->id, 'assignment' => $instance->id),
    1280                                           '*',
    1281                                           MUST_EXIST);
    1282          $this->assertEquals($student2grade->grade, '100.0');
    1283      }
    1284  
    1285      /**
    1286       * Test save grades for a team submission
    1287       *
    1288       * @expectedException invalid_parameter_exception
    1289       */
    1290      public function test_save_grades_with_group_submission() {
    1291          global $DB, $USER, $CFG;
    1292          require_once($CFG->dirroot . '/group/lib.php');
    1293  
    1294          $this->resetAfterTest(true);
    1295          // Create a course and assignment and users.
    1296          $course = self::getDataGenerator()->create_course();
    1297  
    1298          $teacher = self::getDataGenerator()->create_user();
    1299          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    1300          $this->getDataGenerator()->enrol_user($teacher->id,
    1301                                                $course->id,
    1302                                                $teacherrole->id);
    1303  
    1304          $groupingdata = array();
    1305          $groupingdata['courseid'] = $course->id;
    1306          $groupingdata['name'] = 'Group assignment grouping';
    1307  
    1308          $grouping = self::getDataGenerator()->create_grouping($groupingdata);
    1309  
    1310          $group1data = array();
    1311          $group1data['courseid'] = $course->id;
    1312          $group1data['name'] = 'Team 1';
    1313          $group2data = array();
    1314          $group2data['courseid'] = $course->id;
    1315          $group2data['name'] = 'Team 2';
    1316  
    1317          $group1 = self::getDataGenerator()->create_group($group1data);
    1318          $group2 = self::getDataGenerator()->create_group($group2data);
    1319  
    1320          groups_assign_grouping($grouping->id, $group1->id);
    1321          groups_assign_grouping($grouping->id, $group2->id);
    1322  
    1323          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1324          $params['course'] = $course->id;
    1325          $params['teamsubmission'] = 1;
    1326          $params['teamsubmissiongroupingid'] = $grouping->id;
    1327          $instance = $generator->create_instance($params);
    1328          $cm = get_coursemodule_from_instance('assign', $instance->id);
    1329          $context = context_module::instance($cm->id);
    1330  
    1331          $assign = new assign($context, $cm, $course);
    1332  
    1333          $student1 = self::getDataGenerator()->create_user();
    1334          $student2 = self::getDataGenerator()->create_user();
    1335          $student3 = self::getDataGenerator()->create_user();
    1336          $student4 = self::getDataGenerator()->create_user();
    1337          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
    1338          $this->getDataGenerator()->enrol_user($student1->id,
    1339                                                $course->id,
    1340                                                $studentrole->id);
    1341          $this->getDataGenerator()->enrol_user($student2->id,
    1342                                                $course->id,
    1343                                                $studentrole->id);
    1344          $this->getDataGenerator()->enrol_user($student3->id,
    1345                                                $course->id,
    1346                                                $studentrole->id);
    1347          $this->getDataGenerator()->enrol_user($student4->id,
    1348                                                $course->id,
    1349                                                $studentrole->id);
    1350  
    1351          groups_add_member($group1->id, $student1->id);
    1352          groups_add_member($group1->id, $student2->id);
    1353          groups_add_member($group1->id, $student3->id);
    1354          groups_add_member($group2->id, $student4->id);
    1355          $this->setUser($teacher);
    1356  
    1357          $feedbackpluginparams = array();
    1358          $feedbackpluginparams['files_filemanager'] = 0;
    1359          $feedbackeditorparams = array('text' => '', 'format' => 1);
    1360          $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
    1361  
    1362          $grades1 = array();
    1363          $student1gradeinfo = array();
    1364          $student1gradeinfo['userid'] = $student1->id;
    1365          $student1gradeinfo['grade'] = 50;
    1366          $student1gradeinfo['attemptnumber'] = -1;
    1367          $student1gradeinfo['addattempt'] = true;
    1368          $student1gradeinfo['workflowstate'] = 'released';
    1369          $student1gradeinfo['plugindata'] = $feedbackpluginparams;
    1370          $grades1[] = $student1gradeinfo;
    1371  
    1372          $student2gradeinfo = array();
    1373          $student2gradeinfo['userid'] = $student2->id;
    1374          $student2gradeinfo['grade'] = 75;
    1375          $student2gradeinfo['attemptnumber'] = -1;
    1376          $student2gradeinfo['addattempt'] = true;
    1377          $student2gradeinfo['workflowstate'] = 'released';
    1378          $student2gradeinfo['plugindata'] = $feedbackpluginparams;
    1379          $grades1[] = $student2gradeinfo;
    1380  
    1381          // Expect an exception since 2 grades have been submitted for the same team.
    1382          $result = mod_assign_external::save_grades($instance->id, true, $grades1);
    1383          $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
    1384  
    1385          $grades2 = array();
    1386          $student3gradeinfo = array();
    1387          $student3gradeinfo['userid'] = $student3->id;
    1388          $student3gradeinfo['grade'] = 50;
    1389          $student3gradeinfo['attemptnumber'] = -1;
    1390          $student3gradeinfo['addattempt'] = true;
    1391          $student3gradeinfo['workflowstate'] = 'released';
    1392          $student3gradeinfo['plugindata'] = $feedbackpluginparams;
    1393          $grades2[] = $student3gradeinfo;
    1394  
    1395          $student4gradeinfo = array();
    1396          $student4gradeinfo['userid'] = $student4->id;
    1397          $student4gradeinfo['grade'] = 75;
    1398          $student4gradeinfo['attemptnumber'] = -1;
    1399          $student4gradeinfo['addattempt'] = true;
    1400          $student4gradeinfo['workflowstate'] = 'released';
    1401          $student4gradeinfo['plugindata'] = $feedbackpluginparams;
    1402          $grades2[] = $student4gradeinfo;
    1403          $result = mod_assign_external::save_grades($instance->id, true, $grades2);
    1404          $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
    1405          // There should be no warnings.
    1406          $this->assertEquals(0, count($result));
    1407  
    1408          $student3grade = $DB->get_record('assign_grades',
    1409                                           array('userid' => $student3->id, 'assignment' => $instance->id),
    1410                                           '*',
    1411                                           MUST_EXIST);
    1412          $this->assertEquals($student3grade->grade, '50.0');
    1413  
    1414          $student4grade = $DB->get_record('assign_grades',
    1415                                           array('userid' => $student4->id, 'assignment' => $instance->id),
    1416                                           '*',
    1417                                           MUST_EXIST);
    1418          $this->assertEquals($student4grade->grade, '75.0');
    1419      }
    1420  
    1421      /**
    1422       * Test copy_previous_attempt
    1423       */
    1424      public function test_copy_previous_attempt() {
    1425          global $DB, $USER;
    1426  
    1427          $this->resetAfterTest(true);
    1428          // Create a course and assignment and users.
    1429          $course = self::getDataGenerator()->create_course();
    1430  
    1431          $teacher = self::getDataGenerator()->create_user();
    1432          $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
    1433          $this->getDataGenerator()->enrol_user($teacher->id,
    1434                                                $course->id,
    1435                                                $teacherrole->id);
    1436          $this->setUser($teacher);
    1437  
    1438          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1439          $params['course'] = $course->id;
    1440          $params['assignsubmission_onlinetext_enabled'] = 1;
    1441          $params['assignsubmission_file_enabled'] = 0;
    1442          $params['assignfeedback_file_enabled'] = 0;
    1443          $params['attemptreopenmethod'] = 'manual';
    1444          $params['maxattempts'] = 5;
    1445          $instance = $generator->create_instance($params);
    1446          $cm = get_coursemodule_from_instance('assign', $instance->id);
    1447          $context = context_module::instance($cm->id);
    1448  
    1449          $assign = new assign($context, $cm, $course);
    1450  
    1451          $student1 = self::getDataGenerator()->create_user();
    1452          $studentrole = $DB->get_record('role', array('shortname'=>'student'));
    1453          $this->getDataGenerator()->enrol_user($student1->id,
    1454                                                $course->id,
    1455                                                $studentrole->id);
    1456          // Now try a submission.
    1457          $this->setUser($student1);
    1458          $draftidonlinetext = file_get_unused_draft_itemid();
    1459          $submissionpluginparams = array();
    1460          $onlinetexteditorparams = array('text'=>'Yeeha!',
    1461                                          'format'=>1,
    1462                                          'itemid'=>$draftidonlinetext);
    1463          $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
    1464          $submissionpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
    1465          $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
    1466          $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
    1467  
    1468          $this->setUser($teacher);
    1469          // Add a grade and reopen the attempt.
    1470          // Now try a grade.
    1471          $feedbackpluginparams = array();
    1472          $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
    1473          $feedbackeditorparams = array('text'=>'Yeeha!',
    1474                                          'format'=>1);
    1475          $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
    1476          $result = mod_assign_external::save_grade($instance->id,
    1477                                                    $student1->id,
    1478                                                    50.0,
    1479                                                    -1,
    1480                                                    true,
    1481                                                    'released',
    1482                                                    false,
    1483                                                    $feedbackpluginparams);
    1484          $this->assertNull($result);
    1485  
    1486          $this->setUser($student1);
    1487          // Now copy the previous attempt.
    1488          $result = mod_assign_external::copy_previous_attempt($instance->id);
    1489          $result = external_api::clean_returnvalue(mod_assign_external::copy_previous_attempt_returns(), $result);
    1490          // No warnings.
    1491          $this->assertEquals(0, count($result));
    1492  
    1493          $this->setUser($teacher);
    1494          $result = mod_assign_external::get_submissions(array($instance->id));
    1495          $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
    1496  
    1497          // Check we are now on the second attempt.
    1498          $this->assertEquals($result['assignments'][0]['submissions'][0]['attemptnumber'], 1);
    1499          // Check the plugins data is not empty.
    1500          $this->assertNotEmpty($result['assignments'][0]['submissions'][0]['plugins']);
    1501  
    1502      }
    1503  
    1504      /**
    1505       * Test set_user_flags
    1506       */
    1507      public function test_set_user_flags() {
    1508          global $DB, $USER;
    1509  
    1510          $this->resetAfterTest(true);
    1511          // Create a course and assignment.
    1512          $coursedata['idnumber'] = 'idnumbercourse';
    1513          $coursedata['fullname'] = 'Lightwork Course';
    1514          $coursedata['summary'] = 'Lightwork Course description';
    1515          $coursedata['summaryformat'] = FORMAT_MOODLE;
    1516          $course = self::getDataGenerator()->create_course($coursedata);
    1517  
    1518          $assigndata['course'] = $course->id;
    1519          $assigndata['name'] = 'lightwork assignment';
    1520  
    1521          $assign = self::getDataGenerator()->create_module('assign', $assigndata);
    1522  
    1523          // Create a manual enrolment record.
    1524          $manualenroldata['enrol'] = 'manual';
    1525          $manualenroldata['status'] = 0;
    1526          $manualenroldata['courseid'] = $course->id;
    1527          $enrolid = $DB->insert_record('enrol', $manualenroldata);
    1528  
    1529          // Create a teacher and give them capabilities.
    1530          $context = context_course::instance($course->id);
    1531          $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
    1532          $context = context_module::instance($assign->cmid);
    1533          $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
    1534  
    1535          // Create the teacher's enrolment record.
    1536          $userenrolmentdata['status'] = 0;
    1537          $userenrolmentdata['enrolid'] = $enrolid;
    1538          $userenrolmentdata['userid'] = $USER->id;
    1539          $DB->insert_record('user_enrolments', $userenrolmentdata);
    1540  
    1541          // Create a student.
    1542          $student = self::getDataGenerator()->create_user();
    1543  
    1544          // Create test user flags record.
    1545          $userflags = array();
    1546          $userflag['userid'] = $student->id;
    1547          $userflag['workflowstate'] = 'inmarking';
    1548          $userflag['allocatedmarker'] = $USER->id;
    1549          $userflags = array($userflag);
    1550  
    1551          $createduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
    1552          // We need to execute the return values cleaning process to simulate the web service server.
    1553          $createduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $createduserflags);
    1554  
    1555          $this->assertEquals($student->id, $createduserflags[0]['userid']);
    1556          $createduserflag = $DB->get_record('assign_user_flags', array('id' => $createduserflags[0]['id']));
    1557  
    1558          // Confirm that all data was inserted correctly.
    1559          $this->assertEquals($student->id,  $createduserflag->userid);
    1560          $this->assertEquals($assign->id, $createduserflag->assignment);
    1561          $this->assertEquals(0, $createduserflag->locked);
    1562          $this->assertEquals(2, $createduserflag->mailed);
    1563          $this->assertEquals(0, $createduserflag->extensionduedate);
    1564          $this->assertEquals('inmarking', $createduserflag->workflowstate);
    1565          $this->assertEquals($USER->id, $createduserflag->allocatedmarker);
    1566  
    1567          // Create update data.
    1568          $userflags = array();
    1569          $userflag['userid'] = $createduserflag->userid;
    1570          $userflag['workflowstate'] = 'readyforreview';
    1571          $userflags = array($userflag);
    1572  
    1573          $updateduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
    1574          // We need to execute the return values cleaning process to simulate the web service server.
    1575          $updateduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $updateduserflags);
    1576  
    1577          $this->assertEquals($student->id, $updateduserflags[0]['userid']);
    1578          $updateduserflag = $DB->get_record('assign_user_flags', array('id' => $updateduserflags[0]['id']));
    1579  
    1580          // Confirm that all data was updated correctly.
    1581          $this->assertEquals($student->id,  $updateduserflag->userid);
    1582          $this->assertEquals($assign->id, $updateduserflag->assignment);
    1583          $this->assertEquals(0, $updateduserflag->locked);
    1584          $this->assertEquals(2, $updateduserflag->mailed);
    1585          $this->assertEquals(0, $updateduserflag->extensionduedate);
    1586          $this->assertEquals('readyforreview', $updateduserflag->workflowstate);
    1587          $this->assertEquals($USER->id, $updateduserflag->allocatedmarker);
    1588      }
    1589  
    1590      /**
    1591       * Test view_grading_table
    1592       *
    1593       * @expectedException dml_missing_record_exception
    1594       */
    1595      public function test_view_grading_table_invalid_instance() {
    1596          global $DB;
    1597  
    1598          $this->resetAfterTest(true);
    1599  
    1600          // Setup test data.
    1601          $course = $this->getDataGenerator()->create_course();
    1602          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
    1603          $context = context_module::instance($assign->cmid);
    1604          $cm = get_coursemodule_from_instance('assign', $assign->id);
    1605  
    1606          // Test invalid instance id.
    1607          mod_assign_external::view_grading_table(0);
    1608      }
    1609  
    1610      /**
    1611       * Test view_grading_table
    1612       *
    1613       * @expectedException require_login_exception
    1614       */
    1615      public function test_view_grading_table_not_enrolled() {
    1616          global $DB;
    1617  
    1618          $this->resetAfterTest(true);
    1619  
    1620          // Setup test data.
    1621          $course = $this->getDataGenerator()->create_course();
    1622          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
    1623          $context = context_module::instance($assign->cmid);
    1624          $cm = get_coursemodule_from_instance('assign', $assign->id);
    1625  
    1626          // Test not-enrolled user.
    1627          $user = self::getDataGenerator()->create_user();
    1628          $this->setUser($user);
    1629  
    1630          mod_assign_external::view_grading_table($assign->id);
    1631      }
    1632  
    1633      /**
    1634       * Test view_grading_table
    1635       */
    1636      public function test_view_grading_table_correct() {
    1637          global $DB;
    1638  
    1639          $this->resetAfterTest(true);
    1640  
    1641          // Setup test data.
    1642          $course = $this->getDataGenerator()->create_course();
    1643          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
    1644          $context = context_module::instance($assign->cmid);
    1645          $cm = get_coursemodule_from_instance('assign', $assign->id);
    1646  
    1647          // Test user with full capabilities.
    1648          $user = self::getDataGenerator()->create_user();
    1649          $this->setUser($user);
    1650          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    1651          $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
    1652  
    1653          // Trigger and capture the event.
    1654          $sink = $this->redirectEvents();
    1655  
    1656          $result = mod_assign_external::view_grading_table($assign->id);
    1657          $result = external_api::clean_returnvalue(mod_assign_external::view_grading_table_returns(), $result);
    1658  
    1659          $events = $sink->get_events();
    1660          $this->assertCount(1, $events);
    1661          $event = array_shift($events);
    1662  
    1663          // Checking that the event contains the expected values.
    1664          $this->assertInstanceOf('\mod_assign\event\grading_table_viewed', $event);
    1665          $this->assertEquals($context, $event->get_context());
    1666          $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
    1667          $this->assertEquals($moodleurl, $event->get_url());
    1668          $this->assertEventContextNotUsed($event);
    1669          $this->assertNotEmpty($event->get_name());
    1670      }
    1671  
    1672      /**
    1673       * Test view_grading_table
    1674       *
    1675       * @expectedException        require_login_exception
    1676       * @expectedExceptionMessage Course or activity not accessible. (Activity is hidden)
    1677       */
    1678      public function test_view_grading_table_without_capability() {
    1679          global $DB;
    1680  
    1681          $this->resetAfterTest(true);
    1682  
    1683          // Setup test data.
    1684          $course = $this->getDataGenerator()->create_course();
    1685          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
    1686          $context = context_module::instance($assign->cmid);
    1687          $cm = get_coursemodule_from_instance('assign', $assign->id);
    1688  
    1689          // Test user with no capabilities.
    1690          $user = self::getDataGenerator()->create_user();
    1691          $this->setUser($user);
    1692          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    1693          $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
    1694  
    1695          // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
    1696          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    1697          assign_capability('mod/assign:view', CAP_PROHIBIT, $teacherrole->id, $context->id);
    1698          // Empty all the caches that may be affected by this change.
    1699          accesslib_clear_all_caches_for_unit_testing();
    1700          course_modinfo::clear_instance_cache();
    1701  
    1702          mod_assign_external::view_grading_table($assign->id);
    1703      }
    1704  
    1705      /**
    1706       * Test subplugins availability
    1707       */
    1708      public function test_subplugins_availability() {
    1709          global $CFG;
    1710  
    1711          require_once($CFG->dirroot . '/mod/assign/adminlib.php');
    1712          $this->resetAfterTest(true);
    1713  
    1714          // Hide assignment file submissiong plugin.
    1715          $pluginmanager = new assign_plugin_manager('assignsubmission');
    1716          $pluginmanager->hide_plugin('file');
    1717          $parameters = mod_assign_external::save_submission_parameters();
    1718  
    1719          $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
    1720  
    1721          // Show it again and check that the value is returned as optional.
    1722          $pluginmanager->show_plugin('file');
    1723          $parameters = mod_assign_external::save_submission_parameters();
    1724          $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
    1725          $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
    1726  
    1727          // Hide feedback file submissiong plugin.
    1728          $pluginmanager = new assign_plugin_manager('assignfeedback');
    1729          $pluginmanager->hide_plugin('file');
    1730  
    1731          $parameters = mod_assign_external::save_grade_parameters();
    1732  
    1733          $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
    1734  
    1735          // Show it again and check that the value is returned as optional.
    1736          $pluginmanager->show_plugin('file');
    1737          $parameters = mod_assign_external::save_grade_parameters();
    1738  
    1739          $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
    1740          $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
    1741  
    1742          // Check a different one.
    1743          $pluginmanager->show_plugin('comments');
    1744          $this->assertTrue(isset($parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']));
    1745          $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']->required);
    1746      }
    1747  
    1748      /**
    1749       * Test test_view_submission_status
    1750       */
    1751      public function test_view_submission_status() {
    1752          global $DB;
    1753  
    1754          $this->resetAfterTest(true);
    1755  
    1756          $this->setAdminUser();
    1757          // Setup test data.
    1758          $course = $this->getDataGenerator()->create_course();
    1759          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
    1760          $context = context_module::instance($assign->cmid);
    1761          $cm = get_coursemodule_from_instance('assign', $assign->id);
    1762  
    1763          // Test invalid instance id.
    1764          try {
    1765              mod_assign_external::view_submission_status(0);
    1766              $this->fail('Exception expected due to invalid mod_assign instance id.');
    1767          } catch (moodle_exception $e) {
    1768              $this->assertEquals('invalidrecord', $e->errorcode);
    1769          }
    1770  
    1771          // Test not-enrolled user.
    1772          $user = self::getDataGenerator()->create_user();
    1773          $this->setUser($user);
    1774          try {
    1775              mod_assign_external::view_submission_status($assign->id);
    1776              $this->fail('Exception expected due to not enrolled user.');
    1777          } catch (moodle_exception $e) {
    1778              $this->assertEquals('requireloginerror', $e->errorcode);
    1779          }
    1780  
    1781          // Test user with full capabilities.
    1782          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
    1783          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
    1784  
    1785          // Trigger and capture the event.
    1786          $sink = $this->redirectEvents();
    1787  
    1788          $result = mod_assign_external::view_submission_status($assign->id);
    1789          $result = external_api::clean_returnvalue(mod_assign_external::view_submission_status_returns(), $result);
    1790  
    1791          $events = $sink->get_events();
    1792          $this->assertCount(1, $events);
    1793          $event = array_shift($events);
    1794  
    1795          // Checking that the event contains the expected values.
    1796          $this->assertInstanceOf('\mod_assign\event\submission_status_viewed', $event);
    1797          $this->assertEquals($context, $event->get_context());
    1798          $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
    1799          $this->assertEquals($moodleurl, $event->get_url());
    1800          $this->assertEventContextNotUsed($event);
    1801          $this->assertNotEmpty($event->get_name());
    1802  
    1803          // Test user with no capabilities.
    1804          // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
    1805          assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id);
    1806          accesslib_clear_all_caches_for_unit_testing();
    1807          course_modinfo::clear_instance_cache();
    1808  
    1809          try {
    1810              mod_assign_external::view_submission_status($assign->id);
    1811              $this->fail('Exception expected due to missing capability.');
    1812          } catch (moodle_exception $e) {
    1813              $this->assertEquals('requireloginerror', $e->errorcode);
    1814          }
    1815      }
    1816  
    1817      /**
    1818       * Create a submission for testing the get_submission_status function.
    1819       * @param  boolean $submitforgrading whether to submit for grading the submission
    1820       * @return array an array containing all the required data for testing
    1821       */
    1822      private function create_submission_for_testing_status($submitforgrading = false) {
    1823          global $DB;
    1824  
    1825          // Create a course and assignment and users.
    1826          $course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1));
    1827  
    1828          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
    1829          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
    1830  
    1831          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1832          $params = array(
    1833              'course' => $course->id,
    1834              'assignsubmission_file_maxfiles' => 1,
    1835              'assignsubmission_file_maxsizebytes' => 1024 * 1024,
    1836              'assignsubmission_onlinetext_enabled' => 1,
    1837              'assignsubmission_file_enabled' => 1,
    1838              'submissiondrafts' => 1,
    1839              'assignfeedback_file_enabled' => 1,
    1840              'assignfeedback_comments_enabled' => 1,
    1841              'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
    1842              'sendnotifications' => 0
    1843          );
    1844  
    1845          set_config('submissionreceipts', 0, 'assign');
    1846  
    1847          $instance = $generator->create_instance($params);
    1848          $cm = get_coursemodule_from_instance('assign', $instance->id);
    1849          $context = context_module::instance($cm->id);
    1850  
    1851          $assign = new mod_assign_testable_assign($context, $cm, $course);
    1852  
    1853          $student1 = self::getDataGenerator()->create_user();
    1854          $student2 = self::getDataGenerator()->create_user();
    1855          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
    1856          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
    1857          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
    1858          $teacher = self::getDataGenerator()->create_user();
    1859          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    1860          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
    1861  
    1862          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student1->id));
    1863          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $teacher->id));
    1864          $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $student2->id));
    1865          $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $teacher->id));
    1866  
    1867          $this->setUser($student1);
    1868  
    1869          // Create a student1 with an online text submission.
    1870          // Simulate a submission.
    1871          $submission = $assign->get_user_submission($student1->id, true);
    1872  
    1873          $data = new stdClass();
    1874          $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
    1875                                           'text' => 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>',
    1876                                           'format' => FORMAT_MOODLE);
    1877  
    1878          $draftidfile = file_get_unused_draft_itemid();
    1879          $usercontext = context_user::instance($student1->id);
    1880          $filerecord = array(
    1881              'contextid' => $usercontext->id,
    1882              'component' => 'user',
    1883              'filearea'  => 'draft',
    1884              'itemid'    => $draftidfile,
    1885              'filepath'  => '/',
    1886              'filename'  => 't.txt',
    1887          );
    1888          $fs = get_file_storage();
    1889          $fs->create_file_from_string($filerecord, 'text contents');
    1890  
    1891          $data->files_filemanager = $draftidfile;
    1892  
    1893          $notices = array();
    1894          $assign->save_submission($data, $notices);
    1895  
    1896          if ($submitforgrading) {
    1897              // Now, submit the draft for grading.
    1898              $notices = array();
    1899  
    1900              $data = new stdClass;
    1901              $data->userid = $student1->id;
    1902              $assign->submit_for_grading($data, $notices);
    1903          }
    1904  
    1905          return array($assign, $instance, $student1, $student2, $teacher, $group1, $group2);
    1906      }
    1907  
    1908      /**
    1909       * Test get_submission_status for a draft submission.
    1910       */
    1911      public function test_get_submission_status_in_draft_status() {
    1912          $this->resetAfterTest(true);
    1913  
    1914          list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status();
    1915          $studentsubmission = $assign->get_user_submission($student1->id, true);
    1916  
    1917          $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
    1918          // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
    1919          $this->assertDebuggingCalled();
    1920  
    1921          $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
    1922  
    1923          // The submission is now in draft mode.
    1924          $this->assertCount(0, $result['warnings']);
    1925          $this->assertFalse(isset($result['gradingsummary']));
    1926          $this->assertFalse(isset($result['feedback']));
    1927          $this->assertFalse(isset($result['previousattempts']));
    1928  
    1929          $this->assertTrue($result['lastattempt']['submissionsenabled']);
    1930          $this->assertTrue($result['lastattempt']['canedit']);
    1931          $this->assertTrue($result['lastattempt']['cansubmit']);
    1932          $this->assertFalse($result['lastattempt']['locked']);
    1933          $this->assertFalse($result['lastattempt']['graded']);
    1934          $this->assertEmpty($result['lastattempt']['extensionduedate']);
    1935          $this->assertFalse($result['lastattempt']['blindmarking']);
    1936          $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
    1937          $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
    1938  
    1939          $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
    1940          $this->assertEquals(0, $result['lastattempt']['submission']['attemptnumber']);
    1941          $this->assertEquals('draft', $result['lastattempt']['submission']['status']);
    1942          $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
    1943          $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
    1944          $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
    1945  
    1946          // Map plugins based on their type - we can't rely on them being in a
    1947          // particular order, especially if 3rd party plugins are installed.
    1948          $submissionplugins = array();
    1949          foreach ($result['lastattempt']['submission']['plugins'] as $plugin) {
    1950              $submissionplugins[$plugin['type']] = $plugin;
    1951          }
    1952  
    1953          // Format expected online text.
    1954          $onlinetext = 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>';
    1955          list($expectedtext, $expectedformat) = external_format_text($onlinetext, FORMAT_HTML, $assign->get_context()->id,
    1956                  'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $studentsubmission->id);
    1957  
    1958          $this->assertEquals($expectedtext, $submissionplugins['onlinetext']['editorfields'][0]['text']);
    1959          $this->assertEquals($expectedformat, $submissionplugins['onlinetext']['editorfields'][0]['format']);
    1960          $this->assertEquals('/', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']);
    1961          $this->assertEquals('t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filename']);
    1962      }
    1963  
    1964      /**
    1965       * Test get_submission_status for a submitted submission.
    1966       */
    1967      public function test_get_submission_status_in_submission_status() {
    1968          $this->resetAfterTest(true);
    1969  
    1970          list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
    1971  
    1972          $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
    1973          // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
    1974          $this->assertDebuggingCalled();
    1975          $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
    1976  
    1977          $this->assertCount(0, $result['warnings']);
    1978          $this->assertFalse(isset($result['gradingsummary']));
    1979          $this->assertFalse(isset($result['feedback']));
    1980          $this->assertFalse(isset($result['previousattempts']));
    1981  
    1982          $this->assertTrue($result['lastattempt']['submissionsenabled']);
    1983          $this->assertFalse($result['lastattempt']['canedit']);
    1984          $this->assertFalse($result['lastattempt']['cansubmit']);
    1985          $this->assertFalse($result['lastattempt']['locked']);
    1986          $this->assertFalse($result['lastattempt']['graded']);
    1987          $this->assertEmpty($result['lastattempt']['extensionduedate']);
    1988          $this->assertFalse($result['lastattempt']['blindmarking']);
    1989          $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
    1990          $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
    1991  
    1992      }
    1993  
    1994      /**
    1995       * Test get_submission_status using the teacher role.
    1996       */
    1997      public function test_get_submission_status_in_submission_status_for_teacher() {
    1998          $this->resetAfterTest(true);
    1999  
    2000          list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
    2001  
    2002          // Now, as teacher, see the grading summary.
    2003          $this->setUser($teacher);
    2004          // First one group.
    2005          $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g1->id);
    2006          // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
    2007          $this->assertDebuggingCalled();
    2008          $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
    2009  
    2010          $this->assertCount(0, $result['warnings']);
    2011          $this->assertFalse(isset($result['lastattempt']));
    2012          $this->assertFalse(isset($result['feedback']));
    2013          $this->assertFalse(isset($result['previousattempts']));
    2014  
    2015          $this->assertEquals(1, $result['gradingsummary']['participantcount']);
    2016          $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
    2017          $this->assertEquals(1, $result['gradingsummary']['submissionsenabled']);
    2018          $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
    2019          $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']);  // One student from G1 submitted.
    2020          $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']);    // One student from G1 submitted.
    2021          $this->assertFalse($result['gradingsummary']['warnofungroupedusers']);
    2022  
    2023          // Second group.
    2024          $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g2->id);
    2025          $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
    2026          $this->assertCount(0, $result['warnings']);
    2027          $this->assertEquals(1, $result['gradingsummary']['participantcount']);
    2028          $this->assertEquals(0, $result['gradingsummary']['submissionssubmittedcount']); // G2 students didn't submit yet.
    2029          $this->assertEquals(0, $result['gradingsummary']['submissionsneedgradingcount']);   // G2 students didn't submit yet.
    2030  
    2031          // Should return also 1 participant if we allow the function to auto-select the group.
    2032          $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
    2033          $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
    2034          $this->assertCount(0, $result['warnings']);
    2035          $this->assertEquals(1, $result['gradingsummary']['participantcount']);
    2036          $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
    2037          $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']); // One student from G1 submitted.
    2038          $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']); // One student from G1 submitted.
    2039  
    2040          // Now check draft submissions.
    2041          list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(false);
    2042          $this->setUser($teacher);
    2043          $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g1->id);
    2044          $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
    2045          $this->assertCount(0, $result['warnings']);
    2046          $this->assertEquals(1, $result['gradingsummary']['participantcount']);
    2047          $this->assertEquals(1, $result['gradingsummary']['submissiondraftscount']); // We have a draft submission.
    2048          $this->assertEquals(0, $result['gradingsummary']['submissionssubmittedcount']); // We have only draft submissions.
    2049          $this->assertEquals(0, $result['gradingsummary']['submissionsneedgradingcount']); // We have only draft submissions.
    2050      }
    2051  
    2052      /**
    2053       * Test get_submission_status for a reopened submission.
    2054       */
    2055      public function test_get_submission_status_in_reopened_status() {
    2056          global $USER;
    2057  
    2058          $this->resetAfterTest(true);
    2059  
    2060          list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
    2061          $studentsubmission = $assign->get_user_submission($student1->id, true);
    2062  
    2063          $this->setUser($teacher);
    2064          // Grade and reopen.
    2065          $feedbackpluginparams = array();
    2066          $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
    2067          $feedbackeditorparams = array('text' => 'Yeeha!',
    2068                                          'format' => 1);
    2069          $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
    2070          $result = mod_assign_external::save_grade($instance->id,
    2071                                                    $student1->id,
    2072                                                    50.0,
    2073                                                    -1,
    2074                                                    false,
    2075                                                    'released',
    2076                                                    false,
    2077                                                    $feedbackpluginparams);
    2078          $USER->ignoresesskey = true;
    2079          $assign->testable_process_add_attempt($student1->id);
    2080  
    2081          $this->setUser($student1);
    2082  
    2083          $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
    2084          // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
    2085          $this->assertDebuggingCalled();
    2086          $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
    2087  
    2088          $this->assertCount(0, $result['warnings']);
    2089          $this->assertFalse(isset($result['gradingsummary']));
    2090  
    2091          $this->assertTrue($result['lastattempt']['submissionsenabled']);
    2092          $this->assertTrue($result['lastattempt']['canedit']);
    2093          $this->assertFalse($result['lastattempt']['cansubmit']);
    2094          $this->assertFalse($result['lastattempt']['locked']);
    2095          $this->assertFalse($result['lastattempt']['graded']);
    2096          $this->assertEmpty($result['lastattempt']['extensionduedate']);
    2097          $this->assertFalse($result['lastattempt']['blindmarking']);
    2098          $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
    2099          $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
    2100  
    2101          // Check new attempt reopened.
    2102          $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
    2103          $this->assertEquals(1, $result['lastattempt']['submission']['attemptnumber']);
    2104          $this->assertEquals('reopened', $result['lastattempt']['submission']['status']);
    2105          $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
    2106          $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
    2107          $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
    2108          $this->assertCount(3, $result['lastattempt']['submission']['plugins']);
    2109  
    2110          // Now see feedback and the attempts history (remember, is a submission reopened).
    2111          // Only 2 fields (no grade, no plugins data).
    2112          $this->assertCount(2, $result['feedback']);
    2113  
    2114          // One previous attempt.
    2115          $this->assertCount(1, $result['previousattempts']);
    2116          $this->assertEquals(0, $result['previousattempts'][0]['attemptnumber']);
    2117          $this->assertEquals(50, $result['previousattempts'][0]['grade']['grade']);
    2118          $this->assertEquals($teacher->id, $result['previousattempts'][0]['grade']['grader']);
    2119          $this->assertEquals($student1->id, $result['previousattempts'][0]['grade']['userid']);
    2120  
    2121          // Map plugins based on their type - we can't rely on them being in a
    2122          // particular order, especially if 3rd party plugins are installed.
    2123          $feedbackplugins = array();
    2124          foreach ($result['previousattempts'][0]['feedbackplugins'] as $plugin) {
    2125              $feedbackplugins[$plugin['type']] = $plugin;
    2126          }
    2127          $this->assertEquals('Yeeha!', $feedbackplugins['comments']['editorfields'][0]['text']);
    2128  
    2129          $submissionplugins = array();
    2130          foreach ($result['previousattempts'][0]['submission']['plugins'] as $plugin) {
    2131              $submissionplugins[$plugin['type']] = $plugin;
    2132          }
    2133          // Format expected online text.
    2134          $onlinetext = 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>';
    2135          list($expectedtext, $expectedformat) = external_format_text($onlinetext, FORMAT_HTML, $assign->get_context()->id,
    2136                  'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $studentsubmission->id);
    2137  
    2138          $this->assertEquals($expectedtext, $submissionplugins['onlinetext']['editorfields'][0]['text']);
    2139          $this->assertEquals($expectedformat, $submissionplugins['onlinetext']['editorfields'][0]['format']);
    2140          $this->assertEquals('/', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']);
    2141          $this->assertEquals('t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filename']);
    2142  
    2143      }
    2144  
    2145      /**
    2146       * Test access control for get_submission_status.
    2147       *
    2148       * @expectedException required_capability_exception
    2149       */
    2150      public function test_get_submission_status_access_control() {
    2151          $this->resetAfterTest(true);
    2152  
    2153          list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status();
    2154  
    2155          $this->setUser($student2);
    2156  
    2157          // Access control test.
    2158          mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id);
    2159  
    2160      }
    2161  
    2162      /**
    2163       * get_participant should throw an excaption if the requested assignment doesn't exist.
    2164       *
    2165       * @expectedException moodle_exception
    2166       */
    2167      public function test_get_participant_no_assignment() {
    2168          $this->resetAfterTest(true);
    2169          mod_assign_external::get_participant('-1', '-1', false);
    2170      }
    2171  
    2172      /**
    2173       * get_participant should throw a require_login_exception if the user doesn't have access
    2174       * to view assignments.
    2175       *
    2176       * @expectedException require_login_exception
    2177       */
    2178      public function test_get_participant_no_view_capability() {
    2179          global $DB;
    2180          $this->resetAfterTest(true);
    2181  
    2182          $result = $this->create_assign_with_student_and_teacher();
    2183          $assign = $result['assign'];
    2184          $student = $result['student'];
    2185          $course = $result['course'];
    2186          $context = context_course::instance($course->id);
    2187          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
    2188  
    2189          $this->setUser($student);
    2190          assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id, true);
    2191  
    2192          mod_assign_external::get_participant($assign->id, $student->id, false);
    2193      }
    2194  
    2195      /**
    2196       * get_participant should throw a required_capability_exception if the user doesn't have access
    2197       * to view assignment grades.
    2198       *
    2199       * @expectedException required_capability_exception
    2200       */
    2201      public function test_get_participant_no_grade_capability() {
    2202          global $DB;
    2203          $this->resetAfterTest(true);
    2204  
    2205          $result = $this->create_assign_with_student_and_teacher();
    2206          $assign = $result['assign'];
    2207          $student = $result['student'];
    2208          $teacher = $result['teacher'];
    2209          $course = $result['course'];
    2210          $context = context_course::instance($course->id);
    2211          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    2212  
    2213          $this->setUser($teacher);
    2214          assign_capability('mod/assign:viewgrades', CAP_PROHIBIT, $teacherrole->id, $context->id, true);
    2215          assign_capability('mod/assign:grade', CAP_PROHIBIT, $teacherrole->id, $context->id, true);
    2216          accesslib_clear_all_caches_for_unit_testing();
    2217  
    2218          mod_assign_external::get_participant($assign->id, $student->id, false);
    2219      }
    2220  
    2221      /**
    2222       * get_participant should throw an exception if the user isn't enrolled in the course.
    2223       *
    2224       * @expectedException moodle_exception
    2225       */
    2226      public function test_get_participant_no_participant() {
    2227          global $DB;
    2228          $this->resetAfterTest(true);
    2229  
    2230          $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true));
    2231          $student = $this->getDataGenerator()->create_user();
    2232          $assign = $result['assign'];
    2233          $teacher = $result['teacher'];
    2234  
    2235          $this->setUser($teacher);
    2236  
    2237          $result = mod_assign_external::get_participant($assign->id, $student->id, false);
    2238          $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
    2239      }
    2240  
    2241      /**
    2242       * get_participant should return a summarised list of details with a different fullname if blind
    2243       * marking is on for the requested assignment.
    2244       */
    2245      public function test_get_participant_blind_marking() {
    2246          global $DB;
    2247          $this->resetAfterTest(true);
    2248  
    2249          $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true));
    2250          $assign = $result['assign'];
    2251          $student = $result['student'];
    2252          $teacher = $result['teacher'];
    2253          $course = $result['course'];
    2254          $context = context_course::instance($course->id);
    2255          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    2256  
    2257          $this->setUser($teacher);
    2258  
    2259          $result = mod_assign_external::get_participant($assign->id, $student->id, true);
    2260          $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
    2261          $this->assertEquals($student->id, $result['id']);
    2262          $this->assertFalse(fullname($student) == $result['fullname']);
    2263          $this->assertFalse($result['submitted']);
    2264          $this->assertFalse($result['requiregrading']);
    2265          $this->assertFalse($result['grantedextension']);
    2266          $this->assertTrue($result['blindmarking']);
    2267          // Make sure we don't get any additional info.
    2268          $this->assertArrayNotHasKey('user', $result);
    2269      }
    2270  
    2271      /**
    2272       * get_participant should return a summarised list of details if requested.
    2273       */
    2274      public function test_get_participant_no_user() {
    2275          global $DB;
    2276          $this->resetAfterTest(true);
    2277  
    2278          $result = $this->create_assign_with_student_and_teacher();
    2279          $assignmodule = $result['assign'];
    2280          $student = $result['student'];
    2281          $teacher = $result['teacher'];
    2282          $course = $result['course'];
    2283          $context = context_course::instance($course->id);
    2284          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    2285  
    2286          // Create an assign instance to save a submission.
    2287          set_config('submissionreceipts', 0, 'assign');
    2288  
    2289          $cm = get_coursemodule_from_instance('assign', $assignmodule->id);
    2290          $context = context_module::instance($cm->id);
    2291  
    2292          $assign = new assign($context, $cm, $course);
    2293  
    2294          $this->setUser($student);
    2295  
    2296          // Simulate a submission.
    2297          $data = new stdClass();
    2298          $data->onlinetext_editor = array(
    2299              'itemid' => file_get_unused_draft_itemid(),
    2300              'text' => 'Student submission text',
    2301              'format' => FORMAT_MOODLE
    2302          );
    2303  
    2304          $notices = array();
    2305          $assign->save_submission($data, $notices);
    2306  
    2307          $data = new stdClass;
    2308          $data->userid = $student->id;
    2309          $assign->submit_for_grading($data, array());
    2310  
    2311          $this->setUser($teacher);
    2312  
    2313          $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false);
    2314          $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
    2315          $this->assertEquals($student->id, $result['id']);
    2316          $this->assertEquals(fullname($student), $result['fullname']);
    2317          $this->assertTrue($result['submitted']);
    2318          $this->assertTrue($result['requiregrading']);
    2319          $this->assertFalse($result['grantedextension']);
    2320          $this->assertFalse($result['blindmarking']);
    2321          // Make sure we don't get any additional info.
    2322          $this->assertArrayNotHasKey('user', $result);
    2323      }
    2324  
    2325      /**
    2326       * get_participant should return user details if requested.
    2327       */
    2328      public function test_get_participant_full_details() {
    2329          global $DB;
    2330          $this->resetAfterTest(true);
    2331  
    2332          $result = $this->create_assign_with_student_and_teacher();
    2333          $assign = $result['assign'];
    2334          $student = $result['student'];
    2335          $teacher = $result['teacher'];
    2336          $course = $result['course'];
    2337          $context = context_course::instance($course->id);
    2338          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    2339  
    2340          $this->setUser($teacher);
    2341  
    2342          $result = mod_assign_external::get_participant($assign->id, $student->id, true);
    2343          $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
    2344          // Check some of the extended properties we get when requesting the user.
    2345          $this->assertEquals($student->id, $result['id']);
    2346          // We should get user infomation back.
    2347          $user = $result['user'];
    2348          $this->assertFalse(empty($user));
    2349          $this->assertEquals($student->firstname, $user['firstname']);
    2350          $this->assertEquals($student->lastname, $user['lastname']);
    2351          $this->assertEquals($student->email, $user['email']);
    2352      }
    2353  
    2354      /**
    2355       * get_participant should return group details if a group submission was
    2356       * submitted.
    2357       */
    2358      public function test_get_participant_group_submission() {
    2359          global $DB;
    2360  
    2361          $this->resetAfterTest(true);
    2362  
    2363          $result = $this->create_assign_with_student_and_teacher(array(
    2364              'assignsubmission_onlinetext_enabled' => 1,
    2365              'teamsubmission' => 1
    2366          ));
    2367          $assignmodule = $result['assign'];
    2368          $student = $result['student'];
    2369          $teacher = $result['teacher'];
    2370          $course = $result['course'];
    2371          $context = context_course::instance($course->id);
    2372          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    2373          $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
    2374          $cm = get_coursemodule_from_instance('assign', $assignmodule->id);
    2375          $context = context_module::instance($cm->id);
    2376          $assign = new mod_assign_testable_assign($context, $cm, $course);
    2377  
    2378          groups_add_member($group, $student);
    2379  
    2380          $this->setUser($student);
    2381          $submission = $assign->get_group_submission($student->id, $group->id, true);
    2382          $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
    2383          $assign->testable_update_submission($submission, $student->id, true, false);
    2384          $data = new stdClass();
    2385          $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
    2386                                           'text' => 'Submission text',
    2387                                           'format' => FORMAT_MOODLE);
    2388          $plugin = $assign->get_submission_plugin_by_type('onlinetext');
    2389          $plugin->save($submission, $data);
    2390  
    2391          $this->setUser($teacher);
    2392  
    2393          $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false);
    2394          $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
    2395          // Check some of the extended properties we get when not requesting a summary.
    2396          $this->assertEquals($student->id, $result['id']);
    2397          $this->assertEquals($group->id, $result['groupid']);
    2398          $this->assertEquals($group->name, $result['groupname']);
    2399      }
    2400  
    2401      /**
    2402       * Test for mod_assign_external::list_participants().
    2403       *
    2404       * @throws coding_exception
    2405       */
    2406      public function test_list_participants_user_info_with_special_characters() {
    2407          global $CFG, $DB;
    2408          $this->resetAfterTest(true);
    2409          $CFG->showuseridentity = 'idnumber,email,phone1,phone2,department,institution';
    2410  
    2411          $data = $this->create_assign_with_student_and_teacher();
    2412          $assignment = $data['assign'];
    2413          $teacher = $data['teacher'];
    2414  
    2415          // Set data for student info that contain special characters.
    2416          $student = $data['student'];
    2417          $student->idnumber = '<\'"1am@wesome&c00l"\'>';
    2418          $student->phone1 = '+63 (999) 888-7777';
    2419          $student->phone2 = '(011) [15]4-123-4567';
    2420          $student->department = 'Arts & Sciences & \' " ¢ £ © € ¥ ® < >';
    2421          $student->institution = 'University of Awesome People & \' " ¢ £ © € ¥ ® < >';
    2422          // Assert that we have valid user data.
    2423          $this->assertTrue(core_user::validate($student));
    2424          // Update the user record.
    2425          $DB->update_record('user', $student);
    2426  
    2427          $this->setUser($teacher);
    2428          $participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, true);
    2429          $participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
    2430          $this->assertCount(1, $participants);
    2431  
    2432          // Asser that we have a valid response data.
    2433          $response = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
    2434          $this->assertEquals($response, $participants);
    2435  
    2436          // Check participant data.
    2437          $participant = $participants[0];
    2438          $this->assertEquals($student->idnumber, $participant['idnumber']);
    2439          $this->assertEquals($student->email, $participant['email']);
    2440          $this->assertEquals($student->phone1, $participant['phone1']);
    2441          $this->assertEquals($student->phone2, $participant['phone2']);
    2442          $this->assertEquals($student->department, $participant['department']);
    2443          $this->assertEquals($student->institution, $participant['institution']);
    2444          $this->assertArrayHasKey('enrolledcourses', $participant);
    2445  
    2446          $participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, false);
    2447          $participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
    2448          // Check that the list of courses the participant is enrolled is not returned.
    2449          $participant = $participants[0];
    2450          $this->assertArrayNotHasKey('enrolledcourses', $participant);
    2451      }
    2452  
    2453      /**
    2454       * Test for the type of the user-related properties in mod_assign_external::list_participants_returns().
    2455       */
    2456      public function test_list_participants_returns_user_property_types() {
    2457          // Get user properties.
    2458          $userdesc = core_user_external::user_description();
    2459          $this->assertTrue(isset($userdesc->keys));
    2460          $userproperties = array_keys($userdesc->keys);
    2461  
    2462          // Get returns description for mod_assign_external::list_participants_returns().
    2463          $listreturns = mod_assign_external::list_participants_returns();
    2464          $this->assertTrue(isset($listreturns->content));
    2465          $listreturnsdesc = $listreturns->content->keys;
    2466  
    2467          // Iterate over list returns description's keys.
    2468          foreach ($listreturnsdesc as $key => $desc) {
    2469              // Check if key exists in user properties and the description has a type attribute.
    2470              if (in_array($key, $userproperties) && isset($desc->type)) {
    2471                  try {
    2472                      // The core_user::get_property_type() method might throw a coding_exception since
    2473                      // core_user_external::user_description() might contain properties that are not yet included in
    2474                      // core_user's $propertiescache.
    2475                      $propertytype = core_user::get_property_type($key);
    2476  
    2477                      // Assert that user-related property types match those of the defined in core_user.
    2478                      $this->assertEquals($propertytype, $desc->type);
    2479                  } catch (coding_exception $e) {
    2480                      // All good.
    2481                  }
    2482              }
    2483          }
    2484      }
    2485  
    2486      /**
    2487       * Create a a course, assignment module instance, student and teacher and enrol them in
    2488       * the course.
    2489       *
    2490       * @param array $params parameters to be provided to the assignment module creation
    2491       * @return array containing the course, assignment module, student and teacher
    2492       */
    2493      private function create_assign_with_student_and_teacher($params = array()) {
    2494          global $DB;
    2495  
    2496          $course = $this->getDataGenerator()->create_course();
    2497          $params = array_merge(array(
    2498              'course' => $course->id,
    2499              'name' => 'assignment',
    2500              'intro' => 'assignment intro text',
    2501          ), $params);
    2502  
    2503          // Create a course and assignment and users.
    2504          $assign = $this->getDataGenerator()->create_module('assign', $params);
    2505  
    2506          $cm = get_coursemodule_from_instance('assign', $assign->id);
    2507          $context = context_module::instance($cm->id);
    2508  
    2509          $student = $this->getDataGenerator()->create_user();
    2510          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
    2511          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
    2512          $teacher = $this->getDataGenerator()->create_user();
    2513          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
    2514          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
    2515  
    2516          assign_capability('mod/assign:view', CAP_ALLOW, $teacherrole->id, $context->id, true);
    2517          assign_capability('mod/assign:viewgrades', CAP_ALLOW, $teacherrole->id, $context->id, true);
    2518          assign_capability('mod/assign:grade', CAP_ALLOW, $teacherrole->id, $context->id, true);
    2519          accesslib_clear_all_caches_for_unit_testing();
    2520  
    2521          return array(
    2522              'course' => $course,
    2523              'assign' => $assign,
    2524              'student' => $student,
    2525              'teacher' => $teacher
    2526          );
    2527      }
    2528  
    2529      /**
    2530       * Test test_view_assign
    2531       */
    2532      public function test_view_assign() {
    2533          global $CFG;
    2534  
    2535          $CFG->enablecompletion = 1;
    2536          $this->resetAfterTest();
    2537  
    2538          $this->setAdminUser();
    2539          // Setup test data.
    2540          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
    2541          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id),
    2542                                                              array('completion' => 2, 'completionview' => 1));
    2543          $context = context_module::instance($assign->cmid);
    2544          $cm = get_coursemodule_from_instance('assign', $assign->id);
    2545  
    2546          $result = mod_assign_external::view_assign($assign->id);
    2547          $result = external_api::clean_returnvalue(mod_assign_external::view_assign_returns(), $result);
    2548          $this->assertTrue($result['status']);
    2549          $this->assertEmpty($result['warnings']);
    2550  
    2551          // Check completion status.
    2552          $completion = new completion_info($course);
    2553          $completiondata = $completion->get_data($cm);
    2554          $this->assertEquals(1, $completiondata->completionstate);
    2555      }
    2556  }