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