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 global $DB; 2086 $this->resetAfterTest(true); 2087 2088 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true); 2089 2090 // Now, as teacher, see the grading summary. 2091 $this->setUser($teacher); 2092 // First one group. 2093 $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g1->id); 2094 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request. 2095 $this->assertDebuggingCalled(); 2096 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2097 2098 $this->assertCount(0, $result['warnings']); 2099 $this->assertFalse(isset($result['lastattempt'])); 2100 $this->assertFalse(isset($result['feedback'])); 2101 $this->assertFalse(isset($result['previousattempts'])); 2102 2103 $this->assertEquals(1, $result['gradingsummary']['participantcount']); 2104 $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']); 2105 $this->assertEquals(1, $result['gradingsummary']['submissionsenabled']); 2106 $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']); 2107 $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']); // One student from G1 submitted. 2108 $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']); // One student from G1 submitted. 2109 $this->assertEmpty($result['gradingsummary']['warnofungroupedusers']); 2110 2111 // Second group. 2112 $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g2->id); 2113 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2114 $this->assertCount(0, $result['warnings']); 2115 $this->assertEquals(1, $result['gradingsummary']['participantcount']); 2116 $this->assertEquals(0, $result['gradingsummary']['submissionssubmittedcount']); // G2 students didn't submit yet. 2117 $this->assertEquals(0, $result['gradingsummary']['submissionsneedgradingcount']); // G2 students didn't submit yet. 2118 2119 // Should not return information for all users (missing access to all groups capability for non-editing teacher). 2120 $result = mod_assign_external::get_submission_status($assign->get_instance()->id); 2121 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2122 $this->assertCount(0, $result['warnings']); 2123 $this->assertFalse(isset($result['gradingsummary'])); 2124 2125 // Should return all participants if we grant accessallgroups capability to the normal teacher role. 2126 $context = context_course::instance($assign->get_instance()->course); 2127 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 2128 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id, true); 2129 accesslib_clear_all_caches_for_unit_testing(); 2130 2131 $result = mod_assign_external::get_submission_status($assign->get_instance()->id); 2132 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2133 $this->assertCount(0, $result['warnings']); 2134 $this->assertEquals(2, $result['gradingsummary']['participantcount']); 2135 $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']); 2136 $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']); // One student from G1 submitted. 2137 $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']); // One student from G1 submitted. 2138 2139 // Now check draft submissions. 2140 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(false); 2141 $this->setUser($teacher); 2142 $result = mod_assign_external::get_submission_status($assign->get_instance()->id, 0, $g1->id); 2143 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2144 $this->assertCount(0, $result['warnings']); 2145 $this->assertEquals(1, $result['gradingsummary']['participantcount']); 2146 $this->assertEquals(1, $result['gradingsummary']['submissiondraftscount']); // We have a draft submission. 2147 $this->assertEquals(0, $result['gradingsummary']['submissionssubmittedcount']); // We have only draft submissions. 2148 $this->assertEquals(0, $result['gradingsummary']['submissionsneedgradingcount']); // We have only draft submissions. 2149 } 2150 2151 /** 2152 * Test get_submission_status for a reopened submission. 2153 */ 2154 public function test_get_submission_status_in_reopened_status() { 2155 global $USER; 2156 2157 $this->resetAfterTest(true); 2158 2159 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true); 2160 $studentsubmission = $assign->get_user_submission($student1->id, true); 2161 2162 $this->setUser($teacher); 2163 // Grade and reopen. 2164 $feedbackpluginparams = array(); 2165 $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid(); 2166 $feedbackeditorparams = array('text' => 'Yeeha!', 2167 'format' => 1); 2168 $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams; 2169 $result = mod_assign_external::save_grade( 2170 $instance->id, 2171 $student1->id, 2172 50.0, 2173 -1, 2174 false, 2175 'released', 2176 false, 2177 $feedbackpluginparams); 2178 $USER->ignoresesskey = true; 2179 $assign->testable_process_add_attempt($student1->id); 2180 2181 $this->setUser($student1); 2182 2183 $result = mod_assign_external::get_submission_status($assign->get_instance()->id); 2184 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request. 2185 $this->assertDebuggingCalled(); 2186 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2187 2188 $this->assertCount(0, $result['warnings']); 2189 $this->assertFalse(isset($result['gradingsummary'])); 2190 2191 $this->assertTrue($result['lastattempt']['submissionsenabled']); 2192 $this->assertTrue($result['lastattempt']['canedit']); 2193 $this->assertFalse($result['lastattempt']['cansubmit']); 2194 $this->assertFalse($result['lastattempt']['locked']); 2195 $this->assertFalse($result['lastattempt']['graded']); 2196 $this->assertEmpty($result['lastattempt']['extensionduedate']); 2197 $this->assertFalse($result['lastattempt']['blindmarking']); 2198 $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']); 2199 $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']); 2200 2201 // Check new attempt reopened. 2202 $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']); 2203 $this->assertEquals(1, $result['lastattempt']['submission']['attemptnumber']); 2204 $this->assertEquals('reopened', $result['lastattempt']['submission']['status']); 2205 $this->assertEquals(0, $result['lastattempt']['submission']['groupid']); 2206 $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']); 2207 $this->assertEquals(1, $result['lastattempt']['submission']['latest']); 2208 $this->assertCount(3, $result['lastattempt']['submission']['plugins']); 2209 2210 // Now see feedback and the attempts history (remember, is a submission reopened). 2211 // Only 2 fields (no grade, no plugins data). 2212 $this->assertCount(2, $result['feedback']); 2213 2214 // One previous attempt. 2215 $this->assertCount(1, $result['previousattempts']); 2216 $this->assertEquals(0, $result['previousattempts'][0]['attemptnumber']); 2217 $this->assertEquals(50, $result['previousattempts'][0]['grade']['grade']); 2218 $this->assertEquals($teacher->id, $result['previousattempts'][0]['grade']['grader']); 2219 $this->assertEquals($student1->id, $result['previousattempts'][0]['grade']['userid']); 2220 2221 // Map plugins based on their type - we can't rely on them being in a 2222 // particular order, especially if 3rd party plugins are installed. 2223 $feedbackplugins = array(); 2224 foreach ($result['previousattempts'][0]['feedbackplugins'] as $plugin) { 2225 $feedbackplugins[$plugin['type']] = $plugin; 2226 } 2227 $this->assertEquals('Yeeha!', $feedbackplugins['comments']['editorfields'][0]['text']); 2228 2229 $submissionplugins = array(); 2230 foreach ($result['previousattempts'][0]['submission']['plugins'] as $plugin) { 2231 $submissionplugins[$plugin['type']] = $plugin; 2232 } 2233 // Format expected online text. 2234 $onlinetext = 'Submission text with a <a href="@@PLUGINFILE@@/intro.txt">link</a>'; 2235 list($expectedtext, $expectedformat) = external_format_text($onlinetext, FORMAT_HTML, $assign->get_context()->id, 2236 'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $studentsubmission->id); 2237 2238 $this->assertEquals($expectedtext, $submissionplugins['onlinetext']['editorfields'][0]['text']); 2239 $this->assertEquals($expectedformat, $submissionplugins['onlinetext']['editorfields'][0]['format']); 2240 $this->assertEquals('/', $submissionplugins['file']['fileareas'][0]['files'][0]['filepath']); 2241 $this->assertEquals('t.txt', $submissionplugins['file']['fileareas'][0]['files'][0]['filename']); 2242 2243 } 2244 2245 /** 2246 * Test access control for get_submission_status. 2247 */ 2248 public function test_get_submission_status_access_control() { 2249 $this->resetAfterTest(true); 2250 2251 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(); 2252 2253 $this->setUser($student2); 2254 2255 // Access control test. 2256 $this->expectException(required_capability_exception::class); 2257 mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id); 2258 2259 } 2260 2261 /** 2262 * Test hidden grader for get_submission_status. 2263 */ 2264 public function test_get_submission_status_hidden_grader() { 2265 $this->resetAfterTest(true); 2266 2267 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(true); 2268 2269 // Grade the assign for the student1. 2270 $this->setUser($teacher); 2271 2272 $data = new stdClass(); 2273 $data->grade = '50.0'; 2274 $data->assignfeedbackcomments_editor = ['text' => '']; 2275 $assign->testable_apply_grade_to_user($data, $student1->id, 0); 2276 2277 $this->setUser($student1); 2278 2279 // Check that the student can see the grader by default. 2280 $result = mod_assign_external::get_submission_status($assign->get_instance()->id); 2281 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request. 2282 $this->assertDebuggingCalled(); 2283 2284 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2285 2286 $this->assertTrue(isset($result['feedback'])); 2287 $this->assertTrue(isset($result['feedback']['grade'])); 2288 $this->assertEquals($teacher->id, $result['feedback']['grade']['grader']); 2289 2290 // Now change the setting so the grader is hidden. 2291 $this->setAdminUser(); 2292 2293 $instance = $assign->get_instance(); 2294 $instance->instance = $instance->id; 2295 $instance->hidegrader = true; 2296 $assign->update_instance($instance); 2297 2298 $this->setUser($student1); 2299 2300 // Check that the student cannot see the grader anymore. 2301 $result = mod_assign_external::get_submission_status($assign->get_instance()->id); 2302 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2303 2304 $this->assertTrue(isset($result['feedback'])); 2305 $this->assertTrue(isset($result['feedback']['grade'])); 2306 $this->assertEquals(-1, $result['feedback']['grade']['grader']); 2307 2308 // Check that the teacher can see the grader. 2309 $this->setUser($teacher); 2310 2311 $result = mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id); 2312 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2313 2314 $this->assertTrue(isset($result['feedback'])); 2315 $this->assertTrue(isset($result['feedback']['grade'])); 2316 $this->assertEquals($teacher->id, $result['feedback']['grade']['grader']); 2317 } 2318 2319 /** 2320 * Test get_submission_status with override for student. 2321 */ 2322 public function test_get_submission_status_with_override() { 2323 global $DB; 2324 2325 $this->resetAfterTest(true); 2326 2327 list($assign, $instance, $student1, $student2, $teacher, $g1, $g2) = $this->create_submission_for_testing_status(); 2328 2329 $overridedata = new \stdClass(); 2330 $overridedata->assignid = $assign->get_instance()->id; 2331 $overridedata->userid = $student1->id; 2332 $overridedata->allowsubmissionsfromdate = time() + YEARSECS; 2333 $DB->insert_record('assign_overrides', $overridedata); 2334 2335 $result = mod_assign_external::get_submission_status($assign->get_instance()->id); 2336 // We expect debugging because of the $PAGE object, this won't happen in a normal WS request. 2337 $this->assertDebuggingCalled(); 2338 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2339 2340 $this->assertCount(0, $result['warnings']); 2341 $this->assertFalse(isset($result['gradingsummary'])); 2342 $this->assertFalse(isset($result['feedback'])); 2343 $this->assertFalse(isset($result['previousattempts'])); 2344 2345 $this->assertTrue($result['lastattempt']['submissionsenabled']); 2346 $this->assertFalse($result['lastattempt']['canedit']); // False because of override. 2347 $this->assertFalse($result['lastattempt']['cansubmit']); 2348 $this->assertFalse($result['lastattempt']['locked']); 2349 $this->assertFalse($result['lastattempt']['graded']); 2350 $this->assertEmpty($result['lastattempt']['extensionduedate']); 2351 $this->assertFalse($result['lastattempt']['blindmarking']); 2352 $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']); 2353 $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']); 2354 2355 // Same assignment but user without override. 2356 $this->setUser($student2); 2357 2358 $result = mod_assign_external::get_submission_status($assign->get_instance()->id); 2359 $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result); 2360 2361 // The submission is now in draft mode. 2362 $this->assertCount(0, $result['warnings']); 2363 $this->assertFalse(isset($result['gradingsummary'])); 2364 $this->assertFalse(isset($result['feedback'])); 2365 $this->assertFalse(isset($result['previousattempts'])); 2366 2367 $this->assertTrue($result['lastattempt']['submissionsenabled']); 2368 $this->assertTrue($result['lastattempt']['canedit']); // True because there is not override for this user. 2369 $this->assertFalse($result['lastattempt']['cansubmit']); 2370 $this->assertFalse($result['lastattempt']['locked']); 2371 $this->assertFalse($result['lastattempt']['graded']); 2372 $this->assertEmpty($result['lastattempt']['extensionduedate']); 2373 $this->assertFalse($result['lastattempt']['blindmarking']); 2374 $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']); 2375 $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']); 2376 } 2377 2378 /** 2379 * get_participant should throw an excaption if the requested assignment doesn't exist. 2380 */ 2381 public function test_get_participant_no_assignment() { 2382 $this->resetAfterTest(true); 2383 $this->expectException(moodle_exception::class); 2384 mod_assign_external::get_participant('-1', '-1', false); 2385 } 2386 2387 /** 2388 * get_participant should throw a require_login_exception if the user doesn't have access 2389 * to view assignments. 2390 */ 2391 public function test_get_participant_no_view_capability() { 2392 global $DB; 2393 $this->resetAfterTest(true); 2394 2395 $result = $this->create_assign_with_student_and_teacher(); 2396 $assign = $result['assign']; 2397 $student = $result['student']; 2398 $course = $result['course']; 2399 $context = context_course::instance($course->id); 2400 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 2401 2402 $this->setUser($student); 2403 assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id, true); 2404 2405 $this->expectException(require_login_exception::class); 2406 mod_assign_external::get_participant($assign->id, $student->id, false); 2407 } 2408 2409 /** 2410 * get_participant should throw a required_capability_exception if the user doesn't have access 2411 * to view assignment grades. 2412 */ 2413 public function test_get_participant_no_grade_capability() { 2414 global $DB; 2415 $this->resetAfterTest(true); 2416 2417 $result = $this->create_assign_with_student_and_teacher(); 2418 $assign = $result['assign']; 2419 $student = $result['student']; 2420 $teacher = $result['teacher']; 2421 $course = $result['course']; 2422 $context = context_course::instance($course->id); 2423 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 2424 2425 $this->setUser($teacher); 2426 assign_capability('mod/assign:viewgrades', CAP_PROHIBIT, $teacherrole->id, $context->id, true); 2427 assign_capability('mod/assign:grade', CAP_PROHIBIT, $teacherrole->id, $context->id, true); 2428 accesslib_clear_all_caches_for_unit_testing(); 2429 2430 $this->expectException(required_capability_exception::class); 2431 mod_assign_external::get_participant($assign->id, $student->id, false); 2432 } 2433 2434 /** 2435 * get_participant should throw an exception if the user isn't enrolled in the course. 2436 */ 2437 public function test_get_participant_no_participant() { 2438 global $DB; 2439 $this->resetAfterTest(true); 2440 2441 $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true)); 2442 $student = $this->getDataGenerator()->create_user(); 2443 $assign = $result['assign']; 2444 $teacher = $result['teacher']; 2445 2446 $this->setUser($teacher); 2447 2448 $this->expectException(moodle_exception::class); 2449 $result = mod_assign_external::get_participant($assign->id, $student->id, false); 2450 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result); 2451 } 2452 2453 /** 2454 * get_participant should return a summarised list of details with a different fullname if blind 2455 * marking is on for the requested assignment. 2456 */ 2457 public function test_get_participant_blind_marking() { 2458 global $DB; 2459 $this->resetAfterTest(true); 2460 2461 $result = $this->create_assign_with_student_and_teacher(array('blindmarking' => true)); 2462 $assign = $result['assign']; 2463 $student = $result['student']; 2464 $teacher = $result['teacher']; 2465 $course = $result['course']; 2466 $context = context_course::instance($course->id); 2467 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 2468 2469 $this->setUser($teacher); 2470 2471 $result = mod_assign_external::get_participant($assign->id, $student->id, true); 2472 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result); 2473 $this->assertEquals($student->id, $result['id']); 2474 $this->assertFalse(fullname($student) == $result['fullname']); 2475 $this->assertFalse($result['submitted']); 2476 $this->assertFalse($result['requiregrading']); 2477 $this->assertFalse($result['grantedextension']); 2478 $this->assertTrue($result['blindmarking']); 2479 // Make sure we don't get any additional info. 2480 $this->assertArrayNotHasKey('user', $result); 2481 } 2482 2483 /** 2484 * get_participant should return a summarised list of details if requested. 2485 */ 2486 public function test_get_participant_no_user() { 2487 global $DB; 2488 $this->resetAfterTest(true); 2489 2490 $result = $this->create_assign_with_student_and_teacher(); 2491 $assignmodule = $result['assign']; 2492 $student = $result['student']; 2493 $teacher = $result['teacher']; 2494 $course = $result['course']; 2495 $context = context_course::instance($course->id); 2496 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 2497 2498 // Create an assign instance to save a submission. 2499 set_config('submissionreceipts', 0, 'assign'); 2500 2501 $cm = get_coursemodule_from_instance('assign', $assignmodule->id); 2502 $context = context_module::instance($cm->id); 2503 2504 $assign = new assign($context, $cm, $course); 2505 2506 $this->setUser($student); 2507 2508 // Simulate a submission. 2509 $data = new stdClass(); 2510 $data->onlinetext_editor = array( 2511 'itemid' => file_get_unused_draft_itemid(), 2512 'text' => 'Student submission text', 2513 'format' => FORMAT_MOODLE 2514 ); 2515 2516 $notices = array(); 2517 $assign->save_submission($data, $notices); 2518 2519 $data = new stdClass; 2520 $data->userid = $student->id; 2521 $assign->submit_for_grading($data, array()); 2522 2523 $this->setUser($teacher); 2524 2525 $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false); 2526 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result); 2527 $this->assertEquals($student->id, $result['id']); 2528 $this->assertEquals(fullname($student), $result['fullname']); 2529 $this->assertTrue($result['submitted']); 2530 $this->assertTrue($result['requiregrading']); 2531 $this->assertFalse($result['grantedextension']); 2532 $this->assertFalse($result['blindmarking']); 2533 // Make sure we don't get any additional info. 2534 $this->assertArrayNotHasKey('user', $result); 2535 } 2536 2537 /** 2538 * get_participant should return user details if requested. 2539 */ 2540 public function test_get_participant_full_details() { 2541 global $DB; 2542 $this->resetAfterTest(true); 2543 2544 $result = $this->create_assign_with_student_and_teacher(); 2545 $assign = $result['assign']; 2546 $student = $result['student']; 2547 $teacher = $result['teacher']; 2548 $course = $result['course']; 2549 $context = context_course::instance($course->id); 2550 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 2551 2552 $this->setUser($teacher); 2553 2554 $result = mod_assign_external::get_participant($assign->id, $student->id, true); 2555 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result); 2556 // Check some of the extended properties we get when requesting the user. 2557 $this->assertEquals($student->id, $result['id']); 2558 // We should get user infomation back. 2559 $user = $result['user']; 2560 $this->assertFalse(empty($user)); 2561 $this->assertEquals($student->firstname, $user['firstname']); 2562 $this->assertEquals($student->lastname, $user['lastname']); 2563 $this->assertEquals($student->email, $user['email']); 2564 } 2565 2566 /** 2567 * get_participant should return group details if a group submission was 2568 * submitted. 2569 */ 2570 public function test_get_participant_group_submission() { 2571 global $DB; 2572 2573 $this->resetAfterTest(true); 2574 2575 $result = $this->create_assign_with_student_and_teacher(array( 2576 'assignsubmission_onlinetext_enabled' => 1, 2577 'teamsubmission' => 1 2578 )); 2579 $assignmodule = $result['assign']; 2580 $student = $result['student']; 2581 $teacher = $result['teacher']; 2582 $course = $result['course']; 2583 $context = context_course::instance($course->id); 2584 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 2585 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2586 $cm = get_coursemodule_from_instance('assign', $assignmodule->id); 2587 $context = context_module::instance($cm->id); 2588 $assign = new mod_assign_testable_assign($context, $cm, $course); 2589 2590 groups_add_member($group, $student); 2591 2592 $this->setUser($student); 2593 $submission = $assign->get_group_submission($student->id, $group->id, true); 2594 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; 2595 $assign->testable_update_submission($submission, $student->id, true, false); 2596 $data = new stdClass(); 2597 $data->onlinetext_editor = array( 2598 'itemid' => file_get_unused_draft_itemid(), 2599 'text' => 'Submission text', 2600 'format' => FORMAT_MOODLE); 2601 $plugin = $assign->get_submission_plugin_by_type('onlinetext'); 2602 $plugin->save($submission, $data); 2603 2604 $this->setUser($teacher); 2605 2606 $result = mod_assign_external::get_participant($assignmodule->id, $student->id, false); 2607 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result); 2608 // Check some of the extended properties we get when not requesting a summary. 2609 $this->assertEquals($student->id, $result['id']); 2610 $this->assertEquals($group->id, $result['groupid']); 2611 $this->assertEquals($group->name, $result['groupname']); 2612 } 2613 2614 /** 2615 * Test get_participant() when relative dates mode is enabled on the course. 2616 * 2617 * @dataProvider get_participant_relative_dates_provider 2618 * @param array $courseconfig the config to use when creating the course. 2619 * @param array $assignconfig the config to use when creating the assignment. 2620 * @param array $enrolconfig the enrolement to create. 2621 * @param array $expectedproperties array of expected assign properties. 2622 */ 2623 public function test_get_participant_relative_dates(array $courseconfig, array $assignconfig, array $enrolconfig, 2624 array $expectedproperties) { 2625 $this->resetAfterTest(); 2626 2627 set_config('enablecourserelativedates', true); // Enable relative dates at site level. 2628 2629 $course = $this->getDataGenerator()->create_course($courseconfig); 2630 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); 2631 $assignconfig['course'] = $course->id; 2632 $instance = $generator->create_instance($assignconfig); 2633 $cm = get_coursemodule_from_instance('assign', $instance->id); 2634 $context = context_module::instance($cm->id); 2635 $assign = new assign($context, $cm, $course); 2636 2637 $user = $this->getDataGenerator()->create_and_enrol($course, ...array_values($enrolconfig)); 2638 2639 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher', null, 'manual', time() - 50 * DAYSECS); 2640 2641 $this->setUser($teacher); 2642 $result = mod_assign_external::get_participant($assign->get_instance()->id, $user->id, false); 2643 $result = external_api::clean_returnvalue(mod_assign_external::get_participant_returns(), $result); 2644 2645 foreach ($expectedproperties as $propertyname => $propertyval) { 2646 $this->assertEquals($propertyval, $result[$propertyname]); 2647 } 2648 } 2649 2650 /** 2651 * The test_get_participant_relative_dates data provider. 2652 */ 2653 public function get_participant_relative_dates_provider() { 2654 $timenow = time(); 2655 2656 return [ 2657 'Student whose enrolment starts after the course start date, relative dates mode enabled' => [ 2658 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS], 2659 'assignconfig' => ['duedate' => $timenow + 4 * DAYSECS], 2660 'enrolconfig' => ['shortname' => 'student', 'userparams' => null, 'method' => 'manual', 2661 'startdate' => $timenow - 8 * DAYSECS], 2662 'expectedproperties' => ['duedate' => $timenow + 6 * DAYSECS] 2663 ], 2664 'Student whose enrolment starts before the course start date, relative dates mode enabled' => [ 2665 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS], 2666 'assignconfig' => ['duedate' => $timenow + 4 * DAYSECS], 2667 'enrolconfig' => ['shortname' => 'student', 'userparams' => null, 'method' => 'manual', 2668 'startdate' => $timenow - 12 * DAYSECS], 2669 'expectedproperties' => ['duedate' => $timenow + 4 * DAYSECS] 2670 ], 2671 ]; 2672 } 2673 2674 /** 2675 * Test for mod_assign_external::list_participants(). 2676 * 2677 * @throws coding_exception 2678 */ 2679 public function test_list_participants_user_info_with_special_characters() { 2680 global $CFG, $DB; 2681 $this->resetAfterTest(true); 2682 $CFG->showuseridentity = 'idnumber,email,phone1,phone2,department,institution'; 2683 2684 $data = $this->create_assign_with_student_and_teacher(); 2685 $assignment = $data['assign']; 2686 $teacher = $data['teacher']; 2687 2688 // Set data for student info that contain special characters. 2689 $student = $data['student']; 2690 $student->idnumber = '<\'"1am@wesome&c00l"\'>'; 2691 $student->phone1 = '+63 (999) 888-7777'; 2692 $student->phone2 = '(011) [15]4-123-4567'; 2693 $student->department = 'Arts & Sciences & \' " ¢ £ © € ¥ ® < >'; 2694 $student->institution = 'University of Awesome People & \' " ¢ £ © € ¥ ® < >'; 2695 // Assert that we have valid user data. 2696 $this->assertTrue(core_user::validate($student)); 2697 // Update the user record. 2698 $DB->update_record('user', $student); 2699 2700 $this->setUser($teacher); 2701 $participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, true, true); 2702 $participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants); 2703 $this->assertCount(1, $participants); 2704 2705 // Asser that we have a valid response data. 2706 $response = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants); 2707 $this->assertEquals($response, $participants); 2708 2709 // Check participant data. 2710 $participant = $participants[0]; 2711 $this->assertEquals($student->idnumber, $participant['idnumber']); 2712 $this->assertEquals($student->email, $participant['email']); 2713 $this->assertEquals($student->phone1, $participant['phone1']); 2714 $this->assertEquals($student->phone2, $participant['phone2']); 2715 $this->assertEquals($student->department, $participant['department']); 2716 $this->assertEquals($student->institution, $participant['institution']); 2717 $this->assertArrayHasKey('enrolledcourses', $participant); 2718 2719 $participants = mod_assign_external::list_participants($assignment->id, 0, '', 0, 0, false, false, true); 2720 $participants = external_api::clean_returnvalue(mod_assign_external::list_participants_returns(), $participants); 2721 // Check that the list of courses the participant is enrolled is not returned. 2722 $participant = $participants[0]; 2723 $this->assertArrayNotHasKey('enrolledcourses', $participant); 2724 } 2725 2726 /** 2727 * Test for the type of the user-related properties in mod_assign_external::list_participants_returns(). 2728 */ 2729 public function test_list_participants_returns_user_property_types() { 2730 // Get user properties. 2731 $userdesc = core_user_external::user_description(); 2732 $this->assertTrue(isset($userdesc->keys)); 2733 $userproperties = array_keys($userdesc->keys); 2734 2735 // Get returns description for mod_assign_external::list_participants_returns(). 2736 $listreturns = mod_assign_external::list_participants_returns(); 2737 $this->assertTrue(isset($listreturns->content)); 2738 $listreturnsdesc = $listreturns->content->keys; 2739 2740 // Iterate over list returns description's keys. 2741 foreach ($listreturnsdesc as $key => $desc) { 2742 // Check if key exists in user properties and the description has a type attribute. 2743 if (in_array($key, $userproperties) && isset($desc->type)) { 2744 try { 2745 // The core_user::get_property_type() method might throw a coding_exception since 2746 // core_user_external::user_description() might contain properties that are not yet included in 2747 // core_user's $propertiescache. 2748 $propertytype = core_user::get_property_type($key); 2749 2750 // Assert that user-related property types match those of the defined in core_user. 2751 $this->assertEquals($propertytype, $desc->type); 2752 } catch (coding_exception $e) { 2753 // All good. 2754 } 2755 } 2756 } 2757 } 2758 2759 /** 2760 * Create a a course, assignment module instance, student and teacher and enrol them in 2761 * the course. 2762 * 2763 * @param array $params parameters to be provided to the assignment module creation 2764 * @return array containing the course, assignment module, student and teacher 2765 */ 2766 private function create_assign_with_student_and_teacher($params = array()) { 2767 global $DB; 2768 2769 $course = $this->getDataGenerator()->create_course(); 2770 $params = array_merge(array( 2771 'course' => $course->id, 2772 'name' => 'assignment', 2773 'intro' => 'assignment intro text', 2774 ), $params); 2775 2776 // Create a course and assignment and users. 2777 $assign = $this->getDataGenerator()->create_module('assign', $params); 2778 2779 $cm = get_coursemodule_from_instance('assign', $assign->id); 2780 $context = context_module::instance($cm->id); 2781 2782 $student = $this->getDataGenerator()->create_user(); 2783 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 2784 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id); 2785 $teacher = $this->getDataGenerator()->create_user(); 2786 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 2787 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id); 2788 2789 assign_capability('mod/assign:view', CAP_ALLOW, $teacherrole->id, $context->id, true); 2790 assign_capability('mod/assign:viewgrades', CAP_ALLOW, $teacherrole->id, $context->id, true); 2791 assign_capability('mod/assign:grade', CAP_ALLOW, $teacherrole->id, $context->id, true); 2792 accesslib_clear_all_caches_for_unit_testing(); 2793 2794 return array( 2795 'course' => $course, 2796 'assign' => $assign, 2797 'student' => $student, 2798 'teacher' => $teacher 2799 ); 2800 } 2801 2802 /** 2803 * Test test_view_assign 2804 */ 2805 public function test_view_assign() { 2806 global $CFG; 2807 2808 $CFG->enablecompletion = 1; 2809 $this->resetAfterTest(); 2810 2811 $this->setAdminUser(); 2812 // Setup test data. 2813 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 2814 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id), 2815 array('completion' => 2, 'completionview' => 1)); 2816 $context = context_module::instance($assign->cmid); 2817 $cm = get_coursemodule_from_instance('assign', $assign->id); 2818 2819 $result = mod_assign_external::view_assign($assign->id); 2820 $result = external_api::clean_returnvalue(mod_assign_external::view_assign_returns(), $result); 2821 $this->assertTrue($result['status']); 2822 $this->assertEmpty($result['warnings']); 2823 2824 // Check completion status. 2825 $completion = new completion_info($course); 2826 $completiondata = $completion->get_data($cm); 2827 $this->assertEquals(1, $completiondata->completionstate); 2828 } 2829} 2830