MDL-70110 mod_assign: make phpunit test DB order agnostic
[moodle.git] / mod / assign / tests / externallib_test.php
blobc95c3e7639921bba93e71f4de1d7bcad6b8d69f7
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.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 defined('MOODLE_INTERNAL') || die();
19 global $CFG;
21 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
22 require_once($CFG->dirroot . '/mod/assign/externallib.php');
23 require_once(__DIR__ . '/fixtures/testable_assign.php');
25 /**
26 * External mod assign functions unit tests
28 * @package mod_assign
29 * @category external
30 * @copyright 2012 Paul Charsley
31 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33 class mod_assign_external_testcase extends externallib_advanced_testcase {
35 /**
36 * Test get_grades
38 public function test_get_grades() {
39 global $DB, $USER;
41 $this->resetAfterTest(true);
42 // Create a course and assignment.
43 $coursedata['idnumber'] = 'idnumbercourse';
44 $coursedata['fullname'] = 'Lightwork Course';
45 $coursedata['summary'] = 'Lightwork Course description';
46 $coursedata['summaryformat'] = FORMAT_MOODLE;
47 $course = self::getDataGenerator()->create_course($coursedata);
49 $assigndata['course'] = $course->id;
50 $assigndata['name'] = 'lightwork assignment';
52 $assign = self::getDataGenerator()->create_module('assign', $assigndata);
54 // Create a manual enrolment record.
55 $manualenroldata['enrol'] = 'manual';
56 $manualenroldata['status'] = 0;
57 $manualenroldata['courseid'] = $course->id;
58 $enrolid = $DB->insert_record('enrol', $manualenroldata);
60 // Create a teacher and give them capabilities.
61 $context = context_course::instance($course->id);
62 $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
63 $context = context_module::instance($assign->cmid);
64 $this->assignUserCapability('mod/assign:viewgrades', $context->id, $roleid);
66 // Create the teacher's enrolment record.
67 $userenrolmentdata['status'] = 0;
68 $userenrolmentdata['enrolid'] = $enrolid;
69 $userenrolmentdata['userid'] = $USER->id;
70 $DB->insert_record('user_enrolments', $userenrolmentdata);
72 // Create a student and give them 2 grades (for 2 attempts).
73 $student = self::getDataGenerator()->create_user();
75 $submission = new stdClass();
76 $submission->assignment = $assign->id;
77 $submission->userid = $student->id;
78 $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
79 $submission->latest = 0;
80 $submission->attemptnumber = 0;
81 $submission->groupid = 0;
82 $submission->timecreated = time();
83 $submission->timemodified = time();
84 $DB->insert_record('assign_submission', $submission);
86 $grade = new stdClass();
87 $grade->assignment = $assign->id;
88 $grade->userid = $student->id;
89 $grade->timecreated = time();
90 $grade->timemodified = $grade->timecreated;
91 $grade->grader = $USER->id;
92 $grade->grade = 50;
93 $grade->attemptnumber = 0;
94 $DB->insert_record('assign_grades', $grade);
96 $submission = new stdClass();
97 $submission->assignment = $assign->id;
98 $submission->userid = $student->id;
99 $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
100 $submission->latest = 1;
101 $submission->attemptnumber = 1;
102 $submission->groupid = 0;
103 $submission->timecreated = time();
104 $submission->timemodified = time();
105 $DB->insert_record('assign_submission', $submission);
107 $grade = new stdClass();
108 $grade->assignment = $assign->id;
109 $grade->userid = $student->id;
110 $grade->timecreated = time();
111 $grade->timemodified = $grade->timecreated;
112 $grade->grader = $USER->id;
113 $grade->grade = 75;
114 $grade->attemptnumber = 1;
115 $DB->insert_record('assign_grades', $grade);
117 $assignmentids[] = $assign->id;
118 $result = mod_assign_external::get_grades($assignmentids);
120 // We need to execute the return values cleaning process to simulate the web service server.
121 $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
123 // Check that the correct grade information for the student is returned.
124 $this->assertEquals(1, count($result['assignments']));
125 $assignment = $result['assignments'][0];
126 $this->assertEquals($assign->id, $assignment['assignmentid']);
127 // Should only get the last grade for this student.
128 $this->assertEquals(1, count($assignment['grades']));
129 $grade = $assignment['grades'][0];
130 $this->assertEquals($student->id, $grade['userid']);
131 // Should be the last grade (not the first).
132 $this->assertEquals(75, $grade['grade']);
136 * Test get_assignments
138 public function test_get_assignments() {
139 global $DB, $USER, $CFG;
141 $this->resetAfterTest(true);
143 // Enable multilang filter to on content and heading.
144 filter_set_global_state('multilang', TEXTFILTER_ON);
145 filter_set_applies_to_strings('multilang', 1);
146 // Set WS filtering.
147 $wssettings = external_settings::get_instance();
148 $wssettings->set_filter(true);
150 $category = self::getDataGenerator()->create_category(array(
151 'name' => 'Test category'
154 // Create a course.
155 $course1 = self::getDataGenerator()->create_course(array(
156 'idnumber' => 'idnumbercourse1',
157 'fullname' => '<b>Lightwork Course 1</b>', // Adding tags here to check that external_format_string works.
158 'shortname' => '<b>Lightwork Course 1</b>', // Adding tags here to check that external_format_string works.
159 'summary' => 'Lightwork Course 1 description',
160 'summaryformat' => FORMAT_MOODLE,
161 'category' => $category->id
164 // Create a second course, just for testing.
165 $course2 = self::getDataGenerator()->create_course(array(
166 'idnumber' => 'idnumbercourse2',
167 'fullname' => 'Lightwork Course 2',
168 'summary' => 'Lightwork Course 2 description',
169 'summaryformat' => FORMAT_MOODLE,
170 'category' => $category->id
173 // Create the assignment module with links to a filerecord.
174 $assign1 = self::getDataGenerator()->create_module('assign', array(
175 'course' => $course1->id,
176 'name' => '<span lang="en" class="multilang">English</span><span lang="es" class="multilang">Español</span>',
177 'intro' => 'the assignment intro text here <a href="@@PLUGINFILE@@/intro.txt">link</a>',
178 'introformat' => FORMAT_HTML,
179 'markingworkflow' => 1,
180 'markingallocation' => 1
183 // Add a file as assignment attachment.
184 $context = context_module::instance($assign1->cmid);
185 $filerecord = array('component' => 'mod_assign', 'filearea' => 'intro', 'contextid' => $context->id, 'itemid' => 0,
186 'filename' => 'intro.txt', 'filepath' => '/');
187 $fs = get_file_storage();
188 $fs->create_file_from_string($filerecord, 'Test intro file');
190 // Create manual enrolment record.
191 $enrolid = $DB->insert_record('enrol', (object)array(
192 'enrol' => 'manual',
193 'status' => 0,
194 'courseid' => $course1->id
197 // Create the user and give them capabilities.
198 $context = context_course::instance($course1->id);
199 $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
200 $context = context_module::instance($assign1->cmid);
201 $this->assignUserCapability('mod/assign:view', $context->id, $roleid);
203 // Create the user enrolment record.
204 $DB->insert_record('user_enrolments', (object)array(
205 'status' => 0,
206 'enrolid' => $enrolid,
207 'userid' => $USER->id
210 // Add a file as assignment attachment.
211 $filerecord = array('component' => 'mod_assign', 'filearea' => ASSIGN_INTROATTACHMENT_FILEAREA,
212 'contextid' => $context->id, 'itemid' => 0,
213 'filename' => 'introattachment.txt', 'filepath' => '/');
214 $fs = get_file_storage();
215 $fs->create_file_from_string($filerecord, 'Test intro attachment file');
217 $result = mod_assign_external::get_assignments();
219 // We need to execute the return values cleaning process to simulate the web service server.
220 $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
222 // Check the course and assignment are returned.
223 $this->assertEquals(1, count($result['courses']));
224 $course = $result['courses'][0];
225 $this->assertEquals('Lightwork Course 1', $course['fullname']);
226 $this->assertEquals('Lightwork Course 1', $course['shortname']);
227 $this->assertEquals(1, count($course['assignments']));
228 $assignment = $course['assignments'][0];
229 $this->assertEquals($assign1->id, $assignment['id']);
230 $this->assertEquals($course1->id, $assignment['course']);
231 $this->assertEquals('English', $assignment['name']);
232 $this->assertContains('the assignment intro text here', $assignment['intro']);
233 $this->assertNotEmpty($assignment['configs']);
234 // Check the url of the file attatched.
235 $this->assertRegExp('@"' . $CFG->wwwroot . '/webservice/pluginfile.php/\d+/mod_assign/intro/intro\.txt"@', $assignment['intro']);
236 $this->assertEquals(1, $assignment['markingworkflow']);
237 $this->assertEquals(1, $assignment['markingallocation']);
238 $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
240 $this->assertCount(1, $assignment['introattachments']);
241 $this->assertEquals('introattachment.txt', $assignment['introattachments'][0]['filename']);
243 // Now, hide the descritption until the submission from date.
244 $DB->set_field('assign', 'alwaysshowdescription', 0, array('id' => $assign1->id));
245 $DB->set_field('assign', 'allowsubmissionsfromdate', time() + DAYSECS, array('id' => $assign1->id));
247 $result = mod_assign_external::get_assignments(array($course1->id));
249 // We need to execute the return values cleaning process to simulate the web service server.
250 $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
252 $this->assertEquals(1, count($result['courses']));
253 $course = $result['courses'][0];
254 $this->assertEquals('Lightwork Course 1', $course['fullname']);
255 $this->assertEquals(1, count($course['assignments']));
256 $assignment = $course['assignments'][0];
257 $this->assertEquals($assign1->id, $assignment['id']);
258 $this->assertEquals($course1->id, $assignment['course']);
259 $this->assertEquals('English', $assignment['name']);
260 $this->assertArrayNotHasKey('intro', $assignment);
261 $this->assertArrayNotHasKey('introattachments', $assignment);
262 $this->assertEquals(1, $assignment['markingworkflow']);
263 $this->assertEquals(1, $assignment['markingallocation']);
264 $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
266 $result = mod_assign_external::get_assignments(array($course2->id));
268 // We need to execute the return values cleaning process to simulate the web service server.
269 $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
271 $this->assertEquals(0, count($result['courses']));
272 $this->assertEquals(1, count($result['warnings']));
274 // Test with non-enrolled user, but with view capabilities.
275 $this->setAdminUser();
276 $result = mod_assign_external::get_assignments();
277 $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
278 $this->assertEquals(0, count($result['courses']));
279 $this->assertEquals(0, count($result['warnings']));
281 // Expect no courses, because we are not using the special flag.
282 $result = mod_assign_external::get_assignments(array($course1->id));
283 $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
284 $this->assertCount(0, $result['courses']);
286 // Now use the special flag to return courses where you are not enroled in.
287 $result = mod_assign_external::get_assignments(array($course1->id), array(), true);
288 $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
289 $this->assertCount(1, $result['courses']);
291 $course = $result['courses'][0];
292 $this->assertEquals('Lightwork Course 1', $course['fullname']);
293 $this->assertEquals(1, count($course['assignments']));
294 $assignment = $course['assignments'][0];
295 $this->assertEquals($assign1->id, $assignment['id']);
296 $this->assertEquals($course1->id, $assignment['course']);
297 $this->assertEquals('English', $assignment['name']);
298 $this->assertArrayNotHasKey('intro', $assignment);
299 $this->assertArrayNotHasKey('introattachments', $assignment);
300 $this->assertEquals(1, $assignment['markingworkflow']);
301 $this->assertEquals(1, $assignment['markingallocation']);
302 $this->assertEquals(0, $assignment['preventsubmissionnotingroup']);
306 * Test get_assignments with submissionstatement.
308 public function test_get_assignments_with_submissionstatement() {
309 global $DB, $USER, $CFG;
311 $this->resetAfterTest(true);
313 // Setup test data. Create 2 assigns, one with requiresubmissionstatement and the other without it.
314 $course = $this->getDataGenerator()->create_course();
315 $assign = $this->getDataGenerator()->create_module('assign', array(
316 'course' => $course->id,
317 'requiresubmissionstatement' => 1
319 $assign2 = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
321 // Create student.
322 $student = self::getDataGenerator()->create_user();
324 // Users enrolments.
325 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
326 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
328 // Update the submissionstatement.
329 $submissionstatement = 'This is a fake submission statement.';
330 set_config('submissionstatement', $submissionstatement, 'assign');
332 $this->setUser($student);
334 $result = mod_assign_external::get_assignments();
335 // We need to execute the return values cleaning process to simulate the web service server.
336 $result = external_api::clean_returnvalue(mod_assign_external::get_assignments_returns(), $result);
338 // Check that the amount of courses and assignments is right.
339 $this->assertCount(1, $result['courses']);
340 $assignmentsret = $result['courses'][0]['assignments'];
341 $this->assertCount(2, $assignmentsret);
343 // Order the returned assignments by ID.
344 usort($assignmentsret, function($a, $b) {
345 return strcmp($a['id'], $b['id']);
348 // Check that the first assign contains the submission statement.
349 $assignmentret = $assignmentsret[0];
350 $this->assertEquals($assign->id, $assignmentret['id']);
351 $this->assertEquals(1, $assignmentret['requiresubmissionstatement']);
352 $this->assertEquals($submissionstatement, $assignmentret['submissionstatement']);
354 // Check that the second assign does NOT contain the submission statement.
355 $assignmentret = $assignmentsret[1];
356 $this->assertEquals($assign2->id, $assignmentret['id']);
357 $this->assertEquals(0, $assignmentret['requiresubmissionstatement']);
358 $this->assertArrayNotHasKey('submissionstatement', $assignmentret);
362 * Test get_submissions
364 public function test_get_submissions() {
365 global $DB, $USER;
367 $this->resetAfterTest(true);
368 // Create a course and assignment.
369 $coursedata['idnumber'] = 'idnumbercourse1';
370 $coursedata['fullname'] = 'Lightwork Course 1';
371 $coursedata['summary'] = 'Lightwork Course 1 description';
372 $coursedata['summaryformat'] = FORMAT_MOODLE;
373 $course1 = self::getDataGenerator()->create_course($coursedata);
375 $assigndata['course'] = $course1->id;
376 $assigndata['name'] = 'lightwork assignment';
378 $assign1 = self::getDataGenerator()->create_module('assign', $assigndata);
380 // Create a student with an online text submission.
381 // First attempt.
382 $student = self::getDataGenerator()->create_user();
383 $teacher = self::getDataGenerator()->create_user();
384 $submission = new stdClass();
385 $submission->assignment = $assign1->id;
386 $submission->userid = $student->id;
387 $submission->timecreated = time();
388 $submission->timemodified = $submission->timecreated;
389 $submission->status = 'draft';
390 $submission->attemptnumber = 0;
391 $submission->latest = 0;
392 $sid = $DB->insert_record('assign_submission', $submission);
394 // Second attempt.
395 $submission = new stdClass();
396 $submission->assignment = $assign1->id;
397 $submission->userid = $student->id;
398 $submission->timecreated = time();
399 $submission->timemodified = $submission->timecreated;
400 $submission->status = 'submitted';
401 $submission->attemptnumber = 1;
402 $submission->latest = 1;
403 $sid = $DB->insert_record('assign_submission', $submission);
404 $submission->id = $sid;
406 $onlinetextsubmission = new stdClass();
407 $onlinetextsubmission->onlinetext = "<p>online test text</p>";
408 $onlinetextsubmission->onlineformat = 1;
409 $onlinetextsubmission->submission = $submission->id;
410 $onlinetextsubmission->assignment = $assign1->id;
411 $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
413 // Enrol the teacher in the course.
414 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
415 $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id);
416 $this->setUser($teacher);
418 $assignmentids[] = $assign1->id;
419 $result = mod_assign_external::get_submissions($assignmentids);
420 $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
422 // Check the online text submission is NOT returned because the student is not yet enrolled in the course.
423 $this->assertEquals(1, count($result['assignments']));
424 $assignment = $result['assignments'][0];
425 $this->assertEquals($assign1->id, $assignment['assignmentid']);
426 $this->assertEquals(0, count($assignment['submissions']));
428 // Enrol the student in the course.
429 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
430 $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id);
432 $result = mod_assign_external::get_submissions($assignmentids);
433 $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
435 $this->assertEquals(1, count($result['assignments']));
436 $assignment = $result['assignments'][0];
437 $this->assertEquals($assign1->id, $assignment['assignmentid']);
438 // Now, we get the submission because the user is enrolled.
439 $this->assertEquals(1, count($assignment['submissions']));
440 $submission = $assignment['submissions'][0];
441 $this->assertEquals($sid, $submission['id']);
442 $this->assertCount(1, $submission['plugins']);
443 $this->assertEquals('notgraded', $submission['gradingstatus']);
445 // Test locking the context.
446 set_config('contextlocking', 1);
447 $context = context_course::instance($course1->id);
448 $context->set_locked(true);
450 $this->setUser($teacher);
451 $assignmentids[] = $assign1->id;
452 $result = mod_assign_external::get_submissions($assignmentids);
453 $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
454 $this->assertEquals(1, count($result['assignments']));
458 * Test get_submissions with teamsubmission enabled
460 public function test_get_submissions_group_submission() {
461 global $DB;
463 $this->resetAfterTest(true);
465 $result = $this->create_assign_with_student_and_teacher(array(
466 'assignsubmission_onlinetext_enabled' => 1,
467 'teamsubmission' => 1
469 $assignmodule = $result['assign'];
470 $student = $result['student'];
471 $teacher = $result['teacher'];
472 $course = $result['course'];
473 $context = context_course::instance($course->id);
474 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
475 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
476 $cm = get_coursemodule_from_instance('assign', $assignmodule->id);
477 $context = context_module::instance($cm->id);
478 $assign = new mod_assign_testable_assign($context, $cm, $course);
480 groups_add_member($group, $student);
482 $this->setUser($student);
483 $submission = $assign->get_group_submission($student->id, $group->id, true);
484 $sid = $submission->id;
486 $this->setUser($teacher);
488 $assignmentids[] = $assignmodule->id;
489 $result = mod_assign_external::get_submissions($assignmentids);
490 $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
492 $this->assertEquals(1, count($result['assignments']));
493 $assignment = $result['assignments'][0];
494 $this->assertEquals($assignmodule->id, $assignment['assignmentid']);
495 $this->assertEquals(1, count($assignment['submissions']));
496 $submission = $assignment['submissions'][0];
497 $this->assertEquals($sid, $submission['id']);
498 $this->assertEquals($group->id, $submission['groupid']);
499 $this->assertEquals(0, $submission['userid']);
503 * Test get_submissions with teamsubmission enabled
504 * and a group having a higher attemptnumber than another
506 public function test_get_submissions_group_submission_attemptnumber() {
507 global $DB;
508 $this->resetAfterTest(true);
510 $result = $this->create_assign_with_student_and_teacher([
511 'assignsubmission_onlinetext_enabled' => 1,
512 'attemptreopenmethod' => 'manual',
513 'teamsubmission' => 1,
515 $assignmodule = $result['assign'];
516 $course = $result['course'];
518 $teacher = $result['teacher'];
519 $student1 = $result['student'];
520 $student2 = self::getDataGenerator()->create_user();
522 // Enrol second user into the course.
523 $studentrole = $DB->get_record('role', ['shortname' => 'student']);
524 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
526 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
527 groups_add_member($group1, $student1);
528 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
529 groups_add_member($group2, $student2);
531 $this->setUser($student1);
532 mod_assign_external::save_submission(
533 $assignmodule->id,
535 'onlinetext_editor' => [
536 'text' => 'Group 1, Submission 1',
537 'format' => FORMAT_PLAIN,
538 'itemid' => file_get_unused_draft_itemid(),
542 $this->setUser($student2);
543 mod_assign_external::save_submission(
544 $assignmodule->id,
546 'onlinetext_editor' => [
547 'text' => 'Group 2, Submission 1',
548 'format' => FORMAT_PLAIN,
549 'itemid' => file_get_unused_draft_itemid(),
553 mod_assign_external::submit_for_grading($assignmodule->id, 1);
554 $this->setUser($teacher);
555 mod_assign_external::save_grade($assignmodule->id, $student2->id, 0, -1, 1, "", 1);
556 $this->setUser($student2);
557 mod_assign_external::save_submission(
558 $assignmodule->id,
560 'onlinetext_editor' => [
561 'text' => 'Group 2, Submission 2',
562 'format' => FORMAT_PLAIN,
563 'itemid' => file_get_unused_draft_itemid(),
568 $this->setUser($teacher);
569 $result = mod_assign_external::get_submissions([$assignmodule->id]);
570 $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
572 $this->assertEquals(1, count($result['assignments']));
573 [$assignment] = $result['assignments'];
574 $this->assertEquals($assignmodule->id, $assignment['assignmentid']);
576 $this->assertEquals(2, count($assignment['submissions']));
577 $expectedsubmissions = ['Group 1, Submission 1', 'Group 2, Submission 2'];
578 foreach ($assignment['submissions'] as $submission) {
579 $this->assertContains($submission['plugins'][0]['editorfields'][0]['text'], $expectedsubmissions);
584 * Test get_user_flags
586 public function test_get_user_flags() {
587 global $DB, $USER;
589 $this->resetAfterTest(true);
590 // Create a course and assignment.
591 $coursedata['idnumber'] = 'idnumbercourse';
592 $coursedata['fullname'] = 'Lightwork Course';
593 $coursedata['summary'] = 'Lightwork Course description';
594 $coursedata['summaryformat'] = FORMAT_MOODLE;
595 $course = self::getDataGenerator()->create_course($coursedata);
597 $assigndata['course'] = $course->id;
598 $assigndata['name'] = 'lightwork assignment';
600 $assign = self::getDataGenerator()->create_module('assign', $assigndata);
602 // Create a manual enrolment record.
603 $manualenroldata['enrol'] = 'manual';
604 $manualenroldata['status'] = 0;
605 $manualenroldata['courseid'] = $course->id;
606 $enrolid = $DB->insert_record('enrol', $manualenroldata);
608 // Create a teacher and give them capabilities.
609 $context = context_course::instance($course->id);
610 $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
611 $context = context_module::instance($assign->cmid);
612 $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
614 // Create the teacher's enrolment record.
615 $userenrolmentdata['status'] = 0;
616 $userenrolmentdata['enrolid'] = $enrolid;
617 $userenrolmentdata['userid'] = $USER->id;
618 $DB->insert_record('user_enrolments', $userenrolmentdata);
620 // Create a student and give them a user flag record.
621 $student = self::getDataGenerator()->create_user();
622 $userflag = new stdClass();
623 $userflag->assignment = $assign->id;
624 $userflag->userid = $student->id;
625 $userflag->locked = 0;
626 $userflag->mailed = 0;
627 $userflag->extensionduedate = 0;
628 $userflag->workflowstate = 'inmarking';
629 $userflag->allocatedmarker = $USER->id;
631 $DB->insert_record('assign_user_flags', $userflag);
633 $assignmentids[] = $assign->id;
634 $result = mod_assign_external::get_user_flags($assignmentids);
636 // We need to execute the return values cleaning process to simulate the web service server.
637 $result = external_api::clean_returnvalue(mod_assign_external::get_user_flags_returns(), $result);
639 // Check that the correct user flag information for the student is returned.
640 $this->assertEquals(1, count($result['assignments']));
641 $assignment = $result['assignments'][0];
642 $this->assertEquals($assign->id, $assignment['assignmentid']);
643 // Should be one user flag record.
644 $this->assertEquals(1, count($assignment['userflags']));
645 $userflag = $assignment['userflags'][0];
646 $this->assertEquals($student->id, $userflag['userid']);
647 $this->assertEquals(0, $userflag['locked']);
648 $this->assertEquals(0, $userflag['mailed']);
649 $this->assertEquals(0, $userflag['extensionduedate']);
650 $this->assertEquals('inmarking', $userflag['workflowstate']);
651 $this->assertEquals($USER->id, $userflag['allocatedmarker']);
655 * Test get_user_mappings
657 public function test_get_user_mappings() {
658 global $DB, $USER;
660 $this->resetAfterTest(true);
661 // Create a course and assignment.
662 $coursedata['idnumber'] = 'idnumbercourse';
663 $coursedata['fullname'] = 'Lightwork Course';
664 $coursedata['summary'] = 'Lightwork Course description';
665 $coursedata['summaryformat'] = FORMAT_MOODLE;
666 $course = self::getDataGenerator()->create_course($coursedata);
668 $assigndata['course'] = $course->id;
669 $assigndata['name'] = 'lightwork assignment';
671 $assign = self::getDataGenerator()->create_module('assign', $assigndata);
673 // Create a manual enrolment record.
674 $manualenroldata['enrol'] = 'manual';
675 $manualenroldata['status'] = 0;
676 $manualenroldata['courseid'] = $course->id;
677 $enrolid = $DB->insert_record('enrol', $manualenroldata);
679 // Create a teacher and give them capabilities.
680 $context = context_course::instance($course->id);
681 $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
682 $context = context_module::instance($assign->cmid);
683 $this->assignUserCapability('mod/assign:revealidentities', $context->id, $roleid);
685 // Create the teacher's enrolment record.
686 $userenrolmentdata['status'] = 0;
687 $userenrolmentdata['enrolid'] = $enrolid;
688 $userenrolmentdata['userid'] = $USER->id;
689 $DB->insert_record('user_enrolments', $userenrolmentdata);
691 // Create a student and give them a user mapping record.
692 $student = self::getDataGenerator()->create_user();
693 $mapping = new stdClass();
694 $mapping->assignment = $assign->id;
695 $mapping->userid = $student->id;
697 $DB->insert_record('assign_user_mapping', $mapping);
699 $assignmentids[] = $assign->id;
700 $result = mod_assign_external::get_user_mappings($assignmentids);
702 // We need to execute the return values cleaning process to simulate the web service server.
703 $result = external_api::clean_returnvalue(mod_assign_external::get_user_mappings_returns(), $result);
705 // Check that the correct user mapping information for the student is returned.
706 $this->assertEquals(1, count($result['assignments']));
707 $assignment = $result['assignments'][0];
708 $this->assertEquals($assign->id, $assignment['assignmentid']);
709 // Should be one user mapping record.
710 $this->assertEquals(1, count($assignment['mappings']));
711 $mapping = $assignment['mappings'][0];
712 $this->assertEquals($student->id, $mapping['userid']);
716 * Test lock_submissions
718 * @expectedException moodle_exception
720 public function test_lock_submissions() {
721 global $DB, $USER;
723 $this->resetAfterTest(true);
724 // Create a course and assignment and users.
725 $course = self::getDataGenerator()->create_course();
727 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
728 $params['course'] = $course->id;
729 $params['assignsubmission_onlinetext_enabled'] = 1;
730 $instance = $generator->create_instance($params);
731 $cm = get_coursemodule_from_instance('assign', $instance->id);
732 $context = context_module::instance($cm->id);
734 $assign = new assign($context, $cm, $course);
736 $student1 = self::getDataGenerator()->create_user();
737 $student2 = self::getDataGenerator()->create_user();
738 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
739 $this->getDataGenerator()->enrol_user($student1->id,
740 $course->id,
741 $studentrole->id);
742 $this->getDataGenerator()->enrol_user($student2->id,
743 $course->id,
744 $studentrole->id);
745 $teacher = self::getDataGenerator()->create_user();
746 $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
747 $this->getDataGenerator()->enrol_user($teacher->id,
748 $course->id,
749 $teacherrole->id);
751 // Create a student1 with an online text submission.
752 // Simulate a submission.
753 $this->setUser($student1);
754 $submission = $assign->get_user_submission($student1->id, true);
755 $data = new stdClass();
756 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
757 'text'=>'Submission text',
758 'format'=>FORMAT_MOODLE);
759 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
760 $plugin->save($submission, $data);
762 // Ready to test.
763 $this->setUser($teacher);
764 $students = array($student1->id, $student2->id);
765 $result = mod_assign_external::lock_submissions($instance->id, $students);
766 $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
768 // Check for 0 warnings.
769 $this->assertEquals(0, count($result));
771 $this->setUser($student2);
772 $submission = $assign->get_user_submission($student2->id, true);
773 $data = new stdClass();
774 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
775 'text'=>'Submission text',
776 'format'=>FORMAT_MOODLE);
777 $notices = array();
778 $assign->save_submission($data, $notices);
782 * Test unlock_submissions
784 public function test_unlock_submissions() {
785 global $DB, $USER;
787 $this->resetAfterTest(true);
788 // Create a course and assignment and users.
789 $course = self::getDataGenerator()->create_course();
791 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
792 $params['course'] = $course->id;
793 $params['assignsubmission_onlinetext_enabled'] = 1;
794 $instance = $generator->create_instance($params);
795 $cm = get_coursemodule_from_instance('assign', $instance->id);
796 $context = context_module::instance($cm->id);
798 $assign = new assign($context, $cm, $course);
800 $student1 = self::getDataGenerator()->create_user();
801 $student2 = self::getDataGenerator()->create_user();
802 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
803 $this->getDataGenerator()->enrol_user($student1->id,
804 $course->id,
805 $studentrole->id);
806 $this->getDataGenerator()->enrol_user($student2->id,
807 $course->id,
808 $studentrole->id);
809 $teacher = self::getDataGenerator()->create_user();
810 $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
811 $this->getDataGenerator()->enrol_user($teacher->id,
812 $course->id,
813 $teacherrole->id);
815 // Create a student1 with an online text submission.
816 // Simulate a submission.
817 $this->setUser($student1);
818 $submission = $assign->get_user_submission($student1->id, true);
819 $data = new stdClass();
820 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
821 'text'=>'Submission text',
822 'format'=>FORMAT_MOODLE);
823 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
824 $plugin->save($submission, $data);
826 // Ready to test.
827 $this->setUser($teacher);
828 $students = array($student1->id, $student2->id);
829 $result = mod_assign_external::lock_submissions($instance->id, $students);
830 $result = external_api::clean_returnvalue(mod_assign_external::lock_submissions_returns(), $result);
832 // Check for 0 warnings.
833 $this->assertEquals(0, count($result));
835 $result = mod_assign_external::unlock_submissions($instance->id, $students);
836 $result = external_api::clean_returnvalue(mod_assign_external::unlock_submissions_returns(), $result);
838 // Check for 0 warnings.
839 $this->assertEquals(0, count($result));
841 $this->setUser($student2);
842 $submission = $assign->get_user_submission($student2->id, true);
843 $data = new stdClass();
844 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
845 'text'=>'Submission text',
846 'format'=>FORMAT_MOODLE);
847 $notices = array();
848 $assign->save_submission($data, $notices);
852 * Test submit_for_grading
854 public function test_submit_for_grading() {
855 global $DB, $USER;
857 $this->resetAfterTest(true);
858 // Create a course and assignment and users.
859 $course = self::getDataGenerator()->create_course();
861 set_config('submissionreceipts', 0, 'assign');
862 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
863 $params['course'] = $course->id;
864 $params['assignsubmission_onlinetext_enabled'] = 1;
865 $params['submissiondrafts'] = 1;
866 $params['sendnotifications'] = 0;
867 $params['requiresubmissionstatement'] = 1;
868 $instance = $generator->create_instance($params);
869 $cm = get_coursemodule_from_instance('assign', $instance->id);
870 $context = context_module::instance($cm->id);
872 $assign = new assign($context, $cm, $course);
874 $student1 = self::getDataGenerator()->create_user();
875 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
876 $this->getDataGenerator()->enrol_user($student1->id,
877 $course->id,
878 $studentrole->id);
880 // Create a student1 with an online text submission.
881 // Simulate a submission.
882 $this->setUser($student1);
883 $submission = $assign->get_user_submission($student1->id, true);
884 $data = new stdClass();
885 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
886 'text'=>'Submission text',
887 'format'=>FORMAT_MOODLE);
888 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
889 $plugin->save($submission, $data);
891 $result = mod_assign_external::submit_for_grading($instance->id, false);
892 $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
894 // Should be 1 fail because the submission statement was not aceptted.
895 $this->assertEquals(1, count($result));
897 $result = mod_assign_external::submit_for_grading($instance->id, true);
898 $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
900 // Check for 0 warnings.
901 $this->assertEquals(0, count($result));
903 $submission = $assign->get_user_submission($student1->id, false);
905 $this->assertEquals(ASSIGN_SUBMISSION_STATUS_SUBMITTED, $submission->status);
909 * Test save_user_extensions
911 public function test_save_user_extensions() {
912 global $DB, $USER;
914 $this->resetAfterTest(true);
915 // Create a course and assignment and users.
916 $course = self::getDataGenerator()->create_course();
918 $teacher = self::getDataGenerator()->create_user();
919 $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
920 $this->getDataGenerator()->enrol_user($teacher->id,
921 $course->id,
922 $teacherrole->id);
923 $this->setUser($teacher);
925 $now = time();
926 $yesterday = $now - 24*60*60;
927 $tomorrow = $now + 24*60*60;
928 set_config('submissionreceipts', 0, 'assign');
929 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
930 $params['course'] = $course->id;
931 $params['submissiondrafts'] = 1;
932 $params['sendnotifications'] = 0;
933 $params['duedate'] = $yesterday;
934 $params['cutoffdate'] = $now - 10;
935 $instance = $generator->create_instance($params);
936 $cm = get_coursemodule_from_instance('assign', $instance->id);
937 $context = context_module::instance($cm->id);
939 $assign = new assign($context, $cm, $course);
941 $student1 = self::getDataGenerator()->create_user();
942 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
943 $this->getDataGenerator()->enrol_user($student1->id,
944 $course->id,
945 $studentrole->id);
947 $this->setUser($student1);
948 $result = mod_assign_external::submit_for_grading($instance->id, true);
949 $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
951 // Check for 0 warnings.
952 $this->assertEquals(1, count($result));
954 $this->setUser($teacher);
955 $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
956 $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
957 $this->assertEquals(1, count($result));
959 $this->setUser($teacher);
960 $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($yesterday - 10));
961 $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
962 $this->assertEquals(1, count($result));
964 $this->setUser($teacher);
965 $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($tomorrow));
966 $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
967 $this->assertEquals(0, count($result));
969 $this->setUser($student1);
970 $result = mod_assign_external::submit_for_grading($instance->id, true);
971 $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
972 $this->assertEquals(0, count($result));
974 $this->setUser($student1);
975 $result = mod_assign_external::save_user_extensions($instance->id, array($student1->id), array($now, $tomorrow));
976 $result = external_api::clean_returnvalue(mod_assign_external::save_user_extensions_returns(), $result);
981 * Test reveal_identities
983 * @expectedException required_capability_exception
985 public function test_reveal_identities() {
986 global $DB, $USER;
988 $this->resetAfterTest(true);
989 // Create a course and assignment and users.
990 $course = self::getDataGenerator()->create_course();
992 $teacher = self::getDataGenerator()->create_user();
993 $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
994 $this->getDataGenerator()->enrol_user($teacher->id,
995 $course->id,
996 $teacherrole->id);
997 $this->setUser($teacher);
999 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1000 $params['course'] = $course->id;
1001 $params['submissiondrafts'] = 1;
1002 $params['sendnotifications'] = 0;
1003 $params['blindmarking'] = 1;
1004 $instance = $generator->create_instance($params);
1005 $cm = get_coursemodule_from_instance('assign', $instance->id);
1006 $context = context_module::instance($cm->id);
1008 $assign = new assign($context, $cm, $course);
1010 $student1 = self::getDataGenerator()->create_user();
1011 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
1012 $this->getDataGenerator()->enrol_user($student1->id,
1013 $course->id,
1014 $studentrole->id);
1016 $this->setUser($student1);
1017 $result = mod_assign_external::reveal_identities($instance->id);
1018 $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
1019 $this->assertEquals(1, count($result));
1020 $this->assertEquals(true, $assign->is_blind_marking());
1022 $this->setUser($teacher);
1023 $result = mod_assign_external::reveal_identities($instance->id);
1024 $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
1025 $this->assertEquals(0, count($result));
1026 $this->assertEquals(false, $assign->is_blind_marking());
1028 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1029 $params['course'] = $course->id;
1030 $params['submissiondrafts'] = 1;
1031 $params['sendnotifications'] = 0;
1032 $params['blindmarking'] = 0;
1033 $instance = $generator->create_instance($params);
1034 $cm = get_coursemodule_from_instance('assign', $instance->id);
1035 $context = context_module::instance($cm->id);
1037 $assign = new assign($context, $cm, $course);
1038 $result = mod_assign_external::reveal_identities($instance->id);
1039 $result = external_api::clean_returnvalue(mod_assign_external::reveal_identities_returns(), $result);
1040 $this->assertEquals(1, count($result));
1041 $this->assertEquals(false, $assign->is_blind_marking());
1046 * Test revert_submissions_to_draft
1048 public function test_revert_submissions_to_draft() {
1049 global $DB, $USER;
1051 $this->resetAfterTest(true);
1052 set_config('submissionreceipts', 0, 'assign');
1053 // Create a course and assignment and users.
1054 $course = self::getDataGenerator()->create_course();
1056 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1057 $params['course'] = $course->id;
1058 $params['sendnotifications'] = 0;
1059 $params['submissiondrafts'] = 1;
1060 $instance = $generator->create_instance($params);
1061 $cm = get_coursemodule_from_instance('assign', $instance->id);
1062 $context = context_module::instance($cm->id);
1064 $assign = new assign($context, $cm, $course);
1066 $student1 = self::getDataGenerator()->create_user();
1067 $student2 = self::getDataGenerator()->create_user();
1068 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
1069 $this->getDataGenerator()->enrol_user($student1->id,
1070 $course->id,
1071 $studentrole->id);
1072 $this->getDataGenerator()->enrol_user($student2->id,
1073 $course->id,
1074 $studentrole->id);
1075 $teacher = self::getDataGenerator()->create_user();
1076 $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1077 $this->getDataGenerator()->enrol_user($teacher->id,
1078 $course->id,
1079 $teacherrole->id);
1081 // Create a student1 with an online text submission.
1082 // Simulate a submission.
1083 $this->setUser($student1);
1084 $result = mod_assign_external::submit_for_grading($instance->id, true);
1085 $result = external_api::clean_returnvalue(mod_assign_external::submit_for_grading_returns(), $result);
1086 $this->assertEquals(0, count($result));
1088 // Ready to test.
1089 $this->setUser($teacher);
1090 $students = array($student1->id, $student2->id);
1091 $result = mod_assign_external::revert_submissions_to_draft($instance->id, array($student1->id));
1092 $result = external_api::clean_returnvalue(mod_assign_external::revert_submissions_to_draft_returns(), $result);
1094 // Check for 0 warnings.
1095 $this->assertEquals(0, count($result));
1100 * Test save_submission
1102 public function test_save_submission() {
1103 global $DB, $USER;
1105 $this->resetAfterTest(true);
1106 // Create a course and assignment and users.
1107 $course = self::getDataGenerator()->create_course();
1109 $teacher = self::getDataGenerator()->create_user();
1110 $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1111 $this->getDataGenerator()->enrol_user($teacher->id,
1112 $course->id,
1113 $teacherrole->id);
1114 $this->setUser($teacher);
1116 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1117 $params['course'] = $course->id;
1118 $params['assignsubmission_onlinetext_enabled'] = 1;
1119 $params['assignsubmission_file_enabled'] = 1;
1120 $params['assignsubmission_file_maxfiles'] = 5;
1121 $params['assignsubmission_file_maxsizebytes'] = 1024*1024;
1122 $instance = $generator->create_instance($params);
1123 $cm = get_coursemodule_from_instance('assign', $instance->id);
1124 $context = context_module::instance($cm->id);
1126 $assign = new assign($context, $cm, $course);
1128 $student1 = self::getDataGenerator()->create_user();
1129 $student2 = self::getDataGenerator()->create_user();
1130 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
1131 $this->getDataGenerator()->enrol_user($student1->id,
1132 $course->id,
1133 $studentrole->id);
1134 $this->getDataGenerator()->enrol_user($student2->id,
1135 $course->id,
1136 $studentrole->id);
1137 // Create a student1 with an online text submission.
1138 // Simulate a submission.
1139 $this->setUser($student1);
1141 // Create a file in a draft area.
1142 $draftidfile = file_get_unused_draft_itemid();
1144 $usercontext = context_user::instance($student1->id);
1145 $filerecord = array(
1146 'contextid' => $usercontext->id,
1147 'component' => 'user',
1148 'filearea' => 'draft',
1149 'itemid' => $draftidfile,
1150 'filepath' => '/',
1151 'filename' => 'testtext.txt',
1154 $fs = get_file_storage();
1155 $fs->create_file_from_string($filerecord, 'text contents');
1157 // Create another file in a different draft area.
1158 $draftidonlinetext = file_get_unused_draft_itemid();
1160 $filerecord = array(
1161 'contextid' => $usercontext->id,
1162 'component' => 'user',
1163 'filearea' => 'draft',
1164 'itemid' => $draftidonlinetext,
1165 'filepath' => '/',
1166 'filename' => 'shouldbeanimage.txt',
1169 $fs->create_file_from_string($filerecord, 'image contents (not really)');
1171 // Now try a submission.
1172 $submissionpluginparams = array();
1173 $submissionpluginparams['files_filemanager'] = $draftidfile;
1174 $onlinetexteditorparams = array('text' => '<p>Yeeha!</p>',
1175 'format'=>1,
1176 'itemid'=>$draftidonlinetext);
1177 $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
1178 $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1179 $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1181 $this->assertEquals(0, count($result));
1183 // Set up a due and cutoff passed date.
1184 $instance->duedate = time() - WEEKSECS;
1185 $instance->cutoffdate = time() - WEEKSECS;
1186 $DB->update_record('assign', $instance);
1188 $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1189 $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1191 $this->assertCount(1, $result);
1192 $this->assertEquals(get_string('duedatereached', 'assign'), $result[0]['item']);
1196 * Test save_grade
1198 public function test_save_grade() {
1199 global $DB, $USER;
1201 $this->resetAfterTest(true);
1202 // Create a course and assignment and users.
1203 $course = self::getDataGenerator()->create_course();
1205 $teacher = self::getDataGenerator()->create_user();
1206 $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1207 $this->getDataGenerator()->enrol_user($teacher->id,
1208 $course->id,
1209 $teacherrole->id);
1210 $this->setUser($teacher);
1212 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1213 $params['course'] = $course->id;
1214 $params['assignfeedback_file_enabled'] = 1;
1215 $params['assignfeedback_comments_enabled'] = 1;
1216 $instance = $generator->create_instance($params);
1217 $cm = get_coursemodule_from_instance('assign', $instance->id);
1218 $context = context_module::instance($cm->id);
1220 $assign = new assign($context, $cm, $course);
1222 $student1 = self::getDataGenerator()->create_user();
1223 $student2 = self::getDataGenerator()->create_user();
1224 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1225 $this->getDataGenerator()->enrol_user($student1->id,
1226 $course->id,
1227 $studentrole->id);
1228 $this->getDataGenerator()->enrol_user($student2->id,
1229 $course->id,
1230 $studentrole->id);
1231 // Simulate a grade.
1232 $this->setUser($teacher);
1234 // Create a file in a draft area.
1235 $draftidfile = file_get_unused_draft_itemid();
1237 $usercontext = context_user::instance($teacher->id);
1238 $filerecord = array(
1239 'contextid' => $usercontext->id,
1240 'component' => 'user',
1241 'filearea' => 'draft',
1242 'itemid' => $draftidfile,
1243 'filepath' => '/',
1244 'filename' => 'testtext.txt',
1247 $fs = get_file_storage();
1248 $fs->create_file_from_string($filerecord, 'text contents');
1250 // Now try a grade.
1251 $feedbackpluginparams = array();
1252 $feedbackpluginparams['files_filemanager'] = $draftidfile;
1253 $feedbackeditorparams = array('text' => 'Yeeha!',
1254 'format' => 1);
1255 $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1256 $result = mod_assign_external::save_grade($instance->id,
1257 $student1->id,
1258 50.0,
1260 true,
1261 'released',
1262 false,
1263 $feedbackpluginparams);
1264 // No warnings.
1265 $this->assertNull($result);
1267 $result = mod_assign_external::get_grades(array($instance->id));
1268 $result = external_api::clean_returnvalue(mod_assign_external::get_grades_returns(), $result);
1270 $this->assertEquals((float)$result['assignments'][0]['grades'][0]['grade'], '50.0');
1274 * Test save grades with advanced grading data
1276 public function test_save_grades_with_advanced_grading() {
1277 global $DB, $USER;
1279 $this->resetAfterTest(true);
1280 // Create a course and assignment and users.
1281 $course = self::getDataGenerator()->create_course();
1283 $teacher = self::getDataGenerator()->create_user();
1284 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1285 $this->getDataGenerator()->enrol_user($teacher->id,
1286 $course->id,
1287 $teacherrole->id);
1289 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1290 $params['course'] = $course->id;
1291 $params['assignfeedback_file_enabled'] = 0;
1292 $params['assignfeedback_comments_enabled'] = 0;
1293 $instance = $generator->create_instance($params);
1294 $cm = get_coursemodule_from_instance('assign', $instance->id);
1295 $context = context_module::instance($cm->id);
1297 $assign = new assign($context, $cm, $course);
1299 $student1 = self::getDataGenerator()->create_user();
1300 $student2 = self::getDataGenerator()->create_user();
1301 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1302 $this->getDataGenerator()->enrol_user($student1->id,
1303 $course->id,
1304 $studentrole->id);
1305 $this->getDataGenerator()->enrol_user($student2->id,
1306 $course->id,
1307 $studentrole->id);
1309 $this->setUser($teacher);
1311 $feedbackpluginparams = array();
1312 $feedbackpluginparams['files_filemanager'] = 0;
1313 $feedbackeditorparams = array('text' => '', 'format' => 1);
1314 $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1316 // Create advanced grading data.
1317 // Create grading area.
1318 $gradingarea = array(
1319 'contextid' => $context->id,
1320 'component' => 'mod_assign',
1321 'areaname' => 'submissions',
1322 'activemethod' => 'rubric'
1324 $areaid = $DB->insert_record('grading_areas', $gradingarea);
1326 // Create a rubric grading definition.
1327 $rubricdefinition = array (
1328 'areaid' => $areaid,
1329 'method' => 'rubric',
1330 'name' => 'test',
1331 'status' => 20,
1332 'copiedfromid' => 1,
1333 'timecreated' => 1,
1334 'usercreated' => $teacher->id,
1335 'timemodified' => 1,
1336 'usermodified' => $teacher->id,
1337 'timecopied' => 0
1339 $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
1341 // Create a criterion with a level.
1342 $rubriccriteria = array (
1343 'definitionid' => $definitionid,
1344 'sortorder' => 1,
1345 'description' => 'Demonstrate an understanding of disease control',
1346 'descriptionformat' => 0
1348 $criterionid = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria);
1349 $rubriclevel1 = array (
1350 'criterionid' => $criterionid,
1351 'score' => 50,
1352 'definition' => 'pass',
1353 'definitionformat' => 0
1355 $rubriclevel2 = array (
1356 'criterionid' => $criterionid,
1357 'score' => 100,
1358 'definition' => 'excellent',
1359 'definitionformat' => 0
1361 $rubriclevel3 = array (
1362 'criterionid' => $criterionid,
1363 'score' => 0,
1364 'definition' => 'fail',
1365 'definitionformat' => 0
1367 $levelid1 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
1368 $levelid2 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
1369 $levelid3 = $DB->insert_record('gradingform_rubric_levels', $rubriclevel3);
1371 // Create the filling.
1372 $student1filling = array (
1373 'criterionid' => $criterionid,
1374 'levelid' => $levelid1,
1375 'remark' => 'well done you passed',
1376 'remarkformat' => 0
1379 $student2filling = array (
1380 'criterionid' => $criterionid,
1381 'levelid' => $levelid2,
1382 'remark' => 'Excellent work',
1383 'remarkformat' => 0
1386 $student1criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student1filling)));
1387 $student1advancedgradingdata = array('rubric' => array('criteria' => $student1criteria));
1389 $student2criteria = array(array('criterionid' => $criterionid, 'fillings' => array($student2filling)));
1390 $student2advancedgradingdata = array('rubric' => array('criteria' => $student2criteria));
1392 $grades = array();
1393 $student1gradeinfo = array();
1394 $student1gradeinfo['userid'] = $student1->id;
1395 $student1gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1396 $student1gradeinfo['attemptnumber'] = -1;
1397 $student1gradeinfo['addattempt'] = true;
1398 $student1gradeinfo['workflowstate'] = 'released';
1399 $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1400 $student1gradeinfo['advancedgradingdata'] = $student1advancedgradingdata;
1401 $grades[] = $student1gradeinfo;
1403 $student2gradeinfo = array();
1404 $student2gradeinfo['userid'] = $student2->id;
1405 $student2gradeinfo['grade'] = 0; // Ignored since advanced grading is being used.
1406 $student2gradeinfo['attemptnumber'] = -1;
1407 $student2gradeinfo['addattempt'] = true;
1408 $student2gradeinfo['workflowstate'] = 'released';
1409 $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1410 $student2gradeinfo['advancedgradingdata'] = $student2advancedgradingdata;
1411 $grades[] = $student2gradeinfo;
1413 $result = mod_assign_external::save_grades($instance->id, false, $grades);
1414 $this->assertNull($result);
1416 $student1grade = $DB->get_record('assign_grades',
1417 array('userid' => $student1->id, 'assignment' => $instance->id),
1418 '*',
1419 MUST_EXIST);
1420 $this->assertEquals((float)$student1grade->grade, '50.0');
1422 $student2grade = $DB->get_record('assign_grades',
1423 array('userid' => $student2->id, 'assignment' => $instance->id),
1424 '*',
1425 MUST_EXIST);
1426 $this->assertEquals((float)$student2grade->grade, '100.0');
1430 * Test save grades for a team submission
1432 * @expectedException invalid_parameter_exception
1434 public function test_save_grades_with_group_submission() {
1435 global $DB, $USER, $CFG;
1436 require_once($CFG->dirroot . '/group/lib.php');
1438 $this->resetAfterTest(true);
1439 // Create a course and assignment and users.
1440 $course = self::getDataGenerator()->create_course();
1442 $teacher = self::getDataGenerator()->create_user();
1443 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1444 $this->getDataGenerator()->enrol_user($teacher->id,
1445 $course->id,
1446 $teacherrole->id);
1448 $groupingdata = array();
1449 $groupingdata['courseid'] = $course->id;
1450 $groupingdata['name'] = 'Group assignment grouping';
1452 $grouping = self::getDataGenerator()->create_grouping($groupingdata);
1454 $group1data = array();
1455 $group1data['courseid'] = $course->id;
1456 $group1data['name'] = 'Team 1';
1457 $group2data = array();
1458 $group2data['courseid'] = $course->id;
1459 $group2data['name'] = 'Team 2';
1461 $group1 = self::getDataGenerator()->create_group($group1data);
1462 $group2 = self::getDataGenerator()->create_group($group2data);
1464 groups_assign_grouping($grouping->id, $group1->id);
1465 groups_assign_grouping($grouping->id, $group2->id);
1467 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1468 $params['course'] = $course->id;
1469 $params['teamsubmission'] = 1;
1470 $params['teamsubmissiongroupingid'] = $grouping->id;
1471 $instance = $generator->create_instance($params);
1472 $cm = get_coursemodule_from_instance('assign', $instance->id);
1473 $context = context_module::instance($cm->id);
1475 $assign = new assign($context, $cm, $course);
1477 $student1 = self::getDataGenerator()->create_user();
1478 $student2 = self::getDataGenerator()->create_user();
1479 $student3 = self::getDataGenerator()->create_user();
1480 $student4 = self::getDataGenerator()->create_user();
1481 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1482 $this->getDataGenerator()->enrol_user($student1->id,
1483 $course->id,
1484 $studentrole->id);
1485 $this->getDataGenerator()->enrol_user($student2->id,
1486 $course->id,
1487 $studentrole->id);
1488 $this->getDataGenerator()->enrol_user($student3->id,
1489 $course->id,
1490 $studentrole->id);
1491 $this->getDataGenerator()->enrol_user($student4->id,
1492 $course->id,
1493 $studentrole->id);
1495 groups_add_member($group1->id, $student1->id);
1496 groups_add_member($group1->id, $student2->id);
1497 groups_add_member($group1->id, $student3->id);
1498 groups_add_member($group2->id, $student4->id);
1499 $this->setUser($teacher);
1501 $feedbackpluginparams = array();
1502 $feedbackpluginparams['files_filemanager'] = 0;
1503 $feedbackeditorparams = array('text' => '', 'format' => 1);
1504 $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1506 $grades1 = array();
1507 $student1gradeinfo = array();
1508 $student1gradeinfo['userid'] = $student1->id;
1509 $student1gradeinfo['grade'] = 50;
1510 $student1gradeinfo['attemptnumber'] = -1;
1511 $student1gradeinfo['addattempt'] = true;
1512 $student1gradeinfo['workflowstate'] = 'released';
1513 $student1gradeinfo['plugindata'] = $feedbackpluginparams;
1514 $grades1[] = $student1gradeinfo;
1516 $student2gradeinfo = array();
1517 $student2gradeinfo['userid'] = $student2->id;
1518 $student2gradeinfo['grade'] = 75;
1519 $student2gradeinfo['attemptnumber'] = -1;
1520 $student2gradeinfo['addattempt'] = true;
1521 $student2gradeinfo['workflowstate'] = 'released';
1522 $student2gradeinfo['plugindata'] = $feedbackpluginparams;
1523 $grades1[] = $student2gradeinfo;
1525 // Expect an exception since 2 grades have been submitted for the same team.
1526 $result = mod_assign_external::save_grades($instance->id, true, $grades1);
1527 $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
1529 $grades2 = array();
1530 $student3gradeinfo = array();
1531 $student3gradeinfo['userid'] = $student3->id;
1532 $student3gradeinfo['grade'] = 50;
1533 $student3gradeinfo['attemptnumber'] = -1;
1534 $student3gradeinfo['addattempt'] = true;
1535 $student3gradeinfo['workflowstate'] = 'released';
1536 $student3gradeinfo['plugindata'] = $feedbackpluginparams;
1537 $grades2[] = $student3gradeinfo;
1539 $student4gradeinfo = array();
1540 $student4gradeinfo['userid'] = $student4->id;
1541 $student4gradeinfo['grade'] = 75;
1542 $student4gradeinfo['attemptnumber'] = -1;
1543 $student4gradeinfo['addattempt'] = true;
1544 $student4gradeinfo['workflowstate'] = 'released';
1545 $student4gradeinfo['plugindata'] = $feedbackpluginparams;
1546 $grades2[] = $student4gradeinfo;
1547 $result = mod_assign_external::save_grades($instance->id, true, $grades2);
1548 $result = external_api::clean_returnvalue(mod_assign_external::save_grades_returns(), $result);
1549 // There should be no warnings.
1550 $this->assertEquals(0, count($result));
1552 $student3grade = $DB->get_record('assign_grades',
1553 array('userid' => $student3->id, 'assignment' => $instance->id),
1554 '*',
1555 MUST_EXIST);
1556 $this->assertEquals($student3grade->grade, '50.0');
1558 $student4grade = $DB->get_record('assign_grades',
1559 array('userid' => $student4->id, 'assignment' => $instance->id),
1560 '*',
1561 MUST_EXIST);
1562 $this->assertEquals($student4grade->grade, '75.0');
1566 * Test copy_previous_attempt
1568 public function test_copy_previous_attempt() {
1569 global $DB, $USER;
1571 $this->resetAfterTest(true);
1572 // Create a course and assignment and users.
1573 $course = self::getDataGenerator()->create_course();
1575 $teacher = self::getDataGenerator()->create_user();
1576 $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
1577 $this->getDataGenerator()->enrol_user($teacher->id,
1578 $course->id,
1579 $teacherrole->id);
1580 $this->setUser($teacher);
1582 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1583 $params['course'] = $course->id;
1584 $params['assignsubmission_onlinetext_enabled'] = 1;
1585 $params['assignsubmission_file_enabled'] = 0;
1586 $params['assignfeedback_file_enabled'] = 0;
1587 $params['attemptreopenmethod'] = 'manual';
1588 $params['maxattempts'] = 5;
1589 $instance = $generator->create_instance($params);
1590 $cm = get_coursemodule_from_instance('assign', $instance->id);
1591 $context = context_module::instance($cm->id);
1593 $assign = new assign($context, $cm, $course);
1595 $student1 = self::getDataGenerator()->create_user();
1596 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
1597 $this->getDataGenerator()->enrol_user($student1->id,
1598 $course->id,
1599 $studentrole->id);
1600 // Now try a submission.
1601 $this->setUser($student1);
1602 $draftidonlinetext = file_get_unused_draft_itemid();
1603 $submissionpluginparams = array();
1604 $onlinetexteditorparams = array('text'=>'Yeeha!',
1605 'format'=>1,
1606 'itemid'=>$draftidonlinetext);
1607 $submissionpluginparams['onlinetext_editor'] = $onlinetexteditorparams;
1608 $submissionpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1609 $result = mod_assign_external::save_submission($instance->id, $submissionpluginparams);
1610 $result = external_api::clean_returnvalue(mod_assign_external::save_submission_returns(), $result);
1612 $this->setUser($teacher);
1613 // Add a grade and reopen the attempt.
1614 // Now try a grade.
1615 $feedbackpluginparams = array();
1616 $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
1617 $feedbackeditorparams = array('text'=>'Yeeha!',
1618 'format'=>1);
1619 $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
1620 $result = mod_assign_external::save_grade($instance->id,
1621 $student1->id,
1622 50.0,
1624 true,
1625 'released',
1626 false,
1627 $feedbackpluginparams);
1628 $this->assertNull($result);
1630 $this->setUser($student1);
1631 // Now copy the previous attempt.
1632 $result = mod_assign_external::copy_previous_attempt($instance->id);
1633 $result = external_api::clean_returnvalue(mod_assign_external::copy_previous_attempt_returns(), $result);
1634 // No warnings.
1635 $this->assertEquals(0, count($result));
1637 $this->setUser($teacher);
1638 $result = mod_assign_external::get_submissions(array($instance->id));
1639 $result = external_api::clean_returnvalue(mod_assign_external::get_submissions_returns(), $result);
1641 // Check we are now on the second attempt.
1642 $this->assertEquals($result['assignments'][0]['submissions'][0]['attemptnumber'], 1);
1643 // Check the plugins data is not empty.
1644 $this->assertNotEmpty($result['assignments'][0]['submissions'][0]['plugins']);
1649 * Test set_user_flags
1651 public function test_set_user_flags() {
1652 global $DB, $USER;
1654 $this->resetAfterTest(true);
1655 // Create a course and assignment.
1656 $coursedata['idnumber'] = 'idnumbercourse';
1657 $coursedata['fullname'] = 'Lightwork Course';
1658 $coursedata['summary'] = 'Lightwork Course description';
1659 $coursedata['summaryformat'] = FORMAT_MOODLE;
1660 $course = self::getDataGenerator()->create_course($coursedata);
1662 $assigndata['course'] = $course->id;
1663 $assigndata['name'] = 'lightwork assignment';
1665 $assign = self::getDataGenerator()->create_module('assign', $assigndata);
1667 // Create a manual enrolment record.
1668 $manualenroldata['enrol'] = 'manual';
1669 $manualenroldata['status'] = 0;
1670 $manualenroldata['courseid'] = $course->id;
1671 $enrolid = $DB->insert_record('enrol', $manualenroldata);
1673 // Create a teacher and give them capabilities.
1674 $context = context_course::instance($course->id);
1675 $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
1676 $context = context_module::instance($assign->cmid);
1677 $this->assignUserCapability('mod/assign:grade', $context->id, $roleid);
1679 // Create the teacher's enrolment record.
1680 $userenrolmentdata['status'] = 0;
1681 $userenrolmentdata['enrolid'] = $enrolid;
1682 $userenrolmentdata['userid'] = $USER->id;
1683 $DB->insert_record('user_enrolments', $userenrolmentdata);
1685 // Create a student.
1686 $student = self::getDataGenerator()->create_user();
1688 // Create test user flags record.
1689 $userflags = array();
1690 $userflag['userid'] = $student->id;
1691 $userflag['workflowstate'] = 'inmarking';
1692 $userflag['allocatedmarker'] = $USER->id;
1693 $userflags = array($userflag);
1695 $createduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
1696 // We need to execute the return values cleaning process to simulate the web service server.
1697 $createduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $createduserflags);
1699 $this->assertEquals($student->id, $createduserflags[0]['userid']);
1700 $createduserflag = $DB->get_record('assign_user_flags', array('id' => $createduserflags[0]['id']));
1702 // Confirm that all data was inserted correctly.
1703 $this->assertEquals($student->id, $createduserflag->userid);
1704 $this->assertEquals($assign->id, $createduserflag->assignment);
1705 $this->assertEquals(0, $createduserflag->locked);
1706 $this->assertEquals(2, $createduserflag->mailed);
1707 $this->assertEquals(0, $createduserflag->extensionduedate);
1708 $this->assertEquals('inmarking', $createduserflag->workflowstate);
1709 $this->assertEquals($USER->id, $createduserflag->allocatedmarker);
1711 // Create update data.
1712 $userflags = array();
1713 $userflag['userid'] = $createduserflag->userid;
1714 $userflag['workflowstate'] = 'readyforreview';
1715 $userflags = array($userflag);
1717 $updateduserflags = mod_assign_external::set_user_flags($assign->id, $userflags);
1718 // We need to execute the return values cleaning process to simulate the web service server.
1719 $updateduserflags = external_api::clean_returnvalue(mod_assign_external::set_user_flags_returns(), $updateduserflags);
1721 $this->assertEquals($student->id, $updateduserflags[0]['userid']);
1722 $updateduserflag = $DB->get_record('assign_user_flags', array('id' => $updateduserflags[0]['id']));
1724 // Confirm that all data was updated correctly.
1725 $this->assertEquals($student->id, $updateduserflag->userid);
1726 $this->assertEquals($assign->id, $updateduserflag->assignment);
1727 $this->assertEquals(0, $updateduserflag->locked);
1728 $this->assertEquals(2, $updateduserflag->mailed);
1729 $this->assertEquals(0, $updateduserflag->extensionduedate);
1730 $this->assertEquals('readyforreview', $updateduserflag->workflowstate);
1731 $this->assertEquals($USER->id, $updateduserflag->allocatedmarker);
1735 * Test view_grading_table
1737 * @expectedException dml_missing_record_exception
1739 public function test_view_grading_table_invalid_instance() {
1740 global $DB;
1742 $this->resetAfterTest(true);
1744 // Setup test data.
1745 $course = $this->getDataGenerator()->create_course();
1746 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1747 $context = context_module::instance($assign->cmid);
1748 $cm = get_coursemodule_from_instance('assign', $assign->id);
1750 // Test invalid instance id.
1751 mod_assign_external::view_grading_table(0);
1755 * Test view_grading_table
1757 * @expectedException require_login_exception
1759 public function test_view_grading_table_not_enrolled() {
1760 global $DB;
1762 $this->resetAfterTest(true);
1764 // Setup test data.
1765 $course = $this->getDataGenerator()->create_course();
1766 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1767 $context = context_module::instance($assign->cmid);
1768 $cm = get_coursemodule_from_instance('assign', $assign->id);
1770 // Test not-enrolled user.
1771 $user = self::getDataGenerator()->create_user();
1772 $this->setUser($user);
1774 mod_assign_external::view_grading_table($assign->id);
1778 * Test view_grading_table
1780 public function test_view_grading_table_correct() {
1781 global $DB;
1783 $this->resetAfterTest(true);
1785 // Setup test data.
1786 $course = $this->getDataGenerator()->create_course();
1787 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1788 $context = context_module::instance($assign->cmid);
1789 $cm = get_coursemodule_from_instance('assign', $assign->id);
1791 // Test user with full capabilities.
1792 $user = self::getDataGenerator()->create_user();
1793 $this->setUser($user);
1794 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1795 $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1797 // Trigger and capture the event.
1798 $sink = $this->redirectEvents();
1800 $result = mod_assign_external::view_grading_table($assign->id);
1801 $result = external_api::clean_returnvalue(mod_assign_external::view_grading_table_returns(), $result);
1803 $events = $sink->get_events();
1804 $this->assertCount(1, $events);
1805 $event = array_shift($events);
1807 // Checking that the event contains the expected values.
1808 $this->assertInstanceOf('\mod_assign\event\grading_table_viewed', $event);
1809 $this->assertEquals($context, $event->get_context());
1810 $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1811 $this->assertEquals($moodleurl, $event->get_url());
1812 $this->assertEventContextNotUsed($event);
1813 $this->assertNotEmpty($event->get_name());
1817 * Test view_grading_table
1819 * @expectedException require_login_exception
1820 * @expectedExceptionMessage Course or activity not accessible. (Activity is hidden)
1822 public function test_view_grading_table_without_capability() {
1823 global $DB;
1825 $this->resetAfterTest(true);
1827 // Setup test data.
1828 $course = $this->getDataGenerator()->create_course();
1829 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1830 $context = context_module::instance($assign->cmid);
1831 $cm = get_coursemodule_from_instance('assign', $assign->id);
1833 // Test user with no capabilities.
1834 $user = self::getDataGenerator()->create_user();
1835 $this->setUser($user);
1836 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1837 $this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
1839 // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1840 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1841 assign_capability('mod/assign:view', CAP_PROHIBIT, $teacherrole->id, $context->id);
1842 // Empty all the caches that may be affected by this change.
1843 accesslib_clear_all_caches_for_unit_testing();
1844 course_modinfo::clear_instance_cache();
1846 mod_assign_external::view_grading_table($assign->id);
1850 * Test subplugins availability
1852 public function test_subplugins_availability() {
1853 global $CFG;
1855 require_once($CFG->dirroot . '/mod/assign/adminlib.php');
1856 $this->resetAfterTest(true);
1858 // Hide assignment file submissiong plugin.
1859 $pluginmanager = new assign_plugin_manager('assignsubmission');
1860 $pluginmanager->hide_plugin('file');
1861 $parameters = mod_assign_external::save_submission_parameters();
1863 $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1865 // Show it again and check that the value is returned as optional.
1866 $pluginmanager->show_plugin('file');
1867 $parameters = mod_assign_external::save_submission_parameters();
1868 $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1869 $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1871 // Hide feedback file submissiong plugin.
1872 $pluginmanager = new assign_plugin_manager('assignfeedback');
1873 $pluginmanager->hide_plugin('file');
1875 $parameters = mod_assign_external::save_grade_parameters();
1877 $this->assertTrue(!isset($parameters->keys['plugindata']->keys['files_filemanager']));
1879 // Show it again and check that the value is returned as optional.
1880 $pluginmanager->show_plugin('file');
1881 $parameters = mod_assign_external::save_grade_parameters();
1883 $this->assertTrue(isset($parameters->keys['plugindata']->keys['files_filemanager']));
1884 $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['files_filemanager']->required);
1886 // Check a different one.
1887 $pluginmanager->show_plugin('comments');
1888 $this->assertTrue(isset($parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']));
1889 $this->assertEquals(VALUE_OPTIONAL, $parameters->keys['plugindata']->keys['assignfeedbackcomments_editor']->required);
1893 * Test test_view_submission_status
1895 public function test_view_submission_status() {
1896 global $DB;
1898 $this->resetAfterTest(true);
1900 $this->setAdminUser();
1901 // Setup test data.
1902 $course = $this->getDataGenerator()->create_course();
1903 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
1904 $context = context_module::instance($assign->cmid);
1905 $cm = get_coursemodule_from_instance('assign', $assign->id);
1907 // Test invalid instance id.
1908 try {
1909 mod_assign_external::view_submission_status(0);
1910 $this->fail('Exception expected due to invalid mod_assign instance id.');
1911 } catch (moodle_exception $e) {
1912 $this->assertEquals('invalidrecord', $e->errorcode);
1915 // Test not-enrolled user.
1916 $user = self::getDataGenerator()->create_user();
1917 $this->setUser($user);
1918 try {
1919 mod_assign_external::view_submission_status($assign->id);
1920 $this->fail('Exception expected due to not enrolled user.');
1921 } catch (moodle_exception $e) {
1922 $this->assertEquals('requireloginerror', $e->errorcode);
1925 // Test user with full capabilities.
1926 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1927 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
1929 // Trigger and capture the event.
1930 $sink = $this->redirectEvents();
1932 $result = mod_assign_external::view_submission_status($assign->id);
1933 $result = external_api::clean_returnvalue(mod_assign_external::view_submission_status_returns(), $result);
1935 $events = $sink->get_events();
1936 $this->assertCount(1, $events);
1937 $event = array_shift($events);
1939 // Checking that the event contains the expected values.
1940 $this->assertInstanceOf('\mod_assign\event\submission_status_viewed', $event);
1941 $this->assertEquals($context, $event->get_context());
1942 $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id));
1943 $this->assertEquals($moodleurl, $event->get_url());
1944 $this->assertEventContextNotUsed($event);
1945 $this->assertNotEmpty($event->get_name());
1947 // Test user with no capabilities.
1948 // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
1949 assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id);
1950 accesslib_clear_all_caches_for_unit_testing();
1951 course_modinfo::clear_instance_cache();
1953 try {
1954 mod_assign_external::view_submission_status($assign->id);
1955 $this->fail('Exception expected due to missing capability.');
1956 } catch (moodle_exception $e) {
1957 $this->assertEquals('requireloginerror', $e->errorcode);
1962 * Create a submission for testing the get_submission_status function.
1963 * @param boolean $submitforgrading whether to submit for grading the submission
1964 * @return array an array containing all the required data for testing
1966 private function create_submission_for_testing_status($submitforgrading = false) {
1967 global $DB;
1969 // Create a course and assignment and users.
1970 $course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1));
1972 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1973 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1975 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1976 $params = array(
1977 'course' => $course->id,
1978 'assignsubmission_file_maxfiles' => 1,
1979 'assignsubmission_file_maxsizebytes' => 1024 * 1024,
1980 'assignsubmission_onlinetext_enabled' => 1,
1981 'assignsubmission_file_enabled' => 1,
1982 'submissiondrafts' => 1,
1983 'assignfeedback_file_enabled' => 1,
1984 'assignfeedback_comments_enabled' => 1,
1985 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1986 'sendnotifications' => 0
1989 set_config('submissionreceipts', 0, 'assign');
1991 $instance = $generator->create_instance($params);
1992 $cm = get_coursemodule_from_instance('assign', $instance->id);
1993 $context = context_module::instance($cm->id);
1995 $assign = new mod_assign_testable_assign($context, $cm, $course);
1997 $student1 = self::getDataGenerator()->create_user();
1998 $student2 = self::getDataGenerator()->create_user();
1999 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2000 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
2001 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
2002 $teacher = self::getDataGenerator()->create_user();
2003 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2004 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
2006 $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student1->id));
2007 $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $teacher->id));
2008 $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $student2->id));
2009 $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $teacher->id));
2011 $this->setUser($student1);
2013 // Create a student1 with an online text submission.
2014 // Simulate a submission.
2015 $submission = $assign->get_user_submission($student1->id, true);
2017 $data = new stdClass();
2018 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
2019 'text' => 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>',
2020 'format' => FORMAT_MOODLE);
2022 $draftidfile = file_get_unused_draft_itemid();
2023 $usercontext = context_user::instance($student1->id);
2024 $filerecord = array(
2025 'contextid' => $usercontext->id,
2026 'component' => 'user',
2027 'filearea' => 'draft',
2028 'itemid' => $draftidfile,
2029 'filepath' => '/',
2030 'filename' => 't.txt',
2032 $fs = get_file_storage();
2033 $fs->create_file_from_string($filerecord, 'text contents');
2035 $data->files_filemanager = $draftidfile;
2037 $notices = array();
2038 $assign->save_submission($data, $notices);
2040 if ($submitforgrading) {
2041 // Now, submit the draft for grading.
2042 $notices = array();
2044 $data = new stdClass;
2045 $data->userid = $student1->id;
2046 $assign->submit_for_grading($data, $notices);
2049 return array($assign, $instance, $student1, $student2, $teacher, $group1, $group2);
2053 * Test get_submission_status for a draft submission.
2055 public function test_get_submission_status_in_draft_status() {
2056 $this->resetAfterTest(true);
2058 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status();
2059 $studentsubmission = $assign->get_user_submission($student1->id, true);
2061 $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2062 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2063 $this->assertDebuggingCalled();
2065 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2067 // The submission is now in draft mode.
2068 $this->assertCount(0, $result['warnings']);
2069 $this->assertFalse(isset($result['gradingsummary']));
2070 $this->assertFalse(isset($result['feedback']));
2071 $this->assertFalse(isset($result['previousattempts']));
2073 $this->assertTrue($result['lastattempt']['submissionsenabled']);
2074 $this->assertTrue($result['lastattempt']['canedit']);
2075 $this->assertTrue($result['lastattempt']['cansubmit']);
2076 $this->assertFalse($result['lastattempt']['locked']);
2077 $this->assertFalse($result['lastattempt']['graded']);
2078 $this->assertEmpty($result['lastattempt']['extensionduedate']);
2079 $this->assertFalse($result['lastattempt']['blindmarking']);
2080 $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
2081 $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
2083 $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
2084 $this->assertEquals(0, $result['lastattempt']['submission']['attemptnumber']);
2085 $this->assertEquals('draft', $result['lastattempt']['submission']['status']);
2086 $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
2087 $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
2088 $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
2090 // Map plugins based on their type - we can't rely on them being in a
2091 // particular order, especially if 3rd party plugins are installed.
2092 $submissionplugins = array();
2093 foreach ($result['lastattempt']['submission']['plugins'] as $plugin) {
2094 $submissionplugins[$plugin['type']] = $plugin;
2097 // Format expected online text.
2098 $onlinetext = 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>';
2099 list($expectedtext, $expectedformat) = external_format_text($onlinetext, FORMAT_HTML, $assign->get_context()->id,
2100 'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $studentsubmission->id);
2102 $this->assertEquals($expectedtext, $submissionplugins['onlinetext']['editorfields'][0]['text']);
2103 $this->assertEquals($expectedformat, $submissionplugins['onlinetext']['editorfields'][0]['format']);
2104 $this->assertEquals('/', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']);
2105 $this->assertEquals('t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filename']);
2109 * Test get_submission_status for a submitted submission.
2111 public function test_get_submission_status_in_submission_status() {
2112 $this->resetAfterTest(true);
2114 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
2116 $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2117 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2118 $this->assertDebuggingCalled();
2119 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2121 $this->assertCount(0, $result['warnings']);
2122 $this->assertFalse(isset($result['gradingsummary']));
2123 $this->assertFalse(isset($result['feedback']));
2124 $this->assertFalse(isset($result['previousattempts']));
2126 $this->assertTrue($result['lastattempt']['submissionsenabled']);
2127 $this->assertFalse($result['lastattempt']['canedit']);
2128 $this->assertFalse($result['lastattempt']['cansubmit']);
2129 $this->assertFalse($result['lastattempt']['locked']);
2130 $this->assertFalse($result['lastattempt']['graded']);
2131 $this->assertEmpty($result['lastattempt']['extensionduedate']);
2132 $this->assertFalse($result['lastattempt']['blindmarking']);
2133 $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
2134 $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
2139 * Test get_submission_status using the teacher role.
2141 public function test_get_submission_status_in_submission_status_for_teacher() {
2142 $this->resetAfterTest(true);
2144 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
2146 // Now, as teacher, see the grading summary.
2147 $this->setUser($teacher);
2148 // First one group.
2149 $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g1->id);
2150 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2151 $this->assertDebuggingCalled();
2152 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2154 $this->assertCount(0, $result['warnings']);
2155 $this->assertFalse(isset($result['lastattempt']));
2156 $this->assertFalse(isset($result['feedback']));
2157 $this->assertFalse(isset($result['previousattempts']));
2159 $this->assertEquals(1, $result['gradingsummary']['participantcount']);
2160 $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
2161 $this->assertEquals(1, $result['gradingsummary']['submissionsenabled']);
2162 $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
2163 $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']); // One student from G1 submitted.
2164 $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']); // One student from G1 submitted.
2165 $this->assertEmpty($result['gradingsummary']['warnofungroupedusers']);
2167 // Second group.
2168 $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g2->id);
2169 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2170 $this->assertCount(0, $result['warnings']);
2171 $this->assertEquals(1, $result['gradingsummary']['participantcount']);
2172 $this->assertEquals(0, $result['gradingsummary']['submissionssubmittedcount']); // G2 students didn't submit yet.
2173 $this->assertEquals(0, $result['gradingsummary']['submissionsneedgradingcount']); // G2 students didn't submit yet.
2175 // Should return also 1 participant if we allow the function to auto-select the group.
2176 $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2177 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2178 $this->assertCount(0, $result['warnings']);
2179 $this->assertEquals(1, $result['gradingsummary']['participantcount']);
2180 $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
2181 $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']); // One student from G1 submitted.
2182 $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']); // One student from G1 submitted.
2184 // Now check draft submissions.
2185 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(false);
2186 $this->setUser($teacher);
2187 $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g1->id);
2188 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2189 $this->assertCount(0, $result['warnings']);
2190 $this->assertEquals(1, $result['gradingsummary']['participantcount']);
2191 $this->assertEquals(1, $result['gradingsummary']['submissiondraftscount']); // We have a draft submission.
2192 $this->assertEquals(0, $result['gradingsummary']['submissionssubmittedcount']); // We have only draft submissions.
2193 $this->assertEquals(0, $result['gradingsummary']['submissionsneedgradingcount']); // We have only draft submissions.
2197 * Test get_submission_status for a reopened submission.
2199 public function test_get_submission_status_in_reopened_status() {
2200 global $USER;
2202 $this->resetAfterTest(true);
2204 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
2205 $studentsubmission = $assign->get_user_submission($student1->id, true);
2207 $this->setUser($teacher);
2208 // Grade and reopen.
2209 $feedbackpluginparams = array();
2210 $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
2211 $feedbackeditorparams = array('text' => 'Yeeha!',
2212 'format' => 1);
2213 $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
2214 $result = mod_assign_external::save_grade($instance->id,
2215 $student1->id,
2216 50.0,
2218 false,
2219 'released',
2220 false,
2221 $feedbackpluginparams);
2222 $USER->ignoresesskey = true;
2223 $assign->testable_process_add_attempt($student1->id);
2225 $this->setUser($student1);
2227 $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2228 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2229 $this->assertDebuggingCalled();
2230 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2232 $this->assertCount(0, $result['warnings']);
2233 $this->assertFalse(isset($result['gradingsummary']));
2235 $this->assertTrue($result['lastattempt']['submissionsenabled']);
2236 $this->assertTrue($result['lastattempt']['canedit']);
2237 $this->assertFalse($result['lastattempt']['cansubmit']);
2238 $this->assertFalse($result['lastattempt']['locked']);
2239 $this->assertFalse($result['lastattempt']['graded']);
2240 $this->assertEmpty($result['lastattempt']['extensionduedate']);
2241 $this->assertFalse($result['lastattempt']['blindmarking']);
2242 $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
2243 $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
2245 // Check new attempt reopened.
2246 $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
2247 $this->assertEquals(1, $result['lastattempt']['submission']['attemptnumber']);
2248 $this->assertEquals('reopened', $result['lastattempt']['submission']['status']);
2249 $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
2250 $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
2251 $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
2252 $this->assertCount(3, $result['lastattempt']['submission']['plugins']);
2254 // Now see feedback and the attempts history (remember, is a submission reopened).
2255 // Only 2 fields (no grade, no plugins data).
2256 $this->assertCount(2, $result['feedback']);
2258 // One previous attempt.
2259 $this->assertCount(1, $result['previousattempts']);
2260 $this->assertEquals(0, $result['previousattempts'][0]['attemptnumber']);
2261 $this->assertEquals(50, $result['previousattempts'][0]['grade']['grade']);
2262 $this->assertEquals($teacher->id, $result['previousattempts'][0]['grade']['grader']);
2263 $this->assertEquals($student1->id, $result['previousattempts'][0]['grade']['userid']);
2265 // Map plugins based on their type - we can't rely on them being in a
2266 // particular order, especially if 3rd party plugins are installed.
2267 $feedbackplugins = array();
2268 foreach ($result['previousattempts'][0]['feedbackplugins'] as $plugin) {
2269 $feedbackplugins[$plugin['type']] = $plugin;
2271 $this->assertEquals('Yeeha!', $feedbackplugins['comments']['editorfields'][0]['text']);
2273 $submissionplugins = array();
2274 foreach ($result['previousattempts'][0]['submission']['plugins'] as $plugin) {
2275 $submissionplugins[$plugin['type']] = $plugin;
2277 // Format expected online text.
2278 $onlinetext = 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>';
2279 list($expectedtext, $expectedformat) = external_format_text($onlinetext, FORMAT_HTML, $assign->get_context()->id,
2280 'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $studentsubmission->id);
2282 $this->assertEquals($expectedtext, $submissionplugins['onlinetext']['editorfields'][0]['text']);
2283 $this->assertEquals($expectedformat, $submissionplugins['onlinetext']['editorfields'][0]['format']);
2284 $this->assertEquals('/', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']);
2285 $this->assertEquals('t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filename']);
2290 * Test access control for get_submission_status.
2292 * @expectedException required_capability_exception
2294 public function test_get_submission_status_access_control() {
2295 $this->resetAfterTest(true);
2297 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status();
2299 $this->setUser($student2);
2301 // Access control test.
2302 mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id);
2307 * Test hidden grader for get_submission_status.
2309 public function test_get_submission_status_hidden_grader() {
2310 $this->resetAfterTest(true);
2312 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true);
2314 // Grade the assign for the student1.
2315 $this->setUser($teacher);
2317 $data = new stdClass();
2318 $data->grade = '50.0';
2319 $data->assignfeedbackcomments_editor = ['text' => ''];
2320 $assign->testable_apply_grade_to_user($data, $student1->id, 0);
2322 $this->setUser($student1);
2324 // Check that the student can see the grader by default.
2325 $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2326 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2327 $this->assertDebuggingCalled();
2329 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2331 $this->assertTrue(isset($result['feedback']));
2332 $this->assertTrue(isset($result['feedback']['grade']));
2333 $this->assertEquals($teacher->id, $result['feedback']['grade']['grader']);
2335 // Now change the setting so the grader is hidden.
2336 $this->setAdminUser();
2338 $instance = $assign->get_instance();
2339 $instance->instance = $instance->id;
2340 $instance->hidegrader = true;
2341 $assign->update_instance($instance);
2343 $this->setUser($student1);
2345 // Check that the student cannot see the grader anymore.
2346 $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2347 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2349 $this->assertTrue(isset($result['feedback']));
2350 $this->assertTrue(isset($result['feedback']['grade']));
2351 $this->assertEquals(-1, $result['feedback']['grade']['grader']);
2353 // Check that the teacher can see the grader.
2354 $this->setUser($teacher);
2356 $result = mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id);
2357 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2359 $this->assertTrue(isset($result['feedback']));
2360 $this->assertTrue(isset($result['feedback']['grade']));
2361 $this->assertEquals($teacher->id, $result['feedback']['grade']['grader']);
2365 * Test get_submission_status with override for student.
2367 public function test_get_submission_status_with_override() {
2368 global $DB;
2370 $this->resetAfterTest(true);
2372 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status();
2374 $overridedata = new \stdClass();
2375 $overridedata->assignid = $assign->get_instance()->id;
2376 $overridedata->userid = $student1->id;
2377 $overridedata->allowsubmissionsfromdate = time() + YEARSECS;
2378 $DB->insert_record('assign_overrides', $overridedata);
2380 $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2381 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
2382 $this->assertDebuggingCalled();
2383 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2385 $this->assertCount(0, $result['warnings']);
2386 $this->assertFalse(isset($result['gradingsummary']));
2387 $this->assertFalse(isset($result['feedback']));
2388 $this->assertFalse(isset($result['previousattempts']));
2390 $this->assertTrue($result['lastattempt']['submissionsenabled']);
2391 $this->assertFalse($result['lastattempt']['canedit']); // False because of override.
2392 $this->assertFalse($result['lastattempt']['cansubmit']);
2393 $this->assertFalse($result['lastattempt']['locked']);
2394 $this->assertFalse($result['lastattempt']['graded']);
2395 $this->assertEmpty($result['lastattempt']['extensionduedate']);
2396 $this->assertFalse($result['lastattempt']['blindmarking']);
2397 $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
2398 $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
2400 // Same assignment but user without override.
2401 $this->setUser($student2);
2403 $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
2404 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
2406 // The submission is now in draft mode.
2407 $this->assertCount(0, $result['warnings']);
2408 $this->assertFalse(isset($result['gradingsummary']));
2409 $this->assertFalse(isset($result['feedback']));
2410 $this->assertFalse(isset($result['previousattempts']));
2412 $this->assertTrue($result['lastattempt']['submissionsenabled']);
2413 $this->assertTrue($result['lastattempt']['canedit']); // True because there is not override for this user.
2414 $this->assertFalse($result['lastattempt']['cansubmit']);
2415 $this->assertFalse($result['lastattempt']['locked']);
2416 $this->assertFalse($result['lastattempt']['graded']);
2417 $this->assertEmpty($result['lastattempt']['extensionduedate']);
2418 $this->assertFalse($result['lastattempt']['blindmarking']);
2419 $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
2420 $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
2424 * get_participant should throw an excaption if the requested assignment doesn't exist.
2426 * @expectedException moodle_exception
2428 public function test_get_participant_no_assignment() {
2429 $this->resetAfterTest(true);
2430 mod_assign_external::get_participant('-1', '-1', false);
2434 * get_participant should throw a require_login_exception if the user doesn't have access
2435 * to view assignments.
2437 * @expectedException require_login_exception
2439 public function test_get_participant_no_view_capability() {
2440 global $DB;
2441 $this->resetAfterTest(true);
2443 $result = $this->create_assign_with_student_and_teacher();
2444 $assign = $result['assign'];
2445 $student = $result['student'];
2446 $course = $result['course'];
2447 $context = context_course::instance($course->id);
2448 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2450 $this->setUser($student);
2451 assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id, true);
2453 mod_assign_external::get_participant($assign->id, $student->id, false);
2457 * get_participant should throw a required_capability_exception if the user doesn't have access
2458 * to view assignment grades.
2460 * @expectedException required_capability_exception
2462 public function test_get_participant_no_grade_capability() {
2463 global $DB;
2464 $this->resetAfterTest(true);
2466 $result = $this->create_assign_with_student_and_teacher();
2467 $assign = $result['assign'];
2468 $student = $result['student'];
2469 $teacher = $result['teacher'];
2470 $course = $result['course'];
2471 $context = context_course::instance($course->id);
2472 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2474 $this->setUser($teacher);
2475 assign_capability('mod/assign:viewgrades', CAP_PROHIBIT, $teacherrole->id, $context->id, true);
2476 assign_capability('mod/assign:grade', CAP_PROHIBIT, $teacherrole->id, $context->id, true);
2477 accesslib_clear_all_caches_for_unit_testing();
2479 mod_assign_external::get_participant($assign->id, $student->id, false);
2483 * get_participant should throw an exception if the user isn't enrolled in the course.
2485 * @expectedException moodle_exception
2487 public function test_get_participant_no_participant() {
2488 global $DB;
2489 $this->resetAfterTest(true);
2491 $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true));
2492 $student = $this->getDataGenerator()->create_user();
2493 $assign = $result['assign'];
2494 $teacher = $result['teacher'];
2496 $this->setUser($teacher);
2498 $result = mod_assign_external::get_participant($assign->id, $student->id, false);
2499 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2503 * get_participant should return a summarised list of details with a different fullname if blind
2504 * marking is on for the requested assignment.
2506 public function test_get_participant_blind_marking() {
2507 global $DB;
2508 $this->resetAfterTest(true);
2510 $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true));
2511 $assign = $result['assign'];
2512 $student = $result['student'];
2513 $teacher = $result['teacher'];
2514 $course = $result['course'];
2515 $context = context_course::instance($course->id);
2516 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2518 $this->setUser($teacher);
2520 $result = mod_assign_external::get_participant($assign->id, $student->id, true);
2521 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2522 $this->assertEquals($student->id, $result['id']);
2523 $this->assertFalse(fullname($student) == $result['fullname']);
2524 $this->assertFalse($result['submitted']);
2525 $this->assertFalse($result['requiregrading']);
2526 $this->assertFalse($result['grantedextension']);
2527 $this->assertTrue($result['blindmarking']);
2528 // Make sure we don't get any additional info.
2529 $this->assertArrayNotHasKey('user', $result);
2533 * get_participant should return a summarised list of details if requested.
2535 public function test_get_participant_no_user() {
2536 global $DB;
2537 $this->resetAfterTest(true);
2539 $result = $this->create_assign_with_student_and_teacher();
2540 $assignmodule = $result['assign'];
2541 $student = $result['student'];
2542 $teacher = $result['teacher'];
2543 $course = $result['course'];
2544 $context = context_course::instance($course->id);
2545 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2547 // Create an assign instance to save a submission.
2548 set_config('submissionreceipts', 0, 'assign');
2550 $cm = get_coursemodule_from_instance('assign', $assignmodule->id);
2551 $context = context_module::instance($cm->id);
2553 $assign = new assign($context, $cm, $course);
2555 $this->setUser($student);
2557 // Simulate a submission.
2558 $data = new stdClass();
2559 $data->onlinetext_editor = array(
2560 'itemid' => file_get_unused_draft_itemid(),
2561 'text' => 'Student submission text',
2562 'format' => FORMAT_MOODLE
2565 $notices = array();
2566 $assign->save_submission($data, $notices);
2568 $data = new stdClass;
2569 $data->userid = $student->id;
2570 $assign->submit_for_grading($data, array());
2572 $this->setUser($teacher);
2574 $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false);
2575 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2576 $this->assertEquals($student->id, $result['id']);
2577 $this->assertEquals(fullname($student), $result['fullname']);
2578 $this->assertTrue($result['submitted']);
2579 $this->assertTrue($result['requiregrading']);
2580 $this->assertFalse($result['grantedextension']);
2581 $this->assertFalse($result['blindmarking']);
2582 // Make sure we don't get any additional info.
2583 $this->assertArrayNotHasKey('user', $result);
2587 * get_participant should return user details if requested.
2589 public function test_get_participant_full_details() {
2590 global $DB;
2591 $this->resetAfterTest(true);
2593 $result = $this->create_assign_with_student_and_teacher();
2594 $assign = $result['assign'];
2595 $student = $result['student'];
2596 $teacher = $result['teacher'];
2597 $course = $result['course'];
2598 $context = context_course::instance($course->id);
2599 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2601 $this->setUser($teacher);
2603 $result = mod_assign_external::get_participant($assign->id, $student->id, true);
2604 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2605 // Check some of the extended properties we get when requesting the user.
2606 $this->assertEquals($student->id, $result['id']);
2607 // We should get user infomation back.
2608 $user = $result['user'];
2609 $this->assertFalse(empty($user));
2610 $this->assertEquals($student->firstname, $user['firstname']);
2611 $this->assertEquals($student->lastname, $user['lastname']);
2612 $this->assertEquals($student->email, $user['email']);
2616 * get_participant should return group details if a group submission was
2617 * submitted.
2619 public function test_get_participant_group_submission() {
2620 global $DB;
2622 $this->resetAfterTest(true);
2624 $result = $this->create_assign_with_student_and_teacher(array(
2625 'assignsubmission_onlinetext_enabled' => 1,
2626 'teamsubmission' => 1
2628 $assignmodule = $result['assign'];
2629 $student = $result['student'];
2630 $teacher = $result['teacher'];
2631 $course = $result['course'];
2632 $context = context_course::instance($course->id);
2633 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2634 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
2635 $cm = get_coursemodule_from_instance('assign', $assignmodule->id);
2636 $context = context_module::instance($cm->id);
2637 $assign = new mod_assign_testable_assign($context, $cm, $course);
2639 groups_add_member($group, $student);
2641 $this->setUser($student);
2642 $submission = $assign->get_group_submission($student->id, $group->id, true);
2643 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2644 $assign->testable_update_submission($submission, $student->id, true, false);
2645 $data = new stdClass();
2646 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
2647 'text' => 'Submission text',
2648 'format' => FORMAT_MOODLE);
2649 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2650 $plugin->save($submission, $data);
2652 $this->setUser($teacher);
2654 $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false);
2655 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2656 // Check some of the extended properties we get when not requesting a summary.
2657 $this->assertEquals($student->id, $result['id']);
2658 $this->assertEquals($group->id, $result['groupid']);
2659 $this->assertEquals($group->name, $result['groupname']);
2663 * Test get_participant() when relative dates mode is enabled on the course.
2665 * @dataProvider get_participant_relative_dates_provider
2666 * @param array $courseconfig the config to use when creating the course.
2667 * @param array $assignconfig the config to use when creating the assignment.
2668 * @param array $enrolconfig the enrolement to create.
2669 * @param array $expectedproperties array of expected assign properties.
2671 public function test_get_participant_relative_dates(array $courseconfig, array $assignconfig, array $enrolconfig,
2672 array $expectedproperties) {
2673 $this->resetAfterTest();
2675 set_config('enablecourserelativedates', true); // Enable relative dates at site level.
2677 $course = $this->getDataGenerator()->create_course($courseconfig);
2678 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
2679 $assignconfig['course'] = $course->id;
2680 $instance = $generator->create_instance($assignconfig);
2681 $cm = get_coursemodule_from_instance('assign', $instance->id);
2682 $context = context_module::instance($cm->id);
2683 $assign = new assign($context, $cm, $course);
2685 $user = $this->getDataGenerator()->create_and_enrol($course, ...array_values($enrolconfig));
2687 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher', null, 'manual', time() - 50 * DAYSECS);
2689 $this->setUser($teacher);
2690 $result = mod_assign_external::get_participant($assign->get_instance()->id, $user->id, false);
2691 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result);
2693 foreach ($expectedproperties as $propertyname => $propertyval) {
2694 $this->assertEquals($propertyval, $result[$propertyname]);
2699 * The test_get_participant_relative_dates data provider.
2701 public function get_participant_relative_dates_provider() {
2702 $timenow = time();
2704 return [
2705 'Student whose enrolment starts after the course start date, relative dates mode enabled' => [
2706 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS],
2707 'assignconfig' => ['duedate' => $timenow + 4 * DAYSECS],
2708 'enrolconfig' => ['shortname' => 'student', 'userparams' => null, 'method' => 'manual',
2709 'startdate' => $timenow - 8 * DAYSECS],
2710 'expectedproperties' => ['duedate' => $timenow + 6 * DAYSECS]
2712 'Student whose enrolment starts before the course start date, relative dates mode enabled' => [
2713 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS],
2714 'assignconfig' => ['duedate' => $timenow + 4 * DAYSECS],
2715 'enrolconfig' => ['shortname' => 'student', 'userparams' => null, 'method' => 'manual',
2716 'startdate' => $timenow - 12 * DAYSECS],
2717 'expectedproperties' => ['duedate' => $timenow + 4 * DAYSECS]
2723 * Test for mod_assign_external::list_participants().
2725 * @throws coding_exception
2727 public function test_list_participants_user_info_with_special_characters() {
2728 global $CFG, $DB;
2729 $this->resetAfterTest(true);
2730 $CFG->showuseridentity = 'idnumber,email,phone1,phone2,department,institution';
2732 $data = $this->create_assign_with_student_and_teacher();
2733 $assignment = $data['assign'];
2734 $teacher = $data['teacher'];
2736 // Set data for student info that contain special characters.
2737 $student = $data['student'];
2738 $student->idnumber = '<\'"1am@wesome&c00l"\'>';
2739 $student->phone1 = '+63 (999) 888-7777';
2740 $student->phone2 = '(011) [15]4-123-4567';
2741 $student->department = 'Arts & Sciences & \' " ¢ £ © € ¥ ® < >';
2742 $student->institution = 'University of Awesome People & \' " ¢ £ © € ¥ ® < >';
2743 // Assert that we have valid user data.
2744 $this->assertTrue(core_user::validate($student));
2745 // Update the user record.
2746 $DB->update_record('user', $student);
2748 $this->setUser($teacher);
2749 $participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, true, true);
2750 $participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
2751 $this->assertCount(1, $participants);
2753 // Asser that we have a valid response data.
2754 $response = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
2755 $this->assertEquals($response, $participants);
2757 // Check participant data.
2758 $participant = $participants[0];
2759 $this->assertEquals($student->idnumber, $participant['idnumber']);
2760 $this->assertEquals($student->email, $participant['email']);
2761 $this->assertEquals($student->phone1, $participant['phone1']);
2762 $this->assertEquals($student->phone2, $participant['phone2']);
2763 $this->assertEquals($student->department, $participant['department']);
2764 $this->assertEquals($student->institution, $participant['institution']);
2765 $this->assertArrayHasKey('enrolledcourses', $participant);
2767 $participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, false, true);
2768 $participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants);
2769 // Check that the list of courses the participant is enrolled is not returned.
2770 $participant = $participants[0];
2771 $this->assertArrayNotHasKey('enrolledcourses', $participant);
2775 * Test for the type of the user-related properties in mod_assign_external::list_participants_returns().
2777 public function test_list_participants_returns_user_property_types() {
2778 // Get user properties.
2779 $userdesc = core_user_external::user_description();
2780 $this->assertTrue(isset($userdesc->keys));
2781 $userproperties = array_keys($userdesc->keys);
2783 // Get returns description for mod_assign_external::list_participants_returns().
2784 $listreturns = mod_assign_external::list_participants_returns();
2785 $this->assertTrue(isset($listreturns->content));
2786 $listreturnsdesc = $listreturns->content->keys;
2788 // Iterate over list returns description's keys.
2789 foreach ($listreturnsdesc as $key => $desc) {
2790 // Check if key exists in user properties and the description has a type attribute.
2791 if (in_array($key, $userproperties) && isset($desc->type)) {
2792 try {
2793 // The core_user::get_property_type() method might throw a coding_exception since
2794 // core_user_external::user_description() might contain properties that are not yet included in
2795 // core_user's $propertiescache.
2796 $propertytype = core_user::get_property_type($key);
2798 // Assert that user-related property types match those of the defined in core_user.
2799 $this->assertEquals($propertytype, $desc->type);
2800 } catch (coding_exception $e) {
2801 // All good.
2808 * Create a a course, assignment module instance, student and teacher and enrol them in
2809 * the course.
2811 * @param array $params parameters to be provided to the assignment module creation
2812 * @return array containing the course, assignment module, student and teacher
2814 private function create_assign_with_student_and_teacher($params = array()) {
2815 global $DB;
2817 $course = $this->getDataGenerator()->create_course();
2818 $params = array_merge(array(
2819 'course' => $course->id,
2820 'name' => 'assignment',
2821 'intro' => 'assignment intro text',
2822 ), $params);
2824 // Create a course and assignment and users.
2825 $assign = $this->getDataGenerator()->create_module('assign', $params);
2827 $cm = get_coursemodule_from_instance('assign', $assign->id);
2828 $context = context_module::instance($cm->id);
2830 $student = $this->getDataGenerator()->create_user();
2831 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2832 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
2833 $teacher = $this->getDataGenerator()->create_user();
2834 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2835 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
2837 assign_capability('mod/assign:view', CAP_ALLOW, $teacherrole->id, $context->id, true);
2838 assign_capability('mod/assign:viewgrades', CAP_ALLOW, $teacherrole->id, $context->id, true);
2839 assign_capability('mod/assign:grade', CAP_ALLOW, $teacherrole->id, $context->id, true);
2840 accesslib_clear_all_caches_for_unit_testing();
2842 return array(
2843 'course' => $course,
2844 'assign' => $assign,
2845 'student' => $student,
2846 'teacher' => $teacher
2851 * Test test_view_assign
2853 public function test_view_assign() {
2854 global $CFG;
2856 $CFG->enablecompletion = 1;
2857 $this->resetAfterTest();
2859 $this->setAdminUser();
2860 // Setup test data.
2861 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
2862 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id),
2863 array('completion' => 2, 'completionview' => 1));
2864 $context = context_module::instance($assign->cmid);
2865 $cm = get_coursemodule_from_instance('assign', $assign->id);
2867 $result = mod_assign_external::view_assign($assign->id);
2868 $result = external_api::clean_returnvalue(mod_assign_external::view_assign_returns(), $result);
2869 $this->assertTrue($result['status']);
2870 $this->assertEmpty($result['warnings']);
2872 // Check completion status.
2873 $completion = new completion_info($course);
2874 $completiondata = $completion->get_data($cm);
2875 $this->assertEquals(1, $completiondata->completionstate);