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