MDL-55609 mod_assign: Remove shared setUp for all tests
[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');
24 /**
25  * External mod assign functions unit tests
26  *
27  * @package mod_assign
28  * @category external
29  * @copyright 2012 Paul Charsley
30  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31  */
32 class mod_assign_external_testcase extends externallib_advanced_testcase {
34     /**
35      * Test get_grades
36      */
37     public function test_get_grades() {
38         global $DB, $USER;
40         $this->resetAfterTest(true);
41         // Create a course and assignment.
42         $coursedata['idnumber'] = 'idnumbercourse';
43         $coursedata['fullname'] = 'Lightwork Course';
44         $coursedata['summary'] = 'Lightwork Course description';
45         $coursedata['summaryformat'] = FORMAT_MOODLE;
46         $course = self::getDataGenerator()->create_course($coursedata);
48         $assigndata['course'] = $course->id;
49         $assigndata['name'] = 'lightwork assignment';
51         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
53         // Create a manual enrolment record.
54         $manualenroldata['enrol'] = 'manual';
55         $manualenroldata['status'] = 0;
56         $manualenroldata['courseid'] = $course->id;
57         $enrolid = $DB->insert_record('enrol', $manualenroldata);
59         // Create a teacher and give them capabilities.
60         $context = context_course::instance($course->id);
61         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
62         $context = context_module::instance($assign->cmid);
63         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
65         // Create the teacher's enrolment record.
66         $userenrolmentdata['status'] = 0;
67         $userenrolmentdata['enrolid'] = $enrolid;
68         $userenrolmentdata['userid'] = $USER->id;
69         $DB->insert_record('user_enrolments', $userenrolmentdata);
71         // Create a student and give them 2 grades (for 2 attempts).
72         $student = self::getDataGenerator()->create_user();
74         $submission = new stdClass();
75         $submission->assignment = $assign->id;
76         $submission->userid = $student->id;
77         $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
78         $submission->latest = 0;
79         $submission->attemptnumber = 0;
80         $submission->groupid = 0;
81         $submission->timecreated = time();
82         $submission->timemodified = time();
83         $DB->insert_record('assign_submission', $submission);
85         $grade = new stdClass();
86         $grade->assignment = $assign->id;
87         $grade->userid = $student->id;
88         $grade->timecreated = time();
89         $grade->timemodified = $grade->timecreated;
90         $grade->grader = $USER->id;
91         $grade->grade = 50;
92         $grade->attemptnumber = 0;
93         $DB->insert_record('assign_grades', $grade);
95         $submission = new stdClass();
96         $submission->assignment = $assign->id;
97         $submission->userid = $student->id;
98         $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
99         $submission->latest = 1;
100         $submission->attemptnumber = 1;
101         $submission->groupid = 0;
102         $submission->timecreated = time();
103         $submission->timemodified = time();
104         $DB->insert_record('assign_submission', $submission);
106         $grade = new stdClass();
107         $grade->assignment = $assign->id;
108         $grade->userid = $student->id;
109         $grade->timecreated = time();
110         $grade->timemodified = $grade->timecreated;
111         $grade->grader = $USER->id;
112         $grade->grade = 75;
113         $grade->attemptnumber = 1;
114         $DB->insert_record('assign_grades', $grade);
116         $assignmentids[] = $assign->id;
117         $result = mod_assign_external::get_grades($assignmentids);
119         // We need to execute the return values cleaning process to simulate the web service server.
120         $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
122         // Check that the correct grade information for the student is returned.
123         $this->assertEquals(1, count($result['assignments']));
124         $assignment = $result['assignments'][0];
125         $this->assertEquals($assign->id, $assignment['assignmentid']);
126         // Should only get the last grade for this student.
127         $this->assertEquals(1, count($assignment['grades']));
128         $grade = $assignment['grades'][0];
129         $this->assertEquals($student->id, $grade['userid']);
130         // Should be the last grade (not the first).
131         $this->assertEquals(75, $grade['grade']);
132     }
134     /**
135      * Test get_assignments
136      */
137     public function test_get_assignments() {
138         global $DB, $USER, $CFG;
140         $this->resetAfterTest(true);
142         $category = self::getDataGenerator()->create_category(array(
143             'name' => 'Test category'
144         ));
146         // Create a course.
147         $course1 = self::getDataGenerator()->create_course(array(
148             'idnumber' => 'idnumbercourse1',
149             'fullname' => '<b>Lightwork Course 1</b>',      // Adding tags here to check that external_format_string works.
150             'shortname' => '<b>Lightwork Course 1</b>',     // Adding tags here to check that external_format_string works.
151             'summary' => 'Lightwork Course 1 description',
152             'summaryformat' => FORMAT_MOODLE,
153             'category' => $category->id
154         ));
156         // Create a second course, just for testing.
157         $course2 = self::getDataGenerator()->create_course(array(
158             'idnumber' => 'idnumbercourse2',
159             'fullname' => 'Lightwork Course 2',
160             'summary' => 'Lightwork Course 2 description',
161             'summaryformat' => FORMAT_MOODLE,
162             'category' => $category->id
163         ));
165         // Create the assignment module with links to a filerecord.
166         $assign1 = self::getDataGenerator()->create_module('assign', array(
167             'course' => $course1->id,
168             'name' => 'lightwork assignment',
169             'intro' => 'the assignment intro text here <a href="@@PLUGINFILE@@/intro.txt">link</a>',
170             'introformat' => FORMAT_HTML,
171             'markingworkflow' => 1,
172             'markingallocation' => 1
173         ));
175         // Add a file as assignment attachment.
176         $context = context_module::instance($assign1->cmid);
177         $filerecord = array('component' => 'mod_assign', 'filearea' => 'intro', 'contextid' => $context->id, 'itemid' => 0,
178                 'filename' => 'intro.txt', 'filepath' => '/');
179         $fs = get_file_storage();
180         $fs->create_file_from_string($filerecord, 'Test intro file');
182         // Create manual enrolment record.
183         $enrolid = $DB->insert_record('enrol', (object)array(
184             'enrol' => 'manual',
185             'status' => 0,
186             'courseid' => $course1->id
187         ));
189         // Create the user and give them capabilities.
190         $context = context_course::instance($course1->id);
191         $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
192         $context = context_module::instance($assign1->cmid);
193         $this->assignUserCapability('mod/assign:view', $context->id, $roleid);
195         // Create the user enrolment record.
196         $DB->insert_record('user_enrolments', (object)array(
197             'status' => 0,
198             'enrolid' => $enrolid,
199             'userid' => $USER->id
200         ));
202         // Add a file as assignment attachment.
203         $filerecord = array('component' => 'mod_assign', 'filearea' => ASSIGN_INTROATTACHMENT_FILEAREA,
204                 'contextid' => $context->id, 'itemid' => 0,
205                 'filename' => 'introattachment.txt', 'filepath' => '/');
206         $fs = get_file_storage();
207         $fs->create_file_from_string($filerecord, 'Test intro attachment file');
209         $result = mod_assign_external::get_assignments();
211         // We need to execute the return values cleaning process to simulate the web service server.
212         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
214         // Check the course and assignment are returned.
215         $this->assertEquals(1, count($result['courses']));
216         $course = $result['courses'][0];
217         $this->assertEquals('Lightwork Course 1', $course['fullname']);
218         $this->assertEquals('Lightwork Course 1', $course['shortname']);
219         $this->assertEquals(1, count($course['assignments']));
220         $assignment = $course['assignments'][0];
221         $this->assertEquals($assign1->id, $assignment['id']);
222         $this->assertEquals($course1->id, $assignment['course']);
223         $this->assertEquals('lightwork assignment', $assignment['name']);
224         $this->assertContains('the assignment intro text here', $assignment['intro']);
225         $this->assertNotEmpty($assignment['configs']);
226         // Check the url of the file attatched.
227         $this->assertRegExp('@"' . $CFG->wwwroot . '/webservice/pluginfile.php/\d+/mod_assign/intro/intro\.txt"@', $assignment['intro']);
228         $this->assertEquals(1, $assignment['markingworkflow']);
229         $this->assertEquals(1, $assignment['markingallocation']);
230         $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
232         $this->assertCount(1, $assignment['introattachments']);
233         $this->assertEquals('introattachment.txt', $assignment['introattachments'][0]['filename']);
235         // Now, hide the descritption until the submission from date.
236         $DB->set_field('assign', 'alwaysshowdescription', 0, array('id' => $assign1->id));
237         $DB->set_field('assign', 'allowsubmissionsfromdate', time() + DAYSECS, array('id' => $assign1->id));
239         $result = mod_assign_external::get_assignments(array($course1->id));
241         // We need to execute the return values cleaning process to simulate the web service server.
242         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
244         $this->assertEquals(1, count($result['courses']));
245         $course = $result['courses'][0];
246         $this->assertEquals('Lightwork Course 1', $course['fullname']);
247         $this->assertEquals(1, count($course['assignments']));
248         $assignment = $course['assignments'][0];
249         $this->assertEquals($assign1->id, $assignment['id']);
250         $this->assertEquals($course1->id, $assignment['course']);
251         $this->assertEquals('lightwork assignment', $assignment['name']);
252         $this->assertArrayNotHasKey('intro', $assignment);
253         $this->assertArrayNotHasKey('introattachments', $assignment);
254         $this->assertEquals(1, $assignment['markingworkflow']);
255         $this->assertEquals(1, $assignment['markingallocation']);
256         $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
258         $result = mod_assign_external::get_assignments(array($course2->id));
260         // We need to execute the return values cleaning process to simulate the web service server.
261         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
263         $this->assertEquals(0, count($result['courses']));
264         $this->assertEquals(1, count($result['warnings']));
266         // Test with non-enrolled user, but with view capabilities.
267         $this->setAdminUser();
268         $result = mod_assign_external::get_assignments();
269         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
270         $this->assertEquals(0, count($result['courses']));
271         $this->assertEquals(0, count($result['warnings']));
273         // Expect no courses, because we are not using the special flag.
274         $result = mod_assign_external::get_assignments(array($course1->id));
275         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
276         $this->assertCount(0, $result['courses']);
278         // Now use the special flag to return courses where you are not enroled in.
279         $result = mod_assign_external::get_assignments(array($course1->id), array(), true);
280         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
281         $this->assertCount(1, $result['courses']);
283         $course = $result['courses'][0];
284         $this->assertEquals('Lightwork Course 1', $course['fullname']);
285         $this->assertEquals(1, count($course['assignments']));
286         $assignment = $course['assignments'][0];
287         $this->assertEquals($assign1->id, $assignment['id']);
288         $this->assertEquals($course1->id, $assignment['course']);
289         $this->assertEquals('lightwork assignment', $assignment['name']);
290         $this->assertArrayNotHasKey('intro', $assignment);
291         $this->assertArrayNotHasKey('introattachments', $assignment);
292         $this->assertEquals(1, $assignment['markingworkflow']);
293         $this->assertEquals(1, $assignment['markingallocation']);
294         $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
295     }
297     /**
298      * Test get_assignments with submissionstatement.
299      */
300     public function test_get_assignments_with_submissionstatement() {
301         global $DB, $USER, $CFG;
303         $this->resetAfterTest(true);
305         // Setup test data. Create 2 assigns, one with requiresubmissionstatement and the other without it.
306         $course = $this->getDataGenerator()->create_course();
307         $assign = $this->getDataGenerator()->create_module('assign', array(
308             'course' => $course->id,
309             'requiresubmissionstatement' => 1
310         ));
311         $assign2 = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
313         // Create student.
314         $student = self::getDataGenerator()->create_user();
316         // Users enrolments.
317         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
318         $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
320         // Update the submissionstatement.
321         $submissionstatement = 'This is a fake submission statement.';
322         set_config('submissionstatement', $submissionstatement, 'assign');
324         $this->setUser($student);
326         $result = mod_assign_external::get_assignments();
327         // We need to execute the return values cleaning process to simulate the web service server.
328         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
330         // Check that the amount of courses and assignments is right.
331         $this->assertCount(1, $result['courses']);
332         $assignmentsret = $result['courses'][0]['assignments'];
333         $this->assertCount(2, $assignmentsret);
335         // Order the returned assignments by ID.
336         usort($assignmentsret, function($a, $b) {
337             return strcmp($a['id'], $b['id']);
338         });
340         // Check that the first assign contains the submission statement.
341         $assignmentret = $assignmentsret[0];
342         $this->assertEquals($assign->id, $assignmentret['id']);
343         $this->assertEquals(1, $assignmentret['requiresubmissionstatement']);
344         $this->assertEquals($submissionstatement, $assignmentret['submissionstatement']);
346         // Check that the second assign does NOT contain the submission statement.
347         $assignmentret = $assignmentsret[1];
348         $this->assertEquals($assign2->id, $assignmentret['id']);
349         $this->assertEquals(0, $assignmentret['requiresubmissionstatement']);
350         $this->assertArrayNotHasKey('submissionstatement', $assignmentret);
351     }
353     /**
354      * Test get_submissions
355      */
356     public function test_get_submissions() {
357         global $DB, $USER;
359         $this->resetAfterTest(true);
360         // Create a course and assignment.
361         $coursedata['idnumber'] = 'idnumbercourse1';
362         $coursedata['fullname'] = 'Lightwork Course 1';
363         $coursedata['summary'] = 'Lightwork Course 1 description';
364         $coursedata['summaryformat'] = FORMAT_MOODLE;
365         $course1 = self::getDataGenerator()->create_course($coursedata);
367         $assigndata['course'] = $course1->id;
368         $assigndata['name'] = 'lightwork assignment';
370         $assign1 = self::getDataGenerator()->create_module('assign', $assigndata);
372         // Create a student with an online text submission.
373         // First attempt.
374         $student = self::getDataGenerator()->create_user();
375         $submission = new stdClass();
376         $submission->assignment = $assign1->id;
377         $submission->userid = $student->id;
378         $submission->timecreated = time();
379         $submission->timemodified = $submission->timecreated;
380         $submission->status = 'draft';
381         $submission->attemptnumber = 0;
382         $submission->latest = 0;
383         $sid = $DB->insert_record('assign_submission', $submission);
385         // Second attempt.
386         $submission = new stdClass();
387         $submission->assignment = $assign1->id;
388         $submission->userid = $student->id;
389         $submission->timecreated = time();
390         $submission->timemodified = $submission->timecreated;
391         $submission->status = 'submitted';
392         $submission->attemptnumber = 1;
393         $submission->latest = 1;
394         $sid = $DB->insert_record('assign_submission', $submission);
395         $submission->id = $sid;
397         $onlinetextsubmission = new stdClass();
398         $onlinetextsubmission->onlinetext = "<p>online test text</p>";
399         $onlinetextsubmission->onlineformat = 1;
400         $onlinetextsubmission->submission = $submission->id;
401         $onlinetextsubmission->assignment = $assign1->id;
402         $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
404         // Create manual enrolment record.
405         $manualenroldata['enrol'] = 'manual';
406         $manualenroldata['status'] = 0;
407         $manualenroldata['courseid'] = $course1->id;
408         $enrolid = $DB->insert_record('enrol', $manualenroldata);
410         // Create a teacher and give them capabilities.
411         $context = context_course::instance($course1->id);
412         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
413         $context = context_module::instance($assign1->cmid);
414         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
416         // Create the teacher's enrolment record.
417         $userenrolmentdata['status'] = 0;
418         $userenrolmentdata['enrolid'] = $enrolid;
419         $userenrolmentdata['userid'] = $USER->id;
420         $DB->insert_record('user_enrolments', $userenrolmentdata);
422         $assignmentids[] = $assign1->id;
423         $result = mod_assign_external::get_submissions($assignmentids);
424         $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
426         // Check the online text submission is returned.
427         $this->assertEquals(1, count($result['assignments']));
428         $assignment = $result['assignments'][0];
429         $this->assertEquals($assign1->id, $assignment['assignmentid']);
430         $this->assertEquals(1, count($assignment['submissions']));
431         $submission = $assignment['submissions'][0];
432         $this->assertEquals($sid, $submission['id']);
433         $this->assertCount(1, $submission['plugins']);
434         $this->assertEquals('notgraded', $submission['gradingstatus']);
435     }
437     /**
438      * Test get_user_flags
439      */
440     public function test_get_user_flags() {
441         global $DB, $USER;
443         $this->resetAfterTest(true);
444         // Create a course and assignment.
445         $coursedata['idnumber'] = 'idnumbercourse';
446         $coursedata['fullname'] = 'Lightwork Course';
447         $coursedata['summary'] = 'Lightwork Course description';
448         $coursedata['summaryformat'] = FORMAT_MOODLE;
449         $course = self::getDataGenerator()->create_course($coursedata);
451         $assigndata['course'] = $course->id;
452         $assigndata['name'] = 'lightwork assignment';
454         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
456         // Create a manual enrolment record.
457         $manualenroldata['enrol'] = 'manual';
458         $manualenroldata['status'] = 0;
459         $manualenroldata['courseid'] = $course->id;
460         $enrolid = $DB->insert_record('enrol', $manualenroldata);
462         // Create a teacher and give them capabilities.
463         $context = context_course::instance($course->id);
464         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
465         $context = context_module::instance($assign->cmid);
466         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
468         // Create the teacher's enrolment record.
469         $userenrolmentdata['status'] = 0;
470         $userenrolmentdata['enrolid'] = $enrolid;
471         $userenrolmentdata['userid'] = $USER->id;
472         $DB->insert_record('user_enrolments', $userenrolmentdata);
474         // Create a student and give them a user flag record.
475         $student = self::getDataGenerator()->create_user();
476         $userflag = new stdClass();
477         $userflag->assignment = $assign->id;
478         $userflag->userid = $student->id;
479         $userflag->locked = 0;
480         $userflag->mailed = 0;
481         $userflag->extensionduedate = 0;
482         $userflag->workflowstate = 'inmarking';
483         $userflag->allocatedmarker = $USER->id;
485         $DB->insert_record('assign_user_flags', $userflag);
487         $assignmentids[] = $assign->id;
488         $result = mod_assign_external::get_user_flags($assignmentids);
490         // We need to execute the return values cleaning process to simulate the web service server.
491         $result = external_api::clean_returnvalue(mod_assign_external::get_user_flags_returns(), $result);
493         // Check that the correct user flag information for the student is returned.
494         $this->assertEquals(1, count($result['assignments']));
495         $assignment = $result['assignments'][0];
496         $this->assertEquals($assign->id, $assignment['assignmentid']);
497         // Should be one user flag record.
498         $this->assertEquals(1, count($assignment['userflags']));
499         $userflag = $assignment['userflags'][0];
500         $this->assertEquals($student->id, $userflag['userid']);
501         $this->assertEquals(0, $userflag['locked']);
502         $this->assertEquals(0, $userflag['mailed']);
503         $this->assertEquals(0, $userflag['extensionduedate']);
504         $this->assertEquals('inmarking', $userflag['workflowstate']);
505         $this->assertEquals($USER->id, $userflag['allocatedmarker']);
506     }
508     /**
509      * Test get_user_mappings
510      */
511     public function test_get_user_mappings() {
512         global $DB, $USER;
514         $this->resetAfterTest(true);
515         // Create a course and assignment.
516         $coursedata['idnumber'] = 'idnumbercourse';
517         $coursedata['fullname'] = 'Lightwork Course';
518         $coursedata['summary'] = 'Lightwork Course description';
519         $coursedata['summaryformat'] = FORMAT_MOODLE;
520         $course = self::getDataGenerator()->create_course($coursedata);
522         $assigndata['course'] = $course->id;
523         $assigndata['name'] = 'lightwork assignment';
525         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
527         // Create a manual enrolment record.
528         $manualenroldata['enrol'] = 'manual';
529         $manualenroldata['status'] = 0;
530         $manualenroldata['courseid'] = $course->id;
531         $enrolid = $DB->insert_record('enrol', $manualenroldata);
533         // Create a teacher and give them capabilities.
534         $context = context_course::instance($course->id);
535         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
536         $context = context_module::instance($assign->cmid);
537         $this->assignUserCapability('mod/assign:revealidentities', $context->id, $roleid);
539         // Create the teacher's enrolment record.
540         $userenrolmentdata['status'] = 0;
541         $userenrolmentdata['enrolid'] = $enrolid;
542         $userenrolmentdata['userid'] = $USER->id;
543         $DB->insert_record('user_enrolments', $userenrolmentdata);
545         // Create a student and give them a user mapping record.
546         $student = self::getDataGenerator()->create_user();
547         $mapping = new stdClass();
548         $mapping->assignment = $assign->id;
549         $mapping->userid = $student->id;
551         $DB->insert_record('assign_user_mapping', $mapping);
553         $assignmentids[] = $assign->id;
554         $result = mod_assign_external::get_user_mappings($assignmentids);
556         // We need to execute the return values cleaning process to simulate the web service server.
557         $result = external_api::clean_returnvalue(mod_assign_external::get_user_mappings_returns(), $result);
559         // Check that the correct user mapping information for the student is returned.
560         $this->assertEquals(1, count($result['assignments']));
561         $assignment = $result['assignments'][0];
562         $this->assertEquals($assign->id, $assignment['assignmentid']);
563         // Should be one user mapping record.
564         $this->assertEquals(1, count($assignment['mappings']));
565         $mapping = $assignment['mappings'][0];
566         $this->assertEquals($student->id, $mapping['userid']);
567     }
569     /**
570      * Test lock_submissions
571      *
572      * @expectedException moodle_exception
573      */
574     public function test_lock_submissions() {
575         global $DB, $USER;
577         $this->resetAfterTest(true);
578         // Create a course and assignment and users.
579         $course = self::getDataGenerator()->create_course();
581         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
582         $params['course'] = $course->id;
583         $params['assignsubmission_onlinetext_enabled'] = 1;
584         $instance = $generator->create_instance($params);
585         $cm = get_coursemodule_from_instance('assign', $instance->id);
586         $context = context_module::instance($cm->id);
588         $assign = new assign($context, $cm, $course);
590         $student1 = self::getDataGenerator()->create_user();
591         $student2 = self::getDataGenerator()->create_user();
592         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
593         $this->getDataGenerator()->enrol_user($student1->id,
594                                               $course->id,
595                                               $studentrole->id);
596         $this->getDataGenerator()->enrol_user($student2->id,
597                                               $course->id,
598                                               $studentrole->id);
599         $teacher = self::getDataGenerator()->create_user();
600         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
601         $this->getDataGenerator()->enrol_user($teacher->id,
602                                               $course->id,
603                                               $teacherrole->id);
605         // Create a student1 with an online text submission.
606         // Simulate a submission.
607         $this->setUser($student1);
608         $submission = $assign->get_user_submission($student1->id, true);
609         $data = new stdClass();
610         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
611                                          'text'=>'Submission text',
612                                          'format'=>FORMAT_MOODLE);
613         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
614         $plugin->save($submission, $data);
616         // Ready to test.
617         $this->setUser($teacher);
618         $students = array($student1->id, $student2->id);
619         $result = mod_assign_external::lock_submissions($instance->id, $students);
620         $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
622         // Check for 0 warnings.
623         $this->assertEquals(0, count($result));
625         $this->setUser($student2);
626         $submission = $assign->get_user_submission($student2->id, true);
627         $data = new stdClass();
628         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
629                                          'text'=>'Submission text',
630                                          'format'=>FORMAT_MOODLE);
631         $notices = array();
632         $assign->save_submission($data, $notices);
633     }
635     /**
636      * Test unlock_submissions
637      */
638     public function test_unlock_submissions() {
639         global $DB, $USER;
641         $this->resetAfterTest(true);
642         // Create a course and assignment and users.
643         $course = self::getDataGenerator()->create_course();
645         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
646         $params['course'] = $course->id;
647         $params['assignsubmission_onlinetext_enabled'] = 1;
648         $instance = $generator->create_instance($params);
649         $cm = get_coursemodule_from_instance('assign', $instance->id);
650         $context = context_module::instance($cm->id);
652         $assign = new assign($context, $cm, $course);
654         $student1 = self::getDataGenerator()->create_user();
655         $student2 = self::getDataGenerator()->create_user();
656         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
657         $this->getDataGenerator()->enrol_user($student1->id,
658                                               $course->id,
659                                               $studentrole->id);
660         $this->getDataGenerator()->enrol_user($student2->id,
661                                               $course->id,
662                                               $studentrole->id);
663         $teacher = self::getDataGenerator()->create_user();
664         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
665         $this->getDataGenerator()->enrol_user($teacher->id,
666                                               $course->id,
667                                               $teacherrole->id);
669         // Create a student1 with an online text submission.
670         // Simulate a submission.
671         $this->setUser($student1);
672         $submission = $assign->get_user_submission($student1->id, true);
673         $data = new stdClass();
674         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
675                                          'text'=>'Submission text',
676                                          'format'=>FORMAT_MOODLE);
677         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
678         $plugin->save($submission, $data);
680         // Ready to test.
681         $this->setUser($teacher);
682         $students = array($student1->id, $student2->id);
683         $result = mod_assign_external::lock_submissions($instance->id, $students);
684         $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
686         // Check for 0 warnings.
687         $this->assertEquals(0, count($result));
689         $result = mod_assign_external::unlock_submissions($instance->id, $students);
690         $result = external_api::clean_returnvalue(mod_assign_external::unlock_submissions_returns(), $result);
692         // Check for 0 warnings.
693         $this->assertEquals(0, count($result));
695         $this->setUser($student2);
696         $submission = $assign->get_user_submission($student2->id, true);
697         $data = new stdClass();
698         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
699                                          'text'=>'Submission text',
700                                          'format'=>FORMAT_MOODLE);
701         $notices = array();
702         $assign->save_submission($data, $notices);
703     }
705     /**
706      * Test submit_for_grading
707      */
708     public function test_submit_for_grading() {
709         global $DB, $USER;
711         $this->resetAfterTest(true);
712         // Create a course and assignment and users.
713         $course = self::getDataGenerator()->create_course();
715         set_config('submissionreceipts', 0, 'assign');
716         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
717         $params['course'] = $course->id;
718         $params['assignsubmission_onlinetext_enabled'] = 1;
719         $params['submissiondrafts'] = 1;
720         $params['sendnotifications'] = 0;
721         $params['requiresubmissionstatement'] = 1;
722         $instance = $generator->create_instance($params);
723         $cm = get_coursemodule_from_instance('assign', $instance->id);
724         $context = context_module::instance($cm->id);
726         $assign = new assign($context, $cm, $course);
728         $student1 = self::getDataGenerator()->create_user();
729         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
730         $this->getDataGenerator()->enrol_user($student1->id,
731                                               $course->id,
732                                               $studentrole->id);
734         // Create a student1 with an online text submission.
735         // Simulate a submission.
736         $this->setUser($student1);
737         $submission = $assign->get_user_submission($student1->id, true);
738         $data = new stdClass();
739         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
740                                          'text'=>'Submission text',
741                                          'format'=>FORMAT_MOODLE);
742         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
743         $plugin->save($submission, $data);
745         $result = mod_assign_external::submit_for_grading($instance->id, false);
746         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
748         // Should be 1 fail because the submission statement was not aceptted.
749         $this->assertEquals(1, count($result));
751         $result = mod_assign_external::submit_for_grading($instance->id, true);
752         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
754         // Check for 0 warnings.
755         $this->assertEquals(0, count($result));
757         $submission = $assign->get_user_submission($student1->id, false);
759         $this->assertEquals(ASSIGN_SUBMISSION_STATUS_SUBMITTED, $submission->status);
760     }
762     /**
763      * Test save_user_extensions
764      */
765     public function test_save_user_extensions() {
766         global $DB, $USER;
768         $this->resetAfterTest(true);
769         // Create a course and assignment and users.
770         $course = self::getDataGenerator()->create_course();
772         $teacher = self::getDataGenerator()->create_user();
773         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
774         $this->getDataGenerator()->enrol_user($teacher->id,
775                                               $course->id,
776                                               $teacherrole->id);
777         $this->setUser($teacher);
779         $now = time();
780         $yesterday = $now - 24*60*60;
781         $tomorrow = $now + 24*60*60;
782         set_config('submissionreceipts', 0, 'assign');
783         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
784         $params['course'] = $course->id;
785         $params['submissiondrafts'] = 1;
786         $params['sendnotifications'] = 0;
787         $params['duedate'] = $yesterday;
788         $params['cutoffdate'] = $now - 10;
789         $instance = $generator->create_instance($params);
790         $cm = get_coursemodule_from_instance('assign', $instance->id);
791         $context = context_module::instance($cm->id);
793         $assign = new assign($context, $cm, $course);
795         $student1 = self::getDataGenerator()->create_user();
796         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
797         $this->getDataGenerator()->enrol_user($student1->id,
798                                               $course->id,
799                                               $studentrole->id);
801         $this->setUser($student1);
802         $result = mod_assign_external::submit_for_grading($instance->id, true);
803         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
805         // Check for 0 warnings.
806         $this->assertEquals(1, count($result));
808         $this->setUser($teacher);
809         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
810         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
811         $this->assertEquals(1, count($result));
813         $this->setUser($teacher);
814         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($yesterday - 10));
815         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
816         $this->assertEquals(1, count($result));
818         $this->setUser($teacher);
819         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($tomorrow));
820         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
821         $this->assertEquals(0, count($result));
823         $this->setUser($student1);
824         $result = mod_assign_external::submit_for_grading($instance->id, true);
825         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
826         $this->assertEquals(0, count($result));
828         $this->setUser($student1);
829         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
830         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
832     }
834     /**
835      * Test reveal_identities
836      *
837      * @expectedException required_capability_exception
838      */
839     public function test_reveal_identities() {
840         global $DB, $USER;
842         $this->resetAfterTest(true);
843         // Create a course and assignment and users.
844         $course = self::getDataGenerator()->create_course();
846         $teacher = self::getDataGenerator()->create_user();
847         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
848         $this->getDataGenerator()->enrol_user($teacher->id,
849                                               $course->id,
850                                               $teacherrole->id);
851         $this->setUser($teacher);
853         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
854         $params['course'] = $course->id;
855         $params['submissiondrafts'] = 1;
856         $params['sendnotifications'] = 0;
857         $params['blindmarking'] = 1;
858         $instance = $generator->create_instance($params);
859         $cm = get_coursemodule_from_instance('assign', $instance->id);
860         $context = context_module::instance($cm->id);
862         $assign = new assign($context, $cm, $course);
864         $student1 = self::getDataGenerator()->create_user();
865         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
866         $this->getDataGenerator()->enrol_user($student1->id,
867                                               $course->id,
868                                               $studentrole->id);
870         $this->setUser($student1);
871         $result = mod_assign_external::reveal_identities($instance->id);
872         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
873         $this->assertEquals(1, count($result));
874         $this->assertEquals(true, $assign->is_blind_marking());
876         $this->setUser($teacher);
877         $result = mod_assign_external::reveal_identities($instance->id);
878         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
879         $this->assertEquals(0, count($result));
880         $this->assertEquals(false, $assign->is_blind_marking());
882         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
883         $params['course'] = $course->id;
884         $params['submissiondrafts'] = 1;
885         $params['sendnotifications'] = 0;
886         $params['blindmarking'] = 0;
887         $instance = $generator->create_instance($params);
888         $cm = get_coursemodule_from_instance('assign', $instance->id);
889         $context = context_module::instance($cm->id);
891         $assign = new assign($context, $cm, $course);
892         $result = mod_assign_external::reveal_identities($instance->id);
893         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
894         $this->assertEquals(1, count($result));
895         $this->assertEquals(false, $assign->is_blind_marking());
897     }
899     /**
900      * Test revert_submissions_to_draft
901      */
902     public function test_revert_submissions_to_draft() {
903         global $DB, $USER;
905         $this->resetAfterTest(true);
906         set_config('submissionreceipts', 0, 'assign');
907         // Create a course and assignment and users.
908         $course = self::getDataGenerator()->create_course();
910         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
911         $params['course'] = $course->id;
912         $params['sendnotifications'] = 0;
913         $params['submissiondrafts'] = 1;
914         $instance = $generator->create_instance($params);
915         $cm = get_coursemodule_from_instance('assign', $instance->id);
916         $context = context_module::instance($cm->id);
918         $assign = new assign($context, $cm, $course);
920         $student1 = self::getDataGenerator()->create_user();
921         $student2 = self::getDataGenerator()->create_user();
922         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
923         $this->getDataGenerator()->enrol_user($student1->id,
924                                               $course->id,
925                                               $studentrole->id);
926         $this->getDataGenerator()->enrol_user($student2->id,
927                                               $course->id,
928                                               $studentrole->id);
929         $teacher = self::getDataGenerator()->create_user();
930         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
931         $this->getDataGenerator()->enrol_user($teacher->id,
932                                               $course->id,
933                                               $teacherrole->id);
935         // Create a student1 with an online text submission.
936         // Simulate a submission.
937         $this->setUser($student1);
938         $result = mod_assign_external::submit_for_grading($instance->id, true);
939         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
940         $this->assertEquals(0, count($result));
942         // Ready to test.
943         $this->setUser($teacher);
944         $students = array($student1->id, $student2->id);
945         $result = mod_assign_external::revert_submissions_to_draft($instance->id, array($student1->id));
946         $result = external_api::clean_returnvalue(mod_assign_external::revert_submissions_to_draft_returns(), $result);
948         // Check for 0 warnings.
949         $this->assertEquals(0, count($result));
951     }
953     /**
954      * Test save_submission
955      */
956     public function test_save_submission() {
957         global $DB, $USER;
959         $this->resetAfterTest(true);
960         // Create a course and assignment and users.
961         $course = self::getDataGenerator()->create_course();
963         $teacher = self::getDataGenerator()->create_user();
964         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
965         $this->getDataGenerator()->enrol_user($teacher->id,
966                                               $course->id,
967                                               $teacherrole->id);
968         $this->setUser($teacher);
970         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
971         $params['course'] = $course->id;
972         $params['assignsubmission_onlinetext_enabled'] = 1;
973         $params['assignsubmission_file_enabled'] = 1;
974         $params['assignsubmission_file_maxfiles'] = 5;
975         $params['assignsubmission_file_maxsizebytes'] = 1024*1024;
976         $instance = $generator->create_instance($params);
977         $cm = get_coursemodule_from_instance('assign', $instance->id);
978         $context = context_module::instance($cm->id);
980         $assign = new assign($context, $cm, $course);
982         $student1 = self::getDataGenerator()->create_user();
983         $student2 = self::getDataGenerator()->create_user();
984         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
985         $this->getDataGenerator()->enrol_user($student1->id,
986                                               $course->id,
987                                               $studentrole->id);
988         $this->getDataGenerator()->enrol_user($student2->id,
989                                               $course->id,
990                                               $studentrole->id);
991         // Create a student1 with an online text submission.
992         // Simulate a submission.
993         $this->setUser($student1);
995         // Create a file in a draft area.
996         $draftidfile = file_get_unused_draft_itemid();
998         $usercontext = context_user::instance($student1->id);
999         $filerecord = array(
1000             'contextid' => $usercontext->id,
1001             'component' => 'user',
1002             'filearea'  => 'draft',
1003             'itemid'    => $draftidfile,
1004             'filepath'  => '/',
1005             'filename'  => 'testtext.txt',
1006         );
1008         $fs = get_file_storage();
1009         $fs->create_file_from_string($filerecord, 'text contents');
1011         // Create another file in a different draft area.
1012         $draftidonlinetext = file_get_unused_draft_itemid();
1014         $filerecord = array(
1015             'contextid' => $usercontext->id,
1016             'component' => 'user',
1017             'filearea'  => 'draft',
1018             'itemid'    => $draftidonlinetext,
1019             'filepath'  => '/',
1020             'filename'  => 'shouldbeanimage.txt',
1021         );
1023         $fs->create_file_from_string($filerecord, 'image contents (not really)');
1025         // Now try a submission.
1026         $submissionpluginparams = array();
1027         $submissionpluginparams['files_filemanager'] = $draftidfile;
1028         $onlinetexteditorparams = array('text' => '<p>Yeeha!</p>',
1029                                         'format'=>1,
1030                                         'itemid'=>$draftidonlinetext);
1031         $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
1032         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1033         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1035         $this->assertEquals(0, count($result));
1037         // Set up a due and cutoff passed date.
1038         $instance->duedate = time() - WEEKSECS;
1039         $instance->cutoffdate = time() - WEEKSECS;
1040         $DB->update_record('assign', $instance);
1042         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1043         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1045         $this->assertCount(1, $result);
1046         $this->assertEquals(get_string('duedatereached', 'assign'), $result[0]['item']);
1047     }
1049     /**
1050      * Test save_grade
1051      */
1052     public function test_save_grade() {
1053         global $DB, $USER;
1055         $this->resetAfterTest(true);
1056         // Create a course and assignment and users.
1057         $course = self::getDataGenerator()->create_course();
1059         $teacher = self::getDataGenerator()->create_user();
1060         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1061         $this->getDataGenerator()->enrol_user($teacher->id,
1062                                               $course->id,
1063                                               $teacherrole->id);
1064         $this->setUser($teacher);
1066         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1067         $params['course'] = $course->id;
1068         $params['assignfeedback_file_enabled'] = 1;
1069         $params['assignfeedback_comments_enabled'] = 1;
1070         $instance = $generator->create_instance($params);
1071         $cm = get_coursemodule_from_instance('assign', $instance->id);
1072         $context = context_module::instance($cm->id);
1074         $assign = new assign($context, $cm, $course);
1076         $student1 = self::getDataGenerator()->create_user();
1077         $student2 = self::getDataGenerator()->create_user();
1078         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1079         $this->getDataGenerator()->enrol_user($student1->id,
1080                                               $course->id,
1081                                               $studentrole->id);
1082         $this->getDataGenerator()->enrol_user($student2->id,
1083                                               $course->id,
1084                                               $studentrole->id);
1085         // Simulate a grade.
1086         $this->setUser($teacher);
1088         // Create a file in a draft area.
1089         $draftidfile = file_get_unused_draft_itemid();
1091         $usercontext = context_user::instance($teacher->id);
1092         $filerecord = array(
1093             'contextid' => $usercontext->id,
1094             'component' => 'user',
1095             'filearea'  => 'draft',
1096             'itemid'    => $draftidfile,
1097             'filepath'  => '/',
1098             'filename'  => 'testtext.txt',
1099         );
1101         $fs = get_file_storage();
1102         $fs->create_file_from_string($filerecord, 'text contents');
1104         // Now try a grade.
1105         $feedbackpluginparams = array();
1106         $feedbackpluginparams['files_filemanager'] = $draftidfile;
1107         $feedbackeditorparams = array('text' => 'Yeeha!',
1108                                         'format' => 1);
1109         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1110         $result = mod_assign_external::save_grade($instance->id,
1111                                                   $student1->id,
1112                                                   50.0,
1113                                                   -1,
1114                                                   true,
1115                                                   'released',
1116                                                   false,
1117                                                   $feedbackpluginparams);
1118         // No warnings.
1119         $this->assertNull($result);
1121         $result = mod_assign_external::get_grades(array($instance->id));
1122         $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
1124         $this->assertEquals($result['assignments'][0]['grades'][0]['grade'], '50.0');
1125     }
1127     /**
1128      * Test save grades with advanced grading data
1129      */
1130     public function test_save_grades_with_advanced_grading() {
1131         global $DB, $USER;
1133         $this->resetAfterTest(true);
1134         // Create a course and assignment and users.
1135         $course = self::getDataGenerator()->create_course();
1137         $teacher = self::getDataGenerator()->create_user();
1138         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1139         $this->getDataGenerator()->enrol_user($teacher->id,
1140                                               $course->id,
1141                                               $teacherrole->id);
1143         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1144         $params['course'] = $course->id;
1145         $params['assignfeedback_file_enabled'] = 0;
1146         $params['assignfeedback_comments_enabled'] = 0;
1147         $instance = $generator->create_instance($params);
1148         $cm = get_coursemodule_from_instance('assign', $instance->id);
1149         $context = context_module::instance($cm->id);
1151         $assign = new assign($context, $cm, $course);
1153         $student1 = self::getDataGenerator()->create_user();
1154         $student2 = self::getDataGenerator()->create_user();
1155         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1156         $this->getDataGenerator()->enrol_user($student1->id,
1157                                               $course->id,
1158                                               $studentrole->id);
1159         $this->getDataGenerator()->enrol_user($student2->id,
1160                                               $course->id,
1161                                               $studentrole->id);
1163         $this->setUser($teacher);
1165         $feedbackpluginparams = array();
1166         $feedbackpluginparams['files_filemanager'] = 0;
1167         $feedbackeditorparams = array('text' => '', 'format' => 1);
1168         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1170         // Create advanced grading data.
1171         // Create grading area.
1172         $gradingarea = array(
1173             'contextid' => $context->id,
1174             'component' => 'mod_assign',
1175             'areaname' => 'submissions',
1176             'activemethod' => 'rubric'
1177         );
1178         $areaid = $DB->insert_record('grading_areas', $gradingarea);
1180         // Create a rubric grading definition.
1181         $rubricdefinition = array (
1182             'areaid' => $areaid,
1183             'method' => 'rubric',
1184             'name' => 'test',
1185             'status' => 20,
1186             'copiedfromid' => 1,
1187             'timecreated' => 1,
1188             'usercreated' => $teacher->id,
1189             'timemodified' => 1,
1190             'usermodified' => $teacher->id,
1191             'timecopied' => 0
1192         );
1193         $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
1195         // Create a criterion with a level.
1196         $rubriccriteria = array (
1197              'definitionid' => $definitionid,
1198              'sortorder' => 1,
1199              'description' => 'Demonstrate an understanding of disease control',
1200              'descriptionformat' => 0
1201         );
1202         $criterionid = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria);
1203         $rubriclevel1 = array (
1204             'criterionid' => $criterionid,
1205             'score' => 50,
1206             'definition' => 'pass',
1207             'definitionformat' => 0
1208         );
1209         $rubriclevel2 = array (
1210             'criterionid' => $criterionid,
1211             'score' => 100,
1212             'definition' => 'excellent',
1213             'definitionformat' => 0
1214         );
1215         $rubriclevel3 = array (
1216             'criterionid' => $criterionid,
1217             'score' => 0,
1218             'definition' => 'fail',
1219             'definitionformat' => 0
1220         );
1221         $levelid1 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
1222         $levelid2 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
1223         $levelid3 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel3);
1225         // Create the filling.
1226         $student1filling = array (
1227             'criterionid' => $criterionid,
1228             'levelid' => $levelid1,
1229             'remark' => 'well done you passed',
1230             'remarkformat' => 0
1231         );
1233         $student2filling = array (
1234             'criterionid' => $criterionid,
1235             'levelid' => $levelid2,
1236             'remark' => 'Excellent work',
1237             'remarkformat' => 0
1238         );
1240         $student1criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student1filling)));
1241         $student1advancedgradingdata = array('rubric' => array('criteria' => $student1criteria));
1243         $student2criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student2filling)));
1244         $student2advancedgradingdata = array('rubric' => array('criteria' => $student2criteria));
1246         $grades = array();
1247         $student1gradeinfo = array();
1248         $student1gradeinfo['userid'] = $student1->id;
1249         $student1gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1250         $student1gradeinfo['attemptnumber'] = -1;
1251         $student1gradeinfo['addattempt'] = true;
1252         $student1gradeinfo['workflowstate'] = 'released';
1253         $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1254         $student1gradeinfo['advancedgradingdata'] = $student1advancedgradingdata;
1255         $grades[] = $student1gradeinfo;
1257         $student2gradeinfo = array();
1258         $student2gradeinfo['userid'] = $student2->id;
1259         $student2gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1260         $student2gradeinfo['attemptnumber'] = -1;
1261         $student2gradeinfo['addattempt'] = true;
1262         $student2gradeinfo['workflowstate'] = 'released';
1263         $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1264         $student2gradeinfo['advancedgradingdata'] = $student2advancedgradingdata;
1265         $grades[] = $student2gradeinfo;
1267         $result = mod_assign_external::save_grades($instance->id, false, $grades);
1268         $this->assertNull($result);
1270         $student1grade = $DB->get_record('assign_grades',
1271                                          array('userid' => $student1->id, 'assignment' => $instance->id),
1272                                          '*',
1273                                          MUST_EXIST);
1274         $this->assertEquals($student1grade->grade, '50.0');
1276         $student2grade = $DB->get_record('assign_grades',
1277                                          array('userid' => $student2->id, 'assignment' => $instance->id),
1278                                          '*',
1279                                          MUST_EXIST);
1280         $this->assertEquals($student2grade->grade, '100.0');
1281     }
1283     /**
1284      * Test save grades for a team submission
1285      *
1286      * @expectedException invalid_parameter_exception
1287      */
1288     public function test_save_grades_with_group_submission() {
1289         global $DB, $USER, $CFG;
1290         require_once($CFG->dirroot . '/group/lib.php');
1292         $this->resetAfterTest(true);
1293         // Create a course and assignment and users.
1294         $course = self::getDataGenerator()->create_course();
1296         $teacher = self::getDataGenerator()->create_user();
1297         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1298         $this->getDataGenerator()->enrol_user($teacher->id,
1299                                               $course->id,
1300                                               $teacherrole->id);
1302         $groupingdata = array();
1303         $groupingdata['courseid'] = $course->id;
1304         $groupingdata['name'] = 'Group assignment grouping';
1306         $grouping = self::getDataGenerator()->create_grouping($groupingdata);
1308         $group1data = array();
1309         $group1data['courseid'] = $course->id;
1310         $group1data['name'] = 'Team 1';
1311         $group2data = array();
1312         $group2data['courseid'] = $course->id;
1313         $group2data['name'] = 'Team 2';
1315         $group1 = self::getDataGenerator()->create_group($group1data);
1316         $group2 = self::getDataGenerator()->create_group($group2data);
1318         groups_assign_grouping($grouping->id, $group1->id);
1319         groups_assign_grouping($grouping->id, $group2->id);
1321         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1322         $params['course'] = $course->id;
1323         $params['teamsubmission'] = 1;
1324         $params['teamsubmissiongroupingid'] = $grouping->id;
1325         $instance = $generator->create_instance($params);
1326         $cm = get_coursemodule_from_instance('assign', $instance->id);
1327         $context = context_module::instance($cm->id);
1329         $assign = new assign($context, $cm, $course);
1331         $student1 = self::getDataGenerator()->create_user();
1332         $student2 = self::getDataGenerator()->create_user();
1333         $student3 = self::getDataGenerator()->create_user();
1334         $student4 = self::getDataGenerator()->create_user();
1335         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1336         $this->getDataGenerator()->enrol_user($student1->id,
1337                                               $course->id,
1338                                               $studentrole->id);
1339         $this->getDataGenerator()->enrol_user($student2->id,
1340                                               $course->id,
1341                                               $studentrole->id);
1342         $this->getDataGenerator()->enrol_user($student3->id,
1343                                               $course->id,
1344                                               $studentrole->id);
1345         $this->getDataGenerator()->enrol_user($student4->id,
1346                                               $course->id,
1347                                               $studentrole->id);
1349         groups_add_member($group1->id, $student1->id);
1350         groups_add_member($group1->id, $student2->id);
1351         groups_add_member($group1->id, $student3->id);
1352         groups_add_member($group2->id, $student4->id);
1353         $this->setUser($teacher);
1355         $feedbackpluginparams = array();
1356         $feedbackpluginparams['files_filemanager'] = 0;
1357         $feedbackeditorparams = array('text' => '', 'format' => 1);
1358         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1360         $grades1 = array();
1361         $student1gradeinfo = array();
1362         $student1gradeinfo['userid'] = $student1->id;
1363         $student1gradeinfo['grade'] = 50;
1364         $student1gradeinfo['attemptnumber'] = -1;
1365         $student1gradeinfo['addattempt'] = true;
1366         $student1gradeinfo['workflowstate'] = 'released';
1367         $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1368         $grades1[] = $student1gradeinfo;
1370         $student2gradeinfo = array();
1371         $student2gradeinfo['userid'] = $student2->id;
1372         $student2gradeinfo['grade'] = 75;
1373         $student2gradeinfo['attemptnumber'] = -1;
1374         $student2gradeinfo['addattempt'] = true;
1375         $student2gradeinfo['workflowstate'] = 'released';
1376         $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1377         $grades1[] = $student2gradeinfo;
1379         // Expect an exception since 2 grades have been submitted for the same team.
1380         $result = mod_assign_external::save_grades($instance->id, true, $grades1);
1381         $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
1383         $grades2 = array();
1384         $student3gradeinfo = array();
1385         $student3gradeinfo['userid'] = $student3->id;
1386         $student3gradeinfo['grade'] = 50;
1387         $student3gradeinfo['attemptnumber'] = -1;
1388         $student3gradeinfo['addattempt'] = true;
1389         $student3gradeinfo['workflowstate'] = 'released';
1390         $student3gradeinfo['plugindata'] = $feedbackpluginparams;
1391         $grades2[] = $student3gradeinfo;
1393         $student4gradeinfo = array();
1394         $student4gradeinfo['userid'] = $student4->id;
1395         $student4gradeinfo['grade'] = 75;
1396         $student4gradeinfo['attemptnumber'] = -1;
1397         $student4gradeinfo['addattempt'] = true;
1398         $student4gradeinfo['workflowstate'] = 'released';
1399         $student4gradeinfo['plugindata'] = $feedbackpluginparams;
1400         $grades2[] = $student4gradeinfo;
1401         $result = mod_assign_external::save_grades($instance->id, true, $grades2);
1402         $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
1403         // There should be no warnings.
1404         $this->assertEquals(0, count($result));
1406         $student3grade = $DB->get_record('assign_grades',
1407                                          array('userid' => $student3->id, 'assignment' => $instance->id),
1408                                          '*',
1409                                          MUST_EXIST);
1410         $this->assertEquals($student3grade->grade, '50.0');
1412         $student4grade = $DB->get_record('assign_grades',
1413                                          array('userid' => $student4->id, 'assignment' => $instance->id),
1414                                          '*',
1415                                          MUST_EXIST);
1416         $this->assertEquals($student4grade->grade, '75.0');
1417     }
1419     /**
1420      * Test copy_previous_attempt
1421      */
1422     public function test_copy_previous_attempt() {
1423         global $DB, $USER;
1425         $this->resetAfterTest(true);
1426         // Create a course and assignment and users.
1427         $course = self::getDataGenerator()->create_course();
1429         $teacher = self::getDataGenerator()->create_user();
1430         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1431         $this->getDataGenerator()->enrol_user($teacher->id,
1432                                               $course->id,
1433                                               $teacherrole->id);
1434         $this->setUser($teacher);
1436         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1437         $params['course'] = $course->id;
1438         $params['assignsubmission_onlinetext_enabled'] = 1;
1439         $params['assignsubmission_file_enabled'] = 0;
1440         $params['assignfeedback_file_enabled'] = 0;
1441         $params['attemptreopenmethod'] = 'manual';
1442         $params['maxattempts'] = 5;
1443         $instance = $generator->create_instance($params);
1444         $cm = get_coursemodule_from_instance('assign', $instance->id);
1445         $context = context_module::instance($cm->id);
1447         $assign = new assign($context, $cm, $course);
1449         $student1 = self::getDataGenerator()->create_user();
1450         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
1451         $this->getDataGenerator()->enrol_user($student1->id,
1452                                               $course->id,
1453                                               $studentrole->id);
1454         // Now try a submission.
1455         $this->setUser($student1);
1456         $draftidonlinetext = file_get_unused_draft_itemid();
1457         $submissionpluginparams = array();
1458         $onlinetexteditorparams = array('text'=>'Yeeha!',
1459                                         'format'=>1,
1460                                         'itemid'=>$draftidonlinetext);
1461         $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
1462         $submissionpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1463         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1464         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1466         $this->setUser($teacher);
1467         // Add a grade and reopen the attempt.
1468         // Now try a grade.
1469         $feedbackpluginparams = array();
1470         $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1471         $feedbackeditorparams = array('text'=>'Yeeha!',
1472                                         'format'=>1);
1473         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1474         $result = mod_assign_external::save_grade($instance->id,
1475                                                   $student1->id,
1476                                                   50.0,
1477                                                   -1,
1478                                                   true,
1479                                                   'released',
1480                                                   false,
1481                                                   $feedbackpluginparams);
1482         $this->assertNull($result);
1484         $this->setUser($student1);
1485         // Now copy the previous attempt.
1486         $result = mod_assign_external::copy_previous_attempt($instance->id);
1487         $result = external_api::clean_returnvalue(mod_assign_external::copy_previous_attempt_returns(), $result);
1488         // No warnings.
1489         $this->assertEquals(0, count($result));
1491         $this->setUser($teacher);
1492         $result = mod_assign_external::get_submissions(array($instance->id));
1493         $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
1495         // Check we are now on the second attempt.
1496         $this->assertEquals($result['assignments'][0]['submissions'][0]['attemptnumber'], 1);
1497         // Check the plugins data is not empty.
1498         $this->assertNotEmpty($result['assignments'][0]['submissions'][0]['plugins']);
1500     }
1502     /**
1503      * Test set_user_flags
1504      */
1505     public function test_set_user_flags() {
1506         global $DB, $USER;
1508         $this->resetAfterTest(true);
1509         // Create a course and assignment.
1510         $coursedata['idnumber'] = 'idnumbercourse';
1511         $coursedata['fullname'] = 'Lightwork Course';
1512         $coursedata['summary'] = 'Lightwork Course description';
1513         $coursedata['summaryformat'] = FORMAT_MOODLE;
1514         $course = self::getDataGenerator()->create_course($coursedata);
1516         $assigndata['course'] = $course->id;
1517         $assigndata['name'] = 'lightwork assignment';
1519         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
1521         // Create a manual enrolment record.
1522         $manualenroldata['enrol'] = 'manual';
1523         $manualenroldata['status'] = 0;
1524         $manualenroldata['courseid'] = $course->id;
1525         $enrolid = $DB->insert_record('enrol', $manualenroldata);
1527         // Create a teacher and give them capabilities.
1528         $context = context_course::instance($course->id);
1529         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
1530         $context = context_module::instance($assign->cmid);
1531         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
1533         // Create the teacher's enrolment record.
1534         $userenrolmentdata['status'] = 0;
1535         $userenrolmentdata['enrolid'] = $enrolid;
1536         $userenrolmentdata['userid'] = $USER->id;
1537         $DB->insert_record('user_enrolments', $userenrolmentdata);
1539         // Create a student.
1540         $student = self::getDataGenerator()->create_user();
1542         // Create test user flags record.
1543         $userflags = array();
1544         $userflag['userid'] = $student->id;
1545         $userflag['workflowstate'] = 'inmarking';
1546         $userflag['allocatedmarker'] = $USER->id;
1547         $userflags = array($userflag);
1549         $createduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
1550         // We need to execute the return values cleaning process to simulate the web service server.
1551         $createduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $createduserflags);
1553         $this->assertEquals($student->id, $createduserflags[0]['userid']);
1554         $createduserflag = $DB->get_record('assign_user_flags', array('id' => $createduserflags[0]['id']));
1556         // Confirm that all data was inserted correctly.
1557         $this->assertEquals($student->id,  $createduserflag->userid);
1558         $this->assertEquals($assign->id, $createduserflag->assignment);
1559         $this->assertEquals(0, $createduserflag->locked);
1560         $this->assertEquals(2, $createduserflag->mailed);
1561         $this->assertEquals(0, $createduserflag->extensionduedate);
1562         $this->assertEquals('inmarking', $createduserflag->workflowstate);
1563         $this->assertEquals($USER->id, $createduserflag->allocatedmarker);
1565         // Create update data.
1566         $userflags = array();
1567         $userflag['userid'] = $createduserflag->userid;
1568         $userflag['workflowstate'] = 'readyforreview';
1569         $userflags = array($userflag);
1571         $updateduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
1572         // We need to execute the return values cleaning process to simulate the web service server.
1573         $updateduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $updateduserflags);
1575         $this->assertEquals($student->id, $updateduserflags[0]['userid']);
1576         $updateduserflag = $DB->get_record('assign_user_flags', array('id' => $updateduserflags[0]['id']));
1578         // Confirm that all data was updated correctly.
1579         $this->assertEquals($student->id,  $updateduserflag->userid);
1580         $this->assertEquals($assign->id, $updateduserflag->assignment);
1581         $this->assertEquals(0, $updateduserflag->locked);
1582         $this->assertEquals(2, $updateduserflag->mailed);
1583         $this->assertEquals(0, $updateduserflag->extensionduedate);
1584         $this->assertEquals('readyforreview', $updateduserflag->workflowstate);
1585         $this->assertEquals($USER->id, $updateduserflag->allocatedmarker);
1586     }
1588     /**
1589      * Test view_grading_table
1590      *
1591      * @expectedException dml_missing_record_exception
1592      */
1593     public function test_view_grading_table_invalid_instance() {
1594         global $DB;
1596         $this->resetAfterTest(true);
1598         // Setup test data.
1599         $course = $this->getDataGenerator()->create_course();
1600         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1601         $context = context_module::instance($assign->cmid);
1602         $cm = get_coursemodule_from_instance('assign', $assign->id);
1604         // Test invalid instance id.
1605         mod_assign_external::view_grading_table(0);
1606     }
1608     /**
1609      * Test view_grading_table
1610      *
1611      * @expectedException require_login_exception
1612      */
1613     public function test_view_grading_table_not_enrolled() {
1614         global $DB;
1616         $this->resetAfterTest(true);
1618         // Setup test data.
1619         $course = $this->getDataGenerator()->create_course();
1620         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1621         $context = context_module::instance($assign->cmid);
1622         $cm = get_coursemodule_from_instance('assign', $assign->id);
1624         // Test not-enrolled user.
1625         $user = self::getDataGenerator()->create_user();
1626         $this->setUser($user);
1628         mod_assign_external::view_grading_table($assign->id);
1629     }
1631     /**
1632      * Test view_grading_table
1633      */
1634     public function test_view_grading_table_correct() {
1635         global $DB;
1637         $this->resetAfterTest(true);
1639         // Setup test data.
1640         $course = $this->getDataGenerator()->create_course();
1641         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1642         $context = context_module::instance($assign->cmid);
1643         $cm = get_coursemodule_from_instance('assign', $assign->id);
1645         // Test user with full capabilities.
1646         $user = self::getDataGenerator()->create_user();
1647         $this->setUser($user);
1648         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1649         $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1651         // Trigger and capture the event.
1652         $sink = $this->redirectEvents();
1654         $result = mod_assign_external::view_grading_table($assign->id);
1655         $result = external_api::clean_returnvalue(mod_assign_external::view_grading_table_returns(), $result);
1657         $events = $sink->get_events();
1658         $this->assertCount(1, $events);
1659         $event = array_shift($events);
1661         // Checking that the event contains the expected values.
1662         $this->assertInstanceOf('\mod_assign\event\grading_table_viewed', $event);
1663         $this->assertEquals($context, $event->get_context());
1664         $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1665         $this->assertEquals($moodleurl, $event->get_url());
1666         $this->assertEventContextNotUsed($event);
1667         $this->assertNotEmpty($event->get_name());
1668     }
1670     /**
1671      * Test view_grading_table
1672      *
1673      * @expectedException        require_login_exception
1674      * @expectedExceptionMessage Course or activity not accessible. (Activity is hidden)
1675      */
1676     public function test_view_grading_table_without_capability() {
1677         global $DB;
1679         $this->resetAfterTest(true);
1681         // Setup test data.
1682         $course = $this->getDataGenerator()->create_course();
1683         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1684         $context = context_module::instance($assign->cmid);
1685         $cm = get_coursemodule_from_instance('assign', $assign->id);
1687         // Test user with no capabilities.
1688         $user = self::getDataGenerator()->create_user();
1689         $this->setUser($user);
1690         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1691         $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1693         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1694         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1695         assign_capability('mod/assign:view', CAP_PROHIBIT, $teacherrole->id, $context->id);
1696         // Empty all the caches that may be affected by this change.
1697         accesslib_clear_all_caches_for_unit_testing();
1698         course_modinfo::clear_instance_cache();
1700         mod_assign_external::view_grading_table($assign->id);
1701     }
1703     /**
1704      * Test subplugins availability
1705      */
1706     public function test_subplugins_availability() {
1707         global $CFG;
1709         require_once($CFG->dirroot . '/mod/assign/adminlib.php');
1710         $this->resetAfterTest(true);
1712         // Hide assignment file submissiong plugin.
1713         $pluginmanager = new assign_plugin_manager('assignsubmission');
1714         $pluginmanager->hide_plugin('file');
1715         $parameters = mod_assign_external::save_submission_parameters();
1717         $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1719         // Show it again and check that the value is returned as optional.
1720         $pluginmanager->show_plugin('file');
1721         $parameters = mod_assign_external::save_submission_parameters();
1722         $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1723         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1725         // Hide feedback file submissiong plugin.
1726         $pluginmanager = new assign_plugin_manager('assignfeedback');
1727         $pluginmanager->hide_plugin('file');
1729         $parameters = mod_assign_external::save_grade_parameters();
1731         $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1733         // Show it again and check that the value is returned as optional.
1734         $pluginmanager->show_plugin('file');
1735         $parameters = mod_assign_external::save_grade_parameters();
1737         $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1738         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1740         // Check a different one.
1741         $pluginmanager->show_plugin('comments');
1742         $this->assertTrue(isset($parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']));
1743         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']->required);
1744     }
1746     /**
1747      * Test test_view_submission_status
1748      */
1749     public function test_view_submission_status() {
1750         global $DB;
1752         $this->resetAfterTest(true);
1754         $this->setAdminUser();
1755         // Setup test data.
1756         $course = $this->getDataGenerator()->create_course();
1757         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1758         $context = context_module::instance($assign->cmid);
1759         $cm = get_coursemodule_from_instance('assign', $assign->id);
1761         // Test invalid instance id.
1762         try {
1763             mod_assign_external::view_submission_status(0);
1764             $this->fail('Exception expected due to invalid mod_assign instance id.');
1765         } catch (moodle_exception $e) {
1766             $this->assertEquals('invalidrecord', $e->errorcode);
1767         }
1769         // Test not-enrolled user.
1770         $user = self::getDataGenerator()->create_user();
1771         $this->setUser($user);
1772         try {
1773             mod_assign_external::view_submission_status($assign->id);
1774             $this->fail('Exception expected due to not enrolled user.');
1775         } catch (moodle_exception $e) {
1776             $this->assertEquals('requireloginerror', $e->errorcode);
1777         }
1779         // Test user with full capabilities.
1780         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1781         $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
1783         // Trigger and capture the event.
1784         $sink = $this->redirectEvents();
1786         $result = mod_assign_external::view_submission_status($assign->id);
1787         $result = external_api::clean_returnvalue(mod_assign_external::view_submission_status_returns(), $result);
1789         $events = $sink->get_events();
1790         $this->assertCount(1, $events);
1791         $event = array_shift($events);
1793         // Checking that the event contains the expected values.
1794         $this->assertInstanceOf('\mod_assign\event\submission_status_viewed', $event);
1795         $this->assertEquals($context, $event->get_context());
1796         $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1797         $this->assertEquals($moodleurl, $event->get_url());
1798         $this->assertEventContextNotUsed($event);
1799         $this->assertNotEmpty($event->get_name());
1801         // Test user with no capabilities.
1802         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1803         assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id);
1804         accesslib_clear_all_caches_for_unit_testing();
1805         course_modinfo::clear_instance_cache();
1807         try {
1808             mod_assign_external::view_submission_status($assign->id);
1809             $this->fail('Exception expected due to missing capability.');
1810         } catch (moodle_exception $e) {
1811             $this->assertEquals('requireloginerror', $e->errorcode);
1812         }
1813     }
1815     /**
1816      * Create a submission for testing the get_submission_status function.
1817      * @param  boolean $submitforgrading whether to submit for grading the submission
1818      * @return array an array containing all the required data for testing
1819      */
1820     private function create_submission_for_testing_status($submitforgrading = false) {
1821         global $DB, $CFG;
1822         require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
1824         // Create a course and assignment and users.
1825         $course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1));
1827         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1828         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1830         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1831         $params = array(
1832             'course' => $course->id,
1833             'assignsubmission_file_maxfiles' => 1,
1834             'assignsubmission_file_maxsizebytes' => 1024 * 1024,
1835             'assignsubmission_onlinetext_enabled' => 1,
1836             'assignsubmission_file_enabled' => 1,
1837             'submissiondrafts' => 1,
1838             'assignfeedback_file_enabled' => 1,
1839             'assignfeedback_comments_enabled' => 1,
1840             'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1841             'sendnotifications' => 0
1842         );
1844         set_config('submissionreceipts', 0, 'assign');
1846         $instance = $generator->create_instance($params);
1847         $cm = get_coursemodule_from_instance('assign', $instance->id);
1848         $context = context_module::instance($cm->id);
1850         $assign = new testable_assign($context, $cm, $course);
1852         $student1 = self::getDataGenerator()->create_user();
1853         $student2 = self::getDataGenerator()->create_user();
1854         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1855         $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
1856         $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
1857         $teacher = self::getDataGenerator()->create_user();
1858         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1859         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
1861         $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student1->id));
1862         $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $teacher->id));
1863         $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $student2->id));
1864         $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $teacher->id));
1866         $this->setUser($student1);
1868         // Create a student1 with an online text submission.
1869         // Simulate a submission.
1870         $submission = $assign->get_user_submission($student1->id, true);
1872         $data = new stdClass();
1873         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1874                                          'text' => 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>',
1875                                          'format' => FORMAT_MOODLE);
1877         $draftidfile = file_get_unused_draft_itemid();
1878         $usercontext = context_user::instance($student1->id);
1879         $filerecord = array(
1880             'contextid' => $usercontext->id,
1881             'component' => 'user',
1882             'filearea'  => 'draft',
1883             'itemid'    => $draftidfile,
1884             'filepath'  => '/',
1885             'filename'  => 't.txt',
1886         );
1887         $fs = get_file_storage();
1888         $fs->create_file_from_string($filerecord, 'text contents');
1890         $data->files_filemanager = $draftidfile;
1892         $notices = array();
1893         $assign->save_submission($data, $notices);
1895         if ($submitforgrading) {
1896             // Now, submit the draft for grading.
1897             $notices = array();
1899             $data = new stdClass;
1900             $data->userid = $student1->id;
1901             $assign->submit_for_grading($data, $notices);
1902         }
1904         return array($assign, $instance, $student1, $student2, $teacher, $group1, $group2);
1905     }
1907     /**
1908      * Test get_submission_status for a draft submission.
1909      */
1910     public function test_get_submission_status_in_draft_status() {
1911         $this->resetAfterTest(true);
1913         list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status();
1914         $studentsubmission = $assign->get_user_submission($student1->id, true);
1916         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1917         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1918         $this->assertDebuggingCalled();
1920         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1922         // The submission is now in draft mode.
1923         $this->assertCount(0, $result['warnings']);
1924         $this->assertFalse(isset($result['gradingsummary']));
1925         $this->assertFalse(isset($result['feedback']));
1926         $this->assertFalse(isset($result['previousattempts']));
1928         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1929         $this->assertTrue($result['lastattempt']['canedit']);
1930         $this->assertTrue($result['lastattempt']['cansubmit']);
1931         $this->assertFalse($result['lastattempt']['locked']);
1932         $this->assertFalse($result['lastattempt']['graded']);
1933         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1934         $this->assertFalse($result['lastattempt']['blindmarking']);
1935         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1936         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1938         $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
1939         $this->assertEquals(0, $result['lastattempt']['submission']['attemptnumber']);
1940         $this->assertEquals('draft', $result['lastattempt']['submission']['status']);
1941         $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
1942         $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
1943         $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
1945         // Map plugins based on their type - we can't rely on them being in a
1946         // particular order, especially if 3rd party plugins are installed.
1947         $submissionplugins = array();
1948         foreach ($result['lastattempt']['submission']['plugins'] as $plugin) {
1949             $submissionplugins[$plugin['type']] = $plugin;
1950         }
1952         // Format expected online text.
1953         $onlinetext = 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>';
1954         list($expectedtext, $expectedformat) = external_format_text($onlinetext, FORMAT_HTML, $assign->get_context()->id,
1955                 'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $studentsubmission->id);
1957         $this->assertEquals($expectedtext, $submissionplugins['onlinetext']['editorfields'][0]['text']);
1958         $this->assertEquals($expectedformat, $submissionplugins['onlinetext']['editorfields'][0]['format']);
1959         $this->assertEquals('/', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']);
1960         $this->assertEquals('t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filename']);
1961     }
1963     /**
1964      * Test get_submission_status for a submitted submission.
1965      */
1966     public function test_get_submission_status_in_submission_status() {
1967         $this->resetAfterTest(true);
1969         list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
1971         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1972         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1973         $this->assertDebuggingCalled();
1974         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1976         $this->assertCount(0, $result['warnings']);
1977         $this->assertFalse(isset($result['gradingsummary']));
1978         $this->assertFalse(isset($result['feedback']));
1979         $this->assertFalse(isset($result['previousattempts']));
1981         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1982         $this->assertFalse($result['lastattempt']['canedit']);
1983         $this->assertFalse($result['lastattempt']['cansubmit']);
1984         $this->assertFalse($result['lastattempt']['locked']);
1985         $this->assertFalse($result['lastattempt']['graded']);
1986         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1987         $this->assertFalse($result['lastattempt']['blindmarking']);
1988         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1989         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1991     }
1993     /**
1994      * Test get_submission_status using the teacher role.
1995      */
1996     public function test_get_submission_status_in_submission_status_for_teacher() {
1997         $this->resetAfterTest(true);
1999         list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
2001         // Now, as teacher, see the grading summary.
2002         $this->setUser($teacher);
2003         // First one group.
2004         $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g1->id);
2005         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2006         $this->assertDebuggingCalled();
2007         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2009         $this->assertCount(0, $result['warnings']);
2010         $this->assertFalse(isset($result['lastattempt']));
2011         $this->assertFalse(isset($result['feedback']));
2012         $this->assertFalse(isset($result['previousattempts']));
2014         $this->assertEquals(1, $result['gradingsummary']['participantcount']);
2015         $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
2016         $this->assertEquals(1, $result['gradingsummary']['submissionsenabled']);
2017         $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
2018         $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']);  // One student from G1 submitted.
2019         $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']);    // One student from G1 submitted.
2020         $this->assertFalse($result['gradingsummary']['warnofungroupedusers']);
2022         // Second group.
2023         $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g2->id);
2024         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2025         $this->assertCount(0, $result['warnings']);
2026         $this->assertEquals(1, $result['gradingsummary']['participantcount']);
2027         $this->assertEquals(0, $result['gradingsummary']['submissionssubmittedcount']); // G2 students didn't submit yet.
2028         $this->assertEquals(0, $result['gradingsummary']['submissionsneedgradingcount']);   // G2 students didn't submit yet.
2030         // Should return also 1 participant if we allow the function to auto-select the group.
2031         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2032         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2033         $this->assertCount(0, $result['warnings']);
2034         $this->assertEquals(1, $result['gradingsummary']['participantcount']);
2035         $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
2036         $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']); // One student from G1 submitted.
2037         $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']); // One student from G1 submitted.
2039         // Now check draft submissions.
2040         list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(false);
2041         $this->setUser($teacher);
2042         $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g1->id);
2043         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2044         $this->assertCount(0, $result['warnings']);
2045         $this->assertEquals(1, $result['gradingsummary']['participantcount']);
2046         $this->assertEquals(1, $result['gradingsummary']['submissiondraftscount']); // We have a draft submission.
2047         $this->assertEquals(0, $result['gradingsummary']['submissionssubmittedcount']); // We have only draft submissions.
2048         $this->assertEquals(0, $result['gradingsummary']['submissionsneedgradingcount']); // We have only draft submissions.
2049     }
2051     /**
2052      * Test get_submission_status for a reopened submission.
2053      */
2054     public function test_get_submission_status_in_reopened_status() {
2055         global $USER;
2057         $this->resetAfterTest(true);
2059         list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
2060         $studentsubmission = $assign->get_user_submission($student1->id, true);
2062         $this->setUser($teacher);
2063         // Grade and reopen.
2064         $feedbackpluginparams = array();
2065         $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
2066         $feedbackeditorparams = array('text' => 'Yeeha!',
2067                                         'format' => 1);
2068         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
2069         $result = mod_assign_external::save_grade($instance->id,
2070                                                   $student1->id,
2071                                                   50.0,
2072                                                   -1,
2073                                                   false,
2074                                                   'released',
2075                                                   false,
2076                                                   $feedbackpluginparams);
2077         $USER->ignoresesskey = true;
2078         $assign->testable_process_add_attempt($student1->id);
2080         $this->setUser($student1);
2082         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2083         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2084         $this->assertDebuggingCalled();
2085         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2087         $this->assertCount(0, $result['warnings']);
2088         $this->assertFalse(isset($result['gradingsummary']));
2090         $this->assertTrue($result['lastattempt']['submissionsenabled']);
2091         $this->assertTrue($result['lastattempt']['canedit']);
2092         $this->assertFalse($result['lastattempt']['cansubmit']);
2093         $this->assertFalse($result['lastattempt']['locked']);
2094         $this->assertFalse($result['lastattempt']['graded']);
2095         $this->assertEmpty($result['lastattempt']['extensionduedate']);
2096         $this->assertFalse($result['lastattempt']['blindmarking']);
2097         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
2098         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
2100         // Check new attempt reopened.
2101         $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
2102         $this->assertEquals(1, $result['lastattempt']['submission']['attemptnumber']);
2103         $this->assertEquals('reopened', $result['lastattempt']['submission']['status']);
2104         $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
2105         $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
2106         $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
2107         $this->assertCount(3, $result['lastattempt']['submission']['plugins']);
2109         // Now see feedback and the attempts history (remember, is a submission reopened).
2110         // Only 2 fields (no grade, no plugins data).
2111         $this->assertCount(2, $result['feedback']);
2113         // One previous attempt.
2114         $this->assertCount(1, $result['previousattempts']);
2115         $this->assertEquals(0, $result['previousattempts'][0]['attemptnumber']);
2116         $this->assertEquals(50, $result['previousattempts'][0]['grade']['grade']);
2117         $this->assertEquals($teacher->id, $result['previousattempts'][0]['grade']['grader']);
2118         $this->assertEquals($student1->id, $result['previousattempts'][0]['grade']['userid']);
2120         // Map plugins based on their type - we can't rely on them being in a
2121         // particular order, especially if 3rd party plugins are installed.
2122         $feedbackplugins = array();
2123         foreach ($result['previousattempts'][0]['feedbackplugins'] as $plugin) {
2124             $feedbackplugins[$plugin['type']] = $plugin;
2125         }
2126         $this->assertEquals('Yeeha!', $feedbackplugins['comments']['editorfields'][0]['text']);
2128         $submissionplugins = array();
2129         foreach ($result['previousattempts'][0]['submission']['plugins'] as $plugin) {
2130             $submissionplugins[$plugin['type']] = $plugin;
2131         }
2132         // Format expected online text.
2133         $onlinetext = 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>';
2134         list($expectedtext, $expectedformat) = external_format_text($onlinetext, FORMAT_HTML, $assign->get_context()->id,
2135                 'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $studentsubmission->id);
2137         $this->assertEquals($expectedtext, $submissionplugins['onlinetext']['editorfields'][0]['text']);
2138         $this->assertEquals($expectedformat, $submissionplugins['onlinetext']['editorfields'][0]['format']);
2139         $this->assertEquals('/', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']);
2140         $this->assertEquals('t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filename']);
2142     }
2144     /**
2145      * Test access control for get_submission_status.
2146      *
2147      * @expectedException required_capability_exception
2148      */
2149     public function test_get_submission_status_access_control() {
2150         $this->resetAfterTest(true);
2152         list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status();
2154         $this->setUser($student2);
2156         // Access control test.
2157         mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id);
2159     }
2161     /**
2162      * get_participant should throw an excaption if the requested assignment doesn't exist.
2163      *
2164      * @expectedException moodle_exception
2165      */
2166     public function test_get_participant_no_assignment() {
2167         $this->resetAfterTest(true);
2168         mod_assign_external::get_participant('-1', '-1', false);
2169     }
2171     /**
2172      * get_participant should throw a require_login_exception if the user doesn't have access
2173      * to view assignments.
2174      *
2175      * @expectedException require_login_exception
2176      */
2177     public function test_get_participant_no_view_capability() {
2178         global $DB;
2179         $this->resetAfterTest(true);
2181         $result = $this->create_assign_with_student_and_teacher();
2182         $assign = $result['assign'];
2183         $student = $result['student'];
2184         $course = $result['course'];
2185         $context = context_course::instance($course->id);
2186         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2188         $this->setUser($student);
2189         assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id, true);
2191         mod_assign_external::get_participant($assign->id, $student->id, false);
2192     }
2194     /**
2195      * get_participant should throw a required_capability_exception if the user doesn't have access
2196      * to view assignment grades.
2197      *
2198      * @expectedException required_capability_exception
2199      */
2200     public function test_get_participant_no_grade_capability() {
2201         global $DB;
2202         $this->resetAfterTest(true);
2204         $result = $this->create_assign_with_student_and_teacher();
2205         $assign = $result['assign'];
2206         $student = $result['student'];
2207         $teacher = $result['teacher'];
2208         $course = $result['course'];
2209         $context = context_course::instance($course->id);
2210         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2212         $this->setUser($teacher);
2213         assign_capability('mod/assign:viewgrades', CAP_PROHIBIT, $teacherrole->id, $context->id, true);
2214         assign_capability('mod/assign:grade', CAP_PROHIBIT, $teacherrole->id, $context->id, true);
2215         accesslib_clear_all_caches_for_unit_testing();
2217         mod_assign_external::get_participant($assign->id, $student->id, false);
2218     }
2220     /**
2221      * get_participant should throw an exception if the user isn't enrolled in the course.
2222      *
2223      * @expectedException moodle_exception
2224      */
2225     public function test_get_participant_no_participant() {
2226         global $DB;
2227         $this->resetAfterTest(true);
2229         $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true));
2230         $student = $this->getDataGenerator()->create_user();
2231         $assign = $result['assign'];
2232         $teacher = $result['teacher'];
2234         $this->setUser($teacher);
2236         $result = mod_assign_external::get_participant($assign->id, $student->id, false);
2237         $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2238     }
2240     /**
2241      * get_participant should return a summarised list of details with a different fullname if blind
2242      * marking is on for the requested assignment.
2243      */
2244     public function test_get_participant_blind_marking() {
2245         global $DB;
2246         $this->resetAfterTest(true);
2248         $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true));
2249         $assign = $result['assign'];
2250         $student = $result['student'];
2251         $teacher = $result['teacher'];
2252         $course = $result['course'];
2253         $context = context_course::instance($course->id);
2254         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2256         $this->setUser($teacher);
2258         $result = mod_assign_external::get_participant($assign->id, $student->id, true);
2259         $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2260         $this->assertEquals($student->id, $result['id']);
2261         $this->assertFalse(fullname($student) == $result['fullname']);
2262         $this->assertFalse($result['submitted']);
2263         $this->assertFalse($result['requiregrading']);
2264         $this->assertFalse($result['grantedextension']);
2265         $this->assertTrue($result['blindmarking']);
2266         // Make sure we don't get any additional info.
2267         $this->assertArrayNotHasKey('user', $result);
2268     }
2270     /**
2271      * get_participant should return a summarised list of details if requested.
2272      */
2273     public function test_get_participant_no_user() {
2274         global $DB;
2275         $this->resetAfterTest(true);
2277         $result = $this->create_assign_with_student_and_teacher();
2278         $assignmodule = $result['assign'];
2279         $student = $result['student'];
2280         $teacher = $result['teacher'];
2281         $course = $result['course'];
2282         $context = context_course::instance($course->id);
2283         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2285         // Create an assign instance to save a submission.
2286         set_config('submissionreceipts', 0, 'assign');
2288         $cm = get_coursemodule_from_instance('assign', $assignmodule->id);
2289         $context = context_module::instance($cm->id);
2291         $assign = new assign($context, $cm, $course);
2293         $this->setUser($student);
2295         // Simulate a submission.
2296         $data = new stdClass();
2297         $data->onlinetext_editor = array(
2298             'itemid' => file_get_unused_draft_itemid(),
2299             'text' => 'Student submission text',
2300             'format' => FORMAT_MOODLE
2301         );
2303         $notices = array();
2304         $assign->save_submission($data, $notices);
2306         $data = new stdClass;
2307         $data->userid = $student->id;
2308         $assign->submit_for_grading($data, array());
2310         $this->setUser($teacher);
2312         $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false);
2313         $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2314         $this->assertEquals($student->id, $result['id']);
2315         $this->assertEquals(fullname($student), $result['fullname']);
2316         $this->assertTrue($result['submitted']);
2317         $this->assertTrue($result['requiregrading']);
2318         $this->assertFalse($result['grantedextension']);
2319         $this->assertFalse($result['blindmarking']);
2320         // Make sure we don't get any additional info.
2321         $this->assertArrayNotHasKey('user', $result);
2322     }
2324     /**
2325      * get_participant should return user details if requested.
2326      */
2327     public function test_get_participant_full_details() {
2328         global $DB;
2329         $this->resetAfterTest(true);
2331         $result = $this->create_assign_with_student_and_teacher();
2332         $assign = $result['assign'];
2333         $student = $result['student'];
2334         $teacher = $result['teacher'];
2335         $course = $result['course'];
2336         $context = context_course::instance($course->id);
2337         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2339         $this->setUser($teacher);
2341         $result = mod_assign_external::get_participant($assign->id, $student->id, true);
2342         $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2343         // Check some of the extended properties we get when requesting the user.
2344         $this->assertEquals($student->id, $result['id']);
2345         // We should get user infomation back.
2346         $user = $result['user'];
2347         $this->assertFalse(empty($user));
2348         $this->assertEquals($student->firstname, $user['firstname']);
2349         $this->assertEquals($student->lastname, $user['lastname']);
2350         $this->assertEquals($student->email, $user['email']);
2351     }
2353     /**
2354      * get_participant should return group details if a group submission was
2355      * submitted.
2356      */
2357     public function test_get_participant_group_submission() {
2358         global $DB, $CFG;
2359         require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
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 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);
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);
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     }