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