MDL-53461 assign: Fix intro images to be shown in the mobile app
[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 with links to a filerecord.
172         $assign1 = self::getDataGenerator()->create_module('assign', array(
173             'course' => $course1->id,
174             'name' => 'lightwork assignment',
175             'intro' => 'the assignment intro text here <a href="@@PLUGINFILE@@/intro.txt">link</a>',
176             'introformat' => FORMAT_HTML,
177             'markingworkflow' => 1,
178             'markingallocation' => 1
179         ));
181         // Add a file as assignment attachment.
182         $context = context_module::instance($assign1->cmid);
183         $filerecord = array('component' => 'mod_assign', 'filearea' => 'intro', 'contextid' => $context->id, 'itemid' => 0,
184                 'filename' => 'intro.txt', 'filepath' => '/');
185         $fs = get_file_storage();
186         $fs->create_file_from_string($filerecord, 'Test intro file');
188         // Create manual enrolment record.
189         $enrolid = $DB->insert_record('enrol', (object)array(
190             'enrol' => 'manual',
191             'status' => 0,
192             'courseid' => $course1->id
193         ));
195         // Create the user and give them capabilities.
196         $context = context_course::instance($course1->id);
197         $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
198         $context = context_module::instance($assign1->cmid);
199         $this->assignUserCapability('mod/assign:view', $context->id, $roleid);
201         // Create the user enrolment record.
202         $DB->insert_record('user_enrolments', (object)array(
203             'status' => 0,
204             'enrolid' => $enrolid,
205             'userid' => $USER->id
206         ));
208         // Add a file as assignment attachment.
209         $filerecord = array('component' => 'mod_assign', 'filearea' => ASSIGN_INTROATTACHMENT_FILEAREA,
210                 'contextid' => $context->id, 'itemid' => 0,
211                 'filename' => 'introattachment.txt', 'filepath' => '/');
212         $fs = get_file_storage();
213         $fs->create_file_from_string($filerecord, 'Test intro attachment file');
215         $result = mod_assign_external::get_assignments();
217         // We need to execute the return values cleaning process to simulate the web service server.
218         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
220         // Check the course and assignment are returned.
221         $this->assertEquals(1, count($result['courses']));
222         $course = $result['courses'][0];
223         $this->assertEquals('Lightwork Course 1', $course['fullname']);
224         $this->assertEquals(1, count($course['assignments']));
225         $assignment = $course['assignments'][0];
226         $this->assertEquals($assign1->id, $assignment['id']);
227         $this->assertEquals($course1->id, $assignment['course']);
228         $this->assertEquals('lightwork assignment', $assignment['name']);
229         $this->assertContains('the assignment intro text here', $assignment['intro']);
230         // Check the url of the file attatched.
231         $this->assertRegExp('/.*mod_assign\/intro\/intro\.txt.*/', $assignment['intro']);
232         $this->assertEquals(1, $assignment['markingworkflow']);
233         $this->assertEquals(1, $assignment['markingallocation']);
235         $this->assertCount(1, $assignment['introattachments']);
236         $this->assertEquals('introattachment.txt', $assignment['introattachments'][0]['filename']);
238         // Now, hide the descritption until the submission from date.
239         $DB->set_field('assign', 'alwaysshowdescription', 0, array('id' => $assign1->id));
240         $DB->set_field('assign', 'allowsubmissionsfromdate', time() + DAYSECS, array('id' => $assign1->id));
242         $result = mod_assign_external::get_assignments(array($course1->id));
244         // We need to execute the return values cleaning process to simulate the web service server.
245         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
247         $this->assertEquals(1, count($result['courses']));
248         $course = $result['courses'][0];
249         $this->assertEquals('Lightwork Course 1', $course['fullname']);
250         $this->assertEquals(1, count($course['assignments']));
251         $assignment = $course['assignments'][0];
252         $this->assertEquals($assign1->id, $assignment['id']);
253         $this->assertEquals($course1->id, $assignment['course']);
254         $this->assertEquals('lightwork assignment', $assignment['name']);
255         $this->assertArrayNotHasKey('intro', $assignment);
256         $this->assertArrayNotHasKey('introattachments', $assignment);
257         $this->assertEquals(1, $assignment['markingworkflow']);
258         $this->assertEquals(1, $assignment['markingallocation']);
260         $result = mod_assign_external::get_assignments(array($course2->id));
262         // We need to execute the return values cleaning process to simulate the web service server.
263         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
265         $this->assertEquals(0, count($result['courses']));
266         $this->assertEquals(1, count($result['warnings']));
268         // Test with non-enrolled user, but with view capabilities.
269         $this->setAdminUser();
270         $result = mod_assign_external::get_assignments();
271         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
272         $this->assertEquals(0, count($result['courses']));
273         $this->assertEquals(0, count($result['warnings']));
275         // Expect no courses, because we are not using the special flag.
276         $result = mod_assign_external::get_assignments(array($course1->id));
277         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
278         $this->assertCount(0, $result['courses']);
280         // Now use the special flag to return courses where you are not enroled in.
281         $result = mod_assign_external::get_assignments(array($course1->id), array(), true);
282         $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
283         $this->assertCount(1, $result['courses']);
285         $course = $result['courses'][0];
286         $this->assertEquals('Lightwork Course 1', $course['fullname']);
287         $this->assertEquals(1, count($course['assignments']));
288         $assignment = $course['assignments'][0];
289         $this->assertEquals($assign1->id, $assignment['id']);
290         $this->assertEquals($course1->id, $assignment['course']);
291         $this->assertEquals('lightwork assignment', $assignment['name']);
292         $this->assertArrayNotHasKey('intro', $assignment);
293         $this->assertArrayNotHasKey('introattachments', $assignment);
294         $this->assertEquals(1, $assignment['markingworkflow']);
295         $this->assertEquals(1, $assignment['markingallocation']);
296     }
298     /**
299      * Test get_submissions
300      */
301     public function test_get_submissions() {
302         global $DB, $USER;
304         $this->resetAfterTest(true);
305         // Create a course and assignment.
306         $coursedata['idnumber'] = 'idnumbercourse1';
307         $coursedata['fullname'] = 'Lightwork Course 1';
308         $coursedata['summary'] = 'Lightwork Course 1 description';
309         $coursedata['summaryformat'] = FORMAT_MOODLE;
310         $course1 = self::getDataGenerator()->create_course($coursedata);
312         $assigndata['course'] = $course1->id;
313         $assigndata['name'] = 'lightwork assignment';
315         $assign1 = self::getDataGenerator()->create_module('assign', $assigndata);
317         // Create a student with an online text submission.
318         // First attempt.
319         $student = self::getDataGenerator()->create_user();
320         $submission = new stdClass();
321         $submission->assignment = $assign1->id;
322         $submission->userid = $student->id;
323         $submission->timecreated = time();
324         $submission->timemodified = $submission->timecreated;
325         $submission->status = 'draft';
326         $submission->attemptnumber = 0;
327         $submission->latest = 0;
328         $sid = $DB->insert_record('assign_submission', $submission);
330         // Second attempt.
331         $submission = new stdClass();
332         $submission->assignment = $assign1->id;
333         $submission->userid = $student->id;
334         $submission->timecreated = time();
335         $submission->timemodified = $submission->timecreated;
336         $submission->status = 'submitted';
337         $submission->attemptnumber = 1;
338         $submission->latest = 1;
339         $sid = $DB->insert_record('assign_submission', $submission);
340         $submission->id = $sid;
342         $onlinetextsubmission = new stdClass();
343         $onlinetextsubmission->onlinetext = "<p>online test text</p>";
344         $onlinetextsubmission->onlineformat = 1;
345         $onlinetextsubmission->submission = $submission->id;
346         $onlinetextsubmission->assignment = $assign1->id;
347         $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
349         // Create manual enrolment record.
350         $manualenroldata['enrol'] = 'manual';
351         $manualenroldata['status'] = 0;
352         $manualenroldata['courseid'] = $course1->id;
353         $enrolid = $DB->insert_record('enrol', $manualenroldata);
355         // Create a teacher and give them capabilities.
356         $context = context_course::instance($course1->id);
357         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
358         $context = context_module::instance($assign1->cmid);
359         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
361         // Create the teacher's enrolment record.
362         $userenrolmentdata['status'] = 0;
363         $userenrolmentdata['enrolid'] = $enrolid;
364         $userenrolmentdata['userid'] = $USER->id;
365         $DB->insert_record('user_enrolments', $userenrolmentdata);
367         $assignmentids[] = $assign1->id;
368         $result = mod_assign_external::get_submissions($assignmentids);
369         $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
371         // Check the online text submission is returned.
372         $this->assertEquals(1, count($result['assignments']));
373         $assignment = $result['assignments'][0];
374         $this->assertEquals($assign1->id, $assignment['assignmentid']);
375         $this->assertEquals(1, count($assignment['submissions']));
376         $submission = $assignment['submissions'][0];
377         $this->assertEquals($sid, $submission['id']);
378         $this->assertCount(1, $submission['plugins']);
379     }
381     /**
382      * Test get_user_flags
383      */
384     public function test_get_user_flags() {
385         global $DB, $USER;
387         $this->resetAfterTest(true);
388         // Create a course and assignment.
389         $coursedata['idnumber'] = 'idnumbercourse';
390         $coursedata['fullname'] = 'Lightwork Course';
391         $coursedata['summary'] = 'Lightwork Course description';
392         $coursedata['summaryformat'] = FORMAT_MOODLE;
393         $course = self::getDataGenerator()->create_course($coursedata);
395         $assigndata['course'] = $course->id;
396         $assigndata['name'] = 'lightwork assignment';
398         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
400         // Create a manual enrolment record.
401         $manualenroldata['enrol'] = 'manual';
402         $manualenroldata['status'] = 0;
403         $manualenroldata['courseid'] = $course->id;
404         $enrolid = $DB->insert_record('enrol', $manualenroldata);
406         // Create a teacher and give them capabilities.
407         $context = context_course::instance($course->id);
408         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
409         $context = context_module::instance($assign->cmid);
410         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
412         // Create the teacher's enrolment record.
413         $userenrolmentdata['status'] = 0;
414         $userenrolmentdata['enrolid'] = $enrolid;
415         $userenrolmentdata['userid'] = $USER->id;
416         $DB->insert_record('user_enrolments', $userenrolmentdata);
418         // Create a student and give them a user flag record.
419         $student = self::getDataGenerator()->create_user();
420         $userflag = new stdClass();
421         $userflag->assignment = $assign->id;
422         $userflag->userid = $student->id;
423         $userflag->locked = 0;
424         $userflag->mailed = 0;
425         $userflag->extensionduedate = 0;
426         $userflag->workflowstate = 'inmarking';
427         $userflag->allocatedmarker = $USER->id;
429         $DB->insert_record('assign_user_flags', $userflag);
431         $assignmentids[] = $assign->id;
432         $result = mod_assign_external::get_user_flags($assignmentids);
434         // We need to execute the return values cleaning process to simulate the web service server.
435         $result = external_api::clean_returnvalue(mod_assign_external::get_user_flags_returns(), $result);
437         // Check that the correct user flag information for the student is returned.
438         $this->assertEquals(1, count($result['assignments']));
439         $assignment = $result['assignments'][0];
440         $this->assertEquals($assign->id, $assignment['assignmentid']);
441         // Should be one user flag record.
442         $this->assertEquals(1, count($assignment['userflags']));
443         $userflag = $assignment['userflags'][0];
444         $this->assertEquals($student->id, $userflag['userid']);
445         $this->assertEquals(0, $userflag['locked']);
446         $this->assertEquals(0, $userflag['mailed']);
447         $this->assertEquals(0, $userflag['extensionduedate']);
448         $this->assertEquals('inmarking', $userflag['workflowstate']);
449         $this->assertEquals($USER->id, $userflag['allocatedmarker']);
450     }
452     /**
453      * Test get_user_mappings
454      */
455     public function test_get_user_mappings() {
456         global $DB, $USER;
458         $this->resetAfterTest(true);
459         // Create a course and assignment.
460         $coursedata['idnumber'] = 'idnumbercourse';
461         $coursedata['fullname'] = 'Lightwork Course';
462         $coursedata['summary'] = 'Lightwork Course description';
463         $coursedata['summaryformat'] = FORMAT_MOODLE;
464         $course = self::getDataGenerator()->create_course($coursedata);
466         $assigndata['course'] = $course->id;
467         $assigndata['name'] = 'lightwork assignment';
469         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
471         // Create a manual enrolment record.
472         $manualenroldata['enrol'] = 'manual';
473         $manualenroldata['status'] = 0;
474         $manualenroldata['courseid'] = $course->id;
475         $enrolid = $DB->insert_record('enrol', $manualenroldata);
477         // Create a teacher and give them capabilities.
478         $context = context_course::instance($course->id);
479         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
480         $context = context_module::instance($assign->cmid);
481         $this->assignUserCapability('mod/assign:revealidentities', $context->id, $roleid);
483         // Create the teacher's enrolment record.
484         $userenrolmentdata['status'] = 0;
485         $userenrolmentdata['enrolid'] = $enrolid;
486         $userenrolmentdata['userid'] = $USER->id;
487         $DB->insert_record('user_enrolments', $userenrolmentdata);
489         // Create a student and give them a user mapping record.
490         $student = self::getDataGenerator()->create_user();
491         $mapping = new stdClass();
492         $mapping->assignment = $assign->id;
493         $mapping->userid = $student->id;
495         $DB->insert_record('assign_user_mapping', $mapping);
497         $assignmentids[] = $assign->id;
498         $result = mod_assign_external::get_user_mappings($assignmentids);
500         // We need to execute the return values cleaning process to simulate the web service server.
501         $result = external_api::clean_returnvalue(mod_assign_external::get_user_mappings_returns(), $result);
503         // Check that the correct user mapping information for the student is returned.
504         $this->assertEquals(1, count($result['assignments']));
505         $assignment = $result['assignments'][0];
506         $this->assertEquals($assign->id, $assignment['assignmentid']);
507         // Should be one user mapping record.
508         $this->assertEquals(1, count($assignment['mappings']));
509         $mapping = $assignment['mappings'][0];
510         $this->assertEquals($student->id, $mapping['userid']);
511     }
513     /**
514      * Test lock_submissions
515      */
516     public function test_lock_submissions() {
517         global $DB, $USER;
519         $this->resetAfterTest(true);
520         // Create a course and assignment and users.
521         $course = self::getDataGenerator()->create_course();
523         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
524         $params['course'] = $course->id;
525         $params['assignsubmission_onlinetext_enabled'] = 1;
526         $instance = $generator->create_instance($params);
527         $cm = get_coursemodule_from_instance('assign', $instance->id);
528         $context = context_module::instance($cm->id);
530         $assign = new assign($context, $cm, $course);
532         $student1 = self::getDataGenerator()->create_user();
533         $student2 = self::getDataGenerator()->create_user();
534         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
535         $this->getDataGenerator()->enrol_user($student1->id,
536                                               $course->id,
537                                               $studentrole->id);
538         $this->getDataGenerator()->enrol_user($student2->id,
539                                               $course->id,
540                                               $studentrole->id);
541         $teacher = self::getDataGenerator()->create_user();
542         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
543         $this->getDataGenerator()->enrol_user($teacher->id,
544                                               $course->id,
545                                               $teacherrole->id);
547         // Create a student1 with an online text submission.
548         // Simulate a submission.
549         $this->setUser($student1);
550         $submission = $assign->get_user_submission($student1->id, true);
551         $data = new stdClass();
552         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
553                                          'text'=>'Submission text',
554                                          'format'=>FORMAT_MOODLE);
555         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
556         $plugin->save($submission, $data);
558         // Ready to test.
559         $this->setUser($teacher);
560         $students = array($student1->id, $student2->id);
561         $result = mod_assign_external::lock_submissions($instance->id, $students);
562         $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
564         // Check for 0 warnings.
565         $this->assertEquals(0, count($result));
567         $this->setUser($student2);
568         $submission = $assign->get_user_submission($student2->id, true);
569         $data = new stdClass();
570         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
571                                          'text'=>'Submission text',
572                                          'format'=>FORMAT_MOODLE);
573         $notices = array();
574         $this->setExpectedException('moodle_exception');
575         $assign->save_submission($data, $notices);
576     }
578     /**
579      * Test unlock_submissions
580      */
581     public function test_unlock_submissions() {
582         global $DB, $USER;
584         $this->resetAfterTest(true);
585         // Create a course and assignment and users.
586         $course = self::getDataGenerator()->create_course();
588         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
589         $params['course'] = $course->id;
590         $params['assignsubmission_onlinetext_enabled'] = 1;
591         $instance = $generator->create_instance($params);
592         $cm = get_coursemodule_from_instance('assign', $instance->id);
593         $context = context_module::instance($cm->id);
595         $assign = new assign($context, $cm, $course);
597         $student1 = self::getDataGenerator()->create_user();
598         $student2 = self::getDataGenerator()->create_user();
599         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
600         $this->getDataGenerator()->enrol_user($student1->id,
601                                               $course->id,
602                                               $studentrole->id);
603         $this->getDataGenerator()->enrol_user($student2->id,
604                                               $course->id,
605                                               $studentrole->id);
606         $teacher = self::getDataGenerator()->create_user();
607         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
608         $this->getDataGenerator()->enrol_user($teacher->id,
609                                               $course->id,
610                                               $teacherrole->id);
612         // Create a student1 with an online text submission.
613         // Simulate a submission.
614         $this->setUser($student1);
615         $submission = $assign->get_user_submission($student1->id, true);
616         $data = new stdClass();
617         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
618                                          'text'=>'Submission text',
619                                          'format'=>FORMAT_MOODLE);
620         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
621         $plugin->save($submission, $data);
623         // Ready to test.
624         $this->setUser($teacher);
625         $students = array($student1->id, $student2->id);
626         $result = mod_assign_external::lock_submissions($instance->id, $students);
627         $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
629         // Check for 0 warnings.
630         $this->assertEquals(0, count($result));
632         $result = mod_assign_external::unlock_submissions($instance->id, $students);
633         $result = external_api::clean_returnvalue(mod_assign_external::unlock_submissions_returns(), $result);
635         // Check for 0 warnings.
636         $this->assertEquals(0, count($result));
638         $this->setUser($student2);
639         $submission = $assign->get_user_submission($student2->id, true);
640         $data = new stdClass();
641         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
642                                          'text'=>'Submission text',
643                                          'format'=>FORMAT_MOODLE);
644         $notices = array();
645         $assign->save_submission($data, $notices);
646     }
648     /**
649      * Test submit_for_grading
650      */
651     public function test_submit_for_grading() {
652         global $DB, $USER;
654         $this->resetAfterTest(true);
655         // Create a course and assignment and users.
656         $course = self::getDataGenerator()->create_course();
658         set_config('submissionreceipts', 0, 'assign');
659         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
660         $params['course'] = $course->id;
661         $params['assignsubmission_onlinetext_enabled'] = 1;
662         $params['submissiondrafts'] = 1;
663         $params['sendnotifications'] = 0;
664         $params['requiresubmissionstatement'] = 1;
665         $instance = $generator->create_instance($params);
666         $cm = get_coursemodule_from_instance('assign', $instance->id);
667         $context = context_module::instance($cm->id);
669         $assign = new assign($context, $cm, $course);
671         $student1 = self::getDataGenerator()->create_user();
672         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
673         $this->getDataGenerator()->enrol_user($student1->id,
674                                               $course->id,
675                                               $studentrole->id);
677         // Create a student1 with an online text submission.
678         // Simulate a submission.
679         $this->setUser($student1);
680         $submission = $assign->get_user_submission($student1->id, true);
681         $data = new stdClass();
682         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
683                                          'text'=>'Submission text',
684                                          'format'=>FORMAT_MOODLE);
685         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
686         $plugin->save($submission, $data);
688         $result = mod_assign_external::submit_for_grading($instance->id, false);
689         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
691         // Should be 1 fail because the submission statement was not aceptted.
692         $this->assertEquals(1, count($result));
694         $result = mod_assign_external::submit_for_grading($instance->id, true);
695         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
697         // Check for 0 warnings.
698         $this->assertEquals(0, count($result));
700         $submission = $assign->get_user_submission($student1->id, false);
702         $this->assertEquals(ASSIGN_SUBMISSION_STATUS_SUBMITTED, $submission->status);
703     }
705     /**
706      * Test save_user_extensions
707      */
708     public function test_save_user_extensions() {
709         global $DB, $USER;
711         $this->resetAfterTest(true);
712         // Create a course and assignment and users.
713         $course = self::getDataGenerator()->create_course();
715         $teacher = self::getDataGenerator()->create_user();
716         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
717         $this->getDataGenerator()->enrol_user($teacher->id,
718                                               $course->id,
719                                               $teacherrole->id);
720         $this->setUser($teacher);
722         $now = time();
723         $yesterday = $now - 24*60*60;
724         $tomorrow = $now + 24*60*60;
725         set_config('submissionreceipts', 0, 'assign');
726         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
727         $params['course'] = $course->id;
728         $params['submissiondrafts'] = 1;
729         $params['sendnotifications'] = 0;
730         $params['duedate'] = $yesterday;
731         $params['cutoffdate'] = $now - 10;
732         $instance = $generator->create_instance($params);
733         $cm = get_coursemodule_from_instance('assign', $instance->id);
734         $context = context_module::instance($cm->id);
736         $assign = new assign($context, $cm, $course);
738         $student1 = self::getDataGenerator()->create_user();
739         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
740         $this->getDataGenerator()->enrol_user($student1->id,
741                                               $course->id,
742                                               $studentrole->id);
744         $this->setUser($student1);
745         $result = mod_assign_external::submit_for_grading($instance->id, true);
746         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
748         // Check for 0 warnings.
749         $this->assertEquals(1, count($result));
751         $this->setUser($teacher);
752         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
753         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
754         $this->assertEquals(1, count($result));
756         $this->setUser($teacher);
757         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($yesterday - 10));
758         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
759         $this->assertEquals(1, count($result));
761         $this->setUser($teacher);
762         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($tomorrow));
763         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
764         $this->assertEquals(0, count($result));
766         $this->setUser($student1);
767         $result = mod_assign_external::submit_for_grading($instance->id, true);
768         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
769         $this->assertEquals(0, count($result));
771         $this->setUser($student1);
772         $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
773         $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
775     }
777     /**
778      * Test reveal_identities
779      */
780     public function test_reveal_identities() {
781         global $DB, $USER;
783         $this->resetAfterTest(true);
784         // Create a course and assignment and users.
785         $course = self::getDataGenerator()->create_course();
787         $teacher = self::getDataGenerator()->create_user();
788         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
789         $this->getDataGenerator()->enrol_user($teacher->id,
790                                               $course->id,
791                                               $teacherrole->id);
792         $this->setUser($teacher);
794         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
795         $params['course'] = $course->id;
796         $params['submissiondrafts'] = 1;
797         $params['sendnotifications'] = 0;
798         $params['blindmarking'] = 1;
799         $instance = $generator->create_instance($params);
800         $cm = get_coursemodule_from_instance('assign', $instance->id);
801         $context = context_module::instance($cm->id);
803         $assign = new assign($context, $cm, $course);
805         $student1 = self::getDataGenerator()->create_user();
806         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
807         $this->getDataGenerator()->enrol_user($student1->id,
808                                               $course->id,
809                                               $studentrole->id);
811         $this->setUser($student1);
812         $this->setExpectedException('required_capability_exception');
813         $result = mod_assign_external::reveal_identities($instance->id);
814         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
815         $this->assertEquals(1, count($result));
816         $this->assertEquals(true, $assign->is_blind_marking());
818         $this->setUser($teacher);
819         $result = mod_assign_external::reveal_identities($instance->id);
820         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
821         $this->assertEquals(0, count($result));
822         $this->assertEquals(false, $assign->is_blind_marking());
824         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
825         $params['course'] = $course->id;
826         $params['submissiondrafts'] = 1;
827         $params['sendnotifications'] = 0;
828         $params['blindmarking'] = 0;
829         $instance = $generator->create_instance($params);
830         $cm = get_coursemodule_from_instance('assign', $instance->id);
831         $context = context_module::instance($cm->id);
833         $assign = new assign($context, $cm, $course);
834         $result = mod_assign_external::reveal_identities($instance->id);
835         $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
836         $this->assertEquals(1, count($result));
837         $this->assertEquals(false, $assign->is_blind_marking());
839     }
841     /**
842      * Test revert_submissions_to_draft
843      */
844     public function test_revert_submissions_to_draft() {
845         global $DB, $USER;
847         $this->resetAfterTest(true);
848         set_config('submissionreceipts', 0, 'assign');
849         // Create a course and assignment and users.
850         $course = self::getDataGenerator()->create_course();
852         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
853         $params['course'] = $course->id;
854         $params['sendnotifications'] = 0;
855         $params['submissiondrafts'] = 1;
856         $instance = $generator->create_instance($params);
857         $cm = get_coursemodule_from_instance('assign', $instance->id);
858         $context = context_module::instance($cm->id);
860         $assign = new assign($context, $cm, $course);
862         $student1 = self::getDataGenerator()->create_user();
863         $student2 = self::getDataGenerator()->create_user();
864         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
865         $this->getDataGenerator()->enrol_user($student1->id,
866                                               $course->id,
867                                               $studentrole->id);
868         $this->getDataGenerator()->enrol_user($student2->id,
869                                               $course->id,
870                                               $studentrole->id);
871         $teacher = self::getDataGenerator()->create_user();
872         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
873         $this->getDataGenerator()->enrol_user($teacher->id,
874                                               $course->id,
875                                               $teacherrole->id);
877         // Create a student1 with an online text submission.
878         // Simulate a submission.
879         $this->setUser($student1);
880         $result = mod_assign_external::submit_for_grading($instance->id, true);
881         $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
882         $this->assertEquals(0, count($result));
884         // Ready to test.
885         $this->setUser($teacher);
886         $students = array($student1->id, $student2->id);
887         $result = mod_assign_external::revert_submissions_to_draft($instance->id, array($student1->id));
888         $result = external_api::clean_returnvalue(mod_assign_external::revert_submissions_to_draft_returns(), $result);
890         // Check for 0 warnings.
891         $this->assertEquals(0, count($result));
893     }
895     /**
896      * Test save_submission
897      */
898     public function test_save_submission() {
899         global $DB, $USER;
901         $this->resetAfterTest(true);
902         // Create a course and assignment and users.
903         $course = self::getDataGenerator()->create_course();
905         $teacher = self::getDataGenerator()->create_user();
906         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
907         $this->getDataGenerator()->enrol_user($teacher->id,
908                                               $course->id,
909                                               $teacherrole->id);
910         $this->setUser($teacher);
912         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
913         $params['course'] = $course->id;
914         $params['assignsubmission_onlinetext_enabled'] = 1;
915         $params['assignsubmission_file_enabled'] = 1;
916         $params['assignsubmission_file_maxfiles'] = 5;
917         $params['assignsubmission_file_maxsizebytes'] = 1024*1024;
918         $instance = $generator->create_instance($params);
919         $cm = get_coursemodule_from_instance('assign', $instance->id);
920         $context = context_module::instance($cm->id);
922         $assign = new assign($context, $cm, $course);
924         $student1 = self::getDataGenerator()->create_user();
925         $student2 = self::getDataGenerator()->create_user();
926         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
927         $this->getDataGenerator()->enrol_user($student1->id,
928                                               $course->id,
929                                               $studentrole->id);
930         $this->getDataGenerator()->enrol_user($student2->id,
931                                               $course->id,
932                                               $studentrole->id);
933         // Create a student1 with an online text submission.
934         // Simulate a submission.
935         $this->setUser($student1);
937         // Create a file in a draft area.
938         $draftidfile = file_get_unused_draft_itemid();
940         $usercontext = context_user::instance($student1->id);
941         $filerecord = array(
942             'contextid' => $usercontext->id,
943             'component' => 'user',
944             'filearea'  => 'draft',
945             'itemid'    => $draftidfile,
946             'filepath'  => '/',
947             'filename'  => 'testtext.txt',
948         );
950         $fs = get_file_storage();
951         $fs->create_file_from_string($filerecord, 'text contents');
953         // Create another file in a different draft area.
954         $draftidonlinetext = file_get_unused_draft_itemid();
956         $filerecord = array(
957             'contextid' => $usercontext->id,
958             'component' => 'user',
959             'filearea'  => 'draft',
960             'itemid'    => $draftidonlinetext,
961             'filepath'  => '/',
962             'filename'  => 'shouldbeanimage.txt',
963         );
965         $fs->create_file_from_string($filerecord, 'image contents (not really)');
967         // Now try a submission.
968         $submissionpluginparams = array();
969         $submissionpluginparams['files_filemanager'] = $draftidfile;
970         $onlinetexteditorparams = array('text'=>'Yeeha!',
971                                         'format'=>1,
972                                         'itemid'=>$draftidonlinetext);
973         $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
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->assertEquals(0, count($result));
979         // Set up a due and cutoff passed date.
980         $instance->duedate = time() - WEEKSECS;
981         $instance->cutoffdate = time() - WEEKSECS;
982         $DB->update_record('assign', $instance);
984         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
985         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
987         $this->assertCount(1, $result);
988         $this->assertEquals(get_string('duedatereached', 'assign'), $result[0]['item']);
989     }
991     /**
992      * Test save_grade
993      */
994     public function test_save_grade() {
995         global $DB, $USER;
997         $this->resetAfterTest(true);
998         // Create a course and assignment and users.
999         $course = self::getDataGenerator()->create_course();
1001         $teacher = self::getDataGenerator()->create_user();
1002         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1003         $this->getDataGenerator()->enrol_user($teacher->id,
1004                                               $course->id,
1005                                               $teacherrole->id);
1006         $this->setUser($teacher);
1008         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1009         $params['course'] = $course->id;
1010         $params['assignfeedback_file_enabled'] = 1;
1011         $params['assignfeedback_comments_enabled'] = 1;
1012         $instance = $generator->create_instance($params);
1013         $cm = get_coursemodule_from_instance('assign', $instance->id);
1014         $context = context_module::instance($cm->id);
1016         $assign = new assign($context, $cm, $course);
1018         $student1 = self::getDataGenerator()->create_user();
1019         $student2 = self::getDataGenerator()->create_user();
1020         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1021         $this->getDataGenerator()->enrol_user($student1->id,
1022                                               $course->id,
1023                                               $studentrole->id);
1024         $this->getDataGenerator()->enrol_user($student2->id,
1025                                               $course->id,
1026                                               $studentrole->id);
1027         // Simulate a grade.
1028         $this->setUser($teacher);
1030         // Create a file in a draft area.
1031         $draftidfile = file_get_unused_draft_itemid();
1033         $usercontext = context_user::instance($teacher->id);
1034         $filerecord = array(
1035             'contextid' => $usercontext->id,
1036             'component' => 'user',
1037             'filearea'  => 'draft',
1038             'itemid'    => $draftidfile,
1039             'filepath'  => '/',
1040             'filename'  => 'testtext.txt',
1041         );
1043         $fs = get_file_storage();
1044         $fs->create_file_from_string($filerecord, 'text contents');
1046         // Now try a grade.
1047         $feedbackpluginparams = array();
1048         $feedbackpluginparams['files_filemanager'] = $draftidfile;
1049         $feedbackeditorparams = array('text' => 'Yeeha!',
1050                                         'format' => 1);
1051         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1052         $result = mod_assign_external::save_grade($instance->id,
1053                                                   $student1->id,
1054                                                   50.0,
1055                                                   -1,
1056                                                   true,
1057                                                   'released',
1058                                                   false,
1059                                                   $feedbackpluginparams);
1060         // No warnings.
1061         $this->assertNull($result);
1063         $result = mod_assign_external::get_grades(array($instance->id));
1064         $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
1066         $this->assertEquals($result['assignments'][0]['grades'][0]['grade'], '50.0');
1067     }
1069     /**
1070      * Test save grades with advanced grading data
1071      */
1072     public function test_save_grades_with_advanced_grading() {
1073         global $DB, $USER;
1075         $this->resetAfterTest(true);
1076         // Create a course and assignment and users.
1077         $course = self::getDataGenerator()->create_course();
1079         $teacher = self::getDataGenerator()->create_user();
1080         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1081         $this->getDataGenerator()->enrol_user($teacher->id,
1082                                               $course->id,
1083                                               $teacherrole->id);
1085         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1086         $params['course'] = $course->id;
1087         $params['assignfeedback_file_enabled'] = 0;
1088         $params['assignfeedback_comments_enabled'] = 0;
1089         $instance = $generator->create_instance($params);
1090         $cm = get_coursemodule_from_instance('assign', $instance->id);
1091         $context = context_module::instance($cm->id);
1093         $assign = new assign($context, $cm, $course);
1095         $student1 = self::getDataGenerator()->create_user();
1096         $student2 = self::getDataGenerator()->create_user();
1097         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1098         $this->getDataGenerator()->enrol_user($student1->id,
1099                                               $course->id,
1100                                               $studentrole->id);
1101         $this->getDataGenerator()->enrol_user($student2->id,
1102                                               $course->id,
1103                                               $studentrole->id);
1105         $this->setUser($teacher);
1107         $feedbackpluginparams = array();
1108         $feedbackpluginparams['files_filemanager'] = 0;
1109         $feedbackeditorparams = array('text' => '', 'format' => 1);
1110         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1112         // Create advanced grading data.
1113         // Create grading area.
1114         $gradingarea = array(
1115             'contextid' => $context->id,
1116             'component' => 'mod_assign',
1117             'areaname' => 'submissions',
1118             'activemethod' => 'rubric'
1119         );
1120         $areaid = $DB->insert_record('grading_areas', $gradingarea);
1122         // Create a rubric grading definition.
1123         $rubricdefinition = array (
1124             'areaid' => $areaid,
1125             'method' => 'rubric',
1126             'name' => 'test',
1127             'status' => 20,
1128             'copiedfromid' => 1,
1129             'timecreated' => 1,
1130             'usercreated' => $teacher->id,
1131             'timemodified' => 1,
1132             'usermodified' => $teacher->id,
1133             'timecopied' => 0
1134         );
1135         $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
1137         // Create a criterion with a level.
1138         $rubriccriteria = array (
1139              'definitionid' => $definitionid,
1140              'sortorder' => 1,
1141              'description' => 'Demonstrate an understanding of disease control',
1142              'descriptionformat' => 0
1143         );
1144         $criterionid = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria);
1145         $rubriclevel1 = array (
1146             'criterionid' => $criterionid,
1147             'score' => 50,
1148             'definition' => 'pass',
1149             'definitionformat' => 0
1150         );
1151         $rubriclevel2 = array (
1152             'criterionid' => $criterionid,
1153             'score' => 100,
1154             'definition' => 'excellent',
1155             'definitionformat' => 0
1156         );
1157         $rubriclevel3 = array (
1158             'criterionid' => $criterionid,
1159             'score' => 0,
1160             'definition' => 'fail',
1161             'definitionformat' => 0
1162         );
1163         $levelid1 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
1164         $levelid2 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
1165         $levelid3 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel3);
1167         // Create the filling.
1168         $student1filling = array (
1169             'criterionid' => $criterionid,
1170             'levelid' => $levelid1,
1171             'remark' => 'well done you passed',
1172             'remarkformat' => 0
1173         );
1175         $student2filling = array (
1176             'criterionid' => $criterionid,
1177             'levelid' => $levelid2,
1178             'remark' => 'Excellent work',
1179             'remarkformat' => 0
1180         );
1182         $student1criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student1filling)));
1183         $student1advancedgradingdata = array('rubric' => array('criteria' => $student1criteria));
1185         $student2criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student2filling)));
1186         $student2advancedgradingdata = array('rubric' => array('criteria' => $student2criteria));
1188         $grades = array();
1189         $student1gradeinfo = array();
1190         $student1gradeinfo['userid'] = $student1->id;
1191         $student1gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1192         $student1gradeinfo['attemptnumber'] = -1;
1193         $student1gradeinfo['addattempt'] = true;
1194         $student1gradeinfo['workflowstate'] = 'released';
1195         $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1196         $student1gradeinfo['advancedgradingdata'] = $student1advancedgradingdata;
1197         $grades[] = $student1gradeinfo;
1199         $student2gradeinfo = array();
1200         $student2gradeinfo['userid'] = $student2->id;
1201         $student2gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1202         $student2gradeinfo['attemptnumber'] = -1;
1203         $student2gradeinfo['addattempt'] = true;
1204         $student2gradeinfo['workflowstate'] = 'released';
1205         $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1206         $student2gradeinfo['advancedgradingdata'] = $student2advancedgradingdata;
1207         $grades[] = $student2gradeinfo;
1209         $result = mod_assign_external::save_grades($instance->id, false, $grades);
1210         $this->assertNull($result);
1212         $student1grade = $DB->get_record('assign_grades',
1213                                          array('userid' => $student1->id, 'assignment' => $instance->id),
1214                                          '*',
1215                                          MUST_EXIST);
1216         $this->assertEquals($student1grade->grade, '50.0');
1218         $student2grade = $DB->get_record('assign_grades',
1219                                          array('userid' => $student2->id, 'assignment' => $instance->id),
1220                                          '*',
1221                                          MUST_EXIST);
1222         $this->assertEquals($student2grade->grade, '100.0');
1223     }
1225     /**
1226      * Test save grades for a team submission
1227      */
1228     public function test_save_grades_with_group_submission() {
1229         global $DB, $USER, $CFG;
1230         require_once($CFG->dirroot . '/group/lib.php');
1232         $this->resetAfterTest(true);
1233         // Create a course and assignment and users.
1234         $course = self::getDataGenerator()->create_course();
1236         $teacher = self::getDataGenerator()->create_user();
1237         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1238         $this->getDataGenerator()->enrol_user($teacher->id,
1239                                               $course->id,
1240                                               $teacherrole->id);
1242         $groupingdata = array();
1243         $groupingdata['courseid'] = $course->id;
1244         $groupingdata['name'] = 'Group assignment grouping';
1246         $grouping = self::getDataGenerator()->create_grouping($groupingdata);
1248         $group1data = array();
1249         $group1data['courseid'] = $course->id;
1250         $group1data['name'] = 'Team 1';
1251         $group2data = array();
1252         $group2data['courseid'] = $course->id;
1253         $group2data['name'] = 'Team 2';
1255         $group1 = self::getDataGenerator()->create_group($group1data);
1256         $group2 = self::getDataGenerator()->create_group($group2data);
1258         groups_assign_grouping($grouping->id, $group1->id);
1259         groups_assign_grouping($grouping->id, $group2->id);
1261         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1262         $params['course'] = $course->id;
1263         $params['teamsubmission'] = 1;
1264         $params['teamsubmissiongroupingid'] = $grouping->id;
1265         $instance = $generator->create_instance($params);
1266         $cm = get_coursemodule_from_instance('assign', $instance->id);
1267         $context = context_module::instance($cm->id);
1269         $assign = new assign($context, $cm, $course);
1271         $student1 = self::getDataGenerator()->create_user();
1272         $student2 = self::getDataGenerator()->create_user();
1273         $student3 = self::getDataGenerator()->create_user();
1274         $student4 = self::getDataGenerator()->create_user();
1275         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1276         $this->getDataGenerator()->enrol_user($student1->id,
1277                                               $course->id,
1278                                               $studentrole->id);
1279         $this->getDataGenerator()->enrol_user($student2->id,
1280                                               $course->id,
1281                                               $studentrole->id);
1282         $this->getDataGenerator()->enrol_user($student3->id,
1283                                               $course->id,
1284                                               $studentrole->id);
1285         $this->getDataGenerator()->enrol_user($student4->id,
1286                                               $course->id,
1287                                               $studentrole->id);
1289         groups_add_member($group1->id, $student1->id);
1290         groups_add_member($group1->id, $student2->id);
1291         groups_add_member($group1->id, $student3->id);
1292         groups_add_member($group2->id, $student4->id);
1293         $this->setUser($teacher);
1295         $feedbackpluginparams = array();
1296         $feedbackpluginparams['files_filemanager'] = 0;
1297         $feedbackeditorparams = array('text' => '', 'format' => 1);
1298         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1300         $grades1 = array();
1301         $student1gradeinfo = array();
1302         $student1gradeinfo['userid'] = $student1->id;
1303         $student1gradeinfo['grade'] = 50;
1304         $student1gradeinfo['attemptnumber'] = -1;
1305         $student1gradeinfo['addattempt'] = true;
1306         $student1gradeinfo['workflowstate'] = 'released';
1307         $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1308         $grades1[] = $student1gradeinfo;
1310         $student2gradeinfo = array();
1311         $student2gradeinfo['userid'] = $student2->id;
1312         $student2gradeinfo['grade'] = 75;
1313         $student2gradeinfo['attemptnumber'] = -1;
1314         $student2gradeinfo['addattempt'] = true;
1315         $student2gradeinfo['workflowstate'] = 'released';
1316         $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1317         $grades1[] = $student2gradeinfo;
1319         $this->setExpectedException('invalid_parameter_exception');
1320         // Expect an exception since 2 grades have been submitted for the same team.
1321         $result = mod_assign_external::save_grades($instance->id, true, $grades1);
1322         $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
1324         $grades2 = array();
1325         $student3gradeinfo = array();
1326         $student3gradeinfo['userid'] = $student3->id;
1327         $student3gradeinfo['grade'] = 50;
1328         $student3gradeinfo['attemptnumber'] = -1;
1329         $student3gradeinfo['addattempt'] = true;
1330         $student3gradeinfo['workflowstate'] = 'released';
1331         $student3gradeinfo['plugindata'] = $feedbackpluginparams;
1332         $grades2[] = $student3gradeinfo;
1334         $student4gradeinfo = array();
1335         $student4gradeinfo['userid'] = $student4->id;
1336         $student4gradeinfo['grade'] = 75;
1337         $student4gradeinfo['attemptnumber'] = -1;
1338         $student4gradeinfo['addattempt'] = true;
1339         $student4gradeinfo['workflowstate'] = 'released';
1340         $student4gradeinfo['plugindata'] = $feedbackpluginparams;
1341         $grades2[] = $student4gradeinfo;
1342         $result = mod_assign_external::save_grades($instance->id, true, $grades2);
1343         $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
1344         // There should be no warnings.
1345         $this->assertEquals(0, count($result));
1347         $student3grade = $DB->get_record('assign_grades',
1348                                          array('userid' => $student3->id, 'assignment' => $instance->id),
1349                                          '*',
1350                                          MUST_EXIST);
1351         $this->assertEquals($student3grade->grade, '50.0');
1353         $student4grade = $DB->get_record('assign_grades',
1354                                          array('userid' => $student4->id, 'assignment' => $instance->id),
1355                                          '*',
1356                                          MUST_EXIST);
1357         $this->assertEquals($student4grade->grade, '75.0');
1358     }
1360     /**
1361      * Test copy_previous_attempt
1362      */
1363     public function test_copy_previous_attempt() {
1364         global $DB, $USER;
1366         $this->resetAfterTest(true);
1367         // Create a course and assignment and users.
1368         $course = self::getDataGenerator()->create_course();
1370         $teacher = self::getDataGenerator()->create_user();
1371         $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1372         $this->getDataGenerator()->enrol_user($teacher->id,
1373                                               $course->id,
1374                                               $teacherrole->id);
1375         $this->setUser($teacher);
1377         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1378         $params['course'] = $course->id;
1379         $params['assignsubmission_onlinetext_enabled'] = 1;
1380         $params['assignsubmission_file_enabled'] = 0;
1381         $params['assignfeedback_file_enabled'] = 0;
1382         $params['attemptreopenmethod'] = 'manual';
1383         $params['maxattempts'] = 5;
1384         $instance = $generator->create_instance($params);
1385         $cm = get_coursemodule_from_instance('assign', $instance->id);
1386         $context = context_module::instance($cm->id);
1388         $assign = new assign($context, $cm, $course);
1390         $student1 = self::getDataGenerator()->create_user();
1391         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
1392         $this->getDataGenerator()->enrol_user($student1->id,
1393                                               $course->id,
1394                                               $studentrole->id);
1395         // Now try a submission.
1396         $this->setUser($student1);
1397         $draftidonlinetext = file_get_unused_draft_itemid();
1398         $submissionpluginparams = array();
1399         $onlinetexteditorparams = array('text'=>'Yeeha!',
1400                                         'format'=>1,
1401                                         'itemid'=>$draftidonlinetext);
1402         $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
1403         $submissionpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1404         $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1405         $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1407         $this->setUser($teacher);
1408         // Add a grade and reopen the attempt.
1409         // Now try a grade.
1410         $feedbackpluginparams = array();
1411         $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1412         $feedbackeditorparams = array('text'=>'Yeeha!',
1413                                         'format'=>1);
1414         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1415         $result = mod_assign_external::save_grade($instance->id,
1416                                                   $student1->id,
1417                                                   50.0,
1418                                                   -1,
1419                                                   true,
1420                                                   'released',
1421                                                   false,
1422                                                   $feedbackpluginparams);
1423         $this->assertNull($result);
1425         $this->setUser($student1);
1426         // Now copy the previous attempt.
1427         $result = mod_assign_external::copy_previous_attempt($instance->id);
1428         $result = external_api::clean_returnvalue(mod_assign_external::copy_previous_attempt_returns(), $result);
1429         // No warnings.
1430         $this->assertEquals(0, count($result));
1432         $this->setUser($teacher);
1433         $result = mod_assign_external::get_submissions(array($instance->id));
1434         $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
1436         // Check we are now on the second attempt.
1437         $this->assertEquals($result['assignments'][0]['submissions'][0]['attemptnumber'], 1);
1438         // Check the plugins data is not empty.
1439         $this->assertNotEmpty($result['assignments'][0]['submissions'][0]['plugins']);
1441     }
1443     /**
1444      * Test set_user_flags
1445      */
1446     public function test_set_user_flags() {
1447         global $DB, $USER;
1449         $this->resetAfterTest(true);
1450         // Create a course and assignment.
1451         $coursedata['idnumber'] = 'idnumbercourse';
1452         $coursedata['fullname'] = 'Lightwork Course';
1453         $coursedata['summary'] = 'Lightwork Course description';
1454         $coursedata['summaryformat'] = FORMAT_MOODLE;
1455         $course = self::getDataGenerator()->create_course($coursedata);
1457         $assigndata['course'] = $course->id;
1458         $assigndata['name'] = 'lightwork assignment';
1460         $assign = self::getDataGenerator()->create_module('assign', $assigndata);
1462         // Create a manual enrolment record.
1463         $manualenroldata['enrol'] = 'manual';
1464         $manualenroldata['status'] = 0;
1465         $manualenroldata['courseid'] = $course->id;
1466         $enrolid = $DB->insert_record('enrol', $manualenroldata);
1468         // Create a teacher and give them capabilities.
1469         $context = context_course::instance($course->id);
1470         $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
1471         $context = context_module::instance($assign->cmid);
1472         $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
1474         // Create the teacher's enrolment record.
1475         $userenrolmentdata['status'] = 0;
1476         $userenrolmentdata['enrolid'] = $enrolid;
1477         $userenrolmentdata['userid'] = $USER->id;
1478         $DB->insert_record('user_enrolments', $userenrolmentdata);
1480         // Create a student.
1481         $student = self::getDataGenerator()->create_user();
1483         // Create test user flags record.
1484         $userflags = array();
1485         $userflag['userid'] = $student->id;
1486         $userflag['workflowstate'] = 'inmarking';
1487         $userflag['allocatedmarker'] = $USER->id;
1488         $userflags = array($userflag);
1490         $createduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
1491         // We need to execute the return values cleaning process to simulate the web service server.
1492         $createduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $createduserflags);
1494         $this->assertEquals($student->id, $createduserflags[0]['userid']);
1495         $createduserflag = $DB->get_record('assign_user_flags', array('id' => $createduserflags[0]['id']));
1497         // Confirm that all data was inserted correctly.
1498         $this->assertEquals($student->id,  $createduserflag->userid);
1499         $this->assertEquals($assign->id, $createduserflag->assignment);
1500         $this->assertEquals(0, $createduserflag->locked);
1501         $this->assertEquals(2, $createduserflag->mailed);
1502         $this->assertEquals(0, $createduserflag->extensionduedate);
1503         $this->assertEquals('inmarking', $createduserflag->workflowstate);
1504         $this->assertEquals($USER->id, $createduserflag->allocatedmarker);
1506         // Create update data.
1507         $userflags = array();
1508         $userflag['userid'] = $createduserflag->userid;
1509         $userflag['workflowstate'] = 'readyforreview';
1510         $userflags = array($userflag);
1512         $updateduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
1513         // We need to execute the return values cleaning process to simulate the web service server.
1514         $updateduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $updateduserflags);
1516         $this->assertEquals($student->id, $updateduserflags[0]['userid']);
1517         $updateduserflag = $DB->get_record('assign_user_flags', array('id' => $updateduserflags[0]['id']));
1519         // Confirm that all data was updated correctly.
1520         $this->assertEquals($student->id,  $updateduserflag->userid);
1521         $this->assertEquals($assign->id, $updateduserflag->assignment);
1522         $this->assertEquals(0, $updateduserflag->locked);
1523         $this->assertEquals(2, $updateduserflag->mailed);
1524         $this->assertEquals(0, $updateduserflag->extensionduedate);
1525         $this->assertEquals('readyforreview', $updateduserflag->workflowstate);
1526         $this->assertEquals($USER->id, $updateduserflag->allocatedmarker);
1527     }
1529     /**
1530      * Test view_grading_table
1531      */
1532     public function test_view_grading_table_invalid_instance() {
1533         global $DB;
1535         $this->resetAfterTest(true);
1537         // Setup test data.
1538         $course = $this->getDataGenerator()->create_course();
1539         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1540         $context = context_module::instance($assign->cmid);
1541         $cm = get_coursemodule_from_instance('assign', $assign->id);
1543         // Test invalid instance id.
1544         $this->setExpectedExceptionRegexp('dml_missing_record_exception');
1545         mod_assign_external::view_grading_table(0);
1546     }
1548     /**
1549      * Test view_grading_table
1550      */
1551     public function test_view_grading_table_not_enrolled() {
1552         global $DB;
1554         $this->resetAfterTest(true);
1556         // Setup test data.
1557         $course = $this->getDataGenerator()->create_course();
1558         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1559         $context = context_module::instance($assign->cmid);
1560         $cm = get_coursemodule_from_instance('assign', $assign->id);
1562         // Test not-enrolled user.
1563         $user = self::getDataGenerator()->create_user();
1564         $this->setUser($user);
1566         $this->setExpectedException('require_login_exception');
1567         mod_assign_external::view_grading_table($assign->id);
1568     }
1570     /**
1571      * Test view_grading_table
1572      */
1573     public function test_view_grading_table_correct() {
1574         global $DB;
1576         $this->resetAfterTest(true);
1578         // Setup test data.
1579         $course = $this->getDataGenerator()->create_course();
1580         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1581         $context = context_module::instance($assign->cmid);
1582         $cm = get_coursemodule_from_instance('assign', $assign->id);
1584         // Test user with full capabilities.
1585         $user = self::getDataGenerator()->create_user();
1586         $this->setUser($user);
1587         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1588         $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1590         // Trigger and capture the event.
1591         $sink = $this->redirectEvents();
1593         $result = mod_assign_external::view_grading_table($assign->id);
1594         $result = external_api::clean_returnvalue(mod_assign_external::view_grading_table_returns(), $result);
1596         $events = $sink->get_events();
1597         $this->assertCount(1, $events);
1598         $event = array_shift($events);
1600         // Checking that the event contains the expected values.
1601         $this->assertInstanceOf('\mod_assign\event\grading_table_viewed', $event);
1602         $this->assertEquals($context, $event->get_context());
1603         $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1604         $this->assertEquals($moodleurl, $event->get_url());
1605         $this->assertEventContextNotUsed($event);
1606         $this->assertNotEmpty($event->get_name());
1607     }
1609     /**
1610      * Test view_grading_table
1611      */
1612     public function test_view_grading_table_without_capability() {
1613         global $DB;
1615         $this->resetAfterTest(true);
1617         // Setup test data.
1618         $course = $this->getDataGenerator()->create_course();
1619         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1620         $context = context_module::instance($assign->cmid);
1621         $cm = get_coursemodule_from_instance('assign', $assign->id);
1623         // Test user with no capabilities.
1624         $user = self::getDataGenerator()->create_user();
1625         $this->setUser($user);
1626         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1627         $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1629         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1630         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1631         assign_capability('mod/assign:view', CAP_PROHIBIT, $teacherrole->id, $context->id);
1632         // Empty all the caches that may be affected by this change.
1633         accesslib_clear_all_caches_for_unit_testing();
1634         course_modinfo::clear_instance_cache();
1636         $this->setExpectedException('require_login_exception', 'Course or activity not accessible. (Activity is hidden)');
1637         mod_assign_external::view_grading_table($assign->id);
1638     }
1640     /**
1641      * Test subplugins availability
1642      */
1643     public function test_subplugins_availability() {
1644         global $CFG;
1646         require_once($CFG->dirroot . '/mod/assign/adminlib.php');
1647         $this->resetAfterTest(true);
1649         // Hide assignment file submissiong plugin.
1650         $pluginmanager = new assign_plugin_manager('assignsubmission');
1651         $pluginmanager->hide_plugin('file');
1652         $parameters = mod_assign_external::save_submission_parameters();
1654         $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1656         // Show it again and check that the value is returned as optional.
1657         $pluginmanager->show_plugin('file');
1658         $parameters = mod_assign_external::save_submission_parameters();
1659         $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1660         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1662         // Hide feedback file submissiong plugin.
1663         $pluginmanager = new assign_plugin_manager('assignfeedback');
1664         $pluginmanager->hide_plugin('file');
1666         $parameters = mod_assign_external::save_grade_parameters();
1668         $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1670         // Show it again and check that the value is returned as optional.
1671         $pluginmanager->show_plugin('file');
1672         $parameters = mod_assign_external::save_grade_parameters();
1674         $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1675         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1677         // Check a different one.
1678         $pluginmanager->show_plugin('comments');
1679         $this->assertTrue(isset($parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']));
1680         $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']->required);
1681     }
1683     /**
1684      * Test test_view_submission_status
1685      */
1686     public function test_view_submission_status() {
1687         global $DB;
1689         $this->resetAfterTest(true);
1691         $this->setAdminUser();
1692         // Setup test data.
1693         $course = $this->getDataGenerator()->create_course();
1694         $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1695         $context = context_module::instance($assign->cmid);
1696         $cm = get_coursemodule_from_instance('assign', $assign->id);
1698         // Test invalid instance id.
1699         try {
1700             mod_assign_external::view_submission_status(0);
1701             $this->fail('Exception expected due to invalid mod_assign instance id.');
1702         } catch (moodle_exception $e) {
1703             $this->assertEquals('invalidrecord', $e->errorcode);
1704         }
1706         // Test not-enrolled user.
1707         $user = self::getDataGenerator()->create_user();
1708         $this->setUser($user);
1709         try {
1710             mod_assign_external::view_submission_status($assign->id);
1711             $this->fail('Exception expected due to not enrolled user.');
1712         } catch (moodle_exception $e) {
1713             $this->assertEquals('requireloginerror', $e->errorcode);
1714         }
1716         // Test user with full capabilities.
1717         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1718         $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
1720         // Trigger and capture the event.
1721         $sink = $this->redirectEvents();
1723         $result = mod_assign_external::view_submission_status($assign->id);
1724         $result = external_api::clean_returnvalue(mod_assign_external::view_submission_status_returns(), $result);
1726         $events = $sink->get_events();
1727         $this->assertCount(1, $events);
1728         $event = array_shift($events);
1730         // Checking that the event contains the expected values.
1731         $this->assertInstanceOf('\mod_assign\event\submission_status_viewed', $event);
1732         $this->assertEquals($context, $event->get_context());
1733         $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1734         $this->assertEquals($moodleurl, $event->get_url());
1735         $this->assertEventContextNotUsed($event);
1736         $this->assertNotEmpty($event->get_name());
1738         // Test user with no capabilities.
1739         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1740         assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id);
1741         accesslib_clear_all_caches_for_unit_testing();
1742         course_modinfo::clear_instance_cache();
1744         try {
1745             mod_assign_external::view_submission_status($assign->id);
1746             $this->fail('Exception expected due to missing capability.');
1747         } catch (moodle_exception $e) {
1748             $this->assertEquals('requireloginerror', $e->errorcode);
1749         }
1750     }
1752     /**
1753      * Create a submission for testing the get_submission_status function.
1754      * @param  boolean $submitforgrading whether to submit for grading the submission
1755      * @return array an array containing all the required data for testing
1756      */
1757     private function create_submission_for_testing_status($submitforgrading = false) {
1758         global $DB, $CFG;
1759         require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
1761         // Create a course and assignment and users.
1762         $course = self::getDataGenerator()->create_course();
1764         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1765         $params = array(
1766             'course' => $course->id,
1767             'assignsubmission_file_maxfiles' => 1,
1768             'assignsubmission_file_maxsizebytes' => 1024 * 1024,
1769             'assignsubmission_onlinetext_enabled' => 1,
1770             'assignsubmission_file_enabled' => 1,
1771             'submissiondrafts' => 1,
1772             'assignfeedback_file_enabled' => 1,
1773             'assignfeedback_comments_enabled' => 1,
1774             'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1775             'sendnotifications' => 0
1776         );
1778         set_config('submissionreceipts', 0, 'assign');
1780         $instance = $generator->create_instance($params);
1781         $cm = get_coursemodule_from_instance('assign', $instance->id);
1782         $context = context_module::instance($cm->id);
1784         $assign = new testable_assign($context, $cm, $course);
1786         $student1 = self::getDataGenerator()->create_user();
1787         $student2 = self::getDataGenerator()->create_user();
1788         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1789         $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
1790         $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
1791         $teacher = self::getDataGenerator()->create_user();
1792         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1793         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
1795         $this->setUser($student1);
1797         // Create a student1 with an online text submission.
1798         // Simulate a submission.
1799         $submission = $assign->get_user_submission($student1->id, true);
1801         $data = new stdClass();
1802         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1803                                          'text' => 'Submission text',
1804                                          'format' => FORMAT_MOODLE);
1806         $draftidfile = file_get_unused_draft_itemid();
1807         $usercontext = context_user::instance($student1->id);
1808         $filerecord = array(
1809             'contextid' => $usercontext->id,
1810             'component' => 'user',
1811             'filearea'  => 'draft',
1812             'itemid'    => $draftidfile,
1813             'filepath'  => '/',
1814             'filename'  => 't.txt',
1815         );
1816         $fs = get_file_storage();
1817         $fs->create_file_from_string($filerecord, 'text contents');
1819         $data->files_filemanager = $draftidfile;
1821         $notices = array();
1822         $assign->save_submission($data, $notices);
1824         if ($submitforgrading) {
1825             // Now, submit the draft for grading.
1826             $notices = array();
1828             $data = new stdClass;
1829             $data->userid = $student1->id;
1830             $assign->submit_for_grading($data, $notices);
1831         }
1833         return array($assign, $instance, $student1, $student2, $teacher);
1834     }
1836     /**
1837      * Test get_submission_status for a draft submission.
1838      */
1839     public function test_get_submission_status_in_draft_status() {
1840         $this->resetAfterTest(true);
1842         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status();
1844         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1845         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1846         $this->assertDebuggingCalled();
1848         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1850         // The submission is now in draft mode.
1851         $this->assertCount(0, $result['warnings']);
1852         $this->assertFalse(isset($result['gradingsummary']));
1853         $this->assertFalse(isset($result['feedback']));
1854         $this->assertFalse(isset($result['previousattempts']));
1856         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1857         $this->assertTrue($result['lastattempt']['canedit']);
1858         $this->assertTrue($result['lastattempt']['cansubmit']);
1859         $this->assertFalse($result['lastattempt']['locked']);
1860         $this->assertFalse($result['lastattempt']['graded']);
1861         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1862         $this->assertFalse($result['lastattempt']['blindmarking']);
1863         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1864         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1866         $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
1867         $this->assertEquals(0, $result['lastattempt']['submission']['attemptnumber']);
1868         $this->assertEquals('draft', $result['lastattempt']['submission']['status']);
1869         $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
1870         $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
1871         $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
1872         $this->assertEquals('Submission text', $result['lastattempt']['submission']['plugins'][0]['editorfields'][0]['text']);
1873         $this->assertEquals('/t.txt', $result['lastattempt']['submission']['plugins'][1]['fileareas'][0]['files'][0]['filepath']);
1874     }
1876     /**
1877      * Test get_submission_status for a submitted submission.
1878      */
1879     public function test_get_submission_status_in_submission_status() {
1880         $this->resetAfterTest(true);
1882         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
1884         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1885         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1886         $this->assertDebuggingCalled();
1887         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1889         $this->assertCount(0, $result['warnings']);
1890         $this->assertFalse(isset($result['gradingsummary']));
1891         $this->assertFalse(isset($result['feedback']));
1892         $this->assertFalse(isset($result['previousattempts']));
1894         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1895         $this->assertFalse($result['lastattempt']['canedit']);
1896         $this->assertFalse($result['lastattempt']['cansubmit']);
1897         $this->assertFalse($result['lastattempt']['locked']);
1898         $this->assertFalse($result['lastattempt']['graded']);
1899         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1900         $this->assertFalse($result['lastattempt']['blindmarking']);
1901         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1902         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1904     }
1906     /**
1907      * Test get_submission_status using the teacher role.
1908      */
1909     public function test_get_submission_status_in_submission_status_for_teacher() {
1910         $this->resetAfterTest(true);
1912         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
1914         // Now, as teacher, see the grading summary.
1915         $this->setUser($teacher);
1916         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1917         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1918         $this->assertDebuggingCalled();
1919         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1921         $this->assertCount(0, $result['warnings']);
1922         $this->assertFalse(isset($result['lastattempt']));
1923         $this->assertFalse(isset($result['feedback']));
1924         $this->assertFalse(isset($result['previousattempts']));
1926         $this->assertEquals(2, $result['gradingsummary']['participantcount']);
1927         $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
1928         $this->assertEquals(1, $result['gradingsummary']['submissionsenabled']);
1929         $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']);
1930         $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']);
1931         $this->assertFalse($result['gradingsummary']['warnofungroupedusers']);
1932     }
1934     /**
1935      * Test get_submission_status for a reopened submission.
1936      */
1937     public function test_get_submission_status_in_reopened_status() {
1938         global $USER;
1940         $this->resetAfterTest(true);
1942         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
1944         $this->setUser($teacher);
1945         // Grade and reopen.
1946         $feedbackpluginparams = array();
1947         $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1948         $feedbackeditorparams = array('text' => 'Yeeha!',
1949                                         'format' => 1);
1950         $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1951         $result = mod_assign_external::save_grade($instance->id,
1952                                                   $student1->id,
1953                                                   50.0,
1954                                                   -1,
1955                                                   false,
1956                                                   'released',
1957                                                   false,
1958                                                   $feedbackpluginparams);
1959         $USER->ignoresesskey = true;
1960         $assign->testable_process_add_attempt($student1->id);
1962         $this->setUser($student1);
1964         $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
1965         // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
1966         $this->assertDebuggingCalled();
1967         $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
1969         $this->assertCount(0, $result['warnings']);
1970         $this->assertFalse(isset($result['gradingsummary']));
1972         $this->assertTrue($result['lastattempt']['submissionsenabled']);
1973         $this->assertTrue($result['lastattempt']['canedit']);
1974         $this->assertFalse($result['lastattempt']['cansubmit']);
1975         $this->assertFalse($result['lastattempt']['locked']);
1976         $this->assertFalse($result['lastattempt']['graded']);
1977         $this->assertEmpty($result['lastattempt']['extensionduedate']);
1978         $this->assertFalse($result['lastattempt']['blindmarking']);
1979         $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
1980         $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
1982         // Check new attempt reopened.
1983         $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
1984         $this->assertEquals(1, $result['lastattempt']['submission']['attemptnumber']);
1985         $this->assertEquals('reopened', $result['lastattempt']['submission']['status']);
1986         $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
1987         $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
1988         $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
1989         $this->assertCount(3, $result['lastattempt']['submission']['plugins']);
1991         // Now see feedback and the attempts history (remember, is a submission reopened).
1992         // Only 2 fields (no grade, no plugins data).
1993         $this->assertCount(2, $result['feedback']);
1995         // One previous attempt.
1996         $this->assertCount(1, $result['previousattempts']);
1997         $this->assertEquals(0, $result['previousattempts'][0]['attemptnumber']);
1998         $this->assertEquals(50, $result['previousattempts'][0]['grade']['grade']);
1999         $this->assertEquals($teacher->id, $result['previousattempts'][0]['grade']['grader']);
2000         $this->assertEquals($student1->id, $result['previousattempts'][0]['grade']['userid']);
2001         $this->assertEquals('Yeeha!', $result['previousattempts'][0]['feedbackplugins'][0]['editorfields'][0]['text']);
2002         $submissionplugins = $result['previousattempts'][0]['submission']['plugins'];
2003         $this->assertEquals('Submission text', $submissionplugins[0]['editorfields'][0]['text']);
2004         $this->assertEquals('/t.txt', $submissionplugins[1]['fileareas'][0]['files'][0]['filepath']);
2005     }
2007     /**
2008      * Test access control for get_submission_status.
2009      */
2010     public function test_get_submission_status_access_control() {
2011         $this->resetAfterTest(true);
2013         list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status();
2015         $this->setUser($student2);
2017         // Access control test.
2018         $this->setExpectedException('required_capability_exception');
2019         mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id);
2021     }