Merge branch 'MDL-52924-master' of git://github.com/jleyva/moodle
[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;
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' => 'Lightwork Course 1',
157             'summary' => 'Lightwork Course 1 description',
158             'summaryformat' => FORMAT_MOODLE,
159             'category' => $category->id
160         ));
162         // Create a second course, just for testing.
163         $course2 = self::getDataGenerator()->create_course(array(
164             'idnumber' => 'idnumbercourse2',
165             'fullname' => 'Lightwork Course 2',
166             'summary' => 'Lightwork Course 2 description',
167             'summaryformat' => FORMAT_MOODLE,
168             'category' => $category->id
169         ));
171         // Create the assignment module.
172         $assign1 = self::getDataGenerator()->create_module('assign', array(
173             'course' => $course1->id,
174             'name' => 'lightwork assignment',
175             'intro' => 'the assignment intro text here',
176             'markingworkflow' => 1,
177             'markingallocation' => 1
178         ));
180         // Create manual enrolment record.
181         $enrolid = $DB->insert_record('enrol', (object)array(
182             'enrol' => 'manual',
183             'status' => 0,
184             'courseid' => $course1->id
185         ));
187         // Create the user and give them capabilities.
188         $context = context_course::instance($course1->id);
189         $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
190         $context = context_module::instance($assign1->cmid);
191         $this->assignUserCapability('mod/assign:view', $context->id, $roleid);
193         // Create the user enrolment record.
194         $DB->insert_record('user_enrolments', (object)array(
195             'status' => 0,
196             'enrolid' => $enrolid,
197             'userid' => $USER->id
198         ));
200         // Add a file as assignment attachment.
201         $filerecord = array('component' => 'mod_assign', 'filearea' => ASSIGN_INTROATTACHMENT_FILEAREA,
202                 'contextid' => $context->id, 'itemid' => 0,
203                 'filename' => 'introattachment.txt', 'filepath' => '/');
204         $fs = get_file_storage();
205         $fs->create_file_from_string($filerecord, 'Test intro attachment file');
207         $result = mod_assign_external::get_assignments();
209         // We need to execute the return values cleaning process to simulate the web service server.
210         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
212         // Check the course and assignment are returned.
213         $this->assertEquals(1, count($result['courses']));
214         $course = $result['courses'][0];
215         $this->assertEquals('Lightwork Course 1', $course['fullname']);
216         $this->assertEquals(1, count($course['assignments']));
217         $assignment = $course['assignments'][0];
218         $this->assertEquals($assign1->id, $assignment['id']);
219         $this->assertEquals($course1->id, $assignment['course']);
220         $this->assertEquals('lightwork assignment', $assignment['name']);
221         $this->assertEquals('the assignment intro text here', $assignment['intro']);
222         $this->assertEquals(1, $assignment['markingworkflow']);
223         $this->assertEquals(1, $assignment['markingallocation']);
225         $this->assertCount(1, $assignment['introattachments']);
226         $this->assertEquals('introattachment.txt', $assignment['introattachments'][0]['filename']);
228         // Now, hide the descritption until the submission from date.
229         $DB->set_field('assign', 'alwaysshowdescription', 0, array('id' => $assign1->id));
230         $DB->set_field('assign', 'allowsubmissionsfromdate', time() + DAYSECS, array('id' => $assign1->id));
232         $result = mod_assign_external::get_assignments(array($course1->id));
234         // We need to execute the return values cleaning process to simulate the web service server.
235         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
237         $this->assertEquals(1, count($result['courses']));
238         $course = $result['courses'][0];
239         $this->assertEquals('Lightwork Course 1', $course['fullname']);
240         $this->assertEquals(1, count($course['assignments']));
241         $assignment = $course['assignments'][0];
242         $this->assertEquals($assign1->id, $assignment['id']);
243         $this->assertEquals($course1->id, $assignment['course']);
244         $this->assertEquals('lightwork assignment', $assignment['name']);
245         $this->assertArrayNotHasKey('intro', $assignment);
246         $this->assertArrayNotHasKey('introattachments', $assignment);
247         $this->assertEquals(1, $assignment['markingworkflow']);
248         $this->assertEquals(1, $assignment['markingallocation']);
250         $result = mod_assign_external::get_assignments(array($course2->id));
252         // We need to execute the return values cleaning process to simulate the web service server.
253         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
255         $this->assertEquals(0, count($result['courses']));
256         $this->assertEquals(1, count($result['warnings']));
258         // Test with non-enrolled user, but with view capabilities.
259         $this->setAdminUser();
260         $result = mod_assign_external::get_assignments();
261         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
262         $this->assertEquals(0, count($result['courses']));
263         $this->assertEquals(0, count($result['warnings']));
265         // Expect no courses, because we are not using the special flag.
266         $result = mod_assign_external::get_assignments(array($course1->id));
267         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
268         $this->assertCount(0, $result['courses']);
270         // Now use the special flag to return courses where you are not enroled in.
271         $result = mod_assign_external::get_assignments(array($course1->id), array(), true);
272         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
273         $this->assertCount(1, $result['courses']);
275         $course = $result['courses'][0];
276         $this->assertEquals('Lightwork Course 1', $course['fullname']);
277         $this->assertEquals(1, count($course['assignments']));
278         $assignment = $course['assignments'][0];
279         $this->assertEquals($assign1->id, $assignment['id']);
280         $this->assertEquals($course1->id, $assignment['course']);
281         $this->assertEquals('lightwork assignment', $assignment['name']);
282         $this->assertArrayNotHasKey('intro', $assignment);
283         $this->assertArrayNotHasKey('introattachments', $assignment);
284         $this->assertEquals(1, $assignment['markingworkflow']);
285         $this->assertEquals(1, $assignment['markingallocation']);
286     }
288     /**
289      * Test get_submissions
290      */
291     public function test_get_submissions() {
292         global $DB, $USER;
294         $this->resetAfterTest(true);
295         // Create a course and assignment.
296         $coursedata['idnumber'] = 'idnumbercourse1';
297         $coursedata['fullname'] = 'Lightwork Course 1';
298         $coursedata['summary'] = 'Lightwork Course 1 description';
299         $coursedata['summaryformat'] = FORMAT_MOODLE;
300         $course1 = self::getDataGenerator()->create_course($coursedata);
302         $assigndata['course'] = $course1->id;
303         $assigndata['name'] = 'lightwork assignment';
305         $assign1 = self::getDataGenerator()->create_module('assign', $assigndata);
307         // Create a student with an online text submission.
308         // First attempt.
309         $student = self::getDataGenerator()->create_user();
310         $submission = new stdClass();
311         $submission->assignment = $assign1->id;
312         $submission->userid = $student->id;
313         $submission->timecreated = time();
314         $submission->timemodified = $submission->timecreated;
315         $submission->status = 'draft';
316         $submission->attemptnumber = 0;
317         $submission->latest = 0;
318         $sid = $DB->insert_record('assign_submission', $submission);
320         // Second attempt.
321         $submission = new stdClass();
322         $submission->assignment = $assign1->id;
323         $submission->userid = $student->id;
324         $submission->timecreated = time();
325         $submission->timemodified = $submission->timecreated;
326         $submission->status = 'submitted';
327         $submission->attemptnumber = 1;
328         $submission->latest = 1;
329         $sid = $DB->insert_record('assign_submission', $submission);
330         $submission->id = $sid;
332         $onlinetextsubmission = new stdClass();
333         $onlinetextsubmission->onlinetext = "<p>online test text</p>";
334         $onlinetextsubmission->onlineformat = 1;
335         $onlinetextsubmission->submission = $submission->id;
336         $onlinetextsubmission->assignment = $assign1->id;
337         $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
339         // Create manual enrolment record.
340         $manualenroldata['enrol'] = 'manual';
341         $manualenroldata['status'] = 0;
342         $manualenroldata['courseid'] = $course1->id;
343         $enrolid = $DB->insert_record('enrol', $manualenroldata);
345         // Create a teacher and give them capabilities.
346         $context = context_course::instance($course1->id);
347         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
348         $context = context_module::instance($assign1->cmid);
349         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
351         // Create the teacher's enrolment record.
352         $userenrolmentdata['status'] = 0;
353         $userenrolmentdata['enrolid'] = $enrolid;
354         $userenrolmentdata['userid'] = $USER->id;
355         $DB->insert_record('user_enrolments', $userenrolmentdata);
357         $assignmentids[] = $assign1->id;
358         $result = mod_assign_external::get_submissions($assignmentids);
359         $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
361         // Check the online text submission is returned.
362         $this->assertEquals(1, count($result['assignments']));
363         $assignment = $result['assignments'][0];
364         $this->assertEquals($assign1->id, $assignment['assignmentid']);
365         $this->assertEquals(1, count($assignment['submissions']));
366         $submission = $assignment['submissions'][0];
367         $this->assertEquals($sid, $submission['id']);
368         $this->assertCount(1, $submission['plugins']);
369     }
371     /**
372      * Test get_user_flags
373      */
374     public function test_get_user_flags() {
375         global $DB, $USER;
377         $this->resetAfterTest(true);
378         // Create a course and assignment.
379         $coursedata['idnumber'] = 'idnumbercourse';
380         $coursedata['fullname'] = 'Lightwork Course';
381         $coursedata['summary'] = 'Lightwork Course description';
382         $coursedata['summaryformat'] = FORMAT_MOODLE;
383         $course = self::getDataGenerator()->create_course($coursedata);
385         $assigndata['course'] = $course->id;
386         $assigndata['name'] = 'lightwork assignment';
388         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
390         // Create a manual enrolment record.
391         $manualenroldata['enrol'] = 'manual';
392         $manualenroldata['status'] = 0;
393         $manualenroldata['courseid'] = $course->id;
394         $enrolid = $DB->insert_record('enrol', $manualenroldata);
396         // Create a teacher and give them capabilities.
397         $context = context_course::instance($course->id);
398         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
399         $context = context_module::instance($assign->cmid);
400         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
402         // Create the teacher's enrolment record.
403         $userenrolmentdata['status'] = 0;
404         $userenrolmentdata['enrolid'] = $enrolid;
405         $userenrolmentdata['userid'] = $USER->id;
406         $DB->insert_record('user_enrolments', $userenrolmentdata);
408         // Create a student and give them a user flag record.
409         $student = self::getDataGenerator()->create_user();
410         $userflag = new stdClass();
411         $userflag->assignment = $assign->id;
412         $userflag->userid = $student->id;
413         $userflag->locked = 0;
414         $userflag->mailed = 0;
415         $userflag->extensionduedate = 0;
416         $userflag->workflowstate = 'inmarking';
417         $userflag->allocatedmarker = $USER->id;
419         $DB->insert_record('assign_user_flags', $userflag);
421         $assignmentids[] = $assign->id;
422         $result = mod_assign_external::get_user_flags($assignmentids);
424         // We need to execute the return values cleaning process to simulate the web service server.
425         $result = external_api::clean_returnvalue(mod_assign_external::get_user_flags_returns(), $result);
427         // Check that the correct user flag information for the student is returned.
428         $this->assertEquals(1, count($result['assignments']));
429         $assignment = $result['assignments'][0];
430         $this->assertEquals($assign->id, $assignment['assignmentid']);
431         // Should be one user flag record.
432         $this->assertEquals(1, count($assignment['userflags']));
433         $userflag = $assignment['userflags'][0];
434         $this->assertEquals($student->id, $userflag['userid']);
435         $this->assertEquals(0, $userflag['locked']);
436         $this->assertEquals(0, $userflag['mailed']);
437         $this->assertEquals(0, $userflag['extensionduedate']);
438         $this->assertEquals('inmarking', $userflag['workflowstate']);
439         $this->assertEquals($USER->id, $userflag['allocatedmarker']);
440     }
442     /**
443      * Test get_user_mappings
444      */
445     public function test_get_user_mappings() {
446         global $DB, $USER;
448         $this->resetAfterTest(true);
449         // Create a course and assignment.
450         $coursedata['idnumber'] = 'idnumbercourse';
451         $coursedata['fullname'] = 'Lightwork Course';
452         $coursedata['summary'] = 'Lightwork Course description';
453         $coursedata['summaryformat'] = FORMAT_MOODLE;
454         $course = self::getDataGenerator()->create_course($coursedata);
456         $assigndata['course'] = $course->id;
457         $assigndata['name'] = 'lightwork assignment';
459         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
461         // Create a manual enrolment record.
462         $manualenroldata['enrol'] = 'manual';
463         $manualenroldata['status'] = 0;
464         $manualenroldata['courseid'] = $course->id;
465         $enrolid = $DB->insert_record('enrol', $manualenroldata);
467         // Create a teacher and give them capabilities.
468         $context = context_course::instance($course->id);
469         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
470         $context = context_module::instance($assign->cmid);
471         $this->assignUserCapability('mod/assign:revealidentities', $context->id, $roleid);
473         // Create the teacher's enrolment record.
474         $userenrolmentdata['status'] = 0;
475         $userenrolmentdata['enrolid'] = $enrolid;
476         $userenrolmentdata['userid'] = $USER->id;
477         $DB->insert_record('user_enrolments', $userenrolmentdata);
479         // Create a student and give them a user mapping record.
480         $student = self::getDataGenerator()->create_user();
481         $mapping = new stdClass();
482         $mapping->assignment = $assign->id;
483         $mapping->userid = $student->id;
485         $DB->insert_record('assign_user_mapping', $mapping);
487         $assignmentids[] = $assign->id;
488         $result = mod_assign_external::get_user_mappings($assignmentids);
490         // We need to execute the return values cleaning process to simulate the web service server.
491         $result = external_api::clean_returnvalue(mod_assign_external::get_user_mappings_returns(), $result);
493         // Check that the correct user mapping information for the student is returned.
494         $this->assertEquals(1, count($result['assignments']));
495         $assignment = $result['assignments'][0];
496         $this->assertEquals($assign->id, $assignment['assignmentid']);
497         // Should be one user mapping record.
498         $this->assertEquals(1, count($assignment['mappings']));
499         $mapping = $assignment['mappings'][0];
500         $this->assertEquals($student->id, $mapping['userid']);
501     }
503     /**
504      * Test lock_submissions
505      */
506     public function test_lock_submissions() {
507         global $DB, $USER;
509         $this->resetAfterTest(true);
510         // Create a course and assignment and users.
511         $course = self::getDataGenerator()->create_course();
513         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
514         $params['course'] = $course->id;
515         $params['assignsubmission_onlinetext_enabled'] = 1;
516         $instance = $generator->create_instance($params);
517         $cm = get_coursemodule_from_instance('assign', $instance->id);
518         $context = context_module::instance($cm->id);
520         $assign = new assign($context, $cm, $course);
522         $student1 = self::getDataGenerator()->create_user();
523         $student2 = self::getDataGenerator()->create_user();
524         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
525         $this->getDataGenerator()->enrol_user($student1->id,
526                                               $course->id,
527                                               $studentrole->id);
528         $this->getDataGenerator()->enrol_user($student2->id,
529                                               $course->id,
530                                               $studentrole->id);
531         $teacher = self::getDataGenerator()->create_user();
532         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
533         $this->getDataGenerator()->enrol_user($teacher->id,
534                                               $course->id,
535                                               $teacherrole->id);
537         // Create a student1 with an online text submission.
538         // Simulate a submission.
539         $this->setUser($student1);
540         $submission = $assign->get_user_submission($student1->id, true);
541         $data = new stdClass();
542         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
543                                          'text'=>'Submission text',
544                                          'format'=>FORMAT_MOODLE);
545         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
546         $plugin->save($submission, $data);
548         // Ready to test.
549         $this->setUser($teacher);
550         $students = array($student1->id, $student2->id);
551         $result = mod_assign_external::lock_submissions($instance->id, $students);
552         $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
554         // Check for 0 warnings.
555         $this->assertEquals(0, count($result));
557         $this->setUser($student2);
558         $submission = $assign->get_user_submission($student2->id, true);
559         $data = new stdClass();
560         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
561                                          'text'=>'Submission text',
562                                          'format'=>FORMAT_MOODLE);
563         $notices = array();
564         $this->setExpectedException('moodle_exception');
565         $assign->save_submission($data, $notices);
566     }
568     /**
569      * Test unlock_submissions
570      */
571     public function test_unlock_submissions() {
572         global $DB, $USER;
574         $this->resetAfterTest(true);
575         // Create a course and assignment and users.
576         $course = self::getDataGenerator()->create_course();
578         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
579         $params['course'] = $course->id;
580         $params['assignsubmission_onlinetext_enabled'] = 1;
581         $instance = $generator->create_instance($params);
582         $cm = get_coursemodule_from_instance('assign', $instance->id);
583         $context = context_module::instance($cm->id);
585         $assign = new assign($context, $cm, $course);
587         $student1 = self::getDataGenerator()->create_user();
588         $student2 = self::getDataGenerator()->create_user();
589         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
590         $this->getDataGenerator()->enrol_user($student1->id,
591                                               $course->id,
592                                               $studentrole->id);
593         $this->getDataGenerator()->enrol_user($student2->id,
594                                               $course->id,
595                                               $studentrole->id);
596         $teacher = self::getDataGenerator()->create_user();
597         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
598         $this->getDataGenerator()->enrol_user($teacher->id,
599                                               $course->id,
600                                               $teacherrole->id);
602         // Create a student1 with an online text submission.
603         // Simulate a submission.
604         $this->setUser($student1);
605         $submission = $assign->get_user_submission($student1->id, true);
606         $data = new stdClass();
607         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
608                                          'text'=>'Submission text',
609                                          'format'=>FORMAT_MOODLE);
610         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
611         $plugin->save($submission, $data);
613         // Ready to test.
614         $this->setUser($teacher);
615         $students = array($student1->id, $student2->id);
616         $result = mod_assign_external::lock_submissions($instance->id, $students);
617         $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
619         // Check for 0 warnings.
620         $this->assertEquals(0, count($result));
622         $result = mod_assign_external::unlock_submissions($instance->id, $students);
623         $result = external_api::clean_returnvalue(mod_assign_external::unlock_submissions_returns(), $result);
625         // Check for 0 warnings.
626         $this->assertEquals(0, count($result));
628         $this->setUser($student2);
629         $submission = $assign->get_user_submission($student2->id, true);
630         $data = new stdClass();
631         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
632                                          'text'=>'Submission text',
633                                          'format'=>FORMAT_MOODLE);
634         $notices = array();
635         $assign->save_submission($data, $notices);
636     }
638     /**
639      * Test submit_for_grading
640      */
641     public function test_submit_for_grading() {
642         global $DB, $USER;
644         $this->resetAfterTest(true);
645         // Create a course and assignment and users.
646         $course = self::getDataGenerator()->create_course();
648         set_config('submissionreceipts', 0, 'assign');
649         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
650         $params['course'] = $course->id;
651         $params['assignsubmission_onlinetext_enabled'] = 1;
652         $params['submissiondrafts'] = 1;
653         $params['sendnotifications'] = 0;
654         $params['requiresubmissionstatement'] = 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         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
663         $this->getDataGenerator()->enrol_user($student1->id,
664                                               $course->id,
665                                               $studentrole->id);
667         // Create a student1 with an online text submission.
668         // Simulate a submission.
669         $this->setUser($student1);
670         $submission = $assign->get_user_submission($student1->id, true);
671         $data = new stdClass();
672         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
673                                          'text'=>'Submission text',
674                                          'format'=>FORMAT_MOODLE);
675         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
676         $plugin->save($submission, $data);
678         $result = mod_assign_external::submit_for_grading($instance->id, false);
679         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
681         // Should be 1 fail because the submission statement was not aceptted.
682         $this->assertEquals(1, count($result));
684         $result = mod_assign_external::submit_for_grading($instance->id, true);
685         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
687         // Check for 0 warnings.
688         $this->assertEquals(0, count($result));
690         $submission = $assign->get_user_submission($student1->id, false);
692         $this->assertEquals(ASSIGN_SUBMISSION_STATUS_SUBMITTED, $submission->status);
693     }
695     /**
696      * Test save_user_extensions
697      */
698     public function test_save_user_extensions() {
699         global $DB, $USER;
701         $this->resetAfterTest(true);
702         // Create a course and assignment and users.
703         $course = self::getDataGenerator()->create_course();
705         $teacher = self::getDataGenerator()->create_user();
706         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
707         $this->getDataGenerator()->enrol_user($teacher->id,
708                                               $course->id,
709                                               $teacherrole->id);
710         $this->setUser($teacher);
712         $now = time();
713         $yesterday = $now - 24*60*60;
714         $tomorrow = $now + 24*60*60;
715         set_config('submissionreceipts', 0, 'assign');
716         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
717         $params['course'] = $course->id;
718         $params['submissiondrafts'] = 1;
719         $params['sendnotifications'] = 0;
720         $params['duedate'] = $yesterday;
721         $params['cutoffdate'] = $now - 10;
722         $instance = $generator->create_instance($params);
723         $cm = get_coursemodule_from_instance('assign', $instance->id);
724         $context = context_module::instance($cm->id);
726         $assign = new assign($context, $cm, $course);
728         $student1 = self::getDataGenerator()->create_user();
729         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
730         $this->getDataGenerator()->enrol_user($student1->id,
731                                               $course->id,
732                                               $studentrole->id);
734         $this->setUser($student1);
735         $result = mod_assign_external::submit_for_grading($instance->id, true);
736         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
738         // Check for 0 warnings.
739         $this->assertEquals(1, count($result));
741         $this->setUser($teacher);
742         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
743         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
744         $this->assertEquals(1, count($result));
746         $this->setUser($teacher);
747         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($yesterday - 10));
748         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
749         $this->assertEquals(1, count($result));
751         $this->setUser($teacher);
752         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($tomorrow));
753         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
754         $this->assertEquals(0, count($result));
756         $this->setUser($student1);
757         $result = mod_assign_external::submit_for_grading($instance->id, true);
758         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
759         $this->assertEquals(0, count($result));
761         $this->setUser($student1);
762         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
763         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
765     }
767     /**
768      * Test reveal_identities
769      */
770     public function test_reveal_identities() {
771         global $DB, $USER;
773         $this->resetAfterTest(true);
774         // Create a course and assignment and users.
775         $course = self::getDataGenerator()->create_course();
777         $teacher = self::getDataGenerator()->create_user();
778         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
779         $this->getDataGenerator()->enrol_user($teacher->id,
780                                               $course->id,
781                                               $teacherrole->id);
782         $this->setUser($teacher);
784         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
785         $params['course'] = $course->id;
786         $params['submissiondrafts'] = 1;
787         $params['sendnotifications'] = 0;
788         $params['blindmarking'] = 1;
789         $instance = $generator->create_instance($params);
790         $cm = get_coursemodule_from_instance('assign', $instance->id);
791         $context = context_module::instance($cm->id);
793         $assign = new assign($context, $cm, $course);
795         $student1 = self::getDataGenerator()->create_user();
796         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
797         $this->getDataGenerator()->enrol_user($student1->id,
798                                               $course->id,
799                                               $studentrole->id);
801         $this->setUser($student1);
802         $this->setExpectedException('required_capability_exception');
803         $result = mod_assign_external::reveal_identities($instance->id);
804         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
805         $this->assertEquals(1, count($result));
806         $this->assertEquals(true, $assign->is_blind_marking());
808         $this->setUser($teacher);
809         $result = mod_assign_external::reveal_identities($instance->id);
810         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
811         $this->assertEquals(0, count($result));
812         $this->assertEquals(false, $assign->is_blind_marking());
814         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
815         $params['course'] = $course->id;
816         $params['submissiondrafts'] = 1;
817         $params['sendnotifications'] = 0;
818         $params['blindmarking'] = 0;
819         $instance = $generator->create_instance($params);
820         $cm = get_coursemodule_from_instance('assign', $instance->id);
821         $context = context_module::instance($cm->id);
823         $assign = new assign($context, $cm, $course);
824         $result = mod_assign_external::reveal_identities($instance->id);
825         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
826         $this->assertEquals(1, count($result));
827         $this->assertEquals(false, $assign->is_blind_marking());
829     }
831     /**
832      * Test revert_submissions_to_draft
833      */
834     public function test_revert_submissions_to_draft() {
835         global $DB, $USER;
837         $this->resetAfterTest(true);
838         set_config('submissionreceipts', 0, 'assign');
839         // Create a course and assignment and users.
840         $course = self::getDataGenerator()->create_course();
842         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
843         $params['course'] = $course->id;
844         $params['sendnotifications'] = 0;
845         $params['submissiondrafts'] = 1;
846         $instance = $generator->create_instance($params);
847         $cm = get_coursemodule_from_instance('assign', $instance->id);
848         $context = context_module::instance($cm->id);
850         $assign = new assign($context, $cm, $course);
852         $student1 = self::getDataGenerator()->create_user();
853         $student2 = self::getDataGenerator()->create_user();
854         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
855         $this->getDataGenerator()->enrol_user($student1->id,
856                                               $course->id,
857                                               $studentrole->id);
858         $this->getDataGenerator()->enrol_user($student2->id,
859                                               $course->id,
860                                               $studentrole->id);
861         $teacher = self::getDataGenerator()->create_user();
862         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
863         $this->getDataGenerator()->enrol_user($teacher->id,
864                                               $course->id,
865                                               $teacherrole->id);
867         // Create a student1 with an online text submission.
868         // Simulate a submission.
869         $this->setUser($student1);
870         $result = mod_assign_external::submit_for_grading($instance->id, true);
871         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
872         $this->assertEquals(0, count($result));
874         // Ready to test.
875         $this->setUser($teacher);
876         $students = array($student1->id, $student2->id);
877         $result = mod_assign_external::revert_submissions_to_draft($instance->id, array($student1->id));
878         $result = external_api::clean_returnvalue(mod_assign_external::revert_submissions_to_draft_returns(), $result);
880         // Check for 0 warnings.
881         $this->assertEquals(0, count($result));
883     }
885     /**
886      * Test save_submission
887      */
888     public function test_save_submission() {
889         global $DB, $USER;
891         $this->resetAfterTest(true);
892         // Create a course and assignment and users.
893         $course = self::getDataGenerator()->create_course();
895         $teacher = self::getDataGenerator()->create_user();
896         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
897         $this->getDataGenerator()->enrol_user($teacher->id,
898                                               $course->id,
899                                               $teacherrole->id);
900         $this->setUser($teacher);
902         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
903         $params['course'] = $course->id;
904         $params['assignsubmission_onlinetext_enabled'] = 1;
905         $params['assignsubmission_file_enabled'] = 1;
906         $params['assignsubmission_file_maxfiles'] = 5;
907         $params['assignsubmission_file_maxsizebytes'] = 1024*1024;
908         $instance = $generator->create_instance($params);
909         $cm = get_coursemodule_from_instance('assign', $instance->id);
910         $context = context_module::instance($cm->id);
912         $assign = new assign($context, $cm, $course);
914         $student1 = self::getDataGenerator()->create_user();
915         $student2 = self::getDataGenerator()->create_user();
916         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
917         $this->getDataGenerator()->enrol_user($student1->id,
918                                               $course->id,
919                                               $studentrole->id);
920         $this->getDataGenerator()->enrol_user($student2->id,
921                                               $course->id,
922                                               $studentrole->id);
923         // Create a student1 with an online text submission.
924         // Simulate a submission.
925         $this->setUser($student1);
927         // Create a file in a draft area.
928         $draftidfile = file_get_unused_draft_itemid();
930         $usercontext = context_user::instance($student1->id);
931         $filerecord = array(
932             'contextid' => $usercontext->id,
933             'component' => 'user',
934             'filearea'  => 'draft',
935             'itemid'    => $draftidfile,
936             'filepath'  => '/',
937             'filename'  => 'testtext.txt',
938         );
940         $fs = get_file_storage();
941         $fs->create_file_from_string($filerecord, 'text contents');
943         // Create another file in a different draft area.
944         $draftidonlinetext = file_get_unused_draft_itemid();
946         $filerecord = array(
947             'contextid' => $usercontext->id,
948             'component' => 'user',
949             'filearea'  => 'draft',
950             'itemid'    => $draftidonlinetext,
951             'filepath'  => '/',
952             'filename'  => 'shouldbeanimage.txt',
953         );
955         $fs->create_file_from_string($filerecord, 'image contents (not really)');
957         // Now try a submission.
958         $submissionpluginparams = array();
959         $submissionpluginparams['files_filemanager'] = $draftidfile;
960         $onlinetexteditorparams = array('text'=>'Yeeha!',
961                                         'format'=>1,
962                                         'itemid'=>$draftidonlinetext);
963         $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
964         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
965         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
967         $this->assertEquals(0, count($result));
969         // Set up a due and cutoff passed date.
970         $instance->duedate = time() - WEEKSECS;
971         $instance->cutoffdate = time() - WEEKSECS;
972         $DB->update_record('assign', $instance);
974         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
975         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
977         $this->assertCount(1, $result);
978         $this->assertEquals(get_string('duedatereached', 'assign'), $result[0]['item']);
979     }
981     /**
982      * Test save_grade
983      */
984     public function test_save_grade() {
985         global $DB, $USER;
987         $this->resetAfterTest(true);
988         // Create a course and assignment and users.
989         $course = self::getDataGenerator()->create_course();
991         $teacher = self::getDataGenerator()->create_user();
992         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
993         $this->getDataGenerator()->enrol_user($teacher->id,
994                                               $course->id,
995                                               $teacherrole->id);
996         $this->setUser($teacher);
998         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
999         $params['course'] = $course->id;
1000         $params['assignfeedback_file_enabled'] = 1;
1001         $params['assignfeedback_comments_enabled'] = 1;
1002         $instance = $generator->create_instance($params);
1003         $cm = get_coursemodule_from_instance('assign', $instance->id);
1004         $context = context_module::instance($cm->id);
1006         $assign = new assign($context, $cm, $course);
1008         $student1 = self::getDataGenerator()->create_user();
1009         $student2 = self::getDataGenerator()->create_user();
1010         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1011         $this->getDataGenerator()->enrol_user($student1->id,
1012                                               $course->id,
1013                                               $studentrole->id);
1014         $this->getDataGenerator()->enrol_user($student2->id,
1015                                               $course->id,
1016                                               $studentrole->id);
1017         // Simulate a grade.
1018         $this->setUser($teacher);
1020         // Create a file in a draft area.
1021         $draftidfile = file_get_unused_draft_itemid();
1023         $usercontext = context_user::instance($teacher->id);
1024         $filerecord = array(
1025             'contextid' => $usercontext->id,
1026             'component' => 'user',
1027             'filearea'  => 'draft',
1028             'itemid'    => $draftidfile,
1029             'filepath'  => '/',
1030             'filename'  => 'testtext.txt',
1031         );
1033         $fs = get_file_storage();
1034         $fs->create_file_from_string($filerecord, 'text contents');
1036         // Now try a grade.
1037         $feedbackpluginparams = array();
1038         $feedbackpluginparams['files_filemanager'] = $draftidfile;
1039         $feedbackeditorparams = array('text' => 'Yeeha!',
1040                                         'format' => 1);
1041         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1042         $result = mod_assign_external::save_grade($instance->id,
1043                                                   $student1->id,
1044                                                   50.0,
1045                                                   -1,
1046                                                   true,
1047                                                   'released',
1048                                                   false,
1049                                                   $feedbackpluginparams);
1050         // No warnings.
1051         $this->assertNull($result);
1053         $result = mod_assign_external::get_grades(array($instance->id));
1054         $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
1056         $this->assertEquals($result['assignments'][0]['grades'][0]['grade'], '50.0');
1057     }
1059     /**
1060      * Test save grades with advanced grading data
1061      */
1062     public function test_save_grades_with_advanced_grading() {
1063         global $DB, $USER;
1065         $this->resetAfterTest(true);
1066         // Create a course and assignment and users.
1067         $course = self::getDataGenerator()->create_course();
1069         $teacher = self::getDataGenerator()->create_user();
1070         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1071         $this->getDataGenerator()->enrol_user($teacher->id,
1072                                               $course->id,
1073                                               $teacherrole->id);
1075         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1076         $params['course'] = $course->id;
1077         $params['assignfeedback_file_enabled'] = 0;
1078         $params['assignfeedback_comments_enabled'] = 0;
1079         $instance = $generator->create_instance($params);
1080         $cm = get_coursemodule_from_instance('assign', $instance->id);
1081         $context = context_module::instance($cm->id);
1083         $assign = new assign($context, $cm, $course);
1085         $student1 = self::getDataGenerator()->create_user();
1086         $student2 = self::getDataGenerator()->create_user();
1087         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1088         $this->getDataGenerator()->enrol_user($student1->id,
1089                                               $course->id,
1090                                               $studentrole->id);
1091         $this->getDataGenerator()->enrol_user($student2->id,
1092                                               $course->id,
1093                                               $studentrole->id);
1095         $this->setUser($teacher);
1097         $feedbackpluginparams = array();
1098         $feedbackpluginparams['files_filemanager'] = 0;
1099         $feedbackeditorparams = array('text' => '', 'format' => 1);
1100         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1102         // Create advanced grading data.
1103         // Create grading area.
1104         $gradingarea = array(
1105             'contextid' => $context->id,
1106             'component' => 'mod_assign',
1107             'areaname' => 'submissions',
1108             'activemethod' => 'rubric'
1109         );
1110         $areaid = $DB->insert_record('grading_areas', $gradingarea);
1112         // Create a rubric grading definition.
1113         $rubricdefinition = array (
1114             'areaid' => $areaid,
1115             'method' => 'rubric',
1116             'name' => 'test',
1117             'status' => 20,
1118             'copiedfromid' => 1,
1119             'timecreated' => 1,
1120             'usercreated' => $teacher->id,
1121             'timemodified' => 1,
1122             'usermodified' => $teacher->id,
1123             'timecopied' => 0
1124         );
1125         $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
1127         // Create a criterion with a level.
1128         $rubriccriteria = array (
1129              'definitionid' => $definitionid,
1130              'sortorder' => 1,
1131              'description' => 'Demonstrate an understanding of disease control',
1132              'descriptionformat' => 0
1133         );
1134         $criterionid = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria);
1135         $rubriclevel1 = array (
1136             'criterionid' => $criterionid,
1137             'score' => 50,
1138             'definition' => 'pass',
1139             'definitionformat' => 0
1140         );
1141         $rubriclevel2 = array (
1142             'criterionid' => $criterionid,
1143             'score' => 100,
1144             'definition' => 'excellent',
1145             'definitionformat' => 0
1146         );
1147         $rubriclevel3 = array (
1148             'criterionid' => $criterionid,
1149             'score' => 0,
1150             'definition' => 'fail',
1151             'definitionformat' => 0
1152         );
1153         $levelid1 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
1154         $levelid2 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
1155         $levelid3 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel3);
1157         // Create the filling.
1158         $student1filling = array (
1159             'criterionid' => $criterionid,
1160             'levelid' => $levelid1,
1161             'remark' => 'well done you passed',
1162             'remarkformat' => 0
1163         );
1165         $student2filling = array (
1166             'criterionid' => $criterionid,
1167             'levelid' => $levelid2,
1168             'remark' => 'Excellent work',
1169             'remarkformat' => 0
1170         );
1172         $student1criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student1filling)));
1173         $student1advancedgradingdata = array('rubric' => array('criteria' => $student1criteria));
1175         $student2criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student2filling)));
1176         $student2advancedgradingdata = array('rubric' => array('criteria' => $student2criteria));
1178         $grades = array();
1179         $student1gradeinfo = array();
1180         $student1gradeinfo['userid'] = $student1->id;
1181         $student1gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1182         $student1gradeinfo['attemptnumber'] = -1;
1183         $student1gradeinfo['addattempt'] = true;
1184         $student1gradeinfo['workflowstate'] = 'released';
1185         $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1186         $student1gradeinfo['advancedgradingdata'] = $student1advancedgradingdata;
1187         $grades[] = $student1gradeinfo;
1189         $student2gradeinfo = array();
1190         $student2gradeinfo['userid'] = $student2->id;
1191         $student2gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1192         $student2gradeinfo['attemptnumber'] = -1;
1193         $student2gradeinfo['addattempt'] = true;
1194         $student2gradeinfo['workflowstate'] = 'released';
1195         $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1196         $student2gradeinfo['advancedgradingdata'] = $student2advancedgradingdata;
1197         $grades[] = $student2gradeinfo;
1199         $result = mod_assign_external::save_grades($instance->id, false, $grades);
1200         $this->assertNull($result);
1202         $student1grade = $DB->get_record('assign_grades',
1203                                          array('userid' => $student1->id, 'assignment' => $instance->id),
1204                                          '*',
1205                                          MUST_EXIST);
1206         $this->assertEquals($student1grade->grade, '50.0');
1208         $student2grade = $DB->get_record('assign_grades',
1209                                          array('userid' => $student2->id, 'assignment' => $instance->id),
1210                                          '*',
1211                                          MUST_EXIST);
1212         $this->assertEquals($student2grade->grade, '100.0');
1213     }
1215     /**
1216      * Test save grades for a team submission
1217      */
1218     public function test_save_grades_with_group_submission() {
1219         global $DB, $USER, $CFG;
1220         require_once($CFG->dirroot . '/group/lib.php');
1222         $this->resetAfterTest(true);
1223         // Create a course and assignment and users.
1224         $course = self::getDataGenerator()->create_course();
1226         $teacher = self::getDataGenerator()->create_user();
1227         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1228         $this->getDataGenerator()->enrol_user($teacher->id,
1229                                               $course->id,
1230                                               $teacherrole->id);
1232         $groupingdata = array();
1233         $groupingdata['courseid'] = $course->id;
1234         $groupingdata['name'] = 'Group assignment grouping';
1236         $grouping = self::getDataGenerator()->create_grouping($groupingdata);
1238         $group1data = array();
1239         $group1data['courseid'] = $course->id;
1240         $group1data['name'] = 'Team 1';
1241         $group2data = array();
1242         $group2data['courseid'] = $course->id;
1243         $group2data['name'] = 'Team 2';
1245         $group1 = self::getDataGenerator()->create_group($group1data);
1246         $group2 = self::getDataGenerator()->create_group($group2data);
1248         groups_assign_grouping($grouping->id, $group1->id);
1249         groups_assign_grouping($grouping->id, $group2->id);
1251         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1252         $params['course'] = $course->id;
1253         $params['teamsubmission'] = 1;
1254         $params['teamsubmissiongroupingid'] = $grouping->id;
1255         $instance = $generator->create_instance($params);
1256         $cm = get_coursemodule_from_instance('assign', $instance->id);
1257         $context = context_module::instance($cm->id);
1259         $assign = new assign($context, $cm, $course);
1261         $student1 = self::getDataGenerator()->create_user();
1262         $student2 = self::getDataGenerator()->create_user();
1263         $student3 = self::getDataGenerator()->create_user();
1264         $student4 = self::getDataGenerator()->create_user();
1265         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1266         $this->getDataGenerator()->enrol_user($student1->id,
1267                                               $course->id,
1268                                               $studentrole->id);
1269         $this->getDataGenerator()->enrol_user($student2->id,
1270                                               $course->id,
1271                                               $studentrole->id);
1272         $this->getDataGenerator()->enrol_user($student3->id,
1273                                               $course->id,
1274                                               $studentrole->id);
1275         $this->getDataGenerator()->enrol_user($student4->id,
1276                                               $course->id,
1277                                               $studentrole->id);
1279         groups_add_member($group1->id, $student1->id);
1280         groups_add_member($group1->id, $student2->id);
1281         groups_add_member($group1->id, $student3->id);
1282         groups_add_member($group2->id, $student4->id);
1283         $this->setUser($teacher);
1285         $feedbackpluginparams = array();
1286         $feedbackpluginparams['files_filemanager'] = 0;
1287         $feedbackeditorparams = array('text' => '', 'format' => 1);
1288         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1290         $grades1 = array();
1291         $student1gradeinfo = array();
1292         $student1gradeinfo['userid'] = $student1->id;
1293         $student1gradeinfo['grade'] = 50;
1294         $student1gradeinfo['attemptnumber'] = -1;
1295         $student1gradeinfo['addattempt'] = true;
1296         $student1gradeinfo['workflowstate'] = 'released';
1297         $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1298         $grades1[] = $student1gradeinfo;
1300         $student2gradeinfo = array();
1301         $student2gradeinfo['userid'] = $student2->id;
1302         $student2gradeinfo['grade'] = 75;
1303         $student2gradeinfo['attemptnumber'] = -1;
1304         $student2gradeinfo['addattempt'] = true;
1305         $student2gradeinfo['workflowstate'] = 'released';
1306         $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1307         $grades1[] = $student2gradeinfo;
1309         $this->setExpectedException('invalid_parameter_exception');
1310         // Expect an exception since 2 grades have been submitted for the same team.
1311         $result = mod_assign_external::save_grades($instance->id, true, $grades1);
1312         $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
1314         $grades2 = array();
1315         $student3gradeinfo = array();
1316         $student3gradeinfo['userid'] = $student3->id;
1317         $student3gradeinfo['grade'] = 50;
1318         $student3gradeinfo['attemptnumber'] = -1;
1319         $student3gradeinfo['addattempt'] = true;
1320         $student3gradeinfo['workflowstate'] = 'released';
1321         $student3gradeinfo['plugindata'] = $feedbackpluginparams;
1322         $grades2[] = $student3gradeinfo;
1324         $student4gradeinfo = array();
1325         $student4gradeinfo['userid'] = $student4->id;
1326         $student4gradeinfo['grade'] = 75;
1327         $student4gradeinfo['attemptnumber'] = -1;
1328         $student4gradeinfo['addattempt'] = true;
1329         $student4gradeinfo['workflowstate'] = 'released';
1330         $student4gradeinfo['plugindata'] = $feedbackpluginparams;
1331         $grades2[] = $student4gradeinfo;
1332         $result = mod_assign_external::save_grades($instance->id, true, $grades2);
1333         $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
1334         // There should be no warnings.
1335         $this->assertEquals(0, count($result));
1337         $student3grade = $DB->get_record('assign_grades',
1338                                          array('userid' => $student3->id, 'assignment' => $instance->id),
1339                                          '*',
1340                                          MUST_EXIST);
1341         $this->assertEquals($student3grade->grade, '50.0');
1343         $student4grade = $DB->get_record('assign_grades',
1344                                          array('userid' => $student4->id, 'assignment' => $instance->id),
1345                                          '*',
1346                                          MUST_EXIST);
1347         $this->assertEquals($student4grade->grade, '75.0');
1348     }
1350     /**
1351      * Test copy_previous_attempt
1352      */
1353     public function test_copy_previous_attempt() {
1354         global $DB, $USER;
1356         $this->resetAfterTest(true);
1357         // Create a course and assignment and users.
1358         $course = self::getDataGenerator()->create_course();
1360         $teacher = self::getDataGenerator()->create_user();
1361         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1362         $this->getDataGenerator()->enrol_user($teacher->id,
1363                                               $course->id,
1364                                               $teacherrole->id);
1365         $this->setUser($teacher);
1367         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1368         $params['course'] = $course->id;
1369         $params['assignsubmission_onlinetext_enabled'] = 1;
1370         $params['assignsubmission_file_enabled'] = 0;
1371         $params['assignfeedback_file_enabled'] = 0;
1372         $params['attemptreopenmethod'] = 'manual';
1373         $params['maxattempts'] = 5;
1374         $instance = $generator->create_instance($params);
1375         $cm = get_coursemodule_from_instance('assign', $instance->id);
1376         $context = context_module::instance($cm->id);
1378         $assign = new assign($context, $cm, $course);
1380         $student1 = self::getDataGenerator()->create_user();
1381         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
1382         $this->getDataGenerator()->enrol_user($student1->id,
1383                                               $course->id,
1384                                               $studentrole->id);
1385         // Now try a submission.
1386         $this->setUser($student1);
1387         $draftidonlinetext = file_get_unused_draft_itemid();
1388         $submissionpluginparams = array();
1389         $onlinetexteditorparams = array('text'=>'Yeeha!',
1390                                         'format'=>1,
1391                                         'itemid'=>$draftidonlinetext);
1392         $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
1393         $submissionpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1394         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1395         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1397         $this->setUser($teacher);
1398         // Add a grade and reopen the attempt.
1399         // Now try a grade.
1400         $feedbackpluginparams = array();
1401         $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1402         $feedbackeditorparams = array('text'=>'Yeeha!',
1403                                         'format'=>1);
1404         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1405         $result = mod_assign_external::save_grade($instance->id,
1406                                                   $student1->id,
1407                                                   50.0,
1408                                                   -1,
1409                                                   true,
1410                                                   'released',
1411                                                   false,
1412                                                   $feedbackpluginparams);
1413         $this->assertNull($result);
1415         $this->setUser($student1);
1416         // Now copy the previous attempt.
1417         $result = mod_assign_external::copy_previous_attempt($instance->id);
1418         $result = external_api::clean_returnvalue(mod_assign_external::copy_previous_attempt_returns(), $result);
1419         // No warnings.
1420         $this->assertEquals(0, count($result));
1422         $this->setUser($teacher);
1423         $result = mod_assign_external::get_submissions(array($instance->id));
1424         $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
1426         // Check we are now on the second attempt.
1427         $this->assertEquals($result['assignments'][0]['submissions'][0]['attemptnumber'], 1);
1428         // Check the plugins data is not empty.
1429         $this->assertNotEmpty($result['assignments'][0]['submissions'][0]['plugins']);
1431     }
1433     /**
1434      * Test set_user_flags
1435      */
1436     public function test_set_user_flags() {
1437         global $DB, $USER;
1439         $this->resetAfterTest(true);
1440         // Create a course and assignment.
1441         $coursedata['idnumber'] = 'idnumbercourse';
1442         $coursedata['fullname'] = 'Lightwork Course';
1443         $coursedata['summary'] = 'Lightwork Course description';
1444         $coursedata['summaryformat'] = FORMAT_MOODLE;
1445         $course = self::getDataGenerator()->create_course($coursedata);
1447         $assigndata['course'] = $course->id;
1448         $assigndata['name'] = 'lightwork assignment';
1450         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
1452         // Create a manual enrolment record.
1453         $manualenroldata['enrol'] = 'manual';
1454         $manualenroldata['status'] = 0;
1455         $manualenroldata['courseid'] = $course->id;
1456         $enrolid = $DB->insert_record('enrol', $manualenroldata);
1458         // Create a teacher and give them capabilities.
1459         $context = context_course::instance($course->id);
1460         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
1461         $context = context_module::instance($assign->cmid);
1462         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
1464         // Create the teacher's enrolment record.
1465         $userenrolmentdata['status'] = 0;
1466         $userenrolmentdata['enrolid'] = $enrolid;
1467         $userenrolmentdata['userid'] = $USER->id;
1468         $DB->insert_record('user_enrolments', $userenrolmentdata);
1470         // Create a student.
1471         $student = self::getDataGenerator()->create_user();
1473         // Create test user flags record.
1474         $userflags = array();
1475         $userflag['userid'] = $student->id;
1476         $userflag['workflowstate'] = 'inmarking';
1477         $userflag['allocatedmarker'] = $USER->id;
1478         $userflags = array($userflag);
1480         $createduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
1481         // We need to execute the return values cleaning process to simulate the web service server.
1482         $createduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $createduserflags);
1484         $this->assertEquals($student->id, $createduserflags[0]['userid']);
1485         $createduserflag = $DB->get_record('assign_user_flags', array('id' => $createduserflags[0]['id']));
1487         // Confirm that all data was inserted correctly.
1488         $this->assertEquals($student->id,  $createduserflag->userid);
1489         $this->assertEquals($assign->id, $createduserflag->assignment);
1490         $this->assertEquals(0, $createduserflag->locked);
1491         $this->assertEquals(2, $createduserflag->mailed);
1492         $this->assertEquals(0, $createduserflag->extensionduedate);
1493         $this->assertEquals('inmarking', $createduserflag->workflowstate);
1494         $this->assertEquals($USER->id, $createduserflag->allocatedmarker);
1496         // Create update data.
1497         $userflags = array();
1498         $userflag['userid'] = $createduserflag->userid;
1499         $userflag['workflowstate'] = 'readyforreview';
1500         $userflags = array($userflag);
1502         $updateduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
1503         // We need to execute the return values cleaning process to simulate the web service server.
1504         $updateduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $updateduserflags);
1506         $this->assertEquals($student->id, $updateduserflags[0]['userid']);
1507         $updateduserflag = $DB->get_record('assign_user_flags', array('id' => $updateduserflags[0]['id']));
1509         // Confirm that all data was updated correctly.
1510         $this->assertEquals($student->id,  $updateduserflag->userid);
1511         $this->assertEquals($assign->id, $updateduserflag->assignment);
1512         $this->assertEquals(0, $updateduserflag->locked);
1513         $this->assertEquals(2, $updateduserflag->mailed);
1514         $this->assertEquals(0, $updateduserflag->extensionduedate);
1515         $this->assertEquals('readyforreview', $updateduserflag->workflowstate);
1516         $this->assertEquals($USER->id, $updateduserflag->allocatedmarker);
1517     }
1519     /**
1520      * Test view_grading_table
1521      */
1522     public function test_view_grading_table_invalid_instance() {
1523         global $DB;
1525         $this->resetAfterTest(true);
1527         // Setup test data.
1528         $course = $this->getDataGenerator()->create_course();
1529         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1530         $context = context_module::instance($assign->cmid);
1531         $cm = get_coursemodule_from_instance('assign', $assign->id);
1533         // Test invalid instance id.
1534         $this->setExpectedExceptionRegexp('dml_missing_record_exception');
1535         mod_assign_external::view_grading_table(0);
1536     }
1538     /**
1539      * Test view_grading_table
1540      */
1541     public function test_view_grading_table_not_enrolled() {
1542         global $DB;
1544         $this->resetAfterTest(true);
1546         // Setup test data.
1547         $course = $this->getDataGenerator()->create_course();
1548         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1549         $context = context_module::instance($assign->cmid);
1550         $cm = get_coursemodule_from_instance('assign', $assign->id);
1552         // Test not-enrolled user.
1553         $user = self::getDataGenerator()->create_user();
1554         $this->setUser($user);
1556         $this->setExpectedException('require_login_exception');
1557         mod_assign_external::view_grading_table($assign->id);
1558     }
1560     /**
1561      * Test view_grading_table
1562      */
1563     public function test_view_grading_table_correct() {
1564         global $DB;
1566         $this->resetAfterTest(true);
1568         // Setup test data.
1569         $course = $this->getDataGenerator()->create_course();
1570         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1571         $context = context_module::instance($assign->cmid);
1572         $cm = get_coursemodule_from_instance('assign', $assign->id);
1574         // Test user with full capabilities.
1575         $user = self::getDataGenerator()->create_user();
1576         $this->setUser($user);
1577         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1578         $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1580         // Trigger and capture the event.
1581         $sink = $this->redirectEvents();
1583         $result = mod_assign_external::view_grading_table($assign->id);
1584         $result = external_api::clean_returnvalue(mod_assign_external::view_grading_table_returns(), $result);
1586         $events = $sink->get_events();
1587         $this->assertCount(1, $events);
1588         $event = array_shift($events);
1590         // Checking that the event contains the expected values.
1591         $this->assertInstanceOf('\mod_assign\event\grading_table_viewed', $event);
1592         $this->assertEquals($context, $event->get_context());
1593         $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1594         $this->assertEquals($moodleurl, $event->get_url());
1595         $this->assertEventContextNotUsed($event);
1596         $this->assertNotEmpty($event->get_name());
1597     }
1599     /**
1600      * Test view_grading_table
1601      */
1602     public function test_view_grading_table_without_capability() {
1603         global $DB;
1605         $this->resetAfterTest(true);
1607         // Setup test data.
1608         $course = $this->getDataGenerator()->create_course();
1609         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1610         $context = context_module::instance($assign->cmid);
1611         $cm = get_coursemodule_from_instance('assign', $assign->id);
1613         // Test user with no capabilities.
1614         $user = self::getDataGenerator()->create_user();
1615         $this->setUser($user);
1616         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1617         $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1619         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1620         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1621         assign_capability('mod/assign:view', CAP_PROHIBIT, $teacherrole->id, $context->id);
1622         // Empty all the caches that may be affected by this change.
1623         accesslib_clear_all_caches_for_unit_testing();
1624         course_modinfo::clear_instance_cache();
1626         $this->setExpectedException('require_login_exception', 'Course or activity not accessible. (Activity is hidden)');
1627         mod_assign_external::view_grading_table($assign->id);
1628     }
1630     /**
1631      * Test subplugins availability
1632      */
1633     public function test_subplugins_availability() {
1634         global $CFG;
1636         require_once($CFG->dirroot . '/mod/assign/adminlib.php');
1637         $this->resetAfterTest(true);
1639         // Hide assignment file submissiong plugin.
1640         $pluginmanager = new assign_plugin_manager('assignsubmission');
1641         $pluginmanager->hide_plugin('file');
1642         $parameters = mod_assign_external::save_submission_parameters();
1644         $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1646         // Show it again and check that the value is returned as optional.
1647         $pluginmanager->show_plugin('file');
1648         $parameters = mod_assign_external::save_submission_parameters();
1649         $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1650         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1652         // Hide feedback file submissiong plugin.
1653         $pluginmanager = new assign_plugin_manager('assignfeedback');
1654         $pluginmanager->hide_plugin('file');
1656         $parameters = mod_assign_external::save_grade_parameters();
1658         $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1660         // Show it again and check that the value is returned as optional.
1661         $pluginmanager->show_plugin('file');
1662         $parameters = mod_assign_external::save_grade_parameters();
1664         $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1665         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1667         // Check a different one.
1668         $pluginmanager->show_plugin('comments');
1669         $this->assertTrue(isset($parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']));
1670         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']->required);
1671     }
1673     /**
1674      * Test test_view_submission_status
1675      */
1676     public function test_view_submission_status() {
1677         global $DB;
1679         $this->resetAfterTest(true);
1681         $this->setAdminUser();
1682         // Setup test data.
1683         $course = $this->getDataGenerator()->create_course();
1684         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1685         $context = context_module::instance($assign->cmid);
1686         $cm = get_coursemodule_from_instance('assign', $assign->id);
1688         // Test invalid instance id.
1689         try {
1690             mod_assign_external::view_submission_status(0);
1691             $this->fail('Exception expected due to invalid mod_assign instance id.');
1692         } catch (moodle_exception $e) {
1693             $this->assertEquals('invalidrecord', $e->errorcode);
1694         }
1696         // Test not-enrolled user.
1697         $user = self::getDataGenerator()->create_user();
1698         $this->setUser($user);
1699         try {
1700             mod_assign_external::view_submission_status($assign->id);
1701             $this->fail('Exception expected due to not enrolled user.');
1702         } catch (moodle_exception $e) {
1703             $this->assertEquals('requireloginerror', $e->errorcode);
1704         }
1706         // Test user with full capabilities.
1707         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1708         $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
1710         // Trigger and capture the event.
1711         $sink = $this->redirectEvents();
1713         $result = mod_assign_external::view_submission_status($assign->id);
1714         $result = external_api::clean_returnvalue(mod_assign_external::view_submission_status_returns(), $result);
1716         $events = $sink->get_events();
1717         $this->assertCount(1, $events);
1718         $event = array_shift($events);
1720         // Checking that the event contains the expected values.
1721         $this->assertInstanceOf('\mod_assign\event\submission_status_viewed', $event);
1722         $this->assertEquals($context, $event->get_context());
1723         $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1724         $this->assertEquals($moodleurl, $event->get_url());
1725         $this->assertEventContextNotUsed($event);
1726         $this->assertNotEmpty($event->get_name());
1728         // Test user with no capabilities.
1729         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1730         assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id);
1731         accesslib_clear_all_caches_for_unit_testing();
1732         course_modinfo::clear_instance_cache();
1734         try {
1735             mod_assign_external::view_submission_status($assign->id);
1736             $this->fail('Exception expected due to missing capability.');
1737         } catch (moodle_exception $e) {
1738             $this->assertEquals('requireloginerror', $e->errorcode);
1739         }
1740     }
1742     /**
1743      * Create a submission for testing the get_submission_status function.
1744      * @param  boolean $submitforgrading whether to submit for grading the submission
1745      * @return array an array containing all the required data for testing
1746      */
1747     private function create_submission_for_testing_status($submitforgrading = false) {
1748         global $DB, $CFG;
1749         require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
1751         // Create a course and assignment and users.
1752         $course = self::getDataGenerator()->create_course();
1754         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1755         $params = array(
1756             'course' => $course->id,
1757             'assignsubmission_file_maxfiles' => 1,
1758             'assignsubmission_file_maxsizebytes' => 1024 * 1024,
1759             'assignsubmission_onlinetext_enabled' => 1,
1760             'assignsubmission_file_enabled' => 1,
1761             'submissiondrafts' => 1,
1762             'assignfeedback_file_enabled' => 1,
1763             'assignfeedback_comments_enabled' => 1,
1764             'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1765             'sendnotifications' => 0
1766         );
1768         set_config('submissionreceipts', 0, 'assign');
1770         $instance = $generator->create_instance($params);
1771         $cm = get_coursemodule_from_instance('assign', $instance->id);
1772         $context = context_module::instance($cm->id);
1774         $assign = new testable_assign($context, $cm, $course);
1776         $student1 = self::getDataGenerator()->create_user();
1777         $student2 = self::getDataGenerator()->create_user();
1778         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1779         $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
1780         $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
1781         $teacher = self::getDataGenerator()->create_user();
1782         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1783         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
1785         $this->setUser($student1);
1787         // Create a student1 with an online text submission.
1788         // Simulate a submission.
1789         $submission = $assign->get_user_submission($student1->id, true);
1791         $data = new stdClass();
1792         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1793                                          'text' => 'Submission text',
1794                                          'format' => FORMAT_MOODLE);
1796         $draftidfile = file_get_unused_draft_itemid();
1797         $usercontext = context_user::instance($student1->id);
1798         $filerecord = array(
1799             'contextid' => $usercontext->id,
1800             'component' => 'user',
1801             'filearea'  => 'draft',
1802             'itemid'    => $draftidfile,
1803             'filepath'  => '/',
1804             'filename'  => 't.txt',
1805         );
1806         $fs = get_file_storage();
1807         $fs->create_file_from_string($filerecord, 'text contents');
1809         $data->files_filemanager = $draftidfile;
1811         $notices = array();
1812         $assign->save_submission($data, $notices);
1814         if ($submitforgrading) {
1815             // Now, submit the draft for grading.
1816             $notices = array();
1818             $data = new stdClass;
1819             $data->userid = $student1->id;
1820             $assign->submit_for_grading($data, $notices);
1821         }
1823         return array($assign, $instance, $student1, $student2, $teacher);
1824     }
1826     /**
1827      * Test get_submission_status for a draft submission.
1828      */
1829     public function test_get_submission_status_in_draft_status() {
1830         $this->resetAfterTest(true);
1832         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status();
1834         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1835         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1836         $this->assertDebuggingCalled();
1838         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1840         // The submission is now in draft mode.
1841         $this->assertCount(0, $result['warnings']);
1842         $this->assertFalse(isset($result['gradingsummary']));
1843         $this->assertFalse(isset($result['feedback']));
1844         $this->assertFalse(isset($result['previousattempts']));
1846         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1847         $this->assertTrue($result['lastattempt']['canedit']);
1848         $this->assertTrue($result['lastattempt']['cansubmit']);
1849         $this->assertFalse($result['lastattempt']['locked']);
1850         $this->assertFalse($result['lastattempt']['graded']);
1851         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1852         $this->assertFalse($result['lastattempt']['blindmarking']);
1853         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1854         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1856         $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
1857         $this->assertEquals(0, $result['lastattempt']['submission']['attemptnumber']);
1858         $this->assertEquals('draft', $result['lastattempt']['submission']['status']);
1859         $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
1860         $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
1861         $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
1862         $this->assertEquals('Submission text', $result['lastattempt']['submission']['plugins'][0]['editorfields'][0]['text']);
1863         $this->assertEquals('/t.txt', $result['lastattempt']['submission']['plugins'][1]['fileareas'][0]['files'][0]['filepath']);
1864     }
1866     /**
1867      * Test get_submission_status for a submitted submission.
1868      */
1869     public function test_get_submission_status_in_submission_status() {
1870         $this->resetAfterTest(true);
1872         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
1874         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1875         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1876         $this->assertDebuggingCalled();
1877         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1879         $this->assertCount(0, $result['warnings']);
1880         $this->assertFalse(isset($result['gradingsummary']));
1881         $this->assertFalse(isset($result['feedback']));
1882         $this->assertFalse(isset($result['previousattempts']));
1884         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1885         $this->assertFalse($result['lastattempt']['canedit']);
1886         $this->assertFalse($result['lastattempt']['cansubmit']);
1887         $this->assertFalse($result['lastattempt']['locked']);
1888         $this->assertFalse($result['lastattempt']['graded']);
1889         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1890         $this->assertFalse($result['lastattempt']['blindmarking']);
1891         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1892         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1894     }
1896     /**
1897      * Test get_submission_status using the teacher role.
1898      */
1899     public function test_get_submission_status_in_submission_status_for_teacher() {
1900         $this->resetAfterTest(true);
1902         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
1904         // Now, as teacher, see the grading summary.
1905         $this->setUser($teacher);
1906         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1907         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1908         $this->assertDebuggingCalled();
1909         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1911         $this->assertCount(0, $result['warnings']);
1912         $this->assertFalse(isset($result['lastattempt']));
1913         $this->assertFalse(isset($result['feedback']));
1914         $this->assertFalse(isset($result['previousattempts']));
1916         $this->assertEquals(2, $result['gradingsummary']['participantcount']);
1917         $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
1918         $this->assertEquals(1, $result['gradingsummary']['submissionsenabled']);
1919         $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']);
1920         $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']);
1921         $this->assertFalse($result['gradingsummary']['warnofungroupedusers']);
1922     }
1924     /**
1925      * Test get_submission_status for a reopened submission.
1926      */
1927     public function test_get_submission_status_in_reopened_status() {
1928         global $USER;
1930         $this->resetAfterTest(true);
1932         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
1934         $this->setUser($teacher);
1935         // Grade and reopen.
1936         $feedbackpluginparams = array();
1937         $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1938         $feedbackeditorparams = array('text' => 'Yeeha!',
1939                                         'format' => 1);
1940         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1941         $result = mod_assign_external::save_grade($instance->id,
1942                                                   $student1->id,
1943                                                   50.0,
1944                                                   -1,
1945                                                   false,
1946                                                   'released',
1947                                                   false,
1948                                                   $feedbackpluginparams);
1949         $USER->ignoresesskey = true;
1950         $assign->testable_process_add_attempt($student1->id);
1952         $this->setUser($student1);
1954         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1955         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1956         $this->assertDebuggingCalled();
1957         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1959         $this->assertCount(0, $result['warnings']);
1960         $this->assertFalse(isset($result['gradingsummary']));
1962         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1963         $this->assertTrue($result['lastattempt']['canedit']);
1964         $this->assertFalse($result['lastattempt']['cansubmit']);
1965         $this->assertFalse($result['lastattempt']['locked']);
1966         $this->assertFalse($result['lastattempt']['graded']);
1967         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1968         $this->assertFalse($result['lastattempt']['blindmarking']);
1969         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1970         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1972         // Check new attempt reopened.
1973         $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
1974         $this->assertEquals(1, $result['lastattempt']['submission']['attemptnumber']);
1975         $this->assertEquals('reopened', $result['lastattempt']['submission']['status']);
1976         $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
1977         $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
1978         $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
1979         $this->assertCount(3, $result['lastattempt']['submission']['plugins']);
1981         // Now see feedback and the attempts history (remember, is a submission reopened).
1982         // Only 2 fields (no grade, no plugins data).
1983         $this->assertCount(2, $result['feedback']);
1985         // One previous attempt.
1986         $this->assertCount(1, $result['previousattempts']);
1987         $this->assertEquals(0, $result['previousattempts'][0]['attemptnumber']);
1988         $this->assertEquals(50, $result['previousattempts'][0]['grade']['grade']);
1989         $this->assertEquals($teacher->id, $result['previousattempts'][0]['grade']['grader']);
1990         $this->assertEquals($student1->id, $result['previousattempts'][0]['grade']['userid']);
1991         $this->assertEquals('Yeeha!', $result['previousattempts'][0]['feedbackplugins'][0]['editorfields'][0]['text']);
1992         $submissionplugins = $result['previousattempts'][0]['submission']['plugins'];
1993         $this->assertEquals('Submission text', $submissionplugins[0]['editorfields'][0]['text']);
1994         $this->assertEquals('/t.txt', $submissionplugins[1]['fileareas'][0]['files'][0]['filepath']);
1995     }
1997     /**
1998      * Test access control for get_submission_status.
1999      */
2000     public function test_get_submission_status_access_control() {
2001         $this->resetAfterTest(true);
2003         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status();
2005         $this->setUser($student2);
2007         // Access control test.
2008         $this->setExpectedException('required_capability_exception');
2009         mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id);
2011     }