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