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