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