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