Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * External assign API 19 * 20 * @package mod_assign 21 * @since Moodle 2.4 22 * @copyright 2012 Paul Charsley 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die; 27 28 require_once("$CFG->libdir/externallib.php"); 29 require_once("$CFG->dirroot/user/externallib.php"); 30 require_once("$CFG->dirroot/mod/assign/locallib.php"); 31 32 /** 33 * Assign functions 34 * @copyright 2012 Paul Charsley 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class mod_assign_external extends external_api { 38 39 /** 40 * Generate a warning in a standard structure for a known failure. 41 * 42 * @param int $assignmentid - The assignment 43 * @param string $warningcode - The key for the warning message 44 * @param string $detail - A description of the error 45 * @return array - Warning structure containing item, itemid, warningcode, message 46 */ 47 private static function generate_warning($assignmentid, $warningcode, $detail) { 48 $warningmessages = array( 49 'couldnotlock'=>'Could not lock the submission for this user.', 50 'couldnotunlock'=>'Could not unlock the submission for this user.', 51 'couldnotsubmitforgrading'=>'Could not submit assignment for grading.', 52 'couldnotrevealidentities'=>'Could not reveal identities.', 53 'couldnotgrantextensions'=>'Could not grant submission date extensions.', 54 'couldnotrevert'=>'Could not revert submission to draft.', 55 'invalidparameters'=>'Invalid parameters.', 56 'couldnotsavesubmission'=>'Could not save submission.', 57 'couldnotsavegrade'=>'Could not save grade.' 58 ); 59 60 $message = $warningmessages[$warningcode]; 61 if (empty($message)) { 62 $message = 'Unknown warning type.'; 63 } 64 65 return array('item' => s($detail), 66 'itemid'=>$assignmentid, 67 'warningcode'=>$warningcode, 68 'message'=>$message); 69 } 70 71 /** 72 * Describes the parameters for get_grades 73 * @return external_function_parameters 74 * @since Moodle 2.4 75 */ 76 public static function get_grades_parameters() { 77 return new external_function_parameters( 78 array( 79 'assignmentids' => new external_multiple_structure( 80 new external_value(PARAM_INT, 'assignment id'), 81 '1 or more assignment ids', 82 VALUE_REQUIRED), 83 'since' => new external_value(PARAM_INT, 84 'timestamp, only return records where timemodified >= since', 85 VALUE_DEFAULT, 0) 86 ) 87 ); 88 } 89 90 /** 91 * Returns grade information from assign_grades for the requested assignment ids 92 * @param int[] $assignmentids 93 * @param int $since only return records with timemodified >= since 94 * @return array of grade records for each requested assignment 95 * @since Moodle 2.4 96 */ 97 public static function get_grades($assignmentids, $since = 0) { 98 global $DB; 99 $params = self::validate_parameters(self::get_grades_parameters(), 100 array('assignmentids' => $assignmentids, 101 'since' => $since)); 102 103 $assignments = array(); 104 $warnings = array(); 105 $requestedassignmentids = $params['assignmentids']; 106 107 // Check the user is allowed to get the grades for the assignments requested. 108 $placeholders = array(); 109 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); 110 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ". 111 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids; 112 $placeholders['modname'] = 'assign'; 113 $cms = $DB->get_records_sql($sql, $placeholders); 114 foreach ($cms as $cm) { 115 try { 116 $context = context_module::instance($cm->id); 117 self::validate_context($context); 118 $assign = new assign($context, null, null); 119 $assign->require_view_grades(); 120 } catch (Exception $e) { 121 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance)); 122 $warning = array(); 123 $warning['item'] = 'assignment'; 124 $warning['itemid'] = $cm->instance; 125 $warning['warningcode'] = '1'; 126 $warning['message'] = 'No access rights in module context'; 127 $warnings[] = $warning; 128 } 129 } 130 131 // Create the query and populate an array of grade records from the recordset results. 132 if (count ($requestedassignmentids) > 0) { 133 $placeholders = array(); 134 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); 135 136 $sql = "SELECT ag.id, 137 ag.assignment, 138 ag.userid, 139 ag.timecreated, 140 ag.timemodified, 141 ag.grader, 142 ag.grade, 143 ag.attemptnumber 144 FROM {assign_grades} ag, {assign_submission} s 145 WHERE s.assignment $inorequalsql 146 AND s.userid = ag.userid 147 AND s.latest = 1 148 AND s.attemptnumber = ag.attemptnumber 149 AND ag.timemodified >= :since 150 AND ag.assignment = s.assignment 151 ORDER BY ag.assignment, ag.id"; 152 153 $placeholders['since'] = $params['since']; 154 $rs = $DB->get_recordset_sql($sql, $placeholders); 155 $currentassignmentid = null; 156 $assignment = null; 157 foreach ($rs as $rd) { 158 $grade = array(); 159 $grade['id'] = $rd->id; 160 $grade['userid'] = $rd->userid; 161 $grade['timecreated'] = $rd->timecreated; 162 $grade['timemodified'] = $rd->timemodified; 163 $grade['grader'] = $rd->grader; 164 $grade['attemptnumber'] = $rd->attemptnumber; 165 $grade['grade'] = (string)$rd->grade; 166 167 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) { 168 if (!is_null($assignment)) { 169 $assignments[] = $assignment; 170 } 171 $assignment = array(); 172 $assignment['assignmentid'] = $rd->assignment; 173 $assignment['grades'] = array(); 174 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment)); 175 } 176 $assignment['grades'][] = $grade; 177 178 $currentassignmentid = $rd->assignment; 179 } 180 if (!is_null($assignment)) { 181 $assignments[] = $assignment; 182 } 183 $rs->close(); 184 } 185 foreach ($requestedassignmentids as $assignmentid) { 186 $warning = array(); 187 $warning['item'] = 'assignment'; 188 $warning['itemid'] = $assignmentid; 189 $warning['warningcode'] = '3'; 190 $warning['message'] = 'No grades found'; 191 $warnings[] = $warning; 192 } 193 194 $result = array(); 195 $result['assignments'] = $assignments; 196 $result['warnings'] = $warnings; 197 return $result; 198 } 199 200 /** 201 * Creates a grade single structure. 202 * 203 * @return external_single_structure a grade single structure. 204 * @since Moodle 3.1 205 */ 206 private static function get_grade_structure($required = VALUE_REQUIRED) { 207 return new external_single_structure( 208 array( 209 'id' => new external_value(PARAM_INT, 'grade id'), 210 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL), 211 'userid' => new external_value(PARAM_INT, 'student id'), 212 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'), 213 'timecreated' => new external_value(PARAM_INT, 'grade creation time'), 214 'timemodified' => new external_value(PARAM_INT, 'grade last modified time'), 215 'grader' => new external_value(PARAM_INT, 'grader, -1 if grader is hidden'), 216 'grade' => new external_value(PARAM_TEXT, 'grade'), 217 'gradefordisplay' => new external_value(PARAM_RAW, 'grade rendered into a format suitable for display', 218 VALUE_OPTIONAL), 219 ), 'grade information', $required 220 ); 221 } 222 223 /** 224 * Creates an assign_grades external_single_structure 225 * @return external_single_structure 226 * @since Moodle 2.4 227 */ 228 private static function assign_grades() { 229 return new external_single_structure( 230 array ( 231 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 232 'grades' => new external_multiple_structure(self::get_grade_structure()) 233 ) 234 ); 235 } 236 237 /** 238 * Describes the get_grades return value 239 * @return external_single_structure 240 * @since Moodle 2.4 241 */ 242 public static function get_grades_returns() { 243 return new external_single_structure( 244 array( 245 'assignments' => new external_multiple_structure(self::assign_grades(), 'list of assignment grade information'), 246 'warnings' => new external_warnings('item is always \'assignment\'', 247 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id', 248 'errorcode can be 3 (no grades found) or 1 (no permission to get grades)') 249 ) 250 ); 251 } 252 253 /** 254 * Returns description of method parameters 255 * 256 * @return external_function_parameters 257 * @since Moodle 2.4 258 */ 259 public static function get_assignments_parameters() { 260 return new external_function_parameters( 261 array( 262 'courseids' => new external_multiple_structure( 263 new external_value(PARAM_INT, 'course id, empty for retrieving all the courses where the user is enroled in'), 264 '0 or more course ids', 265 VALUE_DEFAULT, array() 266 ), 267 'capabilities' => new external_multiple_structure( 268 new external_value(PARAM_CAPABILITY, 'capability'), 269 'list of capabilities used to filter courses', 270 VALUE_DEFAULT, array() 271 ), 272 'includenotenrolledcourses' => new external_value(PARAM_BOOL, 'whether to return courses that the user can see 273 even if is not enroled in. This requires the parameter courseids 274 to not be empty.', VALUE_DEFAULT, false) 275 ) 276 ); 277 } 278 279 /** 280 * Returns an array of courses the user is enrolled, and for each course all of the assignments that the user can 281 * view within that course. 282 * 283 * @param array $courseids An optional array of course ids. If provided only assignments within the given course 284 * will be returned. If the user is not enrolled in or can't view a given course a warning will be generated and returned. 285 * @param array $capabilities An array of additional capability checks you wish to be made on the course context. 286 * @param bool $includenotenrolledcourses Wheter to return courses that the user can see even if is not enroled in. 287 * This requires the parameter $courseids to not be empty. 288 * @return An array of courses and warnings. 289 * @since Moodle 2.4 290 */ 291 public static function get_assignments($courseids = array(), $capabilities = array(), $includenotenrolledcourses = false) { 292 global $USER, $DB, $CFG; 293 294 $params = self::validate_parameters( 295 self::get_assignments_parameters(), 296 array( 297 'courseids' => $courseids, 298 'capabilities' => $capabilities, 299 'includenotenrolledcourses' => $includenotenrolledcourses 300 ) 301 ); 302 303 $warnings = array(); 304 $courses = array(); 305 $fields = 'sortorder,shortname,fullname,timemodified'; 306 307 // If the courseids list is empty, we return only the courses where the user is enrolled in. 308 if (empty($params['courseids'])) { 309 $courses = enrol_get_users_courses($USER->id, true, $fields); 310 $courseids = array_keys($courses); 311 } else if ($includenotenrolledcourses) { 312 // In this case, we don't have to check here for enrolmnents. Maybe the user can see the course even if is not enrolled. 313 $courseids = $params['courseids']; 314 } else { 315 // We need to check for enrolments. 316 $mycourses = enrol_get_users_courses($USER->id, true, $fields); 317 $mycourseids = array_keys($mycourses); 318 319 foreach ($params['courseids'] as $courseid) { 320 if (!in_array($courseid, $mycourseids)) { 321 unset($courses[$courseid]); 322 $warnings[] = array( 323 'item' => 'course', 324 'itemid' => $courseid, 325 'warningcode' => '2', 326 'message' => 'User is not enrolled or does not have requested capability' 327 ); 328 } else { 329 $courses[$courseid] = $mycourses[$courseid]; 330 } 331 } 332 $courseids = array_keys($courses); 333 } 334 335 foreach ($courseids as $cid) { 336 337 try { 338 $context = context_course::instance($cid); 339 self::validate_context($context); 340 341 // Check if this course was already loaded (by enrol_get_users_courses). 342 if (!isset($courses[$cid])) { 343 $courses[$cid] = get_course($cid); 344 } 345 $courses[$cid]->contextid = $context->id; 346 } catch (Exception $e) { 347 unset($courses[$cid]); 348 $warnings[] = array( 349 'item' => 'course', 350 'itemid' => $cid, 351 'warningcode' => '1', 352 'message' => 'No access rights in course context '.$e->getMessage() 353 ); 354 continue; 355 } 356 if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) { 357 unset($courses[$cid]); 358 } 359 } 360 $extrafields='m.id as assignmentid, ' . 361 'm.course, ' . 362 'm.nosubmissions, ' . 363 'm.submissiondrafts, ' . 364 'm.sendnotifications, '. 365 'm.sendlatenotifications, ' . 366 'm.sendstudentnotifications, ' . 367 'm.duedate, ' . 368 'm.allowsubmissionsfromdate, '. 369 'm.grade, ' . 370 'm.timemodified, '. 371 'm.completionsubmit, ' . 372 'm.cutoffdate, ' . 373 'm.gradingduedate, ' . 374 'm.teamsubmission, ' . 375 'm.requireallteammemberssubmit, '. 376 'm.teamsubmissiongroupingid, ' . 377 'm.blindmarking, ' . 378 'm.hidegrader, ' . 379 'm.revealidentities, ' . 380 'm.attemptreopenmethod, '. 381 'm.maxattempts, ' . 382 'm.markingworkflow, ' . 383 'm.markingallocation, ' . 384 'm.requiresubmissionstatement, '. 385 'm.preventsubmissionnotingroup, '. 386 'm.intro, '. 387 'm.introformat'; 388 $coursearray = array(); 389 foreach ($courses as $id => $course) { 390 $assignmentarray = array(); 391 // Get a list of assignments for the course. 392 if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) { 393 foreach ($modules as $module) { 394 $context = context_module::instance($module->id); 395 try { 396 self::validate_context($context); 397 require_capability('mod/assign:view', $context); 398 } catch (Exception $e) { 399 $warnings[] = array( 400 'item' => 'module', 401 'itemid' => $module->id, 402 'warningcode' => '1', 403 'message' => 'No access rights in module context' 404 ); 405 continue; 406 } 407 408 $assign = new assign($context, null, null); 409 // Update assign with override information. 410 $assign->update_effective_access($USER->id); 411 412 // Get configurations for only enabled plugins. 413 $plugins = $assign->get_submission_plugins(); 414 $plugins = array_merge($plugins, $assign->get_feedback_plugins()); 415 416 $configarray = array(); 417 foreach ($plugins as $plugin) { 418 if ($plugin->is_enabled() && $plugin->is_visible()) { 419 $configrecords = $plugin->get_config_for_external(); 420 foreach ($configrecords as $name => $value) { 421 $configarray[] = array( 422 'plugin' => $plugin->get_type(), 423 'subtype' => $plugin->get_subtype(), 424 'name' => $name, 425 'value' => $value 426 ); 427 } 428 } 429 } 430 431 $assignment = array( 432 'id' => $module->assignmentid, 433 'cmid' => $module->id, 434 'course' => $module->course, 435 'name' => external_format_string($module->name, $context), 436 'nosubmissions' => $module->nosubmissions, 437 'submissiondrafts' => $module->submissiondrafts, 438 'sendnotifications' => $module->sendnotifications, 439 'sendlatenotifications' => $module->sendlatenotifications, 440 'sendstudentnotifications' => $module->sendstudentnotifications, 441 'duedate' => $assign->get_instance()->duedate, 442 'allowsubmissionsfromdate' => $assign->get_instance()->allowsubmissionsfromdate, 443 'grade' => $module->grade, 444 'timemodified' => $module->timemodified, 445 'completionsubmit' => $module->completionsubmit, 446 'cutoffdate' => $assign->get_instance()->cutoffdate, 447 'gradingduedate' => $assign->get_instance()->gradingduedate, 448 'teamsubmission' => $module->teamsubmission, 449 'requireallteammemberssubmit' => $module->requireallteammemberssubmit, 450 'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid, 451 'blindmarking' => $module->blindmarking, 452 'hidegrader' => $module->hidegrader, 453 'revealidentities' => $module->revealidentities, 454 'attemptreopenmethod' => $module->attemptreopenmethod, 455 'maxattempts' => $module->maxattempts, 456 'markingworkflow' => $module->markingworkflow, 457 'markingallocation' => $module->markingallocation, 458 'requiresubmissionstatement' => $module->requiresubmissionstatement, 459 'preventsubmissionnotingroup' => $module->preventsubmissionnotingroup, 460 'configs' => $configarray 461 ); 462 463 // Return or not intro and file attachments depending on the plugin settings. 464 if ($assign->show_intro()) { 465 $options = array('noclean' => true); 466 list($assignment['intro'], $assignment['introformat']) = 467 external_format_text($module->intro, $module->introformat, $context->id, 'mod_assign', 'intro', null, 468 $options); 469 $assignment['introfiles'] = external_util::get_area_files($context->id, 'mod_assign', 'intro', false, 470 false); 471 472 $assignment['introattachments'] = external_util::get_area_files($context->id, 'mod_assign', 473 ASSIGN_INTROATTACHMENT_FILEAREA, 0); 474 } 475 476 if ($module->requiresubmissionstatement) { 477 // Submission statement is required, return the submission statement value. 478 $adminconfig = get_config('assign'); 479 // Single submission. 480 if (!$module->teamsubmission) { 481 list($assignment['submissionstatement'], $assignment['submissionstatementformat']) = 482 external_format_text($adminconfig->submissionstatement, FORMAT_MOODLE, $context->id, 483 'mod_assign', '', 0); 484 } else { // Team submission. 485 // One user can submit for the whole team. 486 if (!empty($adminconfig->submissionstatementteamsubmission) && !$module->requireallteammemberssubmit) { 487 list($assignment['submissionstatement'], $assignment['submissionstatementformat']) = 488 external_format_text($adminconfig->submissionstatementteamsubmission, 489 FORMAT_MOODLE, $context->id, 'mod_assign', '', 0); 490 } else if (!empty($adminconfig->submissionstatementteamsubmissionallsubmit) && 491 $module->requireallteammemberssubmit) { 492 // All team members must submit. 493 list($assignment['submissionstatement'], $assignment['submissionstatementformat']) = 494 external_format_text($adminconfig->submissionstatementteamsubmissionallsubmit, 495 FORMAT_MOODLE, $context->id, 'mod_assign', '', 0); 496 } 497 } 498 } 499 500 $assignmentarray[] = $assignment; 501 } 502 } 503 $coursearray[]= array( 504 'id' => $courses[$id]->id, 505 'fullname' => external_format_string($courses[$id]->fullname, $course->contextid), 506 'shortname' => external_format_string($courses[$id]->shortname, $course->contextid), 507 'timemodified' => $courses[$id]->timemodified, 508 'assignments' => $assignmentarray 509 ); 510 } 511 512 $result = array( 513 'courses' => $coursearray, 514 'warnings' => $warnings 515 ); 516 return $result; 517 } 518 519 /** 520 * Creates an assignment external_single_structure 521 * 522 * @return external_single_structure 523 * @since Moodle 2.4 524 */ 525 private static function get_assignments_assignment_structure() { 526 return new external_single_structure( 527 array( 528 'id' => new external_value(PARAM_INT, 'assignment id'), 529 'cmid' => new external_value(PARAM_INT, 'course module id'), 530 'course' => new external_value(PARAM_INT, 'course id'), 531 'name' => new external_value(PARAM_RAW, 'assignment name'), 532 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'), 533 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'), 534 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'), 535 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'), 536 'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'), 537 'duedate' => new external_value(PARAM_INT, 'assignment due date'), 538 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'), 539 'grade' => new external_value(PARAM_INT, 'grade type'), 540 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'), 541 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'), 542 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'), 543 'gradingduedate' => new external_value(PARAM_INT, 'the expected date for marking the submissions'), 544 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'), 545 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'), 546 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'), 547 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'), 548 'hidegrader' => new external_value(PARAM_INT, 'If enabled, hide grader to student'), 549 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'), 550 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'), 551 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'), 552 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'), 553 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'), 554 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'), 555 'preventsubmissionnotingroup' => new external_value(PARAM_INT, 'Prevent submission not in group', VALUE_OPTIONAL), 556 'submissionstatement' => new external_value(PARAM_RAW, 'Submission statement formatted.', VALUE_OPTIONAL), 557 'submissionstatementformat' => new external_format_value('submissionstatement', VALUE_OPTIONAL), 558 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings'), 559 'intro' => new external_value(PARAM_RAW, 560 'assignment intro, not allways returned because it deppends on the activity configuration', VALUE_OPTIONAL), 561 'introformat' => new external_format_value('intro', VALUE_OPTIONAL), 562 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL), 563 'introattachments' => new external_files('intro attachments files', VALUE_OPTIONAL), 564 ), 'assignment information object'); 565 } 566 567 /** 568 * Creates an assign_plugin_config external_single_structure 569 * 570 * @return external_single_structure 571 * @since Moodle 2.4 572 */ 573 private static function get_assignments_config_structure() { 574 return new external_single_structure( 575 array( 576 'id' => new external_value(PARAM_INT, 'assign_plugin_config id', VALUE_OPTIONAL), 577 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL), 578 'plugin' => new external_value(PARAM_TEXT, 'plugin'), 579 'subtype' => new external_value(PARAM_TEXT, 'subtype'), 580 'name' => new external_value(PARAM_TEXT, 'name'), 581 'value' => new external_value(PARAM_TEXT, 'value') 582 ), 'assignment configuration object' 583 ); 584 } 585 586 /** 587 * Creates a course external_single_structure 588 * 589 * @return external_single_structure 590 * @since Moodle 2.4 591 */ 592 private static function get_assignments_course_structure() { 593 return new external_single_structure( 594 array( 595 'id' => new external_value(PARAM_INT, 'course id'), 596 'fullname' => new external_value(PARAM_RAW, 'course full name'), 597 'shortname' => new external_value(PARAM_RAW, 'course short name'), 598 'timemodified' => new external_value(PARAM_INT, 'last time modified'), 599 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info') 600 ), 'course information object' 601 ); 602 } 603 604 /** 605 * Describes the return value for get_assignments 606 * 607 * @return external_single_structure 608 * @since Moodle 2.4 609 */ 610 public static function get_assignments_returns() { 611 return new external_single_structure( 612 array( 613 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'), 614 'warnings' => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)', 615 'When item is a course then itemid is a course id. When the item is a module then itemid is a module id', 616 'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)') 617 ) 618 ); 619 } 620 621 /** 622 * Return information (files and text fields) for the given plugins in the assignment. 623 * 624 * @param assign $assign the assignment object 625 * @param array $assignplugins array of assignment plugins (submission or feedback) 626 * @param stdClass $item the item object (submission or grade) 627 * @return array an array containing the plugins returned information 628 */ 629 private static function get_plugins_data($assign, $assignplugins, $item) { 630 global $CFG; 631 632 $plugins = array(); 633 $fs = get_file_storage(); 634 635 foreach ($assignplugins as $assignplugin) { 636 637 if (!$assignplugin->is_enabled() or !$assignplugin->is_visible()) { 638 continue; 639 } 640 641 $plugin = array( 642 'name' => $assignplugin->get_name(), 643 'type' => $assignplugin->get_type() 644 ); 645 // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'. 646 $component = $assignplugin->get_subtype().'_'.$assignplugin->get_type(); 647 648 $fileareas = $assignplugin->get_file_areas(); 649 foreach ($fileareas as $filearea => $name) { 650 $fileareainfo = array('area' => $filearea); 651 652 $fileareainfo['files'] = external_util::get_area_files( 653 $assign->get_context()->id, 654 $component, 655 $filearea, 656 $item->id 657 ); 658 659 $plugin['fileareas'][] = $fileareainfo; 660 } 661 662 $editorfields = $assignplugin->get_editor_fields(); 663 foreach ($editorfields as $name => $description) { 664 $editorfieldinfo = array( 665 'name' => $name, 666 'description' => $description, 667 'text' => $assignplugin->get_editor_text($name, $item->id), 668 'format' => $assignplugin->get_editor_format($name, $item->id) 669 ); 670 671 // Now format the text. 672 foreach ($fileareas as $filearea => $name) { 673 list($editorfieldinfo['text'], $editorfieldinfo['format']) = external_format_text( 674 $editorfieldinfo['text'], $editorfieldinfo['format'], $assign->get_context()->id, 675 $component, $filearea, $item->id); 676 } 677 678 $plugin['editorfields'][] = $editorfieldinfo; 679 } 680 $plugins[] = $plugin; 681 } 682 return $plugins; 683 } 684 685 /** 686 * Describes the parameters for get_submissions 687 * 688 * @return external_function_parameters 689 * @since Moodle 2.5 690 */ 691 public static function get_submissions_parameters() { 692 return new external_function_parameters( 693 array( 694 'assignmentids' => new external_multiple_structure( 695 new external_value(PARAM_INT, 'assignment id'), 696 '1 or more assignment ids', 697 VALUE_REQUIRED), 698 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''), 699 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0), 700 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0) 701 ) 702 ); 703 } 704 705 /** 706 * Returns submissions for the requested assignment ids 707 * 708 * @param int[] $assignmentids 709 * @param string $status only return submissions with this status 710 * @param int $since only return submissions with timemodified >= since 711 * @param int $before only return submissions with timemodified <= before 712 * @return array of submissions for each requested assignment 713 * @since Moodle 2.5 714 */ 715 public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) { 716 global $DB, $CFG; 717 718 $params = self::validate_parameters(self::get_submissions_parameters(), 719 array('assignmentids' => $assignmentids, 720 'status' => $status, 721 'since' => $since, 722 'before' => $before)); 723 724 $warnings = array(); 725 $assignments = array(); 726 727 // Check the user is allowed to get the submissions for the assignments requested. 728 $placeholders = array(); 729 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED); 730 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ". 731 "WHERE md.name = :modname AND cm.instance ".$inorequalsql; 732 $placeholders['modname'] = 'assign'; 733 $cms = $DB->get_records_sql($sql, $placeholders); 734 $assigns = array(); 735 foreach ($cms as $cm) { 736 try { 737 $context = context_module::instance($cm->id); 738 self::validate_context($context); 739 $assign = new assign($context, null, null); 740 $assign->require_view_grades(); 741 $assigns[] = $assign; 742 } catch (Exception $e) { 743 $warnings[] = array( 744 'item' => 'assignment', 745 'itemid' => $cm->instance, 746 'warningcode' => '1', 747 'message' => 'No access rights in module context' 748 ); 749 } 750 } 751 752 foreach ($assigns as $assign) { 753 $submissions = array(); 754 $placeholders = array('assignid1' => $assign->get_instance()->id, 755 'assignid2' => $assign->get_instance()->id); 756 757 $submissionmaxattempt = 'SELECT mxs.userid, mxs.groupid, MAX(mxs.attemptnumber) AS maxattempt 758 FROM {assign_submission} mxs 759 WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid, mxs.groupid'; 760 761 $sql = "SELECT mas.id, mas.assignment,mas.userid,". 762 "mas.timecreated,mas.timemodified,mas.status,mas.groupid,mas.attemptnumber ". 763 "FROM {assign_submission} mas ". 764 "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ". 765 "AND mas.groupid = smx.groupid ". 766 "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt"; 767 768 if (!empty($params['status'])) { 769 $placeholders['status'] = $params['status']; 770 $sql = $sql." AND mas.status = :status"; 771 } 772 if (!empty($params['before'])) { 773 $placeholders['since'] = $params['since']; 774 $placeholders['before'] = $params['before']; 775 $sql = $sql." AND mas.timemodified BETWEEN :since AND :before"; 776 } else { 777 $placeholders['since'] = $params['since']; 778 $sql = $sql." AND mas.timemodified >= :since"; 779 } 780 781 $submissionrecords = $DB->get_records_sql($sql, $placeholders); 782 783 if (!empty($submissionrecords)) { 784 $submissionplugins = $assign->get_submission_plugins(); 785 foreach ($submissionrecords as $submissionrecord) { 786 $submission = array( 787 'id' => $submissionrecord->id, 788 'userid' => $submissionrecord->userid, 789 'timecreated' => $submissionrecord->timecreated, 790 'timemodified' => $submissionrecord->timemodified, 791 'status' => $submissionrecord->status, 792 'attemptnumber' => $submissionrecord->attemptnumber, 793 'groupid' => $submissionrecord->groupid, 794 'plugins' => self::get_plugins_data($assign, $submissionplugins, $submissionrecord), 795 'gradingstatus' => $assign->get_grading_status($submissionrecord->userid) 796 ); 797 798 if (($assign->get_instance()->teamsubmission 799 && $assign->can_view_group_submission($submissionrecord->groupid)) 800 || (!$assign->get_instance()->teamsubmission 801 && $assign->can_view_submission($submissionrecord->userid)) 802 ) { 803 $submissions[] = $submission; 804 } 805 } 806 } else { 807 $warnings[] = array( 808 'item' => 'module', 809 'itemid' => $assign->get_instance()->id, 810 'warningcode' => '3', 811 'message' => 'No submissions found' 812 ); 813 } 814 815 $assignments[] = array( 816 'assignmentid' => $assign->get_instance()->id, 817 'submissions' => $submissions 818 ); 819 820 } 821 822 $result = array( 823 'assignments' => $assignments, 824 'warnings' => $warnings 825 ); 826 return $result; 827 } 828 829 /** 830 * Creates an assignment plugin structure. 831 * 832 * @return external_single_structure the plugin structure 833 */ 834 private static function get_plugin_structure() { 835 return new external_single_structure( 836 array( 837 'type' => new external_value(PARAM_TEXT, 'submission plugin type'), 838 'name' => new external_value(PARAM_TEXT, 'submission plugin name'), 839 'fileareas' => new external_multiple_structure( 840 new external_single_structure( 841 array ( 842 'area' => new external_value (PARAM_TEXT, 'file area'), 843 'files' => new external_files('files', VALUE_OPTIONAL), 844 ) 845 ), 'fileareas', VALUE_OPTIONAL 846 ), 847 'editorfields' => new external_multiple_structure( 848 new external_single_structure( 849 array( 850 'name' => new external_value(PARAM_TEXT, 'field name'), 851 'description' => new external_value(PARAM_RAW, 'field description'), 852 'text' => new external_value (PARAM_RAW, 'field value'), 853 'format' => new external_format_value ('text') 854 ) 855 ) 856 , 'editorfields', VALUE_OPTIONAL 857 ) 858 ) 859 ); 860 } 861 862 /** 863 * Creates a submission structure. 864 * 865 * @return external_single_structure the submission structure 866 */ 867 private static function get_submission_structure($required = VALUE_REQUIRED) { 868 return new external_single_structure( 869 array( 870 'id' => new external_value(PARAM_INT, 'submission id'), 871 'userid' => new external_value(PARAM_INT, 'student id'), 872 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'), 873 'timecreated' => new external_value(PARAM_INT, 'submission creation time'), 874 'timemodified' => new external_value(PARAM_INT, 'submission last modified time'), 875 'status' => new external_value(PARAM_TEXT, 'submission status'), 876 'groupid' => new external_value(PARAM_INT, 'group id'), 877 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL), 878 'latest' => new external_value(PARAM_INT, 'latest attempt', VALUE_OPTIONAL), 879 'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'plugins', VALUE_OPTIONAL), 880 'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.', VALUE_OPTIONAL), 881 ), 'submission info', $required 882 ); 883 } 884 885 /** 886 * Creates an assign_submissions external_single_structure 887 * 888 * @return external_single_structure 889 * @since Moodle 2.5 890 */ 891 private static function get_submissions_structure() { 892 return new external_single_structure( 893 array ( 894 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 895 'submissions' => new external_multiple_structure(self::get_submission_structure()) 896 ) 897 ); 898 } 899 900 /** 901 * Describes the get_submissions return value 902 * 903 * @return external_single_structure 904 * @since Moodle 2.5 905 */ 906 public static function get_submissions_returns() { 907 return new external_single_structure( 908 array( 909 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'), 910 'warnings' => new external_warnings() 911 ) 912 ); 913 } 914 915 /** 916 * Describes the parameters for set_user_flags 917 * @return external_function_parameters 918 * @since Moodle 2.6 919 */ 920 public static function set_user_flags_parameters() { 921 return new external_function_parameters( 922 array( 923 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 924 'userflags' => new external_multiple_structure( 925 new external_single_structure( 926 array( 927 'userid' => new external_value(PARAM_INT, 'student id'), 928 'locked' => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL), 929 'mailed' => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL), 930 'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL), 931 'workflowstate' => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL), 932 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL) 933 ) 934 ) 935 ) 936 ) 937 ); 938 } 939 940 /** 941 * Create or update user_flags records 942 * 943 * @param int $assignmentid the assignment for which the userflags are created or updated 944 * @param array $userflags An array of userflags to create or update 945 * @return array containing success or failure information for each record 946 * @since Moodle 2.6 947 */ 948 public static function set_user_flags($assignmentid, $userflags = array()) { 949 global $CFG, $DB; 950 951 $params = self::validate_parameters(self::set_user_flags_parameters(), 952 array('assignmentid' => $assignmentid, 953 'userflags' => $userflags)); 954 955 // Load assignment if it exists and if the user has the capability. 956 list($assign, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 957 require_capability('mod/assign:grade', $context); 958 959 $results = array(); 960 foreach ($params['userflags'] as $userflag) { 961 $success = true; 962 $result = array(); 963 964 $record = $assign->get_user_flags($userflag['userid'], false); 965 if ($record) { 966 if (isset($userflag['locked'])) { 967 $record->locked = $userflag['locked']; 968 } 969 if (isset($userflag['mailed'])) { 970 $record->mailed = $userflag['mailed']; 971 } 972 if (isset($userflag['extensionduedate'])) { 973 $record->extensionduedate = $userflag['extensionduedate']; 974 } 975 if (isset($userflag['workflowstate'])) { 976 $record->workflowstate = $userflag['workflowstate']; 977 } 978 if (isset($userflag['allocatedmarker'])) { 979 $record->allocatedmarker = $userflag['allocatedmarker']; 980 } 981 if ($assign->update_user_flags($record)) { 982 $result['id'] = $record->id; 983 $result['userid'] = $userflag['userid']; 984 } else { 985 $result['id'] = $record->id; 986 $result['userid'] = $userflag['userid']; 987 $result['errormessage'] = 'Record created but values could not be set'; 988 } 989 } else { 990 $record = $assign->get_user_flags($userflag['userid'], true); 991 $setfields = isset($userflag['locked']) 992 || isset($userflag['mailed']) 993 || isset($userflag['extensionduedate']) 994 || isset($userflag['workflowstate']) 995 || isset($userflag['allocatedmarker']); 996 if ($record) { 997 if ($setfields) { 998 if (isset($userflag['locked'])) { 999 $record->locked = $userflag['locked']; 1000 } 1001 if (isset($userflag['mailed'])) { 1002 $record->mailed = $userflag['mailed']; 1003 } 1004 if (isset($userflag['extensionduedate'])) { 1005 $record->extensionduedate = $userflag['extensionduedate']; 1006 } 1007 if (isset($userflag['workflowstate'])) { 1008 $record->workflowstate = $userflag['workflowstate']; 1009 } 1010 if (isset($userflag['allocatedmarker'])) { 1011 $record->allocatedmarker = $userflag['allocatedmarker']; 1012 } 1013 if ($assign->update_user_flags($record)) { 1014 $result['id'] = $record->id; 1015 $result['userid'] = $userflag['userid']; 1016 } else { 1017 $result['id'] = $record->id; 1018 $result['userid'] = $userflag['userid']; 1019 $result['errormessage'] = 'Record created but values could not be set'; 1020 } 1021 } else { 1022 $result['id'] = $record->id; 1023 $result['userid'] = $userflag['userid']; 1024 } 1025 } else { 1026 $result['id'] = -1; 1027 $result['userid'] = $userflag['userid']; 1028 $result['errormessage'] = 'Record could not be created'; 1029 } 1030 } 1031 1032 $results[] = $result; 1033 } 1034 return $results; 1035 } 1036 1037 /** 1038 * Describes the set_user_flags return value 1039 * @return external_multiple_structure 1040 * @since Moodle 2.6 1041 */ 1042 public static function set_user_flags_returns() { 1043 return new external_multiple_structure( 1044 new external_single_structure( 1045 array( 1046 'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'), 1047 'userid' => new external_value(PARAM_INT, 'userid of record'), 1048 'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL) 1049 ) 1050 ) 1051 ); 1052 } 1053 1054 /** 1055 * Describes the parameters for get_user_flags 1056 * @return external_function_parameters 1057 * @since Moodle 2.6 1058 */ 1059 public static function get_user_flags_parameters() { 1060 return new external_function_parameters( 1061 array( 1062 'assignmentids' => new external_multiple_structure( 1063 new external_value(PARAM_INT, 'assignment id'), 1064 '1 or more assignment ids', 1065 VALUE_REQUIRED) 1066 ) 1067 ); 1068 } 1069 1070 /** 1071 * Returns user flag information from assign_user_flags for the requested assignment ids 1072 * @param int[] $assignmentids 1073 * @return array of user flag records for each requested assignment 1074 * @since Moodle 2.6 1075 */ 1076 public static function get_user_flags($assignmentids) { 1077 global $DB; 1078 $params = self::validate_parameters(self::get_user_flags_parameters(), 1079 array('assignmentids' => $assignmentids)); 1080 1081 $assignments = array(); 1082 $warnings = array(); 1083 $requestedassignmentids = $params['assignmentids']; 1084 1085 // Check the user is allowed to get the user flags for the assignments requested. 1086 $placeholders = array(); 1087 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); 1088 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ". 1089 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids; 1090 $placeholders['modname'] = 'assign'; 1091 $cms = $DB->get_records_sql($sql, $placeholders); 1092 foreach ($cms as $cm) { 1093 try { 1094 $context = context_module::instance($cm->id); 1095 self::validate_context($context); 1096 require_capability('mod/assign:grade', $context); 1097 } catch (Exception $e) { 1098 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance)); 1099 $warning = array(); 1100 $warning['item'] = 'assignment'; 1101 $warning['itemid'] = $cm->instance; 1102 $warning['warningcode'] = '1'; 1103 $warning['message'] = 'No access rights in module context'; 1104 $warnings[] = $warning; 1105 } 1106 } 1107 1108 // Create the query and populate an array of assign_user_flags records from the recordset results. 1109 if (count ($requestedassignmentids) > 0) { 1110 $placeholders = array(); 1111 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); 1112 1113 $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,". 1114 "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ". 1115 "FROM {assign_user_flags} auf ". 1116 "WHERE auf.assignment ".$inorequalsql. 1117 " ORDER BY auf.assignment, auf.id"; 1118 1119 $rs = $DB->get_recordset_sql($sql, $placeholders); 1120 $currentassignmentid = null; 1121 $assignment = null; 1122 foreach ($rs as $rd) { 1123 $userflag = array(); 1124 $userflag['id'] = $rd->id; 1125 $userflag['userid'] = $rd->userid; 1126 $userflag['locked'] = $rd->locked; 1127 $userflag['mailed'] = $rd->mailed; 1128 $userflag['extensionduedate'] = $rd->extensionduedate; 1129 $userflag['workflowstate'] = $rd->workflowstate; 1130 $userflag['allocatedmarker'] = $rd->allocatedmarker; 1131 1132 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) { 1133 if (!is_null($assignment)) { 1134 $assignments[] = $assignment; 1135 } 1136 $assignment = array(); 1137 $assignment['assignmentid'] = $rd->assignment; 1138 $assignment['userflags'] = array(); 1139 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment)); 1140 } 1141 $assignment['userflags'][] = $userflag; 1142 1143 $currentassignmentid = $rd->assignment; 1144 } 1145 if (!is_null($assignment)) { 1146 $assignments[] = $assignment; 1147 } 1148 $rs->close(); 1149 1150 } 1151 1152 foreach ($requestedassignmentids as $assignmentid) { 1153 $warning = array(); 1154 $warning['item'] = 'assignment'; 1155 $warning['itemid'] = $assignmentid; 1156 $warning['warningcode'] = '3'; 1157 $warning['message'] = 'No user flags found'; 1158 $warnings[] = $warning; 1159 } 1160 1161 $result = array(); 1162 $result['assignments'] = $assignments; 1163 $result['warnings'] = $warnings; 1164 return $result; 1165 } 1166 1167 /** 1168 * Creates an assign_user_flags external_single_structure 1169 * @return external_single_structure 1170 * @since Moodle 2.6 1171 */ 1172 private static function assign_user_flags() { 1173 return new external_single_structure( 1174 array ( 1175 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 1176 'userflags' => new external_multiple_structure(new external_single_structure( 1177 array( 1178 'id' => new external_value(PARAM_INT, 'user flag id'), 1179 'userid' => new external_value(PARAM_INT, 'student id'), 1180 'locked' => new external_value(PARAM_INT, 'locked'), 1181 'mailed' => new external_value(PARAM_INT, 'mailed'), 1182 'extensionduedate' => new external_value(PARAM_INT, 'extension due date'), 1183 'workflowstate' => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL), 1184 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker') 1185 ) 1186 ) 1187 ) 1188 ) 1189 ); 1190 } 1191 1192 /** 1193 * Describes the get_user_flags return value 1194 * @return external_single_structure 1195 * @since Moodle 2.6 1196 */ 1197 public static function get_user_flags_returns() { 1198 return new external_single_structure( 1199 array( 1200 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'), 1201 'warnings' => new external_warnings('item is always \'assignment\'', 1202 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id', 1203 'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)') 1204 ) 1205 ); 1206 } 1207 1208 /** 1209 * Describes the parameters for get_user_mappings 1210 * @return external_function_parameters 1211 * @since Moodle 2.6 1212 */ 1213 public static function get_user_mappings_parameters() { 1214 return new external_function_parameters( 1215 array( 1216 'assignmentids' => new external_multiple_structure( 1217 new external_value(PARAM_INT, 'assignment id'), 1218 '1 or more assignment ids', 1219 VALUE_REQUIRED) 1220 ) 1221 ); 1222 } 1223 1224 /** 1225 * Returns user mapping information from assign_user_mapping for the requested assignment ids 1226 * @param int[] $assignmentids 1227 * @return array of user mapping records for each requested assignment 1228 * @since Moodle 2.6 1229 */ 1230 public static function get_user_mappings($assignmentids) { 1231 global $DB; 1232 $params = self::validate_parameters(self::get_user_mappings_parameters(), 1233 array('assignmentids' => $assignmentids)); 1234 1235 $assignments = array(); 1236 $warnings = array(); 1237 $requestedassignmentids = $params['assignmentids']; 1238 1239 // Check the user is allowed to get the mappings for the assignments requested. 1240 $placeholders = array(); 1241 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); 1242 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ". 1243 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids; 1244 $placeholders['modname'] = 'assign'; 1245 $cms = $DB->get_records_sql($sql, $placeholders); 1246 foreach ($cms as $cm) { 1247 try { 1248 $context = context_module::instance($cm->id); 1249 self::validate_context($context); 1250 require_capability('mod/assign:revealidentities', $context); 1251 } catch (Exception $e) { 1252 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance)); 1253 $warning = array(); 1254 $warning['item'] = 'assignment'; 1255 $warning['itemid'] = $cm->instance; 1256 $warning['warningcode'] = '1'; 1257 $warning['message'] = 'No access rights in module context'; 1258 $warnings[] = $warning; 1259 } 1260 } 1261 1262 // Create the query and populate an array of assign_user_mapping records from the recordset results. 1263 if (count ($requestedassignmentids) > 0) { 1264 $placeholders = array(); 1265 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); 1266 1267 $sql = "SELECT aum.id,aum.assignment,aum.userid ". 1268 "FROM {assign_user_mapping} aum ". 1269 "WHERE aum.assignment ".$inorequalsql. 1270 " ORDER BY aum.assignment, aum.id"; 1271 1272 $rs = $DB->get_recordset_sql($sql, $placeholders); 1273 $currentassignmentid = null; 1274 $assignment = null; 1275 foreach ($rs as $rd) { 1276 $mapping = array(); 1277 $mapping['id'] = $rd->id; 1278 $mapping['userid'] = $rd->userid; 1279 1280 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) { 1281 if (!is_null($assignment)) { 1282 $assignments[] = $assignment; 1283 } 1284 $assignment = array(); 1285 $assignment['assignmentid'] = $rd->assignment; 1286 $assignment['mappings'] = array(); 1287 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment)); 1288 } 1289 $assignment['mappings'][] = $mapping; 1290 1291 $currentassignmentid = $rd->assignment; 1292 } 1293 if (!is_null($assignment)) { 1294 $assignments[] = $assignment; 1295 } 1296 $rs->close(); 1297 1298 } 1299 1300 foreach ($requestedassignmentids as $assignmentid) { 1301 $warning = array(); 1302 $warning['item'] = 'assignment'; 1303 $warning['itemid'] = $assignmentid; 1304 $warning['warningcode'] = '3'; 1305 $warning['message'] = 'No mappings found'; 1306 $warnings[] = $warning; 1307 } 1308 1309 $result = array(); 1310 $result['assignments'] = $assignments; 1311 $result['warnings'] = $warnings; 1312 return $result; 1313 } 1314 1315 /** 1316 * Creates an assign_user_mappings external_single_structure 1317 * @return external_single_structure 1318 * @since Moodle 2.6 1319 */ 1320 private static function assign_user_mappings() { 1321 return new external_single_structure( 1322 array ( 1323 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 1324 'mappings' => new external_multiple_structure(new external_single_structure( 1325 array( 1326 'id' => new external_value(PARAM_INT, 'user mapping id'), 1327 'userid' => new external_value(PARAM_INT, 'student id') 1328 ) 1329 ) 1330 ) 1331 ) 1332 ); 1333 } 1334 1335 /** 1336 * Describes the get_user_mappings return value 1337 * @return external_single_structure 1338 * @since Moodle 2.6 1339 */ 1340 public static function get_user_mappings_returns() { 1341 return new external_single_structure( 1342 array( 1343 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'), 1344 'warnings' => new external_warnings('item is always \'assignment\'', 1345 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id', 1346 'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)') 1347 ) 1348 ); 1349 } 1350 1351 /** 1352 * Describes the parameters for lock_submissions 1353 * @return external_function_parameters 1354 * @since Moodle 2.6 1355 */ 1356 public static function lock_submissions_parameters() { 1357 return new external_function_parameters( 1358 array( 1359 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 1360 'userids' => new external_multiple_structure( 1361 new external_value(PARAM_INT, 'user id'), 1362 '1 or more user ids', 1363 VALUE_REQUIRED), 1364 ) 1365 ); 1366 } 1367 1368 /** 1369 * Locks (prevent updates to) submissions in this assignment. 1370 * 1371 * @param int $assignmentid The id of the assignment 1372 * @param array $userids Array of user ids to lock 1373 * @return array of warnings for each submission that could not be locked. 1374 * @since Moodle 2.6 1375 */ 1376 public static function lock_submissions($assignmentid, $userids) { 1377 global $CFG; 1378 1379 $params = self::validate_parameters(self::lock_submissions_parameters(), 1380 array('assignmentid' => $assignmentid, 1381 'userids' => $userids)); 1382 1383 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1384 1385 $warnings = array(); 1386 foreach ($params['userids'] as $userid) { 1387 if (!$assignment->lock_submission($userid)) { 1388 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid']; 1389 $warnings[] = self::generate_warning($params['assignmentid'], 1390 'couldnotlock', 1391 $detail); 1392 } 1393 } 1394 1395 return $warnings; 1396 } 1397 1398 /** 1399 * Describes the return value for lock_submissions 1400 * 1401 * @return external_single_structure 1402 * @since Moodle 2.6 1403 */ 1404 public static function lock_submissions_returns() { 1405 return new external_warnings(); 1406 } 1407 1408 /** 1409 * Describes the parameters for revert_submissions_to_draft 1410 * @return external_function_parameters 1411 * @since Moodle 2.6 1412 */ 1413 public static function revert_submissions_to_draft_parameters() { 1414 return new external_function_parameters( 1415 array( 1416 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 1417 'userids' => new external_multiple_structure( 1418 new external_value(PARAM_INT, 'user id'), 1419 '1 or more user ids', 1420 VALUE_REQUIRED), 1421 ) 1422 ); 1423 } 1424 1425 /** 1426 * Reverts a list of user submissions to draft for a single assignment. 1427 * 1428 * @param int $assignmentid The id of the assignment 1429 * @param array $userids Array of user ids to revert 1430 * @return array of warnings for each submission that could not be reverted. 1431 * @since Moodle 2.6 1432 */ 1433 public static function revert_submissions_to_draft($assignmentid, $userids) { 1434 global $CFG; 1435 1436 $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(), 1437 array('assignmentid' => $assignmentid, 1438 'userids' => $userids)); 1439 1440 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1441 1442 $warnings = array(); 1443 foreach ($params['userids'] as $userid) { 1444 if (!$assignment->revert_to_draft($userid)) { 1445 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid']; 1446 $warnings[] = self::generate_warning($params['assignmentid'], 1447 'couldnotrevert', 1448 $detail); 1449 } 1450 } 1451 1452 return $warnings; 1453 } 1454 1455 /** 1456 * Describes the return value for revert_submissions_to_draft 1457 * 1458 * @return external_single_structure 1459 * @since Moodle 2.6 1460 */ 1461 public static function revert_submissions_to_draft_returns() { 1462 return new external_warnings(); 1463 } 1464 1465 /** 1466 * Describes the parameters for unlock_submissions 1467 * @return external_function_parameters 1468 * @since Moodle 2.6 1469 */ 1470 public static function unlock_submissions_parameters() { 1471 return new external_function_parameters( 1472 array( 1473 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 1474 'userids' => new external_multiple_structure( 1475 new external_value(PARAM_INT, 'user id'), 1476 '1 or more user ids', 1477 VALUE_REQUIRED), 1478 ) 1479 ); 1480 } 1481 1482 /** 1483 * Locks (prevent updates to) submissions in this assignment. 1484 * 1485 * @param int $assignmentid The id of the assignment 1486 * @param array $userids Array of user ids to lock 1487 * @return array of warnings for each submission that could not be locked. 1488 * @since Moodle 2.6 1489 */ 1490 public static function unlock_submissions($assignmentid, $userids) { 1491 global $CFG; 1492 1493 $params = self::validate_parameters(self::unlock_submissions_parameters(), 1494 array('assignmentid' => $assignmentid, 1495 'userids' => $userids)); 1496 1497 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1498 1499 $warnings = array(); 1500 foreach ($params['userids'] as $userid) { 1501 if (!$assignment->unlock_submission($userid)) { 1502 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid']; 1503 $warnings[] = self::generate_warning($params['assignmentid'], 1504 'couldnotunlock', 1505 $detail); 1506 } 1507 } 1508 1509 return $warnings; 1510 } 1511 1512 /** 1513 * Describes the return value for unlock_submissions 1514 * 1515 * @return external_single_structure 1516 * @since Moodle 2.6 1517 */ 1518 public static function unlock_submissions_returns() { 1519 return new external_warnings(); 1520 } 1521 1522 /** 1523 * Describes the parameters for submit_grading_form webservice. 1524 * @return external_function_parameters 1525 * @since Moodle 3.1 1526 */ 1527 public static function submit_grading_form_parameters() { 1528 return new external_function_parameters( 1529 array( 1530 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 1531 'userid' => new external_value(PARAM_INT, 'The user id the submission belongs to'), 1532 'jsonformdata' => new external_value(PARAM_RAW, 'The data from the grading form, encoded as a json array') 1533 ) 1534 ); 1535 } 1536 1537 /** 1538 * Submit the logged in users assignment for grading. 1539 * 1540 * @param int $assignmentid The id of the assignment 1541 * @param int $userid The id of the user the submission belongs to. 1542 * @param string $jsonformdata The data from the form, encoded as a json array. 1543 * @return array of warnings to indicate any errors. 1544 * @since Moodle 3.1 1545 */ 1546 public static function submit_grading_form($assignmentid, $userid, $jsonformdata) { 1547 global $CFG, $USER; 1548 1549 require_once($CFG->dirroot . '/mod/assign/locallib.php'); 1550 require_once($CFG->dirroot . '/mod/assign/gradeform.php'); 1551 1552 $params = self::validate_parameters(self::submit_grading_form_parameters(), 1553 array( 1554 'assignmentid' => $assignmentid, 1555 'userid' => $userid, 1556 'jsonformdata' => $jsonformdata 1557 )); 1558 1559 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1560 1561 $serialiseddata = json_decode($params['jsonformdata']); 1562 1563 $data = array(); 1564 parse_str($serialiseddata, $data); 1565 1566 $warnings = array(); 1567 1568 $options = array( 1569 'userid' => $params['userid'], 1570 'attemptnumber' => $data['attemptnumber'], 1571 'rownum' => 0, 1572 'gradingpanel' => true 1573 ); 1574 1575 if (WS_SERVER) { 1576 // Assume form submission if coming from WS. 1577 $USER->ignoresesskey = true; 1578 $data['_qf__mod_assign_grade_form_'.$params['userid']] = 1; 1579 } 1580 1581 $customdata = (object) $data; 1582 $formparams = array($assignment, $customdata, $options); 1583 1584 // Data is injected into the form by the last param for the constructor. 1585 $mform = new mod_assign_grade_form(null, $formparams, 'post', '', null, true, $data); 1586 $validateddata = $mform->get_data(); 1587 1588 if ($validateddata) { 1589 $assignment->save_grade($params['userid'], $validateddata); 1590 } else { 1591 $warnings[] = self::generate_warning($params['assignmentid'], 1592 'couldnotsavegrade', 1593 'Form validation failed.'); 1594 } 1595 1596 1597 return $warnings; 1598 } 1599 1600 /** 1601 * Describes the return for submit_grading_form 1602 * @return external_function_parameters 1603 * @since Moodle 3.1 1604 */ 1605 public static function submit_grading_form_returns() { 1606 return new external_warnings(); 1607 } 1608 1609 /** 1610 * Describes the parameters for submit_for_grading 1611 * @return external_function_parameters 1612 * @since Moodle 2.6 1613 */ 1614 public static function submit_for_grading_parameters() { 1615 return new external_function_parameters( 1616 array( 1617 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 1618 'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement') 1619 ) 1620 ); 1621 } 1622 1623 /** 1624 * Submit the logged in users assignment for grading. 1625 * 1626 * @param int $assignmentid The id of the assignment 1627 * @return array of warnings to indicate any errors. 1628 * @since Moodle 2.6 1629 */ 1630 public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) { 1631 global $CFG, $USER; 1632 1633 $params = self::validate_parameters(self::submit_for_grading_parameters(), 1634 array('assignmentid' => $assignmentid, 1635 'acceptsubmissionstatement' => $acceptsubmissionstatement)); 1636 1637 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1638 1639 $warnings = array(); 1640 $data = new stdClass(); 1641 $data->submissionstatement = $params['acceptsubmissionstatement']; 1642 $notices = array(); 1643 1644 if (!$assignment->submit_for_grading($data, $notices)) { 1645 $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices); 1646 $warnings[] = self::generate_warning($params['assignmentid'], 1647 'couldnotsubmitforgrading', 1648 $detail); 1649 } 1650 1651 return $warnings; 1652 } 1653 1654 /** 1655 * Describes the return value for submit_for_grading 1656 * 1657 * @return external_single_structure 1658 * @since Moodle 2.6 1659 */ 1660 public static function submit_for_grading_returns() { 1661 return new external_warnings(); 1662 } 1663 1664 /** 1665 * Describes the parameters for save_user_extensions 1666 * @return external_function_parameters 1667 * @since Moodle 2.6 1668 */ 1669 public static function save_user_extensions_parameters() { 1670 return new external_function_parameters( 1671 array( 1672 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 1673 'userids' => new external_multiple_structure( 1674 new external_value(PARAM_INT, 'user id'), 1675 '1 or more user ids', 1676 VALUE_REQUIRED), 1677 'dates' => new external_multiple_structure( 1678 new external_value(PARAM_INT, 'dates'), 1679 '1 or more extension dates (timestamp)', 1680 VALUE_REQUIRED), 1681 ) 1682 ); 1683 } 1684 1685 /** 1686 * Grant extension dates to students for an assignment. 1687 * 1688 * @param int $assignmentid The id of the assignment 1689 * @param array $userids Array of user ids to grant extensions to 1690 * @param array $dates Array of extension dates 1691 * @return array of warnings for each extension date that could not be granted 1692 * @since Moodle 2.6 1693 */ 1694 public static function save_user_extensions($assignmentid, $userids, $dates) { 1695 global $CFG; 1696 1697 $params = self::validate_parameters(self::save_user_extensions_parameters(), 1698 array('assignmentid' => $assignmentid, 1699 'userids' => $userids, 1700 'dates' => $dates)); 1701 1702 if (count($params['userids']) != count($params['dates'])) { 1703 $detail = 'Length of userids and dates parameters differ.'; 1704 $warnings[] = self::generate_warning($params['assignmentid'], 1705 'invalidparameters', 1706 $detail); 1707 1708 return $warnings; 1709 } 1710 1711 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1712 1713 $warnings = array(); 1714 foreach ($params['userids'] as $idx => $userid) { 1715 $duedate = $params['dates'][$idx]; 1716 if (!$assignment->save_user_extension($userid, $duedate)) { 1717 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate; 1718 $warnings[] = self::generate_warning($params['assignmentid'], 1719 'couldnotgrantextensions', 1720 $detail); 1721 } 1722 } 1723 1724 return $warnings; 1725 } 1726 1727 /** 1728 * Describes the return value for save_user_extensions 1729 * 1730 * @return external_single_structure 1731 * @since Moodle 2.6 1732 */ 1733 public static function save_user_extensions_returns() { 1734 return new external_warnings(); 1735 } 1736 1737 /** 1738 * Describes the parameters for reveal_identities 1739 * @return external_function_parameters 1740 * @since Moodle 2.6 1741 */ 1742 public static function reveal_identities_parameters() { 1743 return new external_function_parameters( 1744 array( 1745 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on') 1746 ) 1747 ); 1748 } 1749 1750 /** 1751 * Reveal the identities of anonymous students to markers for a single assignment. 1752 * 1753 * @param int $assignmentid The id of the assignment 1754 * @return array of warnings to indicate any errors. 1755 * @since Moodle 2.6 1756 */ 1757 public static function reveal_identities($assignmentid) { 1758 global $CFG, $USER; 1759 1760 $params = self::validate_parameters(self::reveal_identities_parameters(), 1761 array('assignmentid' => $assignmentid)); 1762 1763 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1764 1765 $warnings = array(); 1766 if (!$assignment->reveal_identities()) { 1767 $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid']; 1768 $warnings[] = self::generate_warning($params['assignmentid'], 1769 'couldnotrevealidentities', 1770 $detail); 1771 } 1772 1773 return $warnings; 1774 } 1775 1776 /** 1777 * Describes the return value for reveal_identities 1778 * 1779 * @return external_single_structure 1780 * @since Moodle 2.6 1781 */ 1782 public static function reveal_identities_returns() { 1783 return new external_warnings(); 1784 } 1785 1786 /** 1787 * Describes the parameters for save_submission 1788 * @return external_function_parameters 1789 * @since Moodle 2.6 1790 */ 1791 public static function save_submission_parameters() { 1792 global $CFG; 1793 $instance = new assign(null, null, null); 1794 $pluginsubmissionparams = array(); 1795 1796 foreach ($instance->get_submission_plugins() as $plugin) { 1797 if ($plugin->is_visible()) { 1798 $pluginparams = $plugin->get_external_parameters(); 1799 if (!empty($pluginparams)) { 1800 $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams); 1801 } 1802 } 1803 } 1804 1805 return new external_function_parameters( 1806 array( 1807 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 1808 'plugindata' => new external_single_structure( 1809 $pluginsubmissionparams 1810 ) 1811 ) 1812 ); 1813 } 1814 1815 /** 1816 * Save a student submission for a single assignment 1817 * 1818 * @param int $assignmentid The id of the assignment 1819 * @param array $plugindata - The submitted data for plugins 1820 * @return array of warnings to indicate any errors 1821 * @since Moodle 2.6 1822 */ 1823 public static function save_submission($assignmentid, $plugindata) { 1824 global $CFG, $USER; 1825 1826 $params = self::validate_parameters(self::save_submission_parameters(), 1827 array('assignmentid' => $assignmentid, 1828 'plugindata' => $plugindata)); 1829 1830 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1831 1832 $notices = array(); 1833 1834 $assignment->update_effective_access($USER->id); 1835 if (!$assignment->submissions_open($USER->id)) { 1836 $notices[] = get_string('duedatereached', 'assign'); 1837 } else { 1838 $submissiondata = (object)$params['plugindata']; 1839 $assignment->save_submission($submissiondata, $notices); 1840 } 1841 1842 $warnings = array(); 1843 foreach ($notices as $notice) { 1844 $warnings[] = self::generate_warning($params['assignmentid'], 1845 'couldnotsavesubmission', 1846 $notice); 1847 } 1848 1849 return $warnings; 1850 } 1851 1852 /** 1853 * Describes the return value for save_submission 1854 * 1855 * @return external_single_structure 1856 * @since Moodle 2.6 1857 */ 1858 public static function save_submission_returns() { 1859 return new external_warnings(); 1860 } 1861 1862 /** 1863 * Describes the parameters for save_grade 1864 * @return external_function_parameters 1865 * @since Moodle 2.6 1866 */ 1867 public static function save_grade_parameters() { 1868 global $CFG; 1869 require_once("$CFG->dirroot/grade/grading/lib.php"); 1870 $instance = new assign(null, null, null); 1871 $pluginfeedbackparams = array(); 1872 1873 foreach ($instance->get_feedback_plugins() as $plugin) { 1874 if ($plugin->is_visible()) { 1875 $pluginparams = $plugin->get_external_parameters(); 1876 if (!empty($pluginparams)) { 1877 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams); 1878 } 1879 } 1880 } 1881 1882 $advancedgradingdata = array(); 1883 $methods = array_keys(grading_manager::available_methods(false)); 1884 foreach ($methods as $method) { 1885 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php'); 1886 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details'); 1887 if (!empty($details)) { 1888 $items = array(); 1889 foreach ($details as $key => $value) { 1890 $value->required = VALUE_OPTIONAL; 1891 unset($value->content->keys['id']); 1892 $items[$key] = new external_multiple_structure (new external_single_structure( 1893 array( 1894 'criterionid' => new external_value(PARAM_INT, 'criterion id'), 1895 'fillings' => $value 1896 ) 1897 )); 1898 } 1899 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL); 1900 } 1901 } 1902 1903 return new external_function_parameters( 1904 array( 1905 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 1906 'userid' => new external_value(PARAM_INT, 'The student id to operate on'), 1907 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'), 1908 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'), 1909 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'), 1910 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'), 1911 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' . 1912 'to all members ' . 1913 'of the group (for group assignments).'), 1914 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()), 1915 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data', 1916 VALUE_DEFAULT, array()) 1917 ) 1918 ); 1919 } 1920 1921 /** 1922 * Save a student grade for a single assignment. 1923 * 1924 * @param int $assignmentid The id of the assignment 1925 * @param int $userid The id of the user 1926 * @param float $grade The grade (ignored if the assignment uses advanced grading) 1927 * @param int $attemptnumber The attempt number 1928 * @param bool $addattempt Allow another attempt 1929 * @param string $workflowstate New workflow state 1930 * @param bool $applytoall Apply the grade to all members of the group 1931 * @param array $plugindata Custom data used by plugins 1932 * @param array $advancedgradingdata Advanced grading data 1933 * @return null 1934 * @since Moodle 2.6 1935 */ 1936 public static function save_grade($assignmentid, 1937 $userid, 1938 $grade, 1939 $attemptnumber, 1940 $addattempt, 1941 $workflowstate, 1942 $applytoall, 1943 $plugindata = array(), 1944 $advancedgradingdata = array()) { 1945 global $CFG, $USER; 1946 1947 $params = self::validate_parameters(self::save_grade_parameters(), 1948 array('assignmentid' => $assignmentid, 1949 'userid' => $userid, 1950 'grade' => $grade, 1951 'attemptnumber' => $attemptnumber, 1952 'workflowstate' => $workflowstate, 1953 'addattempt' => $addattempt, 1954 'applytoall' => $applytoall, 1955 'plugindata' => $plugindata, 1956 'advancedgradingdata' => $advancedgradingdata)); 1957 1958 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 1959 1960 $gradedata = (object)$params['plugindata']; 1961 1962 $gradedata->addattempt = $params['addattempt']; 1963 $gradedata->attemptnumber = $params['attemptnumber']; 1964 $gradedata->workflowstate = $params['workflowstate']; 1965 $gradedata->applytoall = $params['applytoall']; 1966 $gradedata->grade = $params['grade']; 1967 1968 if (!empty($params['advancedgradingdata'])) { 1969 $advancedgrading = array(); 1970 $criteria = reset($params['advancedgradingdata']); 1971 foreach ($criteria as $key => $criterion) { 1972 $details = array(); 1973 foreach ($criterion as $value) { 1974 foreach ($value['fillings'] as $filling) { 1975 $details[$value['criterionid']] = $filling; 1976 } 1977 } 1978 $advancedgrading[$key] = $details; 1979 } 1980 $gradedata->advancedgrading = $advancedgrading; 1981 } 1982 1983 $assignment->save_grade($params['userid'], $gradedata); 1984 1985 return null; 1986 } 1987 1988 /** 1989 * Describes the return value for save_grade 1990 * 1991 * @return external_single_structure 1992 * @since Moodle 2.6 1993 */ 1994 public static function save_grade_returns() { 1995 return null; 1996 } 1997 1998 /** 1999 * Describes the parameters for save_grades 2000 * @return external_function_parameters 2001 * @since Moodle 2.7 2002 */ 2003 public static function save_grades_parameters() { 2004 global $CFG; 2005 require_once("$CFG->dirroot/grade/grading/lib.php"); 2006 $instance = new assign(null, null, null); 2007 $pluginfeedbackparams = array(); 2008 2009 foreach ($instance->get_feedback_plugins() as $plugin) { 2010 if ($plugin->is_visible()) { 2011 $pluginparams = $plugin->get_external_parameters(); 2012 if (!empty($pluginparams)) { 2013 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams); 2014 } 2015 } 2016 } 2017 2018 $advancedgradingdata = array(); 2019 $methods = array_keys(grading_manager::available_methods(false)); 2020 foreach ($methods as $method) { 2021 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php'); 2022 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details'); 2023 if (!empty($details)) { 2024 $items = array(); 2025 foreach ($details as $key => $value) { 2026 $value->required = VALUE_OPTIONAL; 2027 unset($value->content->keys['id']); 2028 $items[$key] = new external_multiple_structure (new external_single_structure( 2029 array( 2030 'criterionid' => new external_value(PARAM_INT, 'criterion id'), 2031 'fillings' => $value 2032 ) 2033 )); 2034 } 2035 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL); 2036 } 2037 } 2038 2039 return new external_function_parameters( 2040 array( 2041 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 2042 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' . 2043 'to all members ' . 2044 'of the group (for group assignments).'), 2045 'grades' => new external_multiple_structure( 2046 new external_single_structure( 2047 array ( 2048 'userid' => new external_value(PARAM_INT, 'The student id to operate on'), 2049 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '. 2050 'Ignored if advanced grading used'), 2051 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'), 2052 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'), 2053 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'), 2054 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', 2055 VALUE_DEFAULT, array()), 2056 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data', 2057 VALUE_DEFAULT, array()) 2058 ) 2059 ) 2060 ) 2061 ) 2062 ); 2063 } 2064 2065 /** 2066 * Save multiple student grades for a single assignment. 2067 * 2068 * @param int $assignmentid The id of the assignment 2069 * @param boolean $applytoall If set to true and this is a team assignment, 2070 * apply the grade to all members of the group 2071 * @param array $grades grade data for one or more students that includes 2072 * userid - The id of the student being graded 2073 * grade - The grade (ignored if the assignment uses advanced grading) 2074 * attemptnumber - The attempt number 2075 * addattempt - Allow another attempt 2076 * workflowstate - New workflow state 2077 * plugindata - Custom data used by plugins 2078 * advancedgradingdata - Optional Advanced grading data 2079 * @throws invalid_parameter_exception if multiple grades are supplied for 2080 * a team assignment that has $applytoall set to true 2081 * @return null 2082 * @since Moodle 2.7 2083 */ 2084 public static function save_grades($assignmentid, $applytoall, $grades) { 2085 global $CFG, $USER; 2086 2087 $params = self::validate_parameters(self::save_grades_parameters(), 2088 array('assignmentid' => $assignmentid, 2089 'applytoall' => $applytoall, 2090 'grades' => $grades)); 2091 2092 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 2093 2094 if ($assignment->get_instance()->teamsubmission && $params['applytoall']) { 2095 // Check that only 1 user per submission group is provided. 2096 $groupids = array(); 2097 foreach ($params['grades'] as $gradeinfo) { 2098 $group = $assignment->get_submission_group($gradeinfo['userid']); 2099 if (in_array($group->id, $groupids)) { 2100 throw new invalid_parameter_exception('Multiple grades for the same team have been supplied ' 2101 .' this is not permitted when the applytoall flag is set'); 2102 } else { 2103 $groupids[] = $group->id; 2104 } 2105 } 2106 } 2107 2108 foreach ($params['grades'] as $gradeinfo) { 2109 $gradedata = (object)$gradeinfo['plugindata']; 2110 $gradedata->addattempt = $gradeinfo['addattempt']; 2111 $gradedata->attemptnumber = $gradeinfo['attemptnumber']; 2112 $gradedata->workflowstate = $gradeinfo['workflowstate']; 2113 $gradedata->applytoall = $params['applytoall']; 2114 $gradedata->grade = $gradeinfo['grade']; 2115 2116 if (!empty($gradeinfo['advancedgradingdata'])) { 2117 $advancedgrading = array(); 2118 $criteria = reset($gradeinfo['advancedgradingdata']); 2119 foreach ($criteria as $key => $criterion) { 2120 $details = array(); 2121 foreach ($criterion as $value) { 2122 foreach ($value['fillings'] as $filling) { 2123 $details[$value['criterionid']] = $filling; 2124 } 2125 } 2126 $advancedgrading[$key] = $details; 2127 } 2128 $gradedata->advancedgrading = $advancedgrading; 2129 } 2130 $assignment->save_grade($gradeinfo['userid'], $gradedata); 2131 } 2132 2133 return null; 2134 } 2135 2136 /** 2137 * Describes the return value for save_grades 2138 * 2139 * @return external_single_structure 2140 * @since Moodle 2.7 2141 */ 2142 public static function save_grades_returns() { 2143 return null; 2144 } 2145 2146 /** 2147 * Describes the parameters for copy_previous_attempt 2148 * @return external_function_parameters 2149 * @since Moodle 2.6 2150 */ 2151 public static function copy_previous_attempt_parameters() { 2152 return new external_function_parameters( 2153 array( 2154 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 2155 ) 2156 ); 2157 } 2158 2159 /** 2160 * Copy a students previous attempt to a new attempt. 2161 * 2162 * @param int $assignmentid 2163 * @return array of warnings to indicate any errors. 2164 * @since Moodle 2.6 2165 */ 2166 public static function copy_previous_attempt($assignmentid) { 2167 2168 $params = self::validate_parameters(self::copy_previous_attempt_parameters(), 2169 array('assignmentid' => $assignmentid)); 2170 2171 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); 2172 2173 $notices = array(); 2174 2175 $assignment->copy_previous_attempt($notices); 2176 2177 $warnings = array(); 2178 foreach ($notices as $notice) { 2179 $warnings[] = self::generate_warning($assignmentid, 2180 'couldnotcopyprevioussubmission', 2181 $notice); 2182 } 2183 2184 return $warnings; 2185 } 2186 2187 /** 2188 * Describes the return value for save_submission 2189 * 2190 * @return external_single_structure 2191 * @since Moodle 2.6 2192 */ 2193 public static function copy_previous_attempt_returns() { 2194 return new external_warnings(); 2195 } 2196 2197 /** 2198 * Returns description of method parameters 2199 * 2200 * @return external_function_parameters 2201 * @since Moodle 3.0 2202 */ 2203 public static function view_grading_table_parameters() { 2204 return new external_function_parameters( 2205 array( 2206 'assignid' => new external_value(PARAM_INT, 'assign instance id') 2207 ) 2208 ); 2209 } 2210 2211 /** 2212 * Trigger the grading_table_viewed event. 2213 * 2214 * @param int $assignid the assign instance id 2215 * @return array of warnings and status result 2216 * @since Moodle 3.0 2217 * @throws moodle_exception 2218 */ 2219 public static function view_grading_table($assignid) { 2220 2221 $params = self::validate_parameters(self::view_grading_table_parameters(), 2222 array( 2223 'assignid' => $assignid 2224 )); 2225 $warnings = array(); 2226 2227 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); 2228 2229 $assign->require_view_grades(); 2230 \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger(); 2231 2232 $result = array(); 2233 $result['status'] = true; 2234 $result['warnings'] = $warnings; 2235 return $result; 2236 } 2237 2238 /** 2239 * Returns description of method result value 2240 * 2241 * @return external_description 2242 * @since Moodle 3.0 2243 */ 2244 public static function view_grading_table_returns() { 2245 return new external_single_structure( 2246 array( 2247 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 2248 'warnings' => new external_warnings() 2249 ) 2250 ); 2251 } 2252 2253 /** 2254 * Describes the parameters for view_submission_status. 2255 * 2256 * @return external_function_parameters 2257 * @since Moodle 3.1 2258 */ 2259 public static function view_submission_status_parameters() { 2260 return new external_function_parameters ( 2261 array( 2262 'assignid' => new external_value(PARAM_INT, 'assign instance id'), 2263 ) 2264 ); 2265 } 2266 2267 /** 2268 * Trigger the submission status viewed event. 2269 * 2270 * @param int $assignid assign instance id 2271 * @return array of warnings and status result 2272 * @since Moodle 3.1 2273 */ 2274 public static function view_submission_status($assignid) { 2275 2276 $warnings = array(); 2277 $params = array( 2278 'assignid' => $assignid, 2279 ); 2280 $params = self::validate_parameters(self::view_submission_status_parameters(), $params); 2281 2282 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); 2283 2284 \mod_assign\event\submission_status_viewed::create_from_assign($assign)->trigger(); 2285 2286 $result = array(); 2287 $result['status'] = true; 2288 $result['warnings'] = $warnings; 2289 return $result; 2290 } 2291 2292 /** 2293 * Describes the view_submission_status return value. 2294 * 2295 * @return external_single_structure 2296 * @since Moodle 3.1 2297 */ 2298 public static function view_submission_status_returns() { 2299 return new external_single_structure( 2300 array( 2301 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 2302 'warnings' => new external_warnings(), 2303 ) 2304 ); 2305 } 2306 2307 /** 2308 * Describes the parameters for get_submission_status. 2309 * 2310 * @return external_function_parameters 2311 * @since Moodle 3.1 2312 */ 2313 public static function get_submission_status_parameters() { 2314 return new external_function_parameters ( 2315 array( 2316 'assignid' => new external_value(PARAM_INT, 'assignment instance id'), 2317 'userid' => new external_value(PARAM_INT, 'user id (empty for current user)', VALUE_DEFAULT, 0), 2318 'groupid' => new external_value(PARAM_INT, 'filter by users in group (used for generating the grading summary). 2319 0 for all groups information, any other empty value will calculate currrent group.', VALUE_DEFAULT, 0), 2320 ) 2321 ); 2322 } 2323 2324 /** 2325 * Returns information about an assignment submission status for a given user. 2326 * 2327 * @param int $assignid assignment instance id 2328 * @param int $userid user id (empty for current user) 2329 * @param int $groupid filter by users in group id (used for generating the grading summary). Use 0 for all groups information. 2330 * @return array of warnings and grading, status, feedback and previous attempts information 2331 * @since Moodle 3.1 2332 * @throws required_capability_exception 2333 */ 2334 public static function get_submission_status($assignid, $userid = 0, $groupid = 0) { 2335 global $USER; 2336 2337 $warnings = array(); 2338 2339 $params = array( 2340 'assignid' => $assignid, 2341 'userid' => $userid, 2342 'groupid' => $groupid, 2343 ); 2344 $params = self::validate_parameters(self::get_submission_status_parameters(), $params); 2345 2346 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); 2347 2348 // Default value for userid. 2349 if (empty($params['userid'])) { 2350 $params['userid'] = $USER->id; 2351 } 2352 $user = core_user::get_user($params['userid'], '*', MUST_EXIST); 2353 core_user::require_active_user($user); 2354 2355 if (!$assign->can_view_submission($user->id)) { 2356 throw new required_capability_exception($context, 'mod/assign:viewgrades', 'nopermission', ''); 2357 } 2358 2359 $assign->update_effective_access($user->id); 2360 2361 $gradingsummary = $lastattempt = $feedback = $previousattempts = null; 2362 2363 // Get the renderable since it contais all the info we need. 2364 if (!empty($params['groupid'])) { 2365 $groupid = $params['groupid']; 2366 // Determine is the group is visible to user. 2367 if (!groups_group_visible($groupid, $course, $cm)) { 2368 throw new moodle_exception('notingroup'); 2369 } 2370 } else { 2371 // A null group means that following functions will calculate the current group. 2372 // A groupid set to 0 means all groups. 2373 $groupid = ($params['groupid'] == 0) ? 0 : null; 2374 } 2375 if ($assign->can_view_grades($groupid)) { 2376 $gradingsummary = $assign->get_assign_grading_summary_renderable($groupid); 2377 } 2378 2379 // Retrieve the rest of the renderable objects. 2380 if (has_capability('mod/assign:viewownsubmissionsummary', $context, $user, false)) { 2381 // The user can view the submission summary. 2382 $lastattempt = $assign->get_assign_submission_status_renderable($user, true); 2383 } 2384 2385 $feedback = $assign->get_assign_feedback_status_renderable($user); 2386 2387 $previousattempts = $assign->get_assign_attempt_history_renderable($user); 2388 2389 // Now, build the result. 2390 $result = array(); 2391 2392 // First of all, grading summary, this is suitable for teachers/managers. 2393 if ($gradingsummary) { 2394 $result['gradingsummary'] = $gradingsummary; 2395 } 2396 // Show the grader's identity if 'Hide Grader' is disabled or has the 'Show Hidden Grader' capability. 2397 $showgradername = (has_capability('mod/assign:showhiddengrader', $context) or 2398 !$assign->is_hidden_grader()); 2399 2400 // Did we submit anything? 2401 if ($lastattempt) { 2402 $submissionplugins = $assign->get_submission_plugins(); 2403 2404 if (empty($lastattempt->submission)) { 2405 unset($lastattempt->submission); 2406 } else { 2407 $lastattempt->submission->plugins = self::get_plugins_data($assign, $submissionplugins, $lastattempt->submission); 2408 } 2409 2410 if (empty($lastattempt->teamsubmission)) { 2411 unset($lastattempt->teamsubmission); 2412 } else { 2413 $lastattempt->teamsubmission->plugins = self::get_plugins_data($assign, $submissionplugins, 2414 $lastattempt->teamsubmission); 2415 } 2416 2417 // We need to change the type of some of the structures retrieved from the renderable. 2418 if (!empty($lastattempt->submissiongroup)) { 2419 $lastattempt->submissiongroup = $lastattempt->submissiongroup->id; 2420 } else { 2421 unset($lastattempt->submissiongroup); 2422 } 2423 2424 if (!empty($lastattempt->usergroups)) { 2425 $lastattempt->usergroups = array_keys($lastattempt->usergroups); 2426 } 2427 // We cannot use array_keys here. 2428 if (!empty($lastattempt->submissiongroupmemberswhoneedtosubmit)) { 2429 $lastattempt->submissiongroupmemberswhoneedtosubmit = array_map( 2430 function($e){ 2431 return $e->id; 2432 }, 2433 $lastattempt->submissiongroupmemberswhoneedtosubmit); 2434 } 2435 2436 // Can edit its own submission? 2437 $lastattempt->caneditowner = has_capability('mod/assign:submit', $context, $user, false) 2438 && $assign->submissions_open($user->id) && $assign->is_any_submission_plugin_enabled(); 2439 2440 $result['lastattempt'] = $lastattempt; 2441 } 2442 2443 // The feedback for our latest submission. 2444 if ($feedback) { 2445 if ($feedback->grade) { 2446 if (!$showgradername) { 2447 $feedback->grade->grader = -1; 2448 } 2449 $feedbackplugins = $assign->get_feedback_plugins(); 2450 $feedback->plugins = self::get_plugins_data($assign, $feedbackplugins, $feedback->grade); 2451 } else { 2452 unset($feedback->plugins); 2453 unset($feedback->grade); 2454 } 2455 2456 $result['feedback'] = $feedback; 2457 } 2458 2459 // Retrieve only previous attempts. 2460 if ($previousattempts and count($previousattempts->submissions) > 1) { 2461 // Don't show the last one because it is the current submission. 2462 array_pop($previousattempts->submissions); 2463 2464 // Show newest to oldest. 2465 $previousattempts->submissions = array_reverse($previousattempts->submissions); 2466 2467 foreach ($previousattempts->submissions as $i => $submission) { 2468 $attempt = array(); 2469 2470 $grade = null; 2471 foreach ($previousattempts->grades as $onegrade) { 2472 if ($onegrade->attemptnumber == $submission->attemptnumber) { 2473 $grade = $onegrade; 2474 break; 2475 } 2476 } 2477 2478 $attempt['attemptnumber'] = $submission->attemptnumber; 2479 2480 if ($submission) { 2481 $submission->plugins = self::get_plugins_data($assign, $previousattempts->submissionplugins, $submission); 2482 $attempt['submission'] = $submission; 2483 } 2484 2485 if ($grade) { 2486 // From object to id. 2487 if (!$showgradername) { 2488 $grade->grader = -1; 2489 } else { 2490 $grade->grader = $grade->grader->id; 2491 } 2492 2493 $feedbackplugins = self::get_plugins_data($assign, $previousattempts->feedbackplugins, $grade); 2494 2495 $attempt['grade'] = $grade; 2496 $attempt['feedbackplugins'] = $feedbackplugins; 2497 } 2498 $result['previousattempts'][] = $attempt; 2499 } 2500 } 2501 2502 $result['warnings'] = $warnings; 2503 return $result; 2504 } 2505 2506 /** 2507 * Describes the get_submission_status return value. 2508 * 2509 * @return external_single_structure 2510 * @since Moodle 3.1 2511 */ 2512 public static function get_submission_status_returns() { 2513 return new external_single_structure( 2514 array( 2515 'gradingsummary' => new external_single_structure( 2516 array( 2517 'participantcount' => new external_value(PARAM_INT, 'Number of users who can submit.'), 2518 'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'), 2519 'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'), 2520 'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'), 2521 'submissionssubmittedcount' => new external_value(PARAM_INT, 'Number of submissions in submitted status.'), 2522 'submissionsneedgradingcount' => new external_value(PARAM_INT, 'Number of submissions that need grading.'), 2523 'warnofungroupedusers' => new external_value(PARAM_ALPHA, 'Whether we need to warn people that there 2524 are users without groups (\'warningrequired\'), warn 2525 people there are users who will submit in the default 2526 group (\'warningoptional\') or no warning (\'\').'), 2527 ), 'Grading information.', VALUE_OPTIONAL 2528 ), 2529 'lastattempt' => new external_single_structure( 2530 array( 2531 'submission' => self::get_submission_structure(VALUE_OPTIONAL), 2532 'teamsubmission' => self::get_submission_structure(VALUE_OPTIONAL), 2533 'submissiongroup' => new external_value(PARAM_INT, 'The submission group id (for group submissions only).', 2534 VALUE_OPTIONAL), 2535 'submissiongroupmemberswhoneedtosubmit' => new external_multiple_structure( 2536 new external_value(PARAM_INT, 'USER id.'), 2537 'List of users who still need to submit (for group submissions only).', 2538 VALUE_OPTIONAL 2539 ), 2540 'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'), 2541 'locked' => new external_value(PARAM_BOOL, 'Whether new submissions are locked.'), 2542 'graded' => new external_value(PARAM_BOOL, 'Whether the submission is graded.'), 2543 'canedit' => new external_value(PARAM_BOOL, 'Whether the user can edit the current submission.'), 2544 'caneditowner' => new external_value(PARAM_BOOL, 'Whether the owner of the submission can edit it.'), 2545 'cansubmit' => new external_value(PARAM_BOOL, 'Whether the user can submit.'), 2546 'extensionduedate' => new external_value(PARAM_INT, 'Extension due date.'), 2547 'blindmarking' => new external_value(PARAM_BOOL, 'Whether blind marking is enabled.'), 2548 'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.'), 2549 'usergroups' => new external_multiple_structure( 2550 new external_value(PARAM_INT, 'Group id.'), 'User groups in the course.' 2551 ), 2552 ), 'Last attempt information.', VALUE_OPTIONAL 2553 ), 2554 'feedback' => new external_single_structure( 2555 array( 2556 'grade' => self::get_grade_structure(VALUE_OPTIONAL), 2557 'gradefordisplay' => new external_value(PARAM_RAW, 'Grade rendered into a format suitable for display.'), 2558 'gradeddate' => new external_value(PARAM_INT, 'The date the user was graded.'), 2559 'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'Plugins info.', VALUE_OPTIONAL), 2560 ), 'Feedback for the last attempt.', VALUE_OPTIONAL 2561 ), 2562 'previousattempts' => new external_multiple_structure( 2563 new external_single_structure( 2564 array( 2565 'attemptnumber' => new external_value(PARAM_INT, 'Attempt number.'), 2566 'submission' => self::get_submission_structure(VALUE_OPTIONAL), 2567 'grade' => self::get_grade_structure(VALUE_OPTIONAL), 2568 'feedbackplugins' => new external_multiple_structure(self::get_plugin_structure(), 'Feedback info.', 2569 VALUE_OPTIONAL), 2570 ) 2571 ), 'List all the previous attempts did by the user.', VALUE_OPTIONAL 2572 ), 2573 'warnings' => new external_warnings(), 2574 ) 2575 ); 2576 } 2577 2578 /** 2579 * Returns description of method parameters 2580 * 2581 * @return external_function_parameters 2582 * @since Moodle 3.1 2583 */ 2584 public static function list_participants_parameters() { 2585 return new external_function_parameters( 2586 array( 2587 'assignid' => new external_value(PARAM_INT, 'assign instance id'), 2588 'groupid' => new external_value(PARAM_INT, 'group id'), 2589 'filter' => new external_value(PARAM_RAW, 'search string to filter the results'), 2590 'skip' => new external_value(PARAM_INT, 'number of records to skip', VALUE_DEFAULT, 0), 2591 'limit' => new external_value(PARAM_INT, 'maximum number of records to return', VALUE_DEFAULT, 0), 2592 'onlyids' => new external_value(PARAM_BOOL, 'Do not return all user fields', VALUE_DEFAULT, false), 2593 'includeenrolments' => new external_value(PARAM_BOOL, 'Do return courses where the user is enrolled', 2594 VALUE_DEFAULT, true), 2595 'tablesort' => new external_value(PARAM_BOOL, 'Apply current user table sorting preferences.', 2596 VALUE_DEFAULT, false) 2597 ) 2598 ); 2599 } 2600 2601 /** 2602 * Retrieves the list of students to be graded for the assignment. 2603 * 2604 * @param int $assignid the assign instance id 2605 * @param int $groupid the current group id 2606 * @param string $filter search string to filter the results. 2607 * @param int $skip Number of records to skip 2608 * @param int $limit Maximum number of records to return 2609 * @param bool $onlyids Only return user ids. 2610 * @param bool $includeenrolments Return courses where the user is enrolled. 2611 * @param bool $tablesort Apply current user table sorting params from the grading table. 2612 * @return array of warnings and status result 2613 * @since Moodle 3.1 2614 * @throws moodle_exception 2615 */ 2616 public static function list_participants($assignid, $groupid, $filter, $skip, 2617 $limit, $onlyids, $includeenrolments, $tablesort) { 2618 global $DB, $CFG; 2619 require_once($CFG->dirroot . "/mod/assign/locallib.php"); 2620 require_once($CFG->dirroot . "/user/lib.php"); 2621 2622 $params = self::validate_parameters(self::list_participants_parameters(), 2623 array( 2624 'assignid' => $assignid, 2625 'groupid' => $groupid, 2626 'filter' => $filter, 2627 'skip' => $skip, 2628 'limit' => $limit, 2629 'onlyids' => $onlyids, 2630 'includeenrolments' => $includeenrolments, 2631 'tablesort' => $tablesort 2632 )); 2633 $warnings = array(); 2634 2635 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); 2636 2637 require_capability('mod/assign:view', $context); 2638 2639 $assign->require_view_grades(); 2640 2641 $participants = array(); 2642 if (groups_group_visible($params['groupid'], $course, $cm)) { 2643 $participants = $assign->list_participants_with_filter_status_and_group($params['groupid'], $params['tablesort']); 2644 } 2645 2646 $userfields = user_get_default_fields(); 2647 if (!$params['includeenrolments']) { 2648 // Remove enrolled courses from users fields to be returned. 2649 $key = array_search('enrolledcourses', $userfields); 2650 if ($key !== false) { 2651 unset($userfields[$key]); 2652 } else { 2653 throw new moodle_exception('invaliduserfield', 'error', '', 'enrolledcourses'); 2654 } 2655 } 2656 2657 $result = array(); 2658 $index = 0; 2659 foreach ($participants as $record) { 2660 // Preserve the fullname set by the assignment. 2661 $fullname = $record->fullname; 2662 $searchable = $fullname; 2663 $match = false; 2664 if (empty($filter)) { 2665 $match = true; 2666 } else { 2667 $filter = core_text::strtolower($filter); 2668 $value = core_text::strtolower($searchable); 2669 if (is_string($value) && (core_text::strpos($value, $filter) !== false)) { 2670 $match = true; 2671 } 2672 } 2673 if ($match) { 2674 $index++; 2675 if ($index <= $params['skip']) { 2676 continue; 2677 } 2678 if (($params['limit'] > 0) && (($index - $params['skip']) > $params['limit'])) { 2679 break; 2680 } 2681 // Now we do the expensive lookup of user details because we completed the filtering. 2682 if (!$assign->is_blind_marking() && !$params['onlyids']) { 2683 $userdetails = user_get_user_details($record, $course, $userfields); 2684 } else { 2685 $userdetails = array('id' => $record->id); 2686 } 2687 $userdetails['fullname'] = $fullname; 2688 $userdetails['submitted'] = $record->submitted; 2689 $userdetails['requiregrading'] = $record->requiregrading; 2690 $userdetails['grantedextension'] = $record->grantedextension; 2691 if (!empty($record->groupid)) { 2692 $userdetails['groupid'] = $record->groupid; 2693 } 2694 if (!empty($record->groupname)) { 2695 $userdetails['groupname'] = $record->groupname; 2696 } 2697 // Unique id is required for blind marking. 2698 $userdetails['recordid'] = -1; 2699 if (!empty($record->recordid)) { 2700 $userdetails['recordid'] = $record->recordid; 2701 } 2702 2703 $result[] = $userdetails; 2704 } 2705 } 2706 return $result; 2707 } 2708 2709 /** 2710 * Returns the description of the results of the mod_assign_external::list_participants() method. 2711 * 2712 * @return external_description 2713 * @since Moodle 3.1 2714 */ 2715 public static function list_participants_returns() { 2716 // Get user description. 2717 $userdesc = core_user_external::user_description(); 2718 // List unneeded properties. 2719 $unneededproperties = [ 2720 'auth', 'confirmed', 'lang', 'calendartype', 'theme', 'timezone', 'mailformat' 2721 ]; 2722 // Remove unneeded properties for consistency with the previous version. 2723 foreach ($unneededproperties as $prop) { 2724 unset($userdesc->keys[$prop]); 2725 } 2726 2727 // Override property attributes for consistency with the previous version. 2728 $userdesc->keys['fullname']->type = PARAM_NOTAGS; 2729 $userdesc->keys['profileimageurlsmall']->required = VALUE_OPTIONAL; 2730 $userdesc->keys['profileimageurl']->required = VALUE_OPTIONAL; 2731 $userdesc->keys['email']->desc = 'Email address'; 2732 $userdesc->keys['idnumber']->desc = 'The idnumber of the user'; 2733 $userdesc->keys['recordid'] = new external_value(PARAM_INT, 'record id'); 2734 2735 // Define other keys. 2736 $otherkeys = [ 2737 'groups' => new external_multiple_structure( 2738 new external_single_structure( 2739 [ 2740 'id' => new external_value(PARAM_INT, 'group id'), 2741 'name' => new external_value(PARAM_RAW, 'group name'), 2742 'description' => new external_value(PARAM_RAW, 'group description'), 2743 ] 2744 ), 'user groups', VALUE_OPTIONAL 2745 ), 2746 'roles' => new external_multiple_structure( 2747 new external_single_structure( 2748 [ 2749 'roleid' => new external_value(PARAM_INT, 'role id'), 2750 'name' => new external_value(PARAM_RAW, 'role name'), 2751 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'), 2752 'sortorder' => new external_value(PARAM_INT, 'role sortorder') 2753 ] 2754 ), 'user roles', VALUE_OPTIONAL 2755 ), 2756 'enrolledcourses' => new external_multiple_structure( 2757 new external_single_structure( 2758 [ 2759 'id' => new external_value(PARAM_INT, 'Id of the course'), 2760 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'), 2761 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course') 2762 ] 2763 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL 2764 ), 2765 'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'), 2766 'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'), 2767 'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'), 2768 'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL), 2769 'groupname' => new external_value(PARAM_TEXT, 'for group assignments this is the group name', VALUE_OPTIONAL), 2770 ]; 2771 2772 // Merge keys. 2773 $userdesc->keys = array_merge($userdesc->keys, $otherkeys); 2774 return new external_multiple_structure($userdesc); 2775 } 2776 2777 /** 2778 * Returns description of method parameters 2779 * 2780 * @return external_function_parameters 2781 * @since Moodle 3.1 2782 */ 2783 public static function get_participant_parameters() { 2784 return new external_function_parameters( 2785 array( 2786 'assignid' => new external_value(PARAM_INT, 'assign instance id'), 2787 'userid' => new external_value(PARAM_INT, 'user id'), 2788 'embeduser' => new external_value(PARAM_BOOL, 'user id', VALUE_DEFAULT, false), 2789 ) 2790 ); 2791 } 2792 2793 /** 2794 * Get the user participating in the given assignment. An error with code 'usernotincourse' 2795 * is thrown is the user isn't a participant of the given assignment. 2796 * 2797 * @param int $assignid the assign instance id 2798 * @param int $userid the user id 2799 * @param bool $embeduser return user details (only applicable if not blind marking) 2800 * @return array of warnings and status result 2801 * @since Moodle 3.1 2802 * @throws moodle_exception 2803 */ 2804 public static function get_participant($assignid, $userid, $embeduser) { 2805 global $DB, $CFG; 2806 require_once($CFG->dirroot . "/mod/assign/locallib.php"); 2807 require_once($CFG->dirroot . "/user/lib.php"); 2808 2809 $params = self::validate_parameters(self::get_participant_parameters(), array( 2810 'assignid' => $assignid, 2811 'userid' => $userid, 2812 'embeduser' => $embeduser 2813 )); 2814 2815 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); 2816 $assign->require_view_grades(); 2817 2818 $participant = $assign->get_participant($params['userid']); 2819 2820 // Update assign with override information. 2821 $assign->update_effective_access($params['userid']); 2822 2823 if (!$participant) { 2824 // No participant found so we can return early. 2825 throw new moodle_exception('usernotincourse'); 2826 } 2827 2828 $return = array( 2829 'id' => $participant->id, 2830 'fullname' => $participant->fullname, 2831 'submitted' => $participant->submitted, 2832 'requiregrading' => $participant->requiregrading, 2833 'grantedextension' => $participant->grantedextension, 2834 'blindmarking' => $assign->is_blind_marking(), 2835 'allowsubmissionsfromdate' => $assign->get_instance($userid)->allowsubmissionsfromdate, 2836 'duedate' => $assign->get_instance($userid)->duedate, 2837 'cutoffdate' => $assign->get_instance($userid)->cutoffdate, 2838 'duedatestr' => userdate($assign->get_instance($userid)->duedate, get_string('strftimedatetime', 'langconfig')), 2839 ); 2840 2841 if (!empty($participant->groupid)) { 2842 $return['groupid'] = $participant->groupid; 2843 } 2844 if (!empty($participant->groupname)) { 2845 $return['groupname'] = $participant->groupname; 2846 } 2847 2848 // Skip the expensive lookup of user detail if we're blind marking or the caller 2849 // hasn't asked for user details to be embedded. 2850 if (!$assign->is_blind_marking() && $embeduser) { 2851 if ($userdetails = user_get_user_details($participant, $course)) { 2852 $return['user'] = $userdetails; 2853 } 2854 } 2855 2856 return $return; 2857 } 2858 2859 /** 2860 * Returns description of method result value 2861 * 2862 * @return external_description 2863 * @since Moodle 3.1 2864 */ 2865 public static function get_participant_returns() { 2866 $userdescription = core_user_external::user_description(); 2867 $userdescription->default = []; 2868 $userdescription->required = VALUE_OPTIONAL; 2869 2870 return new external_single_structure(array( 2871 'id' => new external_value(PARAM_INT, 'ID of the user'), 2872 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'), 2873 'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'), 2874 'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'), 2875 'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'), 2876 'blindmarking' => new external_value(PARAM_BOOL, 'is blind marking enabled for this assignment'), 2877 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allowsubmissionsfromdate for the user'), 2878 'duedate' => new external_value(PARAM_INT, 'duedate for the user'), 2879 'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user'), 2880 'duedatestr' => new external_value(PARAM_TEXT, 'duedate for the user'), 2881 'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL), 2882 'groupname' => new external_value(PARAM_TEXT, 'for group assignments this is the group name', VALUE_OPTIONAL), 2883 'user' => $userdescription, 2884 )); 2885 } 2886 2887 /** 2888 * Utility function for validating an assign. 2889 * 2890 * @param int $assignid assign instance id 2891 * @return array array containing the assign, course, context and course module objects 2892 * @since Moodle 3.2 2893 */ 2894 protected static function validate_assign($assignid) { 2895 global $DB; 2896 2897 // Request and permission validation. 2898 $assign = $DB->get_record('assign', array('id' => $assignid), 'id', MUST_EXIST); 2899 list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign'); 2900 2901 $context = context_module::instance($cm->id); 2902 // Please, note that is not required to check mod/assign:view because is done by validate_context->require_login. 2903 self::validate_context($context); 2904 $assign = new assign($context, $cm, $course); 2905 2906 return array($assign, $course, $cm, $context); 2907 } 2908 2909 /** 2910 * Describes the parameters for view_assign. 2911 * 2912 * @return external_function_parameters 2913 * @since Moodle 3.2 2914 */ 2915 public static function view_assign_parameters() { 2916 return new external_function_parameters ( 2917 array( 2918 'assignid' => new external_value(PARAM_INT, 'assign instance id'), 2919 ) 2920 ); 2921 } 2922 2923 /** 2924 * Update the module completion status. 2925 * 2926 * @param int $assignid assign instance id 2927 * @return array of warnings and status result 2928 * @since Moodle 3.2 2929 */ 2930 public static function view_assign($assignid) { 2931 $warnings = array(); 2932 $params = array( 2933 'assignid' => $assignid, 2934 ); 2935 $params = self::validate_parameters(self::view_assign_parameters(), $params); 2936 2937 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); 2938 2939 $assign->set_module_viewed(); 2940 2941 $result = array(); 2942 $result['status'] = true; 2943 $result['warnings'] = $warnings; 2944 return $result; 2945 } 2946 2947 /** 2948 * Describes the view_assign return value. 2949 * 2950 * @return external_single_structure 2951 * @since Moodle 3.2 2952 */ 2953 public static function view_assign_returns() { 2954 return new external_single_structure( 2955 array( 2956 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 2957 'warnings' => new external_warnings(), 2958 ) 2959 ); 2960 } 2961 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body