MDL-55046 assign: Return submissionstatement in get_assignments
[moodle.git] / mod / assign / tests / externallib_test.php
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/>.
17 defined('MOODLE_INTERNAL') || die();
19 global $CFG;
21 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
23 /**
24  * External mod assign functions unit tests
25  *
26  * @package mod_assign
27  * @category external
28  * @copyright 2012 Paul Charsley
29  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30  */
31 class mod_assign_external_testcase extends externallib_advanced_testcase {
33     /**
34      * Tests set up
35      */
36     protected function setUp() {
37         global $CFG;
38         require_once($CFG->dirroot . '/mod/assign/externallib.php');
39     }
41     /**
42      * Test get_grades
43      */
44     public function test_get_grades() {
45         global $DB, $USER;
47         $this->resetAfterTest(true);
48         // Create a course and assignment.
49         $coursedata['idnumber'] = 'idnumbercourse';
50         $coursedata['fullname'] = 'Lightwork Course';
51         $coursedata['summary'] = 'Lightwork Course description';
52         $coursedata['summaryformat'] = FORMAT_MOODLE;
53         $course = self::getDataGenerator()->create_course($coursedata);
55         $assigndata['course'] = $course->id;
56         $assigndata['name'] = 'lightwork assignment';
58         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
60         // Create a manual enrolment record.
61         $manualenroldata['enrol'] = 'manual';
62         $manualenroldata['status'] = 0;
63         $manualenroldata['courseid'] = $course->id;
64         $enrolid = $DB->insert_record('enrol', $manualenroldata);
66         // Create a teacher and give them capabilities.
67         $context = context_course::instance($course->id);
68         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
69         $context = context_module::instance($assign->cmid);
70         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
72         // Create the teacher's enrolment record.
73         $userenrolmentdata['status'] = 0;
74         $userenrolmentdata['enrolid'] = $enrolid;
75         $userenrolmentdata['userid'] = $USER->id;
76         $DB->insert_record('user_enrolments', $userenrolmentdata);
78         // Create a student and give them 2 grades (for 2 attempts).
79         $student = self::getDataGenerator()->create_user();
81         $submission = new stdClass();
82         $submission->assignment = $assign->id;
83         $submission->userid = $student->id;
84         $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
85         $submission->latest = 0;
86         $submission->attemptnumber = 0;
87         $submission->groupid = 0;
88         $submission->timecreated = time();
89         $submission->timemodified = time();
90         $DB->insert_record('assign_submission', $submission);
92         $grade = new stdClass();
93         $grade->assignment = $assign->id;
94         $grade->userid = $student->id;
95         $grade->timecreated = time();
96         $grade->timemodified = $grade->timecreated;
97         $grade->grader = $USER->id;
98         $grade->grade = 50;
99         $grade->attemptnumber = 0;
100         $DB->insert_record('assign_grades', $grade);
102         $submission = new stdClass();
103         $submission->assignment = $assign->id;
104         $submission->userid = $student->id;
105         $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
106         $submission->latest = 1;
107         $submission->attemptnumber = 1;
108         $submission->groupid = 0;
109         $submission->timecreated = time();
110         $submission->timemodified = time();
111         $DB->insert_record('assign_submission', $submission);
113         $grade = new stdClass();
114         $grade->assignment = $assign->id;
115         $grade->userid = $student->id;
116         $grade->timecreated = time();
117         $grade->timemodified = $grade->timecreated;
118         $grade->grader = $USER->id;
119         $grade->grade = 75;
120         $grade->attemptnumber = 1;
121         $DB->insert_record('assign_grades', $grade);
123         $assignmentids[] = $assign->id;
124         $result = mod_assign_external::get_grades($assignmentids);
126         // We need to execute the return values cleaning process to simulate the web service server.
127         $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
129         // Check that the correct grade information for the student is returned.
130         $this->assertEquals(1, count($result['assignments']));
131         $assignment = $result['assignments'][0];
132         $this->assertEquals($assign->id, $assignment['assignmentid']);
133         // Should only get the last grade for this student.
134         $this->assertEquals(1, count($assignment['grades']));
135         $grade = $assignment['grades'][0];
136         $this->assertEquals($student->id, $grade['userid']);
137         // Should be the last grade (not the first).
138         $this->assertEquals(75, $grade['grade']);
139     }
141     /**
142      * Test get_assignments
143      */
144     public function test_get_assignments() {
145         global $DB, $USER, $CFG;
147         $this->resetAfterTest(true);
149         $category = self::getDataGenerator()->create_category(array(
150             'name' => 'Test category'
151         ));
153         // Create a course.
154         $course1 = self::getDataGenerator()->create_course(array(
155             'idnumber' => 'idnumbercourse1',
156             'fullname' => '<b>Lightwork Course 1</b>',      // Adding tags here to check that external_format_string works.
157             'shortname' => '<b>Lightwork Course 1</b>',     // Adding tags here to check that external_format_string works.
158             'summary' => 'Lightwork Course 1 description',
159             'summaryformat' => FORMAT_MOODLE,
160             'category' => $category->id
161         ));
163         // Create a second course, just for testing.
164         $course2 = self::getDataGenerator()->create_course(array(
165             'idnumber' => 'idnumbercourse2',
166             'fullname' => 'Lightwork Course 2',
167             'summary' => 'Lightwork Course 2 description',
168             'summaryformat' => FORMAT_MOODLE,
169             'category' => $category->id
170         ));
172         // Create the assignment module with links to a filerecord.
173         $assign1 = self::getDataGenerator()->create_module('assign', array(
174             'course' => $course1->id,
175             'name' => 'lightwork assignment',
176             'intro' => 'the assignment intro text here <a href="@@PLUGINFILE@@/intro.txt">link</a>',
177             'introformat' => FORMAT_HTML,
178             'markingworkflow' => 1,
179             'markingallocation' => 1
180         ));
182         // Add a file as assignment attachment.
183         $context = context_module::instance($assign1->cmid);
184         $filerecord = array('component' => 'mod_assign', 'filearea' => 'intro', 'contextid' => $context->id, 'itemid' => 0,
185                 'filename' => 'intro.txt', 'filepath' => '/');
186         $fs = get_file_storage();
187         $fs->create_file_from_string($filerecord, 'Test intro file');
189         // Create manual enrolment record.
190         $enrolid = $DB->insert_record('enrol', (object)array(
191             'enrol' => 'manual',
192             'status' => 0,
193             'courseid' => $course1->id
194         ));
196         // Create the user and give them capabilities.
197         $context = context_course::instance($course1->id);
198         $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
199         $context = context_module::instance($assign1->cmid);
200         $this->assignUserCapability('mod/assign:view', $context->id, $roleid);
202         // Create the user enrolment record.
203         $DB->insert_record('user_enrolments', (object)array(
204             'status' => 0,
205             'enrolid' => $enrolid,
206             'userid' => $USER->id
207         ));
209         // Add a file as assignment attachment.
210         $filerecord = array('component' => 'mod_assign', 'filearea' => ASSIGN_INTROATTACHMENT_FILEAREA,
211                 'contextid' => $context->id, 'itemid' => 0,
212                 'filename' => 'introattachment.txt', 'filepath' => '/');
213         $fs = get_file_storage();
214         $fs->create_file_from_string($filerecord, 'Test intro attachment file');
216         $result = mod_assign_external::get_assignments();
218         // We need to execute the return values cleaning process to simulate the web service server.
219         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
221         // Check the course and assignment are returned.
222         $this->assertEquals(1, count($result['courses']));
223         $course = $result['courses'][0];
224         $this->assertEquals('Lightwork Course 1', $course['fullname']);
225         $this->assertEquals('Lightwork Course 1', $course['shortname']);
226         $this->assertEquals(1, count($course['assignments']));
227         $assignment = $course['assignments'][0];
228         $this->assertEquals($assign1->id, $assignment['id']);
229         $this->assertEquals($course1->id, $assignment['course']);
230         $this->assertEquals('lightwork assignment', $assignment['name']);
231         $this->assertContains('the assignment intro text here', $assignment['intro']);
232         // Check the url of the file attatched.
233         $this->assertRegExp('@"' . $CFG->wwwroot . '/webservice/pluginfile.php/\d+/mod_assign/intro/intro\.txt"@', $assignment['intro']);
234         $this->assertEquals(1, $assignment['markingworkflow']);
235         $this->assertEquals(1, $assignment['markingallocation']);
236         $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
238         $this->assertCount(1, $assignment['introattachments']);
239         $this->assertEquals('introattachment.txt', $assignment['introattachments'][0]['filename']);
241         // Now, hide the descritption until the submission from date.
242         $DB->set_field('assign', 'alwaysshowdescription', 0, array('id' => $assign1->id));
243         $DB->set_field('assign', 'allowsubmissionsfromdate', time() + DAYSECS, array('id' => $assign1->id));
245         $result = mod_assign_external::get_assignments(array($course1->id));
247         // We need to execute the return values cleaning process to simulate the web service server.
248         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
250         $this->assertEquals(1, count($result['courses']));
251         $course = $result['courses'][0];
252         $this->assertEquals('Lightwork Course 1', $course['fullname']);
253         $this->assertEquals(1, count($course['assignments']));
254         $assignment = $course['assignments'][0];
255         $this->assertEquals($assign1->id, $assignment['id']);
256         $this->assertEquals($course1->id, $assignment['course']);
257         $this->assertEquals('lightwork assignment', $assignment['name']);
258         $this->assertArrayNotHasKey('intro', $assignment);
259         $this->assertArrayNotHasKey('introattachments', $assignment);
260         $this->assertEquals(1, $assignment['markingworkflow']);
261         $this->assertEquals(1, $assignment['markingallocation']);
262         $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
264         $result = mod_assign_external::get_assignments(array($course2->id));
266         // We need to execute the return values cleaning process to simulate the web service server.
267         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
269         $this->assertEquals(0, count($result['courses']));
270         $this->assertEquals(1, count($result['warnings']));
272         // Test with non-enrolled user, but with view capabilities.
273         $this->setAdminUser();
274         $result = mod_assign_external::get_assignments();
275         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
276         $this->assertEquals(0, count($result['courses']));
277         $this->assertEquals(0, count($result['warnings']));
279         // Expect no courses, because we are not using the special flag.
280         $result = mod_assign_external::get_assignments(array($course1->id));
281         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
282         $this->assertCount(0, $result['courses']);
284         // Now use the special flag to return courses where you are not enroled in.
285         $result = mod_assign_external::get_assignments(array($course1->id), array(), true);
286         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
287         $this->assertCount(1, $result['courses']);
289         $course = $result['courses'][0];
290         $this->assertEquals('Lightwork Course 1', $course['fullname']);
291         $this->assertEquals(1, count($course['assignments']));
292         $assignment = $course['assignments'][0];
293         $this->assertEquals($assign1->id, $assignment['id']);
294         $this->assertEquals($course1->id, $assignment['course']);
295         $this->assertEquals('lightwork assignment', $assignment['name']);
296         $this->assertArrayNotHasKey('intro', $assignment);
297         $this->assertArrayNotHasKey('introattachments', $assignment);
298         $this->assertEquals(1, $assignment['markingworkflow']);
299         $this->assertEquals(1, $assignment['markingallocation']);
300         $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
301     }
303     /**
304      * Test get_assignments with submissionstatement.
305      */
306     public function test_get_assignments_with_submissionstatement() {
307         global $DB, $USER, $CFG;
309         $this->resetAfterTest(true);
311         // Setup test data. Create 2 assigns, one with requiresubmissionstatement and the other without it.
312         $course = $this->getDataGenerator()->create_course();
313         $assign = $this->getDataGenerator()->create_module('assign', array(
314             'course' => $course->id,
315             'requiresubmissionstatement' => 1
316         ));
317         $assign2 = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
319         // Create student.
320         $student = self::getDataGenerator()->create_user();
322         // Users enrolments.
323         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
324         $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
326         // Update the submissionstatement.
327         $submissionstatement = 'This is a fake submission statement.';
328         set_config('submissionstatement', $submissionstatement, 'assign');
330         $this->setUser($student);
332         $result = mod_assign_external::get_assignments();
333         // We need to execute the return values cleaning process to simulate the web service server.
334         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
336         // Check that the amount of courses and assignments is right.
337         $this->assertCount(1, $result['courses']);
338         $assignmentsret = $result['courses'][0]['assignments'];
339         $this->assertCount(2, $assignmentsret);
341         // Order the returned assignments by ID.
342         usort($assignmentsret, function($a, $b) {
343             return strcmp($a['id'], $b['id']);
344         });
346         // Check that the first assign contains the submission statement.
347         $assignmentret = $assignmentsret[0];
348         $this->assertEquals($assign->id, $assignmentret['id']);
349         $this->assertEquals(1, $assignmentret['requiresubmissionstatement']);
350         $this->assertEquals($submissionstatement, $assignmentret['submissionstatement']);
352         // Check that the second assign does NOT contain the submission statement.
353         $assignmentret = $assignmentsret[1];
354         $this->assertEquals($assign2->id, $assignmentret['id']);
355         $this->assertEquals(0, $assignmentret['requiresubmissionstatement']);
356         $this->assertArrayNotHasKey('submissionstatement', $assignmentret);
357     }
359     /**
360      * Test get_submissions
361      */
362     public function test_get_submissions() {
363         global $DB, $USER;
365         $this->resetAfterTest(true);
366         // Create a course and assignment.
367         $coursedata['idnumber'] = 'idnumbercourse1';
368         $coursedata['fullname'] = 'Lightwork Course 1';
369         $coursedata['summary'] = 'Lightwork Course 1 description';
370         $coursedata['summaryformat'] = FORMAT_MOODLE;
371         $course1 = self::getDataGenerator()->create_course($coursedata);
373         $assigndata['course'] = $course1->id;
374         $assigndata['name'] = 'lightwork assignment';
376         $assign1 = self::getDataGenerator()->create_module('assign', $assigndata);
378         // Create a student with an online text submission.
379         // First attempt.
380         $student = self::getDataGenerator()->create_user();
381         $submission = new stdClass();
382         $submission->assignment = $assign1->id;
383         $submission->userid = $student->id;
384         $submission->timecreated = time();
385         $submission->timemodified = $submission->timecreated;
386         $submission->status = 'draft';
387         $submission->attemptnumber = 0;
388         $submission->latest = 0;
389         $sid = $DB->insert_record('assign_submission', $submission);
391         // Second attempt.
392         $submission = new stdClass();
393         $submission->assignment = $assign1->id;
394         $submission->userid = $student->id;
395         $submission->timecreated = time();
396         $submission->timemodified = $submission->timecreated;
397         $submission->status = 'submitted';
398         $submission->attemptnumber = 1;
399         $submission->latest = 1;
400         $sid = $DB->insert_record('assign_submission', $submission);
401         $submission->id = $sid;
403         $onlinetextsubmission = new stdClass();
404         $onlinetextsubmission->onlinetext = "<p>online test text</p>";
405         $onlinetextsubmission->onlineformat = 1;
406         $onlinetextsubmission->submission = $submission->id;
407         $onlinetextsubmission->assignment = $assign1->id;
408         $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
410         // Create manual enrolment record.
411         $manualenroldata['enrol'] = 'manual';
412         $manualenroldata['status'] = 0;
413         $manualenroldata['courseid'] = $course1->id;
414         $enrolid = $DB->insert_record('enrol', $manualenroldata);
416         // Create a teacher and give them capabilities.
417         $context = context_course::instance($course1->id);
418         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
419         $context = context_module::instance($assign1->cmid);
420         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
422         // Create the teacher's enrolment record.
423         $userenrolmentdata['status'] = 0;
424         $userenrolmentdata['enrolid'] = $enrolid;
425         $userenrolmentdata['userid'] = $USER->id;
426         $DB->insert_record('user_enrolments', $userenrolmentdata);
428         $assignmentids[] = $assign1->id;
429         $result = mod_assign_external::get_submissions($assignmentids);
430         $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
432         // Check the online text submission is returned.
433         $this->assertEquals(1, count($result['assignments']));
434         $assignment = $result['assignments'][0];
435         $this->assertEquals($assign1->id, $assignment['assignmentid']);
436         $this->assertEquals(1, count($assignment['submissions']));
437         $submission = $assignment['submissions'][0];
438         $this->assertEquals($sid, $submission['id']);
439         $this->assertCount(1, $submission['plugins']);
440     }
442     /**
443      * Test get_user_flags
444      */
445     public function test_get_user_flags() {
446         global $DB, $USER;
448         $this->resetAfterTest(true);
449         // Create a course and assignment.
450         $coursedata['idnumber'] = 'idnumbercourse';
451         $coursedata['fullname'] = 'Lightwork Course';
452         $coursedata['summary'] = 'Lightwork Course description';
453         $coursedata['summaryformat'] = FORMAT_MOODLE;
454         $course = self::getDataGenerator()->create_course($coursedata);
456         $assigndata['course'] = $course->id;
457         $assigndata['name'] = 'lightwork assignment';
459         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
461         // Create a manual enrolment record.
462         $manualenroldata['enrol'] = 'manual';
463         $manualenroldata['status'] = 0;
464         $manualenroldata['courseid'] = $course->id;
465         $enrolid = $DB->insert_record('enrol', $manualenroldata);
467         // Create a teacher and give them capabilities.
468         $context = context_course::instance($course->id);
469         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
470         $context = context_module::instance($assign->cmid);
471         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
473         // Create the teacher's enrolment record.
474         $userenrolmentdata['status'] = 0;
475         $userenrolmentdata['enrolid'] = $enrolid;
476         $userenrolmentdata['userid'] = $USER->id;
477         $DB->insert_record('user_enrolments', $userenrolmentdata);
479         // Create a student and give them a user flag record.
480         $student = self::getDataGenerator()->create_user();
481         $userflag = new stdClass();
482         $userflag->assignment = $assign->id;
483         $userflag->userid = $student->id;
484         $userflag->locked = 0;
485         $userflag->mailed = 0;
486         $userflag->extensionduedate = 0;
487         $userflag->workflowstate = 'inmarking';
488         $userflag->allocatedmarker = $USER->id;
490         $DB->insert_record('assign_user_flags', $userflag);
492         $assignmentids[] = $assign->id;
493         $result = mod_assign_external::get_user_flags($assignmentids);
495         // We need to execute the return values cleaning process to simulate the web service server.
496         $result = external_api::clean_returnvalue(mod_assign_external::get_user_flags_returns(), $result);
498         // Check that the correct user flag information for the student is returned.
499         $this->assertEquals(1, count($result['assignments']));
500         $assignment = $result['assignments'][0];
501         $this->assertEquals($assign->id, $assignment['assignmentid']);
502         // Should be one user flag record.
503         $this->assertEquals(1, count($assignment['userflags']));
504         $userflag = $assignment['userflags'][0];
505         $this->assertEquals($student->id, $userflag['userid']);
506         $this->assertEquals(0, $userflag['locked']);
507         $this->assertEquals(0, $userflag['mailed']);
508         $this->assertEquals(0, $userflag['extensionduedate']);
509         $this->assertEquals('inmarking', $userflag['workflowstate']);
510         $this->assertEquals($USER->id, $userflag['allocatedmarker']);
511     }
513     /**
514      * Test get_user_mappings
515      */
516     public function test_get_user_mappings() {
517         global $DB, $USER;
519         $this->resetAfterTest(true);
520         // Create a course and assignment.
521         $coursedata['idnumber'] = 'idnumbercourse';
522         $coursedata['fullname'] = 'Lightwork Course';
523         $coursedata['summary'] = 'Lightwork Course description';
524         $coursedata['summaryformat'] = FORMAT_MOODLE;
525         $course = self::getDataGenerator()->create_course($coursedata);
527         $assigndata['course'] = $course->id;
528         $assigndata['name'] = 'lightwork assignment';
530         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
532         // Create a manual enrolment record.
533         $manualenroldata['enrol'] = 'manual';
534         $manualenroldata['status'] = 0;
535         $manualenroldata['courseid'] = $course->id;
536         $enrolid = $DB->insert_record('enrol', $manualenroldata);
538         // Create a teacher and give them capabilities.
539         $context = context_course::instance($course->id);
540         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
541         $context = context_module::instance($assign->cmid);
542         $this->assignUserCapability('mod/assign:revealidentities', $context->id, $roleid);
544         // Create the teacher's enrolment record.
545         $userenrolmentdata['status'] = 0;
546         $userenrolmentdata['enrolid'] = $enrolid;
547         $userenrolmentdata['userid'] = $USER->id;
548         $DB->insert_record('user_enrolments', $userenrolmentdata);
550         // Create a student and give them a user mapping record.
551         $student = self::getDataGenerator()->create_user();
552         $mapping = new stdClass();
553         $mapping->assignment = $assign->id;
554         $mapping->userid = $student->id;
556         $DB->insert_record('assign_user_mapping', $mapping);
558         $assignmentids[] = $assign->id;
559         $result = mod_assign_external::get_user_mappings($assignmentids);
561         // We need to execute the return values cleaning process to simulate the web service server.
562         $result = external_api::clean_returnvalue(mod_assign_external::get_user_mappings_returns(), $result);
564         // Check that the correct user mapping information for the student is returned.
565         $this->assertEquals(1, count($result['assignments']));
566         $assignment = $result['assignments'][0];
567         $this->assertEquals($assign->id, $assignment['assignmentid']);
568         // Should be one user mapping record.
569         $this->assertEquals(1, count($assignment['mappings']));
570         $mapping = $assignment['mappings'][0];
571         $this->assertEquals($student->id, $mapping['userid']);
572     }
574     /**
575      * Test lock_submissions
576      */
577     public function test_lock_submissions() {
578         global $DB, $USER;
580         $this->resetAfterTest(true);
581         // Create a course and assignment and users.
582         $course = self::getDataGenerator()->create_course();
584         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
585         $params['course'] = $course->id;
586         $params['assignsubmission_onlinetext_enabled'] = 1;
587         $instance = $generator->create_instance($params);
588         $cm = get_coursemodule_from_instance('assign', $instance->id);
589         $context = context_module::instance($cm->id);
591         $assign = new assign($context, $cm, $course);
593         $student1 = self::getDataGenerator()->create_user();
594         $student2 = self::getDataGenerator()->create_user();
595         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
596         $this->getDataGenerator()->enrol_user($student1->id,
597                                               $course->id,
598                                               $studentrole->id);
599         $this->getDataGenerator()->enrol_user($student2->id,
600                                               $course->id,
601                                               $studentrole->id);
602         $teacher = self::getDataGenerator()->create_user();
603         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
604         $this->getDataGenerator()->enrol_user($teacher->id,
605                                               $course->id,
606                                               $teacherrole->id);
608         // Create a student1 with an online text submission.
609         // Simulate a submission.
610         $this->setUser($student1);
611         $submission = $assign->get_user_submission($student1->id, true);
612         $data = new stdClass();
613         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
614                                          'text'=>'Submission text',
615                                          'format'=>FORMAT_MOODLE);
616         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
617         $plugin->save($submission, $data);
619         // Ready to test.
620         $this->setUser($teacher);
621         $students = array($student1->id, $student2->id);
622         $result = mod_assign_external::lock_submissions($instance->id, $students);
623         $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
625         // Check for 0 warnings.
626         $this->assertEquals(0, count($result));
628         $this->setUser($student2);
629         $submission = $assign->get_user_submission($student2->id, true);
630         $data = new stdClass();
631         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
632                                          'text'=>'Submission text',
633                                          'format'=>FORMAT_MOODLE);
634         $notices = array();
635         $this->setExpectedException('moodle_exception');
636         $assign->save_submission($data, $notices);
637     }
639     /**
640      * Test unlock_submissions
641      */
642     public function test_unlock_submissions() {
643         global $DB, $USER;
645         $this->resetAfterTest(true);
646         // Create a course and assignment and users.
647         $course = self::getDataGenerator()->create_course();
649         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
650         $params['course'] = $course->id;
651         $params['assignsubmission_onlinetext_enabled'] = 1;
652         $instance = $generator->create_instance($params);
653         $cm = get_coursemodule_from_instance('assign', $instance->id);
654         $context = context_module::instance($cm->id);
656         $assign = new assign($context, $cm, $course);
658         $student1 = self::getDataGenerator()->create_user();
659         $student2 = self::getDataGenerator()->create_user();
660         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
661         $this->getDataGenerator()->enrol_user($student1->id,
662                                               $course->id,
663                                               $studentrole->id);
664         $this->getDataGenerator()->enrol_user($student2->id,
665                                               $course->id,
666                                               $studentrole->id);
667         $teacher = self::getDataGenerator()->create_user();
668         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
669         $this->getDataGenerator()->enrol_user($teacher->id,
670                                               $course->id,
671                                               $teacherrole->id);
673         // Create a student1 with an online text submission.
674         // Simulate a submission.
675         $this->setUser($student1);
676         $submission = $assign->get_user_submission($student1->id, true);
677         $data = new stdClass();
678         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
679                                          'text'=>'Submission text',
680                                          'format'=>FORMAT_MOODLE);
681         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
682         $plugin->save($submission, $data);
684         // Ready to test.
685         $this->setUser($teacher);
686         $students = array($student1->id, $student2->id);
687         $result = mod_assign_external::lock_submissions($instance->id, $students);
688         $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
690         // Check for 0 warnings.
691         $this->assertEquals(0, count($result));
693         $result = mod_assign_external::unlock_submissions($instance->id, $students);
694         $result = external_api::clean_returnvalue(mod_assign_external::unlock_submissions_returns(), $result);
696         // Check for 0 warnings.
697         $this->assertEquals(0, count($result));
699         $this->setUser($student2);
700         $submission = $assign->get_user_submission($student2->id, true);
701         $data = new stdClass();
702         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
703                                          'text'=>'Submission text',
704                                          'format'=>FORMAT_MOODLE);
705         $notices = array();
706         $assign->save_submission($data, $notices);
707     }
709     /**
710      * Test submit_for_grading
711      */
712     public function test_submit_for_grading() {
713         global $DB, $USER;
715         $this->resetAfterTest(true);
716         // Create a course and assignment and users.
717         $course = self::getDataGenerator()->create_course();
719         set_config('submissionreceipts', 0, 'assign');
720         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
721         $params['course'] = $course->id;
722         $params['assignsubmission_onlinetext_enabled'] = 1;
723         $params['submissiondrafts'] = 1;
724         $params['sendnotifications'] = 0;
725         $params['requiresubmissionstatement'] = 1;
726         $instance = $generator->create_instance($params);
727         $cm = get_coursemodule_from_instance('assign', $instance->id);
728         $context = context_module::instance($cm->id);
730         $assign = new assign($context, $cm, $course);
732         $student1 = self::getDataGenerator()->create_user();
733         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
734         $this->getDataGenerator()->enrol_user($student1->id,
735                                               $course->id,
736                                               $studentrole->id);
738         // Create a student1 with an online text submission.
739         // Simulate a submission.
740         $this->setUser($student1);
741         $submission = $assign->get_user_submission($student1->id, true);
742         $data = new stdClass();
743         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
744                                          'text'=>'Submission text',
745                                          'format'=>FORMAT_MOODLE);
746         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
747         $plugin->save($submission, $data);
749         $result = mod_assign_external::submit_for_grading($instance->id, false);
750         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
752         // Should be 1 fail because the submission statement was not aceptted.
753         $this->assertEquals(1, count($result));
755         $result = mod_assign_external::submit_for_grading($instance->id, true);
756         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
758         // Check for 0 warnings.
759         $this->assertEquals(0, count($result));
761         $submission = $assign->get_user_submission($student1->id, false);
763         $this->assertEquals(ASSIGN_SUBMISSION_STATUS_SUBMITTED, $submission->status);
764     }
766     /**
767      * Test save_user_extensions
768      */
769     public function test_save_user_extensions() {
770         global $DB, $USER;
772         $this->resetAfterTest(true);
773         // Create a course and assignment and users.
774         $course = self::getDataGenerator()->create_course();
776         $teacher = self::getDataGenerator()->create_user();
777         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
778         $this->getDataGenerator()->enrol_user($teacher->id,
779                                               $course->id,
780                                               $teacherrole->id);
781         $this->setUser($teacher);
783         $now = time();
784         $yesterday = $now - 24*60*60;
785         $tomorrow = $now + 24*60*60;
786         set_config('submissionreceipts', 0, 'assign');
787         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
788         $params['course'] = $course->id;
789         $params['submissiondrafts'] = 1;
790         $params['sendnotifications'] = 0;
791         $params['duedate'] = $yesterday;
792         $params['cutoffdate'] = $now - 10;
793         $instance = $generator->create_instance($params);
794         $cm = get_coursemodule_from_instance('assign', $instance->id);
795         $context = context_module::instance($cm->id);
797         $assign = new assign($context, $cm, $course);
799         $student1 = self::getDataGenerator()->create_user();
800         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
801         $this->getDataGenerator()->enrol_user($student1->id,
802                                               $course->id,
803                                               $studentrole->id);
805         $this->setUser($student1);
806         $result = mod_assign_external::submit_for_grading($instance->id, true);
807         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
809         // Check for 0 warnings.
810         $this->assertEquals(1, count($result));
812         $this->setUser($teacher);
813         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
814         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
815         $this->assertEquals(1, count($result));
817         $this->setUser($teacher);
818         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($yesterday - 10));
819         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
820         $this->assertEquals(1, count($result));
822         $this->setUser($teacher);
823         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($tomorrow));
824         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
825         $this->assertEquals(0, count($result));
827         $this->setUser($student1);
828         $result = mod_assign_external::submit_for_grading($instance->id, true);
829         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
830         $this->assertEquals(0, count($result));
832         $this->setUser($student1);
833         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
834         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
836     }
838     /**
839      * Test reveal_identities
840      */
841     public function test_reveal_identities() {
842         global $DB, $USER;
844         $this->resetAfterTest(true);
845         // Create a course and assignment and users.
846         $course = self::getDataGenerator()->create_course();
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);
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);
864         $assign = new assign($context, $cm, $course);
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);
872         $this->setUser($student1);
873         $this->setExpectedException('required_capability_exception');
874         $result = mod_assign_external::reveal_identities($instance->id);
875         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
876         $this->assertEquals(1, count($result));
877         $this->assertEquals(true, $assign->is_blind_marking());
879         $this->setUser($teacher);
880         $result = mod_assign_external::reveal_identities($instance->id);
881         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
882         $this->assertEquals(0, count($result));
883         $this->assertEquals(false, $assign->is_blind_marking());
885         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
886         $params['course'] = $course->id;
887         $params['submissiondrafts'] = 1;
888         $params['sendnotifications'] = 0;
889         $params['blindmarking'] = 0;
890         $instance = $generator->create_instance($params);
891         $cm = get_coursemodule_from_instance('assign', $instance->id);
892         $context = context_module::instance($cm->id);
894         $assign = new assign($context, $cm, $course);
895         $result = mod_assign_external::reveal_identities($instance->id);
896         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
897         $this->assertEquals(1, count($result));
898         $this->assertEquals(false, $assign->is_blind_marking());
900     }
902     /**
903      * Test revert_submissions_to_draft
904      */
905     public function test_revert_submissions_to_draft() {
906         global $DB, $USER;
908         $this->resetAfterTest(true);
909         set_config('submissionreceipts', 0, 'assign');
910         // Create a course and assignment and users.
911         $course = self::getDataGenerator()->create_course();
913         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
914         $params['course'] = $course->id;
915         $params['sendnotifications'] = 0;
916         $params['submissiondrafts'] = 1;
917         $instance = $generator->create_instance($params);
918         $cm = get_coursemodule_from_instance('assign', $instance->id);
919         $context = context_module::instance($cm->id);
921         $assign = new assign($context, $cm, $course);
923         $student1 = self::getDataGenerator()->create_user();
924         $student2 = self::getDataGenerator()->create_user();
925         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
926         $this->getDataGenerator()->enrol_user($student1->id,
927                                               $course->id,
928                                               $studentrole->id);
929         $this->getDataGenerator()->enrol_user($student2->id,
930                                               $course->id,
931                                               $studentrole->id);
932         $teacher = self::getDataGenerator()->create_user();
933         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
934         $this->getDataGenerator()->enrol_user($teacher->id,
935                                               $course->id,
936                                               $teacherrole->id);
938         // Create a student1 with an online text submission.
939         // Simulate a submission.
940         $this->setUser($student1);
941         $result = mod_assign_external::submit_for_grading($instance->id, true);
942         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
943         $this->assertEquals(0, count($result));
945         // Ready to test.
946         $this->setUser($teacher);
947         $students = array($student1->id, $student2->id);
948         $result = mod_assign_external::revert_submissions_to_draft($instance->id, array($student1->id));
949         $result = external_api::clean_returnvalue(mod_assign_external::revert_submissions_to_draft_returns(), $result);
951         // Check for 0 warnings.
952         $this->assertEquals(0, count($result));
954     }
956     /**
957      * Test save_submission
958      */
959     public function test_save_submission() {
960         global $DB, $USER;
962         $this->resetAfterTest(true);
963         // Create a course and assignment and users.
964         $course = self::getDataGenerator()->create_course();
966         $teacher = self::getDataGenerator()->create_user();
967         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
968         $this->getDataGenerator()->enrol_user($teacher->id,
969                                               $course->id,
970                                               $teacherrole->id);
971         $this->setUser($teacher);
973         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
974         $params['course'] = $course->id;
975         $params['assignsubmission_onlinetext_enabled'] = 1;
976         $params['assignsubmission_file_enabled'] = 1;
977         $params['assignsubmission_file_maxfiles'] = 5;
978         $params['assignsubmission_file_maxsizebytes'] = 1024*1024;
979         $instance = $generator->create_instance($params);
980         $cm = get_coursemodule_from_instance('assign', $instance->id);
981         $context = context_module::instance($cm->id);
983         $assign = new assign($context, $cm, $course);
985         $student1 = self::getDataGenerator()->create_user();
986         $student2 = self::getDataGenerator()->create_user();
987         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
988         $this->getDataGenerator()->enrol_user($student1->id,
989                                               $course->id,
990                                               $studentrole->id);
991         $this->getDataGenerator()->enrol_user($student2->id,
992                                               $course->id,
993                                               $studentrole->id);
994         // Create a student1 with an online text submission.
995         // Simulate a submission.
996         $this->setUser($student1);
998         // Create a file in a draft area.
999         $draftidfile = file_get_unused_draft_itemid();
1001         $usercontext = context_user::instance($student1->id);
1002         $filerecord = array(
1003             'contextid' => $usercontext->id,
1004             'component' => 'user',
1005             'filearea'  => 'draft',
1006             'itemid'    => $draftidfile,
1007             'filepath'  => '/',
1008             'filename'  => 'testtext.txt',
1009         );
1011         $fs = get_file_storage();
1012         $fs->create_file_from_string($filerecord, 'text contents');
1014         // Create another file in a different draft area.
1015         $draftidonlinetext = file_get_unused_draft_itemid();
1017         $filerecord = array(
1018             'contextid' => $usercontext->id,
1019             'component' => 'user',
1020             'filearea'  => 'draft',
1021             'itemid'    => $draftidonlinetext,
1022             'filepath'  => '/',
1023             'filename'  => 'shouldbeanimage.txt',
1024         );
1026         $fs->create_file_from_string($filerecord, 'image contents (not really)');
1028         // Now try a submission.
1029         $submissionpluginparams = array();
1030         $submissionpluginparams['files_filemanager'] = $draftidfile;
1031         $onlinetexteditorparams = array('text' => '<p>Yeeha!</p>',
1032                                         'format'=>1,
1033                                         'itemid'=>$draftidonlinetext);
1034         $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
1035         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1036         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1038         $this->assertEquals(0, count($result));
1040         // Set up a due and cutoff passed date.
1041         $instance->duedate = time() - WEEKSECS;
1042         $instance->cutoffdate = time() - WEEKSECS;
1043         $DB->update_record('assign', $instance);
1045         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1046         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1048         $this->assertCount(1, $result);
1049         $this->assertEquals(get_string('duedatereached', 'assign'), $result[0]['item']);
1050     }
1052     /**
1053      * Test save_grade
1054      */
1055     public function test_save_grade() {
1056         global $DB, $USER;
1058         $this->resetAfterTest(true);
1059         // Create a course and assignment and users.
1060         $course = self::getDataGenerator()->create_course();
1062         $teacher = self::getDataGenerator()->create_user();
1063         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1064         $this->getDataGenerator()->enrol_user($teacher->id,
1065                                               $course->id,
1066                                               $teacherrole->id);
1067         $this->setUser($teacher);
1069         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1070         $params['course'] = $course->id;
1071         $params['assignfeedback_file_enabled'] = 1;
1072         $params['assignfeedback_comments_enabled'] = 1;
1073         $instance = $generator->create_instance($params);
1074         $cm = get_coursemodule_from_instance('assign', $instance->id);
1075         $context = context_module::instance($cm->id);
1077         $assign = new assign($context, $cm, $course);
1079         $student1 = self::getDataGenerator()->create_user();
1080         $student2 = self::getDataGenerator()->create_user();
1081         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1082         $this->getDataGenerator()->enrol_user($student1->id,
1083                                               $course->id,
1084                                               $studentrole->id);
1085         $this->getDataGenerator()->enrol_user($student2->id,
1086                                               $course->id,
1087                                               $studentrole->id);
1088         // Simulate a grade.
1089         $this->setUser($teacher);
1091         // Create a file in a draft area.
1092         $draftidfile = file_get_unused_draft_itemid();
1094         $usercontext = context_user::instance($teacher->id);
1095         $filerecord = array(
1096             'contextid' => $usercontext->id,
1097             'component' => 'user',
1098             'filearea'  => 'draft',
1099             'itemid'    => $draftidfile,
1100             'filepath'  => '/',
1101             'filename'  => 'testtext.txt',
1102         );
1104         $fs = get_file_storage();
1105         $fs->create_file_from_string($filerecord, 'text contents');
1107         // Now try a grade.
1108         $feedbackpluginparams = array();
1109         $feedbackpluginparams['files_filemanager'] = $draftidfile;
1110         $feedbackeditorparams = array('text' => 'Yeeha!',
1111                                         'format' => 1);
1112         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1113         $result = mod_assign_external::save_grade($instance->id,
1114                                                   $student1->id,
1115                                                   50.0,
1116                                                   -1,
1117                                                   true,
1118                                                   'released',
1119                                                   false,
1120                                                   $feedbackpluginparams);
1121         // No warnings.
1122         $this->assertNull($result);
1124         $result = mod_assign_external::get_grades(array($instance->id));
1125         $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
1127         $this->assertEquals($result['assignments'][0]['grades'][0]['grade'], '50.0');
1128     }
1130     /**
1131      * Test save grades with advanced grading data
1132      */
1133     public function test_save_grades_with_advanced_grading() {
1134         global $DB, $USER;
1136         $this->resetAfterTest(true);
1137         // Create a course and assignment and users.
1138         $course = self::getDataGenerator()->create_course();
1140         $teacher = self::getDataGenerator()->create_user();
1141         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1142         $this->getDataGenerator()->enrol_user($teacher->id,
1143                                               $course->id,
1144                                               $teacherrole->id);
1146         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1147         $params['course'] = $course->id;
1148         $params['assignfeedback_file_enabled'] = 0;
1149         $params['assignfeedback_comments_enabled'] = 0;
1150         $instance = $generator->create_instance($params);
1151         $cm = get_coursemodule_from_instance('assign', $instance->id);
1152         $context = context_module::instance($cm->id);
1154         $assign = new assign($context, $cm, $course);
1156         $student1 = self::getDataGenerator()->create_user();
1157         $student2 = self::getDataGenerator()->create_user();
1158         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1159         $this->getDataGenerator()->enrol_user($student1->id,
1160                                               $course->id,
1161                                               $studentrole->id);
1162         $this->getDataGenerator()->enrol_user($student2->id,
1163                                               $course->id,
1164                                               $studentrole->id);
1166         $this->setUser($teacher);
1168         $feedbackpluginparams = array();
1169         $feedbackpluginparams['files_filemanager'] = 0;
1170         $feedbackeditorparams = array('text' => '', 'format' => 1);
1171         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1173         // Create advanced grading data.
1174         // Create grading area.
1175         $gradingarea = array(
1176             'contextid' => $context->id,
1177             'component' => 'mod_assign',
1178             'areaname' => 'submissions',
1179             'activemethod' => 'rubric'
1180         );
1181         $areaid = $DB->insert_record('grading_areas', $gradingarea);
1183         // Create a rubric grading definition.
1184         $rubricdefinition = array (
1185             'areaid' => $areaid,
1186             'method' => 'rubric',
1187             'name' => 'test',
1188             'status' => 20,
1189             'copiedfromid' => 1,
1190             'timecreated' => 1,
1191             'usercreated' => $teacher->id,
1192             'timemodified' => 1,
1193             'usermodified' => $teacher->id,
1194             'timecopied' => 0
1195         );
1196         $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
1198         // Create a criterion with a level.
1199         $rubriccriteria = array (
1200              'definitionid' => $definitionid,
1201              'sortorder' => 1,
1202              'description' => 'Demonstrate an understanding of disease control',
1203              'descriptionformat' => 0
1204         );
1205         $criterionid = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria);
1206         $rubriclevel1 = array (
1207             'criterionid' => $criterionid,
1208             'score' => 50,
1209             'definition' => 'pass',
1210             'definitionformat' => 0
1211         );
1212         $rubriclevel2 = array (
1213             'criterionid' => $criterionid,
1214             'score' => 100,
1215             'definition' => 'excellent',
1216             'definitionformat' => 0
1217         );
1218         $rubriclevel3 = array (
1219             'criterionid' => $criterionid,
1220             'score' => 0,
1221             'definition' => 'fail',
1222             'definitionformat' => 0
1223         );
1224         $levelid1 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
1225         $levelid2 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
1226         $levelid3 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel3);
1228         // Create the filling.
1229         $student1filling = array (
1230             'criterionid' => $criterionid,
1231             'levelid' => $levelid1,
1232             'remark' => 'well done you passed',
1233             'remarkformat' => 0
1234         );
1236         $student2filling = array (
1237             'criterionid' => $criterionid,
1238             'levelid' => $levelid2,
1239             'remark' => 'Excellent work',
1240             'remarkformat' => 0
1241         );
1243         $student1criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student1filling)));
1244         $student1advancedgradingdata = array('rubric' => array('criteria' => $student1criteria));
1246         $student2criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student2filling)));
1247         $student2advancedgradingdata = array('rubric' => array('criteria' => $student2criteria));
1249         $grades = array();
1250         $student1gradeinfo = array();
1251         $student1gradeinfo['userid'] = $student1->id;
1252         $student1gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1253         $student1gradeinfo['attemptnumber'] = -1;
1254         $student1gradeinfo['addattempt'] = true;
1255         $student1gradeinfo['workflowstate'] = 'released';
1256         $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1257         $student1gradeinfo['advancedgradingdata'] = $student1advancedgradingdata;
1258         $grades[] = $student1gradeinfo;
1260         $student2gradeinfo = array();
1261         $student2gradeinfo['userid'] = $student2->id;
1262         $student2gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1263         $student2gradeinfo['attemptnumber'] = -1;
1264         $student2gradeinfo['addattempt'] = true;
1265         $student2gradeinfo['workflowstate'] = 'released';
1266         $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1267         $student2gradeinfo['advancedgradingdata'] = $student2advancedgradingdata;
1268         $grades[] = $student2gradeinfo;
1270         $result = mod_assign_external::save_grades($instance->id, false, $grades);
1271         $this->assertNull($result);
1273         $student1grade = $DB->get_record('assign_grades',
1274                                          array('userid' => $student1->id, 'assignment' => $instance->id),
1275                                          '*',
1276                                          MUST_EXIST);
1277         $this->assertEquals($student1grade->grade, '50.0');
1279         $student2grade = $DB->get_record('assign_grades',
1280                                          array('userid' => $student2->id, 'assignment' => $instance->id),
1281                                          '*',
1282                                          MUST_EXIST);
1283         $this->assertEquals($student2grade->grade, '100.0');
1284     }
1286     /**
1287      * Test save grades for a team submission
1288      */
1289     public function test_save_grades_with_group_submission() {
1290         global $DB, $USER, $CFG;
1291         require_once($CFG->dirroot . '/group/lib.php');
1293         $this->resetAfterTest(true);
1294         // Create a course and assignment and users.
1295         $course = self::getDataGenerator()->create_course();
1297         $teacher = self::getDataGenerator()->create_user();
1298         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1299         $this->getDataGenerator()->enrol_user($teacher->id,
1300                                               $course->id,
1301                                               $teacherrole->id);
1303         $groupingdata = array();
1304         $groupingdata['courseid'] = $course->id;
1305         $groupingdata['name'] = 'Group assignment grouping';
1307         $grouping = self::getDataGenerator()->create_grouping($groupingdata);
1309         $group1data = array();
1310         $group1data['courseid'] = $course->id;
1311         $group1data['name'] = 'Team 1';
1312         $group2data = array();
1313         $group2data['courseid'] = $course->id;
1314         $group2data['name'] = 'Team 2';
1316         $group1 = self::getDataGenerator()->create_group($group1data);
1317         $group2 = self::getDataGenerator()->create_group($group2data);
1319         groups_assign_grouping($grouping->id, $group1->id);
1320         groups_assign_grouping($grouping->id, $group2->id);
1322         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1323         $params['course'] = $course->id;
1324         $params['teamsubmission'] = 1;
1325         $params['teamsubmissiongroupingid'] = $grouping->id;
1326         $instance = $generator->create_instance($params);
1327         $cm = get_coursemodule_from_instance('assign', $instance->id);
1328         $context = context_module::instance($cm->id);
1330         $assign = new assign($context, $cm, $course);
1332         $student1 = self::getDataGenerator()->create_user();
1333         $student2 = self::getDataGenerator()->create_user();
1334         $student3 = self::getDataGenerator()->create_user();
1335         $student4 = self::getDataGenerator()->create_user();
1336         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1337         $this->getDataGenerator()->enrol_user($student1->id,
1338                                               $course->id,
1339                                               $studentrole->id);
1340         $this->getDataGenerator()->enrol_user($student2->id,
1341                                               $course->id,
1342                                               $studentrole->id);
1343         $this->getDataGenerator()->enrol_user($student3->id,
1344                                               $course->id,
1345                                               $studentrole->id);
1346         $this->getDataGenerator()->enrol_user($student4->id,
1347                                               $course->id,
1348                                               $studentrole->id);
1350         groups_add_member($group1->id, $student1->id);
1351         groups_add_member($group1->id, $student2->id);
1352         groups_add_member($group1->id, $student3->id);
1353         groups_add_member($group2->id, $student4->id);
1354         $this->setUser($teacher);
1356         $feedbackpluginparams = array();
1357         $feedbackpluginparams['files_filemanager'] = 0;
1358         $feedbackeditorparams = array('text' => '', 'format' => 1);
1359         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1361         $grades1 = array();
1362         $student1gradeinfo = array();
1363         $student1gradeinfo['userid'] = $student1->id;
1364         $student1gradeinfo['grade'] = 50;
1365         $student1gradeinfo['attemptnumber'] = -1;
1366         $student1gradeinfo['addattempt'] = true;
1367         $student1gradeinfo['workflowstate'] = 'released';
1368         $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1369         $grades1[] = $student1gradeinfo;
1371         $student2gradeinfo = array();
1372         $student2gradeinfo['userid'] = $student2->id;
1373         $student2gradeinfo['grade'] = 75;
1374         $student2gradeinfo['attemptnumber'] = -1;
1375         $student2gradeinfo['addattempt'] = true;
1376         $student2gradeinfo['workflowstate'] = 'released';
1377         $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1378         $grades1[] = $student2gradeinfo;
1380         $this->setExpectedException('invalid_parameter_exception');
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);
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;
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));
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');
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     }
1421     /**
1422      * Test copy_previous_attempt
1423      */
1424     public function test_copy_previous_attempt() {
1425         global $DB, $USER;
1427         $this->resetAfterTest(true);
1428         // Create a course and assignment and users.
1429         $course = self::getDataGenerator()->create_course();
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);
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);
1449         $assign = new assign($context, $cm, $course);
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);
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);
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));
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);
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']);
1502     }
1504     /**
1505      * Test set_user_flags
1506      */
1507     public function test_set_user_flags() {
1508         global $DB, $USER;
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);
1518         $assigndata['course'] = $course->id;
1519         $assigndata['name'] = 'lightwork assignment';
1521         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
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);
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);
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);
1541         // Create a student.
1542         $student = self::getDataGenerator()->create_user();
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);
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);
1555         $this->assertEquals($student->id, $createduserflags[0]['userid']);
1556         $createduserflag = $DB->get_record('assign_user_flags', array('id' => $createduserflags[0]['id']));
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);
1567         // Create update data.
1568         $userflags = array();
1569         $userflag['userid'] = $createduserflag->userid;
1570         $userflag['workflowstate'] = 'readyforreview';
1571         $userflags = array($userflag);
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);
1577         $this->assertEquals($student->id, $updateduserflags[0]['userid']);
1578         $updateduserflag = $DB->get_record('assign_user_flags', array('id' => $updateduserflags[0]['id']));
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     }
1590     /**
1591      * Test view_grading_table
1592      */
1593     public function test_view_grading_table_invalid_instance() {
1594         global $DB;
1596         $this->resetAfterTest(true);
1598         // Setup test data.
1599         $course = $this->getDataGenerator()->create_course();
1600         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1601         $context = context_module::instance($assign->cmid);
1602         $cm = get_coursemodule_from_instance('assign', $assign->id);
1604         // Test invalid instance id.
1605         $this->setExpectedExceptionRegexp('dml_missing_record_exception');
1606         mod_assign_external::view_grading_table(0);
1607     }
1609     /**
1610      * Test view_grading_table
1611      */
1612     public function test_view_grading_table_not_enrolled() {
1613         global $DB;
1615         $this->resetAfterTest(true);
1617         // Setup test data.
1618         $course = $this->getDataGenerator()->create_course();
1619         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1620         $context = context_module::instance($assign->cmid);
1621         $cm = get_coursemodule_from_instance('assign', $assign->id);
1623         // Test not-enrolled user.
1624         $user = self::getDataGenerator()->create_user();
1625         $this->setUser($user);
1627         $this->setExpectedException('require_login_exception');
1628         mod_assign_external::view_grading_table($assign->id);
1629     }
1631     /**
1632      * Test view_grading_table
1633      */
1634     public function test_view_grading_table_correct() {
1635         global $DB;
1637         $this->resetAfterTest(true);
1639         // Setup test data.
1640         $course = $this->getDataGenerator()->create_course();
1641         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1642         $context = context_module::instance($assign->cmid);
1643         $cm = get_coursemodule_from_instance('assign', $assign->id);
1645         // Test user with full capabilities.
1646         $user = self::getDataGenerator()->create_user();
1647         $this->setUser($user);
1648         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1649         $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1651         // Trigger and capture the event.
1652         $sink = $this->redirectEvents();
1654         $result = mod_assign_external::view_grading_table($assign->id);
1655         $result = external_api::clean_returnvalue(mod_assign_external::view_grading_table_returns(), $result);
1657         $events = $sink->get_events();
1658         $this->assertCount(1, $events);
1659         $event = array_shift($events);
1661         // Checking that the event contains the expected values.
1662         $this->assertInstanceOf('\mod_assign\event\grading_table_viewed', $event);
1663         $this->assertEquals($context, $event->get_context());
1664         $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1665         $this->assertEquals($moodleurl, $event->get_url());
1666         $this->assertEventContextNotUsed($event);
1667         $this->assertNotEmpty($event->get_name());
1668     }
1670     /**
1671      * Test view_grading_table
1672      */
1673     public function test_view_grading_table_without_capability() {
1674         global $DB;
1676         $this->resetAfterTest(true);
1678         // Setup test data.
1679         $course = $this->getDataGenerator()->create_course();
1680         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1681         $context = context_module::instance($assign->cmid);
1682         $cm = get_coursemodule_from_instance('assign', $assign->id);
1684         // Test user with no capabilities.
1685         $user = self::getDataGenerator()->create_user();
1686         $this->setUser($user);
1687         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1688         $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1690         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1691         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1692         assign_capability('mod/assign:view', CAP_PROHIBIT, $teacherrole->id, $context->id);
1693         // Empty all the caches that may be affected by this change.
1694         accesslib_clear_all_caches_for_unit_testing();
1695         course_modinfo::clear_instance_cache();
1697         $this->setExpectedException('require_login_exception', 'Course or activity not accessible. (Activity is hidden)');
1698         mod_assign_external::view_grading_table($assign->id);
1699     }
1701     /**
1702      * Test subplugins availability
1703      */
1704     public function test_subplugins_availability() {
1705         global $CFG;
1707         require_once($CFG->dirroot . '/mod/assign/adminlib.php');
1708         $this->resetAfterTest(true);
1710         // Hide assignment file submissiong plugin.
1711         $pluginmanager = new assign_plugin_manager('assignsubmission');
1712         $pluginmanager->hide_plugin('file');
1713         $parameters = mod_assign_external::save_submission_parameters();
1715         $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1717         // Show it again and check that the value is returned as optional.
1718         $pluginmanager->show_plugin('file');
1719         $parameters = mod_assign_external::save_submission_parameters();
1720         $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1721         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1723         // Hide feedback file submissiong plugin.
1724         $pluginmanager = new assign_plugin_manager('assignfeedback');
1725         $pluginmanager->hide_plugin('file');
1727         $parameters = mod_assign_external::save_grade_parameters();
1729         $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1731         // Show it again and check that the value is returned as optional.
1732         $pluginmanager->show_plugin('file');
1733         $parameters = mod_assign_external::save_grade_parameters();
1735         $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1736         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1738         // Check a different one.
1739         $pluginmanager->show_plugin('comments');
1740         $this->assertTrue(isset($parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']));
1741         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']->required);
1742     }
1744     /**
1745      * Test test_view_submission_status
1746      */
1747     public function test_view_submission_status() {
1748         global $DB;
1750         $this->resetAfterTest(true);
1752         $this->setAdminUser();
1753         // Setup test data.
1754         $course = $this->getDataGenerator()->create_course();
1755         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1756         $context = context_module::instance($assign->cmid);
1757         $cm = get_coursemodule_from_instance('assign', $assign->id);
1759         // Test invalid instance id.
1760         try {
1761             mod_assign_external::view_submission_status(0);
1762             $this->fail('Exception expected due to invalid mod_assign instance id.');
1763         } catch (moodle_exception $e) {
1764             $this->assertEquals('invalidrecord', $e->errorcode);
1765         }
1767         // Test not-enrolled user.
1768         $user = self::getDataGenerator()->create_user();
1769         $this->setUser($user);
1770         try {
1771             mod_assign_external::view_submission_status($assign->id);
1772             $this->fail('Exception expected due to not enrolled user.');
1773         } catch (moodle_exception $e) {
1774             $this->assertEquals('requireloginerror', $e->errorcode);
1775         }
1777         // Test user with full capabilities.
1778         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1779         $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
1781         // Trigger and capture the event.
1782         $sink = $this->redirectEvents();
1784         $result = mod_assign_external::view_submission_status($assign->id);
1785         $result = external_api::clean_returnvalue(mod_assign_external::view_submission_status_returns(), $result);
1787         $events = $sink->get_events();
1788         $this->assertCount(1, $events);
1789         $event = array_shift($events);
1791         // Checking that the event contains the expected values.
1792         $this->assertInstanceOf('\mod_assign\event\submission_status_viewed', $event);
1793         $this->assertEquals($context, $event->get_context());
1794         $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1795         $this->assertEquals($moodleurl, $event->get_url());
1796         $this->assertEventContextNotUsed($event);
1797         $this->assertNotEmpty($event->get_name());
1799         // Test user with no capabilities.
1800         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1801         assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id);
1802         accesslib_clear_all_caches_for_unit_testing();
1803         course_modinfo::clear_instance_cache();
1805         try {
1806             mod_assign_external::view_submission_status($assign->id);
1807             $this->fail('Exception expected due to missing capability.');
1808         } catch (moodle_exception $e) {
1809             $this->assertEquals('requireloginerror', $e->errorcode);
1810         }
1811     }
1813     /**
1814      * Create a submission for testing the get_submission_status function.
1815      * @param  boolean $submitforgrading whether to submit for grading the submission
1816      * @return array an array containing all the required data for testing
1817      */
1818     private function create_submission_for_testing_status($submitforgrading = false) {
1819         global $DB, $CFG;
1820         require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
1822         // Create a course and assignment and users.
1823         $course = self::getDataGenerator()->create_course();
1825         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1826         $params = array(
1827             'course' => $course->id,
1828             'assignsubmission_file_maxfiles' => 1,
1829             'assignsubmission_file_maxsizebytes' => 1024 * 1024,
1830             'assignsubmission_onlinetext_enabled' => 1,
1831             'assignsubmission_file_enabled' => 1,
1832             'submissiondrafts' => 1,
1833             'assignfeedback_file_enabled' => 1,
1834             'assignfeedback_comments_enabled' => 1,
1835             'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1836             'sendnotifications' => 0
1837         );
1839         set_config('submissionreceipts', 0, 'assign');
1841         $instance = $generator->create_instance($params);
1842         $cm = get_coursemodule_from_instance('assign', $instance->id);
1843         $context = context_module::instance($cm->id);
1845         $assign = new testable_assign($context, $cm, $course);
1847         $student1 = self::getDataGenerator()->create_user();
1848         $student2 = self::getDataGenerator()->create_user();
1849         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1850         $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
1851         $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
1852         $teacher = self::getDataGenerator()->create_user();
1853         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1854         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
1856         $this->setUser($student1);
1858         // Create a student1 with an online text submission.
1859         // Simulate a submission.
1860         $submission = $assign->get_user_submission($student1->id, true);
1862         $data = new stdClass();
1863         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1864                                          'text' => 'Submission text',
1865                                          'format' => FORMAT_MOODLE);
1867         $draftidfile = file_get_unused_draft_itemid();
1868         $usercontext = context_user::instance($student1->id);
1869         $filerecord = array(
1870             'contextid' => $usercontext->id,
1871             'component' => 'user',
1872             'filearea'  => 'draft',
1873             'itemid'    => $draftidfile,
1874             'filepath'  => '/',
1875             'filename'  => 't.txt',
1876         );
1877         $fs = get_file_storage();
1878         $fs->create_file_from_string($filerecord, 'text contents');
1880         $data->files_filemanager = $draftidfile;
1882         $notices = array();
1883         $assign->save_submission($data, $notices);
1885         if ($submitforgrading) {
1886             // Now, submit the draft for grading.
1887             $notices = array();
1889             $data = new stdClass;
1890             $data->userid = $student1->id;
1891             $assign->submit_for_grading($data, $notices);
1892         }
1894         return array($assign, $instance, $student1, $student2, $teacher);
1895     }
1897     /**
1898      * Test get_submission_status for a draft submission.
1899      */
1900     public function test_get_submission_status_in_draft_status() {
1901         $this->resetAfterTest(true);
1903         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status();
1905         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1906         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1907         $this->assertDebuggingCalled();
1909         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1911         // The submission is now in draft mode.
1912         $this->assertCount(0, $result['warnings']);
1913         $this->assertFalse(isset($result['gradingsummary']));
1914         $this->assertFalse(isset($result['feedback']));
1915         $this->assertFalse(isset($result['previousattempts']));
1917         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1918         $this->assertTrue($result['lastattempt']['canedit']);
1919         $this->assertTrue($result['lastattempt']['cansubmit']);
1920         $this->assertFalse($result['lastattempt']['locked']);
1921         $this->assertFalse($result['lastattempt']['graded']);
1922         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1923         $this->assertFalse($result['lastattempt']['blindmarking']);
1924         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1925         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1927         $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
1928         $this->assertEquals(0, $result['lastattempt']['submission']['attemptnumber']);
1929         $this->assertEquals('draft', $result['lastattempt']['submission']['status']);
1930         $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
1931         $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
1932         $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
1934         // Map plugins based on their type - we can't rely on them being in a
1935         // particular order, especially if 3rd party plugins are installed.
1936         $submissionplugins = array();
1937         foreach ($result['lastattempt']['submission']['plugins'] as $plugin) {
1938             $submissionplugins[$plugin['type']] = $plugin;
1939         }
1940         $this->assertEquals('Submission text', $submissionplugins['onlinetext']['editorfields'][0]['text']);
1941         $this->assertEquals('/t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']);
1942     }
1944     /**
1945      * Test get_submission_status for a submitted submission.
1946      */
1947     public function test_get_submission_status_in_submission_status() {
1948         $this->resetAfterTest(true);
1950         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
1952         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1953         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1954         $this->assertDebuggingCalled();
1955         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1957         $this->assertCount(0, $result['warnings']);
1958         $this->assertFalse(isset($result['gradingsummary']));
1959         $this->assertFalse(isset($result['feedback']));
1960         $this->assertFalse(isset($result['previousattempts']));
1962         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1963         $this->assertFalse($result['lastattempt']['canedit']);
1964         $this->assertFalse($result['lastattempt']['cansubmit']);
1965         $this->assertFalse($result['lastattempt']['locked']);
1966         $this->assertFalse($result['lastattempt']['graded']);
1967         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1968         $this->assertFalse($result['lastattempt']['blindmarking']);
1969         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1970         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1972     }
1974     /**
1975      * Test get_submission_status using the teacher role.
1976      */
1977     public function test_get_submission_status_in_submission_status_for_teacher() {
1978         $this->resetAfterTest(true);
1980         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
1982         // Now, as teacher, see the grading summary.
1983         $this->setUser($teacher);
1984         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1985         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1986         $this->assertDebuggingCalled();
1987         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1989         $this->assertCount(0, $result['warnings']);
1990         $this->assertFalse(isset($result['lastattempt']));
1991         $this->assertFalse(isset($result['feedback']));
1992         $this->assertFalse(isset($result['previousattempts']));
1994         $this->assertEquals(2, $result['gradingsummary']['participantcount']);
1995         $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
1996         $this->assertEquals(1, $result['gradingsummary']['submissionsenabled']);
1997         $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']);
1998         $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']);
1999         $this->assertFalse($result['gradingsummary']['warnofungroupedusers']);
2000     }
2002     /**
2003      * Test get_submission_status for a reopened submission.
2004      */
2005     public function test_get_submission_status_in_reopened_status() {
2006         global $USER;
2008         $this->resetAfterTest(true);
2010         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
2012         $this->setUser($teacher);
2013         // Grade and reopen.
2014         $feedbackpluginparams = array();
2015         $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
2016         $feedbackeditorparams = array('text' => 'Yeeha!',
2017                                         'format' => 1);
2018         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
2019         $result = mod_assign_external::save_grade($instance->id,
2020                                                   $student1->id,
2021                                                   50.0,
2022                                                   -1,
2023                                                   false,
2024                                                   'released',
2025                                                   false,
2026                                                   $feedbackpluginparams);
2027         $USER->ignoresesskey = true;
2028         $assign->testable_process_add_attempt($student1->id);
2030         $this->setUser($student1);
2032         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2033         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2034         $this->assertDebuggingCalled();
2035         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2037         $this->assertCount(0, $result['warnings']);
2038         $this->assertFalse(isset($result['gradingsummary']));
2040         $this->assertTrue($result['lastattempt']['submissionsenabled']);
2041         $this->assertTrue($result['lastattempt']['canedit']);
2042         $this->assertFalse($result['lastattempt']['cansubmit']);
2043         $this->assertFalse($result['lastattempt']['locked']);
2044         $this->assertFalse($result['lastattempt']['graded']);
2045         $this->assertEmpty($result['lastattempt']['extensionduedate']);
2046         $this->assertFalse($result['lastattempt']['blindmarking']);
2047         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
2048         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
2050         // Check new attempt reopened.
2051         $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
2052         $this->assertEquals(1, $result['lastattempt']['submission']['attemptnumber']);
2053         $this->assertEquals('reopened', $result['lastattempt']['submission']['status']);
2054         $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
2055         $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
2056         $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
2057         $this->assertCount(3, $result['lastattempt']['submission']['plugins']);
2059         // Now see feedback and the attempts history (remember, is a submission reopened).
2060         // Only 2 fields (no grade, no plugins data).
2061         $this->assertCount(2, $result['feedback']);
2063         // One previous attempt.
2064         $this->assertCount(1, $result['previousattempts']);
2065         $this->assertEquals(0, $result['previousattempts'][0]['attemptnumber']);
2066         $this->assertEquals(50, $result['previousattempts'][0]['grade']['grade']);
2067         $this->assertEquals($teacher->id, $result['previousattempts'][0]['grade']['grader']);
2068         $this->assertEquals($student1->id, $result['previousattempts'][0]['grade']['userid']);
2070         // Map plugins based on their type - we can't rely on them being in a
2071         // particular order, especially if 3rd party plugins are installed.
2072         $feedbackplugins = array();
2073         foreach ($result['previousattempts'][0]['feedbackplugins'] as $plugin) {
2074             $feedbackplugins[$plugin['type']] = $plugin;
2075         }
2076         $this->assertEquals('Yeeha!', $feedbackplugins['comments']['editorfields'][0]['text']);
2078         $submissionplugins = array();
2079         foreach ($result['previousattempts'][0]['submission']['plugins'] as $plugin) {
2080             $submissionplugins[$plugin['type']] = $plugin;
2081         }
2082         $this->assertEquals('Submission text', $submissionplugins['onlinetext']['editorfields'][0]['text']);
2083         $this->assertEquals('/t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']);
2084     }
2086     /**
2087      * Test access control for get_submission_status.
2088      */
2089     public function test_get_submission_status_access_control() {
2090         $this->resetAfterTest(true);
2092         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status();
2094         $this->setUser($student2);
2096         // Access control test.
2097         $this->setExpectedException('required_capability_exception');
2098         mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id);
2100     }
2102     /**
2103      * get_participant should throw an excaption if the requested assignment doesn't exist.
2104      */
2105     public function test_get_participant_no_assignment() {
2106         $this->resetAfterTest(true);
2107         $this->setExpectedException('moodle_exception');
2108         mod_assign_external::get_participant('-1', '-1', false);
2109     }
2111     /**
2112      * get_participant should throw a require_login_exception if the user doesn't have access
2113      * to view assignments.
2114      */
2115     public function test_get_participant_no_view_capability() {
2116         global $DB;
2117         $this->resetAfterTest(true);
2119         $result = $this->create_assign_with_student_and_teacher();
2120         $assign = $result['assign'];
2121         $student = $result['student'];
2122         $course = $result['course'];
2123         $context = context_course::instance($course->id);
2124         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2126         $this->setUser($student);
2127         assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id, true);
2129         $this->setExpectedException('require_login_exception');
2130         mod_assign_external::get_participant($assign->id, $student->id, false);
2131     }
2133     /**
2134      * get_participant should throw a required_capability_exception if the user doesn't have access
2135      * to view assignment grades.
2136      */
2137     public function test_get_participant_no_grade_capability() {
2138         global $DB;
2139         $this->resetAfterTest(true);
2141         $result = $this->create_assign_with_student_and_teacher();
2142         $assign = $result['assign'];
2143         $student = $result['student'];
2144         $teacher = $result['teacher'];
2145         $course = $result['course'];
2146         $context = context_course::instance($course->id);
2147         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2149         $this->setUser($teacher);
2150         assign_capability('mod/assign:viewgrades', CAP_PROHIBIT, $teacherrole->id, $context->id, true);
2151         assign_capability('mod/assign:grade', CAP_PROHIBIT, $teacherrole->id, $context->id, true);
2152         accesslib_clear_all_caches_for_unit_testing();
2154         $this->setExpectedException('required_capability_exception');
2155         mod_assign_external::get_participant($assign->id, $student->id, false);
2156     }
2158     /**
2159      * get_participant should throw an exception if the user isn't enrolled in the course.
2160      */
2161     public function test_get_participant_no_participant() {
2162         global $DB;
2163         $this->resetAfterTest(true);
2165         $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true));
2166         $student = $this->getDataGenerator()->create_user();
2167         $assign = $result['assign'];
2168         $teacher = $result['teacher'];
2170         $this->setUser($teacher);
2172         $this->setExpectedException('moodle_exception');
2173         $result = mod_assign_external::get_participant($assign->id, $student->id, false);
2174     }
2176     /**
2177      * get_participant should return a summarised list of details with a different fullname if blind
2178      * marking is on for the requested assignment.
2179      */
2180     public function test_get_participant_blind_marking() {
2181         global $DB;
2182         $this->resetAfterTest(true);
2184         $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true));
2185         $assign = $result['assign'];
2186         $student = $result['student'];
2187         $teacher = $result['teacher'];
2188         $course = $result['course'];
2189         $context = context_course::instance($course->id);
2190         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2192         $this->setUser($teacher);
2194         $result = mod_assign_external::get_participant($assign->id, $student->id, true);
2195         $this->assertEquals($student->id, $result['id']);
2196         $this->assertFalse(fullname($student) == $result['fullname']);
2197         $this->assertFalse($result['submitted']);
2198         $this->assertFalse($result['requiregrading']);
2199         $this->assertTrue($result['blindmarking']);
2200         // Make sure we don't get any additional info.
2201         $this->assertTrue(empty($result['user']));
2202     }
2204     /**
2205      * get_participant should return a summarised list of details if requested.
2206      */
2207     public function test_get_participant_no_user() {
2208         global $DB;
2209         $this->resetAfterTest(true);
2211         $result = $this->create_assign_with_student_and_teacher();
2212         $assignmodule = $result['assign'];
2213         $student = $result['student'];
2214         $teacher = $result['teacher'];
2215         $course = $result['course'];
2216         $context = context_course::instance($course->id);
2217         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2219         // Create an assign instance to save a submission.
2220         set_config('submissionreceipts', 0, 'assign');
2222         $cm = get_coursemodule_from_instance('assign', $assignmodule->id);
2223         $context = context_module::instance($cm->id);
2225         $assign = new assign($context, $cm, $course);
2227         $this->setUser($student);
2229         // Simulate a submission.
2230         $data = new stdClass();
2231         $data->onlinetext_editor = array(
2232             'itemid' => file_get_unused_draft_itemid(),
2233             'text' => 'Student submission text',
2234             'format' => FORMAT_MOODLE
2235         );
2237         $notices = array();
2238         $assign->save_submission($data, $notices);
2240         $data = new stdClass;
2241         $data->userid = $student->id;
2242         $assign->submit_for_grading($data, array());
2244         $this->setUser($teacher);
2246         $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false);
2247         $this->assertEquals($student->id, $result['id']);
2248         $this->assertEquals(fullname($student), $result['fullname']);
2249         $this->assertTrue($result['submitted']);
2250         $this->assertTrue($result['requiregrading']);
2251         $this->assertFalse($result['blindmarking']);
2252         // Make sure we don't get any additional info.
2253         $this->assertTrue(empty($result['user']));
2254     }
2256     /**
2257      * get_participant should return user details if requested.
2258      */
2259     public function test_get_participant_full_details() {
2260         global $DB;
2261         $this->resetAfterTest(true);
2263         $result = $this->create_assign_with_student_and_teacher();
2264         $assign = $result['assign'];
2265         $student = $result['student'];
2266         $teacher = $result['teacher'];
2267         $course = $result['course'];
2268         $context = context_course::instance($course->id);
2269         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2271         $this->setUser($teacher);
2273         $result = mod_assign_external::get_participant($assign->id, $student->id, true);
2274         // Check some of the extended properties we get when requesting the user.
2275         $this->assertEquals($student->id, $result['id']);
2276         // We should get user infomation back.
2277         $user = $result['user'];
2278         $this->assertFalse(empty($user));
2279         $this->assertEquals($student->firstname, $user['firstname']);
2280         $this->assertEquals($student->lastname, $user['lastname']);
2281         $this->assertEquals($student->email, $user['email']);
2282     }
2284     /**
2285      * get_participant should return group details if a group submission was
2286      * submitted.
2287      */
2288     public function test_get_participant_group_submission() {
2289         global $DB, $CFG;
2290         require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
2292         $this->resetAfterTest(true);
2294         $result = $this->create_assign_with_student_and_teacher(array(
2295             'assignsubmission_onlinetext_enabled' => 1,
2296             'teamsubmission' => 1
2297         ));
2298         $assignmodule = $result['assign'];
2299         $student = $result['student'];
2300         $teacher = $result['teacher'];
2301         $course = $result['course'];
2302         $context = context_course::instance($course->id);
2303         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2304         $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
2305         $cm = get_coursemodule_from_instance('assign', $assignmodule->id);
2306         $context = context_module::instance($cm->id);
2307         $assign = new testable_assign($context, $cm, $course);
2309         groups_add_member($group, $student);
2311         $this->setUser($student);
2312         $submission = $assign->get_group_submission($student->id, $group->id, true);
2313         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2314         $assign->testable_update_submission($submission, $student->id, true, false);
2315         $data = new stdClass();
2316         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
2317                                          'text' => 'Submission text',
2318                                          'format' => FORMAT_MOODLE);
2319         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2320         $plugin->save($submission, $data);
2322         $this->setUser($teacher);
2324         $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false);
2325         // Check some of the extended properties we get when not requesting a summary.
2326         $this->assertEquals($student->id, $result['id']);
2327         $this->assertEquals($group->id, $result['groupid']);
2328         $this->assertEquals($group->name, $result['groupname']);
2329     }
2331     /**
2332      * Test for mod_assign_external::list_participants().
2333      *
2334      * @throws coding_exception
2335      */
2336     public function test_list_participants_user_info_with_special_characters() {
2337         global $CFG, $DB;
2338         $this->resetAfterTest(true);
2339         $CFG->showuseridentity = 'idnumber,email,phone1,phone2,department,institution';
2341         $data = $this->create_assign_with_student_and_teacher();
2342         $assignment = $data['assign'];
2343         $teacher = $data['teacher'];
2345         // Set data for student info that contain special characters.
2346         $student = $data['student'];
2347         $student->idnumber = '<\'"1am@wesome&c00l"\'>';
2348         $student->phone1 = '+63 (999) 888-7777';
2349         $student->phone2 = '(011) [15]4-123-4567';
2350         $student->department = 'Arts & Sciences & \' " ¢ £ © € ¥ ® < >';
2351         $student->institution = 'University of Awesome People & \' " ¢ £ © € ¥ ® < >';
2352         // Assert that we have valid user data.
2353         $this->assertTrue(core_user::validate($student));
2354         // Update the user record.
2355         $DB->update_record('user', $student);
2357         $this->setUser($teacher);
2358         $participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0);
2359         $this->assertCount(1, $participants);
2361         // Asser that we have a valid response data.
2362         $response = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
2363         $this->assertEquals($response, $participants);
2365         // Check participant data.
2366         $participant = $participants[0];
2367         $this->assertEquals($student->idnumber, $participant['idnumber']);
2368         $this->assertEquals($student->email, $participant['email']);
2369         $this->assertEquals($student->phone1, $participant['phone1']);
2370         $this->assertEquals($student->phone2, $participant['phone2']);
2371         $this->assertEquals($student->department, $participant['department']);
2372         $this->assertEquals($student->institution, $participant['institution']);
2373     }
2375     /**
2376      * Test for the type of the user-related properties in mod_assign_external::list_participants_returns().
2377      */
2378     public function test_list_participants_returns_user_property_types() {
2379         // Get user properties.
2380         $userdesc = core_user_external::user_description();
2381         $this->assertTrue(isset($userdesc->keys));
2382         $userproperties = array_keys($userdesc->keys);
2384         // Get returns description for mod_assign_external::list_participants_returns().
2385         $listreturns = mod_assign_external::list_participants_returns();
2386         $this->assertTrue(isset($listreturns->content));
2387         $listreturnsdesc = $listreturns->content->keys;
2389         // Iterate over list returns description's keys.
2390         foreach ($listreturnsdesc as $key => $desc) {
2391             // Check if key exists in user properties and the description has a type attribute.
2392             if (in_array($key, $userproperties) && isset($desc->type)) {
2393                 try {
2394                     // The core_user::get_property_type() method might throw a coding_exception since
2395                     // core_user_external::user_description() might contain properties that are not yet included in
2396                     // core_user's $propertiescache.
2397                     $propertytype = core_user::get_property_type($key);
2399                     // Assert that user-related property types match those of the defined in core_user.
2400                     $this->assertEquals($propertytype, $desc->type);
2401                 } catch (coding_exception $e) {
2402                     // All good.
2403                 }
2404             }
2405         }
2406     }
2408     /**
2409      * Create a a course, assignment module instance, student and teacher and enrol them in
2410      * the course.
2411      *
2412      * @param array $params parameters to be provided to the assignment module creation
2413      * @return array containing the course, assignment module, student and teacher
2414      */
2415     private function create_assign_with_student_and_teacher($params = array()) {
2416         global $DB;
2418         $course = $this->getDataGenerator()->create_course();
2419         $params = array_merge(array(
2420             'course' => $course->id,
2421             'name' => 'assignment',
2422             'intro' => 'assignment intro text',
2423         ), $params);
2425         // Create a course and assignment and users.
2426         $assign = $this->getDataGenerator()->create_module('assign', $params);
2428         $cm = get_coursemodule_from_instance('assign', $assign->id);
2429         $context = context_module::instance($cm->id);
2431         $student = $this->getDataGenerator()->create_user();
2432         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2433         $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
2434         $teacher = $this->getDataGenerator()->create_user();
2435         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2436         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
2438         assign_capability('mod/assign:view', CAP_ALLOW, $teacherrole->id, $context->id, true);
2439         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $teacherrole->id, $context->id, true);
2440         assign_capability('mod/assign:grade', CAP_ALLOW, $teacherrole->id, $context->id, true);
2441         accesslib_clear_all_caches_for_unit_testing();
2443         return array(
2444             'course' => $course,
2445             'assign' => $assign,
2446             'student' => $student,
2447             'teacher' => $teacher
2448         );
2449     }