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