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