Merge branch 'MDL-74441-400' of https://github.com/cameron1729/moodle into MOODLE_400...
[moodle.git] / mod / assign / externallib.php
blobd0968f5a497b6866dba8cf94f7f26507997df0f8
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.
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/>.
17 /**
18 * External assign API
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
26 defined('MOODLE_INTERNAL') || die;
28 require_once("$CFG->libdir/externallib.php");
29 require_once("$CFG->dirroot/user/externallib.php");
30 require_once("$CFG->dirroot/mod/assign/locallib.php");
32 /**
33 * Assign functions
34 * @copyright 2012 Paul Charsley
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 class mod_assign_external extends \mod_assign\external\external_api {
39 /**
40 * Describes the parameters for get_grades
41 * @return external_function_parameters
42 * @since Moodle 2.4
44 public static function get_grades_parameters() {
45 return new external_function_parameters(
46 array(
47 'assignmentids' => new external_multiple_structure(
48 new external_value(PARAM_INT, 'assignment id'),
49 '1 or more assignment ids',
50 VALUE_REQUIRED),
51 'since' => new external_value(PARAM_INT,
52 'timestamp, only return records where timemodified >= since',
53 VALUE_DEFAULT, 0)
58 /**
59 * Returns grade information from assign_grades for the requested assignment ids
60 * @param int[] $assignmentids
61 * @param int $since only return records with timemodified >= since
62 * @return array of grade records for each requested assignment
63 * @since Moodle 2.4
65 public static function get_grades($assignmentids, $since = 0) {
66 global $DB;
67 $params = self::validate_parameters(self::get_grades_parameters(),
68 array('assignmentids' => $assignmentids,
69 'since' => $since));
71 $assignments = array();
72 $warnings = array();
73 $requestedassignmentids = $params['assignmentids'];
75 // Check the user is allowed to get the grades for the assignments requested.
76 $placeholders = array();
77 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
78 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
79 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
80 $placeholders['modname'] = 'assign';
81 $cms = $DB->get_records_sql($sql, $placeholders);
82 foreach ($cms as $cm) {
83 try {
84 $context = context_module::instance($cm->id);
85 self::validate_context($context);
86 $assign = new assign($context, null, null);
87 $assign->require_view_grades();
88 } catch (Exception $e) {
89 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
90 $warning = array();
91 $warning['item'] = 'assignment';
92 $warning['itemid'] = $cm->instance;
93 $warning['warningcode'] = '1';
94 $warning['message'] = 'No access rights in module context';
95 $warnings[] = $warning;
99 // Create the query and populate an array of grade records from the recordset results.
100 if (count ($requestedassignmentids) > 0) {
101 $placeholders = array();
102 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
104 $sql = "SELECT ag.id,
105 ag.assignment,
106 ag.userid,
107 ag.timecreated,
108 ag.timemodified,
109 ag.grader,
110 ag.grade,
111 ag.attemptnumber
112 FROM {assign_grades} ag, {assign_submission} s
113 WHERE s.assignment $inorequalsql
114 AND s.userid = ag.userid
115 AND s.latest = 1
116 AND s.attemptnumber = ag.attemptnumber
117 AND ag.timemodified >= :since
118 AND ag.assignment = s.assignment
119 ORDER BY ag.assignment, ag.id";
121 $placeholders['since'] = $params['since'];
122 $rs = $DB->get_recordset_sql($sql, $placeholders);
123 $currentassignmentid = null;
124 $assignment = null;
125 foreach ($rs as $rd) {
126 $grade = array();
127 $grade['id'] = $rd->id;
128 $grade['userid'] = $rd->userid;
129 $grade['timecreated'] = $rd->timecreated;
130 $grade['timemodified'] = $rd->timemodified;
131 $grade['grader'] = $rd->grader;
132 $grade['attemptnumber'] = $rd->attemptnumber;
133 $grade['grade'] = (string)$rd->grade;
135 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
136 if (!is_null($assignment)) {
137 $assignments[] = $assignment;
139 $assignment = array();
140 $assignment['assignmentid'] = $rd->assignment;
141 $assignment['grades'] = array();
142 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
144 $assignment['grades'][] = $grade;
146 $currentassignmentid = $rd->assignment;
148 if (!is_null($assignment)) {
149 $assignments[] = $assignment;
151 $rs->close();
153 foreach ($requestedassignmentids as $assignmentid) {
154 $warning = array();
155 $warning['item'] = 'assignment';
156 $warning['itemid'] = $assignmentid;
157 $warning['warningcode'] = '3';
158 $warning['message'] = 'No grades found';
159 $warnings[] = $warning;
162 $result = array();
163 $result['assignments'] = $assignments;
164 $result['warnings'] = $warnings;
165 return $result;
169 * Creates a grade single structure.
171 * @return external_single_structure a grade single structure.
172 * @since Moodle 3.1
174 private static function get_grade_structure($required = VALUE_REQUIRED) {
175 return new external_single_structure(
176 array(
177 'id' => new external_value(PARAM_INT, 'grade id'),
178 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
179 'userid' => new external_value(PARAM_INT, 'student id'),
180 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
181 'timecreated' => new external_value(PARAM_INT, 'grade creation time'),
182 'timemodified' => new external_value(PARAM_INT, 'grade last modified time'),
183 'grader' => new external_value(PARAM_INT, 'grader, -1 if grader is hidden'),
184 'grade' => new external_value(PARAM_TEXT, 'grade'),
185 'gradefordisplay' => new external_value(PARAM_RAW, 'grade rendered into a format suitable for display',
186 VALUE_OPTIONAL),
187 ), 'grade information', $required
192 * Creates an assign_grades external_single_structure
193 * @return external_single_structure
194 * @since Moodle 2.4
196 private static function assign_grades() {
197 return new external_single_structure(
198 array (
199 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
200 'grades' => new external_multiple_structure(self::get_grade_structure())
206 * Describes the get_grades return value
207 * @return external_single_structure
208 * @since Moodle 2.4
210 public static function get_grades_returns() {
211 return new external_single_structure(
212 array(
213 'assignments' => new external_multiple_structure(self::assign_grades(), 'list of assignment grade information'),
214 'warnings' => new external_warnings('item is always \'assignment\'',
215 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
216 'errorcode can be 3 (no grades found) or 1 (no permission to get grades)')
222 * Returns description of method parameters
224 * @return external_function_parameters
225 * @since Moodle 2.4
227 public static function get_assignments_parameters() {
228 return new external_function_parameters(
229 array(
230 'courseids' => new external_multiple_structure(
231 new external_value(PARAM_INT, 'course id, empty for retrieving all the courses where the user is enroled in'),
232 '0 or more course ids',
233 VALUE_DEFAULT, array()
235 'capabilities' => new external_multiple_structure(
236 new external_value(PARAM_CAPABILITY, 'capability'),
237 'list of capabilities used to filter courses',
238 VALUE_DEFAULT, array()
240 'includenotenrolledcourses' => new external_value(PARAM_BOOL, 'whether to return courses that the user can see
241 even if is not enroled in. This requires the parameter courseids
242 to not be empty.', VALUE_DEFAULT, false)
248 * Returns an array of courses the user is enrolled, and for each course all of the assignments that the user can
249 * view within that course.
251 * @param array $courseids An optional array of course ids. If provided only assignments within the given course
252 * will be returned. If the user is not enrolled in or can't view a given course a warning will be generated and returned.
253 * @param array $capabilities An array of additional capability checks you wish to be made on the course context.
254 * @param bool $includenotenrolledcourses Wheter to return courses that the user can see even if is not enroled in.
255 * This requires the parameter $courseids to not be empty.
256 * @return An array of courses and warnings.
257 * @since Moodle 2.4
259 public static function get_assignments($courseids = array(), $capabilities = array(), $includenotenrolledcourses = false) {
260 global $USER, $DB, $CFG;
262 $params = self::validate_parameters(
263 self::get_assignments_parameters(),
264 array(
265 'courseids' => $courseids,
266 'capabilities' => $capabilities,
267 'includenotenrolledcourses' => $includenotenrolledcourses
271 $warnings = array();
272 $courses = array();
273 $fields = 'sortorder,shortname,fullname,timemodified';
275 // If the courseids list is empty, we return only the courses where the user is enrolled in.
276 if (empty($params['courseids'])) {
277 $courses = enrol_get_users_courses($USER->id, true, $fields);
278 $courseids = array_keys($courses);
279 } else if ($includenotenrolledcourses) {
280 // In this case, we don't have to check here for enrolmnents. Maybe the user can see the course even if is not enrolled.
281 $courseids = $params['courseids'];
282 } else {
283 // We need to check for enrolments.
284 $mycourses = enrol_get_users_courses($USER->id, true, $fields);
285 $mycourseids = array_keys($mycourses);
287 foreach ($params['courseids'] as $courseid) {
288 if (!in_array($courseid, $mycourseids)) {
289 unset($courses[$courseid]);
290 $warnings[] = array(
291 'item' => 'course',
292 'itemid' => $courseid,
293 'warningcode' => '2',
294 'message' => 'User is not enrolled or does not have requested capability'
296 } else {
297 $courses[$courseid] = $mycourses[$courseid];
300 $courseids = array_keys($courses);
303 foreach ($courseids as $cid) {
305 try {
306 $context = context_course::instance($cid);
307 self::validate_context($context);
309 // Check if this course was already loaded (by enrol_get_users_courses).
310 if (!isset($courses[$cid])) {
311 $courses[$cid] = get_course($cid);
313 $courses[$cid]->contextid = $context->id;
314 } catch (Exception $e) {
315 unset($courses[$cid]);
316 $warnings[] = array(
317 'item' => 'course',
318 'itemid' => $cid,
319 'warningcode' => '1',
320 'message' => 'No access rights in course context '.$e->getMessage()
322 continue;
324 if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) {
325 unset($courses[$cid]);
328 $extrafields='m.id as assignmentid, ' .
329 'm.course, ' .
330 'm.nosubmissions, ' .
331 'm.submissiondrafts, ' .
332 'm.sendnotifications, '.
333 'm.sendlatenotifications, ' .
334 'm.sendstudentnotifications, ' .
335 'm.duedate, ' .
336 'm.allowsubmissionsfromdate, '.
337 'm.grade, ' .
338 'm.timemodified, '.
339 'm.completionsubmit, ' .
340 'm.cutoffdate, ' .
341 'm.gradingduedate, ' .
342 'm.teamsubmission, ' .
343 'm.requireallteammemberssubmit, '.
344 'm.teamsubmissiongroupingid, ' .
345 'm.blindmarking, ' .
346 'm.hidegrader, ' .
347 'm.revealidentities, ' .
348 'm.attemptreopenmethod, '.
349 'm.maxattempts, ' .
350 'm.markingworkflow, ' .
351 'm.markingallocation, ' .
352 'm.requiresubmissionstatement, '.
353 'm.preventsubmissionnotingroup, '.
354 'm.intro, '.
355 'm.introformat,' .
356 'm.activity,' .
357 'm.activityformat,' .
358 'm.timelimit,' .
359 'm.submissionattachments';
360 $coursearray = array();
361 foreach ($courses as $id => $course) {
362 $assignmentarray = array();
363 // Get a list of assignments for the course.
364 if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
365 foreach ($modules as $module) {
366 $context = context_module::instance($module->id);
367 try {
368 self::validate_context($context);
369 require_capability('mod/assign:view', $context);
370 } catch (Exception $e) {
371 $warnings[] = array(
372 'item' => 'module',
373 'itemid' => $module->id,
374 'warningcode' => '1',
375 'message' => 'No access rights in module context'
377 continue;
380 $assign = new assign($context, null, null);
381 // Update assign with override information.
382 $assign->update_effective_access($USER->id);
384 // Get configurations for only enabled plugins.
385 $plugins = $assign->get_submission_plugins();
386 $plugins = array_merge($plugins, $assign->get_feedback_plugins());
388 $configarray = array();
389 foreach ($plugins as $plugin) {
390 if ($plugin->is_enabled() && $plugin->is_visible()) {
391 $configrecords = $plugin->get_config_for_external();
392 foreach ($configrecords as $name => $value) {
393 $configarray[] = array(
394 'plugin' => $plugin->get_type(),
395 'subtype' => $plugin->get_subtype(),
396 'name' => $name,
397 'value' => $value
403 $assignment = array(
404 'id' => $module->assignmentid,
405 'cmid' => $module->id,
406 'course' => $module->course,
407 'name' => external_format_string($module->name, $context),
408 'nosubmissions' => $module->nosubmissions,
409 'submissiondrafts' => $module->submissiondrafts,
410 'sendnotifications' => $module->sendnotifications,
411 'sendlatenotifications' => $module->sendlatenotifications,
412 'sendstudentnotifications' => $module->sendstudentnotifications,
413 'duedate' => $assign->get_instance()->duedate,
414 'allowsubmissionsfromdate' => $assign->get_instance()->allowsubmissionsfromdate,
415 'grade' => $module->grade,
416 'timemodified' => $module->timemodified,
417 'completionsubmit' => $module->completionsubmit,
418 'cutoffdate' => $assign->get_instance()->cutoffdate,
419 'gradingduedate' => $assign->get_instance()->gradingduedate,
420 'teamsubmission' => $module->teamsubmission,
421 'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
422 'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
423 'blindmarking' => $module->blindmarking,
424 'hidegrader' => $module->hidegrader,
425 'revealidentities' => $module->revealidentities,
426 'attemptreopenmethod' => $module->attemptreopenmethod,
427 'maxattempts' => $module->maxattempts,
428 'markingworkflow' => $module->markingworkflow,
429 'markingallocation' => $module->markingallocation,
430 'requiresubmissionstatement' => $module->requiresubmissionstatement,
431 'preventsubmissionnotingroup' => $module->preventsubmissionnotingroup,
432 'timelimit' => $module->timelimit,
433 'submissionattachments' => $module->submissionattachments,
434 'configs' => $configarray
437 // Return or not intro and file attachments depending on the plugin settings.
438 if ($assign->show_intro()) {
439 $options = array('noclean' => true);
440 list($assignment['intro'], $assignment['introformat']) =
441 external_format_text($module->intro, $module->introformat, $context->id, 'mod_assign', 'intro', null,
442 $options);
443 $assignment['introfiles'] = external_util::get_area_files($context->id, 'mod_assign', 'intro', false,
444 false);
445 if ($assign->should_provide_intro_attachments($USER->id)) {
446 $assignment['introattachments'] = external_util::get_area_files($context->id, 'mod_assign',
447 ASSIGN_INTROATTACHMENT_FILEAREA, 0);
451 if ($module->requiresubmissionstatement) {
452 // Submission statement is required, return the submission statement value.
453 $adminconfig = get_config('assign');
454 // Single submission.
455 if (!$module->teamsubmission) {
456 list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
457 external_format_text($adminconfig->submissionstatement, FORMAT_MOODLE, $context->id,
458 'mod_assign', '', 0);
459 } else { // Team submission.
460 // One user can submit for the whole team.
461 if (!empty($adminconfig->submissionstatementteamsubmission) && !$module->requireallteammemberssubmit) {
462 list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
463 external_format_text($adminconfig->submissionstatementteamsubmission,
464 FORMAT_MOODLE, $context->id, 'mod_assign', '', 0);
465 } else if (!empty($adminconfig->submissionstatementteamsubmissionallsubmit) &&
466 $module->requireallteammemberssubmit) {
467 // All team members must submit.
468 list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
469 external_format_text($adminconfig->submissionstatementteamsubmissionallsubmit,
470 FORMAT_MOODLE, $context->id, 'mod_assign', '', 0);
475 if ($module->activity && $assign->submissions_open($USER->id, true)) {
476 list($assignment['activity'], $assignment['activityformat']) = external_format_text($module->activity,
477 $module->activityformat, $context->id, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0);
478 $assignment['activityattachments'] = external_util::get_area_files($context->id, 'mod_assign',
479 ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0);
482 $assignmentarray[] = $assignment;
485 $coursearray[]= array(
486 'id' => $courses[$id]->id,
487 'fullname' => external_format_string($courses[$id]->fullname, $course->contextid),
488 'shortname' => external_format_string($courses[$id]->shortname, $course->contextid),
489 'timemodified' => $courses[$id]->timemodified,
490 'assignments' => $assignmentarray
494 $result = array(
495 'courses' => $coursearray,
496 'warnings' => $warnings
498 return $result;
502 * Creates an assignment external_single_structure
504 * @return external_single_structure
505 * @since Moodle 2.4
507 private static function get_assignments_assignment_structure() {
508 return new external_single_structure(
509 array(
510 'id' => new external_value(PARAM_INT, 'assignment id'),
511 'cmid' => new external_value(PARAM_INT, 'course module id'),
512 'course' => new external_value(PARAM_INT, 'course id'),
513 'name' => new external_value(PARAM_RAW, 'assignment name'),
514 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
515 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
516 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
517 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
518 'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'),
519 'duedate' => new external_value(PARAM_INT, 'assignment due date'),
520 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
521 'grade' => new external_value(PARAM_INT, 'grade type'),
522 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
523 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
524 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
525 'gradingduedate' => new external_value(PARAM_INT, 'the expected date for marking the submissions'),
526 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
527 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
528 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
529 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
530 'hidegrader' => new external_value(PARAM_INT, 'If enabled, hide grader to student'),
531 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
532 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
533 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
534 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
535 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
536 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
537 'preventsubmissionnotingroup' => new external_value(PARAM_INT, 'Prevent submission not in group', VALUE_OPTIONAL),
538 'submissionstatement' => new external_value(PARAM_RAW, 'Submission statement formatted.', VALUE_OPTIONAL),
539 'submissionstatementformat' => new external_format_value('submissionstatement', VALUE_OPTIONAL),
540 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings'),
541 'intro' => new external_value(PARAM_RAW,
542 'assignment intro, not allways returned because it deppends on the activity configuration', VALUE_OPTIONAL),
543 'introformat' => new external_format_value('intro', VALUE_OPTIONAL),
544 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
545 'introattachments' => new external_files('intro attachments files', VALUE_OPTIONAL),
546 'activity' => new external_value(PARAM_RAW, 'Description of activity', VALUE_OPTIONAL),
547 'activityformat' => new external_format_value('activity', VALUE_OPTIONAL),
548 'activityattachments' => new external_files('Files from activity field', VALUE_OPTIONAL),
549 'timelimit' => new external_value(PARAM_INT, 'Time limit to complete assigment', VALUE_OPTIONAL),
550 'submissionattachments' => new external_value(PARAM_INT,
551 'Flag to only show files during submission', VALUE_OPTIONAL),
552 ), 'assignment information object');
556 * Creates an assign_plugin_config external_single_structure
558 * @return external_single_structure
559 * @since Moodle 2.4
561 private static function get_assignments_config_structure() {
562 return new external_single_structure(
563 array(
564 'id' => new external_value(PARAM_INT, 'assign_plugin_config id', VALUE_OPTIONAL),
565 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
566 'plugin' => new external_value(PARAM_TEXT, 'plugin'),
567 'subtype' => new external_value(PARAM_TEXT, 'subtype'),
568 'name' => new external_value(PARAM_TEXT, 'name'),
569 'value' => new external_value(PARAM_TEXT, 'value')
570 ), 'assignment configuration object'
575 * Creates a course external_single_structure
577 * @return external_single_structure
578 * @since Moodle 2.4
580 private static function get_assignments_course_structure() {
581 return new external_single_structure(
582 array(
583 'id' => new external_value(PARAM_INT, 'course id'),
584 'fullname' => new external_value(PARAM_RAW, 'course full name'),
585 'shortname' => new external_value(PARAM_RAW, 'course short name'),
586 'timemodified' => new external_value(PARAM_INT, 'last time modified'),
587 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
588 ), 'course information object'
593 * Describes the return value for get_assignments
595 * @return external_single_structure
596 * @since Moodle 2.4
598 public static function get_assignments_returns() {
599 return new external_single_structure(
600 array(
601 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
602 'warnings' => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
603 'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
604 'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
610 * Return information (files and text fields) for the given plugins in the assignment.
612 * @param assign $assign the assignment object
613 * @param array $assignplugins array of assignment plugins (submission or feedback)
614 * @param stdClass $item the item object (submission or grade)
615 * @return array an array containing the plugins returned information
617 private static function get_plugins_data($assign, $assignplugins, $item) {
618 global $CFG;
620 $plugins = array();
621 $fs = get_file_storage();
623 foreach ($assignplugins as $assignplugin) {
625 if (!$assignplugin->is_enabled() or !$assignplugin->is_visible()) {
626 continue;
629 $plugin = array(
630 'name' => $assignplugin->get_name(),
631 'type' => $assignplugin->get_type()
633 // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
634 $component = $assignplugin->get_subtype().'_'.$assignplugin->get_type();
636 $fileareas = $assignplugin->get_file_areas();
637 foreach ($fileareas as $filearea => $name) {
638 $fileareainfo = array('area' => $filearea);
640 $fileareainfo['files'] = external_util::get_area_files(
641 $assign->get_context()->id,
642 $component,
643 $filearea,
644 $item->id
647 $plugin['fileareas'][] = $fileareainfo;
650 $editorfields = $assignplugin->get_editor_fields();
651 foreach ($editorfields as $name => $description) {
652 $editorfieldinfo = array(
653 'name' => $name,
654 'description' => $description,
655 'text' => $assignplugin->get_editor_text($name, $item->id),
656 'format' => $assignplugin->get_editor_format($name, $item->id)
659 // Now format the text.
660 foreach ($fileareas as $filearea => $name) {
661 list($editorfieldinfo['text'], $editorfieldinfo['format']) = external_format_text(
662 $editorfieldinfo['text'], $editorfieldinfo['format'], $assign->get_context()->id,
663 $component, $filearea, $item->id);
666 $plugin['editorfields'][] = $editorfieldinfo;
668 $plugins[] = $plugin;
670 return $plugins;
674 * Describes the parameters for get_submissions
676 * @return external_function_parameters
677 * @since Moodle 2.5
679 public static function get_submissions_parameters() {
680 return new external_function_parameters(
681 array(
682 'assignmentids' => new external_multiple_structure(
683 new external_value(PARAM_INT, 'assignment id'),
684 '1 or more assignment ids',
685 VALUE_REQUIRED),
686 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
687 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
688 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
694 * Returns submissions for the requested assignment ids
696 * @param int[] $assignmentids
697 * @param string $status only return submissions with this status
698 * @param int $since only return submissions with timemodified >= since
699 * @param int $before only return submissions with timemodified <= before
700 * @return array of submissions for each requested assignment
701 * @since Moodle 2.5
703 public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
704 global $DB, $CFG;
706 $params = self::validate_parameters(self::get_submissions_parameters(),
707 array('assignmentids' => $assignmentids,
708 'status' => $status,
709 'since' => $since,
710 'before' => $before));
712 $warnings = array();
713 $assignments = array();
715 // Check the user is allowed to get the submissions for the assignments requested.
716 $placeholders = array();
717 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
718 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
719 "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
720 $placeholders['modname'] = 'assign';
721 $cms = $DB->get_records_sql($sql, $placeholders);
722 $assigns = array();
723 foreach ($cms as $cm) {
724 try {
725 $context = context_module::instance($cm->id);
726 self::validate_context($context);
727 $assign = new assign($context, null, null);
728 $assign->require_view_grades();
729 $assigns[] = $assign;
730 } catch (Exception $e) {
731 $warnings[] = array(
732 'item' => 'assignment',
733 'itemid' => $cm->instance,
734 'warningcode' => '1',
735 'message' => 'No access rights in module context'
740 foreach ($assigns as $assign) {
741 $submissions = array();
742 $placeholders = array('assignid1' => $assign->get_instance()->id,
743 'assignid2' => $assign->get_instance()->id);
745 $submissionmaxattempt = 'SELECT mxs.userid, mxs.groupid, MAX(mxs.attemptnumber) AS maxattempt
746 FROM {assign_submission} mxs
747 WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid, mxs.groupid';
749 $sql = "SELECT mas.id, mas.assignment,mas.userid,".
750 "mas.timecreated,mas.timemodified,mas.timestarted,mas.status,mas.groupid,mas.attemptnumber ".
751 "FROM {assign_submission} mas ".
752 "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
753 "AND mas.groupid = smx.groupid ".
754 "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
756 if (!empty($params['status'])) {
757 $placeholders['status'] = $params['status'];
758 $sql = $sql." AND mas.status = :status";
760 if (!empty($params['before'])) {
761 $placeholders['since'] = $params['since'];
762 $placeholders['before'] = $params['before'];
763 $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
764 } else {
765 $placeholders['since'] = $params['since'];
766 $sql = $sql." AND mas.timemodified >= :since";
769 $submissionrecords = $DB->get_records_sql($sql, $placeholders);
771 if (!empty($submissionrecords)) {
772 $submissionplugins = $assign->get_submission_plugins();
773 foreach ($submissionrecords as $submissionrecord) {
774 $submission = array(
775 'id' => $submissionrecord->id,
776 'userid' => $submissionrecord->userid,
777 'timecreated' => $submissionrecord->timecreated,
778 'timemodified' => $submissionrecord->timemodified,
779 'timestarted' => $submissionrecord->timestarted,
780 'status' => $submissionrecord->status,
781 'attemptnumber' => $submissionrecord->attemptnumber,
782 'groupid' => $submissionrecord->groupid,
783 'plugins' => self::get_plugins_data($assign, $submissionplugins, $submissionrecord),
784 'gradingstatus' => $assign->get_grading_status($submissionrecord->userid)
787 if (($assign->get_instance()->teamsubmission
788 && $assign->can_view_group_submission($submissionrecord->groupid))
789 || (!$assign->get_instance()->teamsubmission
790 && $assign->can_view_submission($submissionrecord->userid))
792 $submissions[] = $submission;
795 } else {
796 $warnings[] = array(
797 'item' => 'module',
798 'itemid' => $assign->get_instance()->id,
799 'warningcode' => '3',
800 'message' => 'No submissions found'
804 $assignments[] = array(
805 'assignmentid' => $assign->get_instance()->id,
806 'submissions' => $submissions
811 $result = array(
812 'assignments' => $assignments,
813 'warnings' => $warnings
815 return $result;
819 * Creates an assignment plugin structure.
821 * @return external_single_structure the plugin structure
823 private static function get_plugin_structure() {
824 return new external_single_structure(
825 array(
826 'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
827 'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
828 'fileareas' => new external_multiple_structure(
829 new external_single_structure(
830 array (
831 'area' => new external_value (PARAM_TEXT, 'file area'),
832 'files' => new external_files('files', VALUE_OPTIONAL),
834 ), 'fileareas', VALUE_OPTIONAL
836 'editorfields' => new external_multiple_structure(
837 new external_single_structure(
838 array(
839 'name' => new external_value(PARAM_TEXT, 'field name'),
840 'description' => new external_value(PARAM_RAW, 'field description'),
841 'text' => new external_value (PARAM_RAW, 'field value'),
842 'format' => new external_format_value ('text')
845 , 'editorfields', VALUE_OPTIONAL
852 * Creates a submission structure.
854 * @return external_single_structure the submission structure
856 private static function get_submission_structure($required = VALUE_REQUIRED) {
857 return new external_single_structure(
858 array(
859 'id' => new external_value(PARAM_INT, 'submission id'),
860 'userid' => new external_value(PARAM_INT, 'student id'),
861 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
862 'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
863 'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
864 'timestarted' => new external_value(PARAM_INT, 'submission start time', VALUE_OPTIONAL),
865 'status' => new external_value(PARAM_TEXT, 'submission status'),
866 'groupid' => new external_value(PARAM_INT, 'group id'),
867 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
868 'latest' => new external_value(PARAM_INT, 'latest attempt', VALUE_OPTIONAL),
869 'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'plugins', VALUE_OPTIONAL),
870 'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.', VALUE_OPTIONAL),
871 ), 'submission info', $required
876 * Creates an assign_submissions external_single_structure
878 * @return external_single_structure
879 * @since Moodle 2.5
881 private static function get_submissions_structure() {
882 return new external_single_structure(
883 array (
884 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
885 'submissions' => new external_multiple_structure(self::get_submission_structure())
891 * Describes the get_submissions return value
893 * @return external_single_structure
894 * @since Moodle 2.5
896 public static function get_submissions_returns() {
897 return new external_single_structure(
898 array(
899 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
900 'warnings' => new external_warnings()
906 * Describes the parameters for set_user_flags
907 * @return external_function_parameters
908 * @since Moodle 2.6
910 public static function set_user_flags_parameters() {
911 return new external_function_parameters(
912 array(
913 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
914 'userflags' => new external_multiple_structure(
915 new external_single_structure(
916 array(
917 'userid' => new external_value(PARAM_INT, 'student id'),
918 'locked' => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL),
919 'mailed' => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL),
920 'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL),
921 'workflowstate' => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL),
922 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL)
931 * Create or update user_flags records
933 * @param int $assignmentid the assignment for which the userflags are created or updated
934 * @param array $userflags An array of userflags to create or update
935 * @return array containing success or failure information for each record
936 * @since Moodle 2.6
938 public static function set_user_flags($assignmentid, $userflags = array()) {
939 global $CFG, $DB;
941 $params = self::validate_parameters(self::set_user_flags_parameters(),
942 array('assignmentid' => $assignmentid,
943 'userflags' => $userflags));
945 // Load assignment if it exists and if the user has the capability.
946 list($assign, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
947 require_capability('mod/assign:grade', $context);
949 $results = array();
950 foreach ($params['userflags'] as $userflag) {
951 $success = true;
952 $result = array();
954 $record = $assign->get_user_flags($userflag['userid'], false);
955 if ($record) {
956 if (isset($userflag['locked'])) {
957 $record->locked = $userflag['locked'];
959 if (isset($userflag['mailed'])) {
960 $record->mailed = $userflag['mailed'];
962 if (isset($userflag['extensionduedate'])) {
963 $record->extensionduedate = $userflag['extensionduedate'];
965 if (isset($userflag['workflowstate'])) {
966 $record->workflowstate = $userflag['workflowstate'];
968 if (isset($userflag['allocatedmarker'])) {
969 $record->allocatedmarker = $userflag['allocatedmarker'];
971 if ($assign->update_user_flags($record)) {
972 $result['id'] = $record->id;
973 $result['userid'] = $userflag['userid'];
974 } else {
975 $result['id'] = $record->id;
976 $result['userid'] = $userflag['userid'];
977 $result['errormessage'] = 'Record created but values could not be set';
979 } else {
980 $record = $assign->get_user_flags($userflag['userid'], true);
981 $setfields = isset($userflag['locked'])
982 || isset($userflag['mailed'])
983 || isset($userflag['extensionduedate'])
984 || isset($userflag['workflowstate'])
985 || isset($userflag['allocatedmarker']);
986 if ($record) {
987 if ($setfields) {
988 if (isset($userflag['locked'])) {
989 $record->locked = $userflag['locked'];
991 if (isset($userflag['mailed'])) {
992 $record->mailed = $userflag['mailed'];
994 if (isset($userflag['extensionduedate'])) {
995 $record->extensionduedate = $userflag['extensionduedate'];
997 if (isset($userflag['workflowstate'])) {
998 $record->workflowstate = $userflag['workflowstate'];
1000 if (isset($userflag['allocatedmarker'])) {
1001 $record->allocatedmarker = $userflag['allocatedmarker'];
1003 if ($assign->update_user_flags($record)) {
1004 $result['id'] = $record->id;
1005 $result['userid'] = $userflag['userid'];
1006 } else {
1007 $result['id'] = $record->id;
1008 $result['userid'] = $userflag['userid'];
1009 $result['errormessage'] = 'Record created but values could not be set';
1011 } else {
1012 $result['id'] = $record->id;
1013 $result['userid'] = $userflag['userid'];
1015 } else {
1016 $result['id'] = -1;
1017 $result['userid'] = $userflag['userid'];
1018 $result['errormessage'] = 'Record could not be created';
1022 $results[] = $result;
1024 return $results;
1028 * Describes the set_user_flags return value
1029 * @return external_multiple_structure
1030 * @since Moodle 2.6
1032 public static function set_user_flags_returns() {
1033 return new external_multiple_structure(
1034 new external_single_structure(
1035 array(
1036 'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'),
1037 'userid' => new external_value(PARAM_INT, 'userid of record'),
1038 'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL)
1045 * Describes the parameters for get_user_flags
1046 * @return external_function_parameters
1047 * @since Moodle 2.6
1049 public static function get_user_flags_parameters() {
1050 return new external_function_parameters(
1051 array(
1052 'assignmentids' => new external_multiple_structure(
1053 new external_value(PARAM_INT, 'assignment id'),
1054 '1 or more assignment ids',
1055 VALUE_REQUIRED)
1061 * Returns user flag information from assign_user_flags for the requested assignment ids
1062 * @param int[] $assignmentids
1063 * @return array of user flag records for each requested assignment
1064 * @since Moodle 2.6
1066 public static function get_user_flags($assignmentids) {
1067 global $DB;
1068 $params = self::validate_parameters(self::get_user_flags_parameters(),
1069 array('assignmentids' => $assignmentids));
1071 $assignments = array();
1072 $warnings = array();
1073 $requestedassignmentids = $params['assignmentids'];
1075 // Check the user is allowed to get the user flags for the assignments requested.
1076 $placeholders = array();
1077 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1078 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1079 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1080 $placeholders['modname'] = 'assign';
1081 $cms = $DB->get_records_sql($sql, $placeholders);
1082 foreach ($cms as $cm) {
1083 try {
1084 $context = context_module::instance($cm->id);
1085 self::validate_context($context);
1086 require_capability('mod/assign:grade', $context);
1087 } catch (Exception $e) {
1088 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1089 $warning = array();
1090 $warning['item'] = 'assignment';
1091 $warning['itemid'] = $cm->instance;
1092 $warning['warningcode'] = '1';
1093 $warning['message'] = 'No access rights in module context';
1094 $warnings[] = $warning;
1098 // Create the query and populate an array of assign_user_flags records from the recordset results.
1099 if (count ($requestedassignmentids) > 0) {
1100 $placeholders = array();
1101 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1103 $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
1104 "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
1105 "FROM {assign_user_flags} auf ".
1106 "WHERE auf.assignment ".$inorequalsql.
1107 " ORDER BY auf.assignment, auf.id";
1109 $rs = $DB->get_recordset_sql($sql, $placeholders);
1110 $currentassignmentid = null;
1111 $assignment = null;
1112 foreach ($rs as $rd) {
1113 $userflag = array();
1114 $userflag['id'] = $rd->id;
1115 $userflag['userid'] = $rd->userid;
1116 $userflag['locked'] = $rd->locked;
1117 $userflag['mailed'] = $rd->mailed;
1118 $userflag['extensionduedate'] = $rd->extensionduedate;
1119 $userflag['workflowstate'] = $rd->workflowstate;
1120 $userflag['allocatedmarker'] = $rd->allocatedmarker;
1122 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1123 if (!is_null($assignment)) {
1124 $assignments[] = $assignment;
1126 $assignment = array();
1127 $assignment['assignmentid'] = $rd->assignment;
1128 $assignment['userflags'] = array();
1129 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1131 $assignment['userflags'][] = $userflag;
1133 $currentassignmentid = $rd->assignment;
1135 if (!is_null($assignment)) {
1136 $assignments[] = $assignment;
1138 $rs->close();
1142 foreach ($requestedassignmentids as $assignmentid) {
1143 $warning = array();
1144 $warning['item'] = 'assignment';
1145 $warning['itemid'] = $assignmentid;
1146 $warning['warningcode'] = '3';
1147 $warning['message'] = 'No user flags found';
1148 $warnings[] = $warning;
1151 $result = array();
1152 $result['assignments'] = $assignments;
1153 $result['warnings'] = $warnings;
1154 return $result;
1158 * Creates an assign_user_flags external_single_structure
1159 * @return external_single_structure
1160 * @since Moodle 2.6
1162 private static function assign_user_flags() {
1163 return new external_single_structure(
1164 array (
1165 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
1166 'userflags' => new external_multiple_structure(new external_single_structure(
1167 array(
1168 'id' => new external_value(PARAM_INT, 'user flag id'),
1169 'userid' => new external_value(PARAM_INT, 'student id'),
1170 'locked' => new external_value(PARAM_INT, 'locked'),
1171 'mailed' => new external_value(PARAM_INT, 'mailed'),
1172 'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
1173 'workflowstate' => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL),
1174 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker')
1183 * Describes the get_user_flags return value
1184 * @return external_single_structure
1185 * @since Moodle 2.6
1187 public static function get_user_flags_returns() {
1188 return new external_single_structure(
1189 array(
1190 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
1191 'warnings' => new external_warnings('item is always \'assignment\'',
1192 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1193 'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
1199 * Describes the parameters for get_user_mappings
1200 * @return external_function_parameters
1201 * @since Moodle 2.6
1203 public static function get_user_mappings_parameters() {
1204 return new external_function_parameters(
1205 array(
1206 'assignmentids' => new external_multiple_structure(
1207 new external_value(PARAM_INT, 'assignment id'),
1208 '1 or more assignment ids',
1209 VALUE_REQUIRED)
1215 * Returns user mapping information from assign_user_mapping for the requested assignment ids
1216 * @param int[] $assignmentids
1217 * @return array of user mapping records for each requested assignment
1218 * @since Moodle 2.6
1220 public static function get_user_mappings($assignmentids) {
1221 global $DB;
1222 $params = self::validate_parameters(self::get_user_mappings_parameters(),
1223 array('assignmentids' => $assignmentids));
1225 $assignments = array();
1226 $warnings = array();
1227 $requestedassignmentids = $params['assignmentids'];
1229 // Check the user is allowed to get the mappings for the assignments requested.
1230 $placeholders = array();
1231 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1232 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1233 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1234 $placeholders['modname'] = 'assign';
1235 $cms = $DB->get_records_sql($sql, $placeholders);
1236 foreach ($cms as $cm) {
1237 try {
1238 $context = context_module::instance($cm->id);
1239 self::validate_context($context);
1240 require_capability('mod/assign:revealidentities', $context);
1241 } catch (Exception $e) {
1242 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1243 $warning = array();
1244 $warning['item'] = 'assignment';
1245 $warning['itemid'] = $cm->instance;
1246 $warning['warningcode'] = '1';
1247 $warning['message'] = 'No access rights in module context';
1248 $warnings[] = $warning;
1252 // Create the query and populate an array of assign_user_mapping records from the recordset results.
1253 if (count ($requestedassignmentids) > 0) {
1254 $placeholders = array();
1255 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1257 $sql = "SELECT aum.id,aum.assignment,aum.userid ".
1258 "FROM {assign_user_mapping} aum ".
1259 "WHERE aum.assignment ".$inorequalsql.
1260 " ORDER BY aum.assignment, aum.id";
1262 $rs = $DB->get_recordset_sql($sql, $placeholders);
1263 $currentassignmentid = null;
1264 $assignment = null;
1265 foreach ($rs as $rd) {
1266 $mapping = array();
1267 $mapping['id'] = $rd->id;
1268 $mapping['userid'] = $rd->userid;
1270 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1271 if (!is_null($assignment)) {
1272 $assignments[] = $assignment;
1274 $assignment = array();
1275 $assignment['assignmentid'] = $rd->assignment;
1276 $assignment['mappings'] = array();
1277 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1279 $assignment['mappings'][] = $mapping;
1281 $currentassignmentid = $rd->assignment;
1283 if (!is_null($assignment)) {
1284 $assignments[] = $assignment;
1286 $rs->close();
1290 foreach ($requestedassignmentids as $assignmentid) {
1291 $warning = array();
1292 $warning['item'] = 'assignment';
1293 $warning['itemid'] = $assignmentid;
1294 $warning['warningcode'] = '3';
1295 $warning['message'] = 'No mappings found';
1296 $warnings[] = $warning;
1299 $result = array();
1300 $result['assignments'] = $assignments;
1301 $result['warnings'] = $warnings;
1302 return $result;
1306 * Creates an assign_user_mappings external_single_structure
1307 * @return external_single_structure
1308 * @since Moodle 2.6
1310 private static function assign_user_mappings() {
1311 return new external_single_structure(
1312 array (
1313 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
1314 'mappings' => new external_multiple_structure(new external_single_structure(
1315 array(
1316 'id' => new external_value(PARAM_INT, 'user mapping id'),
1317 'userid' => new external_value(PARAM_INT, 'student id')
1326 * Describes the get_user_mappings return value
1327 * @return external_single_structure
1328 * @since Moodle 2.6
1330 public static function get_user_mappings_returns() {
1331 return new external_single_structure(
1332 array(
1333 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
1334 'warnings' => new external_warnings('item is always \'assignment\'',
1335 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1336 'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
1342 * Describes the parameters for lock_submissions
1343 * @return external_function_parameters
1344 * @since Moodle 2.6
1346 public static function lock_submissions_parameters() {
1347 return new external_function_parameters(
1348 array(
1349 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1350 'userids' => new external_multiple_structure(
1351 new external_value(PARAM_INT, 'user id'),
1352 '1 or more user ids',
1353 VALUE_REQUIRED),
1359 * Locks (prevent updates to) submissions in this assignment.
1361 * @param int $assignmentid The id of the assignment
1362 * @param array $userids Array of user ids to lock
1363 * @return array of warnings for each submission that could not be locked.
1364 * @since Moodle 2.6
1366 public static function lock_submissions($assignmentid, $userids) {
1367 global $CFG;
1369 $params = self::validate_parameters(self::lock_submissions_parameters(),
1370 array('assignmentid' => $assignmentid,
1371 'userids' => $userids));
1373 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1375 $warnings = array();
1376 foreach ($params['userids'] as $userid) {
1377 if (!$assignment->lock_submission($userid)) {
1378 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1379 $warnings[] = self::generate_warning($params['assignmentid'],
1380 'couldnotlock',
1381 $detail);
1385 return $warnings;
1389 * Describes the return value for lock_submissions
1391 * @return external_single_structure
1392 * @since Moodle 2.6
1394 public static function lock_submissions_returns() {
1395 return new external_warnings();
1399 * Describes the parameters for revert_submissions_to_draft
1400 * @return external_function_parameters
1401 * @since Moodle 2.6
1403 public static function revert_submissions_to_draft_parameters() {
1404 return new external_function_parameters(
1405 array(
1406 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1407 'userids' => new external_multiple_structure(
1408 new external_value(PARAM_INT, 'user id'),
1409 '1 or more user ids',
1410 VALUE_REQUIRED),
1416 * Reverts a list of user submissions to draft for a single assignment.
1418 * @param int $assignmentid The id of the assignment
1419 * @param array $userids Array of user ids to revert
1420 * @return array of warnings for each submission that could not be reverted.
1421 * @since Moodle 2.6
1423 public static function revert_submissions_to_draft($assignmentid, $userids) {
1424 global $CFG;
1426 $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
1427 array('assignmentid' => $assignmentid,
1428 'userids' => $userids));
1430 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1432 $warnings = array();
1433 foreach ($params['userids'] as $userid) {
1434 if (!$assignment->revert_to_draft($userid)) {
1435 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1436 $warnings[] = self::generate_warning($params['assignmentid'],
1437 'couldnotrevert',
1438 $detail);
1442 return $warnings;
1446 * Describes the return value for revert_submissions_to_draft
1448 * @return external_single_structure
1449 * @since Moodle 2.6
1451 public static function revert_submissions_to_draft_returns() {
1452 return new external_warnings();
1456 * Describes the parameters for unlock_submissions
1457 * @return external_function_parameters
1458 * @since Moodle 2.6
1460 public static function unlock_submissions_parameters() {
1461 return new external_function_parameters(
1462 array(
1463 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1464 'userids' => new external_multiple_structure(
1465 new external_value(PARAM_INT, 'user id'),
1466 '1 or more user ids',
1467 VALUE_REQUIRED),
1473 * Locks (prevent updates to) submissions in this assignment.
1475 * @param int $assignmentid The id of the assignment
1476 * @param array $userids Array of user ids to lock
1477 * @return array of warnings for each submission that could not be locked.
1478 * @since Moodle 2.6
1480 public static function unlock_submissions($assignmentid, $userids) {
1481 global $CFG;
1483 $params = self::validate_parameters(self::unlock_submissions_parameters(),
1484 array('assignmentid' => $assignmentid,
1485 'userids' => $userids));
1487 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1489 $warnings = array();
1490 foreach ($params['userids'] as $userid) {
1491 if (!$assignment->unlock_submission($userid)) {
1492 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1493 $warnings[] = self::generate_warning($params['assignmentid'],
1494 'couldnotunlock',
1495 $detail);
1499 return $warnings;
1503 * Describes the return value for unlock_submissions
1505 * @return external_single_structure
1506 * @since Moodle 2.6
1508 public static function unlock_submissions_returns() {
1509 return new external_warnings();
1513 * Describes the parameters for submit_grading_form webservice.
1514 * @return external_function_parameters
1515 * @since Moodle 3.1
1517 public static function submit_grading_form_parameters() {
1518 return new external_function_parameters(
1519 array(
1520 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1521 'userid' => new external_value(PARAM_INT, 'The user id the submission belongs to'),
1522 'jsonformdata' => new external_value(PARAM_RAW, 'The data from the grading form, encoded as a json array')
1528 * Submit the logged in users assignment for grading.
1530 * @param int $assignmentid The id of the assignment
1531 * @param int $userid The id of the user the submission belongs to.
1532 * @param string $jsonformdata The data from the form, encoded as a json array.
1533 * @return array of warnings to indicate any errors.
1534 * @since Moodle 3.1
1536 public static function submit_grading_form($assignmentid, $userid, $jsonformdata) {
1537 global $CFG, $USER;
1539 require_once($CFG->dirroot . '/mod/assign/locallib.php');
1540 require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1542 $params = self::validate_parameters(self::submit_grading_form_parameters(),
1543 array(
1544 'assignmentid' => $assignmentid,
1545 'userid' => $userid,
1546 'jsonformdata' => $jsonformdata
1549 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1551 $serialiseddata = json_decode($params['jsonformdata']);
1553 $data = array();
1554 parse_str($serialiseddata, $data);
1556 $warnings = array();
1558 $options = array(
1559 'userid' => $params['userid'],
1560 'attemptnumber' => $data['attemptnumber'],
1561 'rownum' => 0,
1562 'gradingpanel' => true
1565 if (WS_SERVER) {
1566 // Assume form submission if coming from WS.
1567 $USER->ignoresesskey = true;
1568 $data['_qf__mod_assign_grade_form_'.$params['userid']] = 1;
1571 $customdata = (object) $data;
1572 $formparams = array($assignment, $customdata, $options);
1574 // Data is injected into the form by the last param for the constructor.
1575 $mform = new mod_assign_grade_form(null, $formparams, 'post', '', null, true, $data);
1576 $validateddata = $mform->get_data();
1578 if ($validateddata) {
1579 $assignment->save_grade($params['userid'], $validateddata);
1580 } else {
1581 $warnings[] = self::generate_warning($params['assignmentid'],
1582 'couldnotsavegrade',
1583 'Form validation failed.');
1587 return $warnings;
1591 * Describes the return for submit_grading_form
1592 * @return external_function_parameters
1593 * @since Moodle 3.1
1595 public static function submit_grading_form_returns() {
1596 return new external_warnings();
1600 * Describes the parameters for submit_for_grading
1601 * @return external_function_parameters
1602 * @since Moodle 2.6
1604 public static function submit_for_grading_parameters() {
1605 return new external_function_parameters(
1606 array(
1607 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1608 'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement')
1614 * Submit the logged in users assignment for grading.
1616 * @param int $assignmentid The id of the assignment
1617 * @return array of warnings to indicate any errors.
1618 * @since Moodle 2.6
1620 public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
1621 global $CFG, $USER;
1623 $params = self::validate_parameters(self::submit_for_grading_parameters(),
1624 array('assignmentid' => $assignmentid,
1625 'acceptsubmissionstatement' => $acceptsubmissionstatement));
1627 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1629 $warnings = array();
1630 $data = new stdClass();
1631 $data->submissionstatement = $params['acceptsubmissionstatement'];
1632 $notices = array();
1634 if (!$assignment->submit_for_grading($data, $notices)) {
1635 $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices);
1636 $warnings[] = self::generate_warning($params['assignmentid'],
1637 'couldnotsubmitforgrading',
1638 $detail);
1641 return $warnings;
1645 * Describes the return value for submit_for_grading
1647 * @return external_single_structure
1648 * @since Moodle 2.6
1650 public static function submit_for_grading_returns() {
1651 return new external_warnings();
1655 * Describes the parameters for save_user_extensions
1656 * @return external_function_parameters
1657 * @since Moodle 2.6
1659 public static function save_user_extensions_parameters() {
1660 return new external_function_parameters(
1661 array(
1662 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1663 'userids' => new external_multiple_structure(
1664 new external_value(PARAM_INT, 'user id'),
1665 '1 or more user ids',
1666 VALUE_REQUIRED),
1667 'dates' => new external_multiple_structure(
1668 new external_value(PARAM_INT, 'dates'),
1669 '1 or more extension dates (timestamp)',
1670 VALUE_REQUIRED),
1676 * Grant extension dates to students for an assignment.
1678 * @param int $assignmentid The id of the assignment
1679 * @param array $userids Array of user ids to grant extensions to
1680 * @param array $dates Array of extension dates
1681 * @return array of warnings for each extension date that could not be granted
1682 * @since Moodle 2.6
1684 public static function save_user_extensions($assignmentid, $userids, $dates) {
1685 global $CFG;
1687 $params = self::validate_parameters(self::save_user_extensions_parameters(),
1688 array('assignmentid' => $assignmentid,
1689 'userids' => $userids,
1690 'dates' => $dates));
1692 if (count($params['userids']) != count($params['dates'])) {
1693 $detail = 'Length of userids and dates parameters differ.';
1694 $warnings[] = self::generate_warning($params['assignmentid'],
1695 'invalidparameters',
1696 $detail);
1698 return $warnings;
1701 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1703 $warnings = array();
1704 foreach ($params['userids'] as $idx => $userid) {
1705 $duedate = $params['dates'][$idx];
1706 if (!$assignment->save_user_extension($userid, $duedate)) {
1707 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate;
1708 $warnings[] = self::generate_warning($params['assignmentid'],
1709 'couldnotgrantextensions',
1710 $detail);
1714 return $warnings;
1718 * Describes the return value for save_user_extensions
1720 * @return external_single_structure
1721 * @since Moodle 2.6
1723 public static function save_user_extensions_returns() {
1724 return new external_warnings();
1728 * Describes the parameters for reveal_identities
1729 * @return external_function_parameters
1730 * @since Moodle 2.6
1732 public static function reveal_identities_parameters() {
1733 return new external_function_parameters(
1734 array(
1735 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
1741 * Reveal the identities of anonymous students to markers for a single assignment.
1743 * @param int $assignmentid The id of the assignment
1744 * @return array of warnings to indicate any errors.
1745 * @since Moodle 2.6
1747 public static function reveal_identities($assignmentid) {
1748 global $CFG, $USER;
1750 $params = self::validate_parameters(self::reveal_identities_parameters(),
1751 array('assignmentid' => $assignmentid));
1753 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1755 $warnings = array();
1756 if (!$assignment->reveal_identities()) {
1757 $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'];
1758 $warnings[] = self::generate_warning($params['assignmentid'],
1759 'couldnotrevealidentities',
1760 $detail);
1763 return $warnings;
1767 * Describes the return value for reveal_identities
1769 * @return external_single_structure
1770 * @since Moodle 2.6
1772 public static function reveal_identities_returns() {
1773 return new external_warnings();
1777 * Describes the parameters for save_submission
1778 * @return external_function_parameters
1779 * @since Moodle 2.6
1781 public static function save_submission_parameters() {
1782 global $CFG;
1783 $instance = new assign(null, null, null);
1784 $pluginsubmissionparams = array();
1786 foreach ($instance->get_submission_plugins() as $plugin) {
1787 if ($plugin->is_visible()) {
1788 $pluginparams = $plugin->get_external_parameters();
1789 if (!empty($pluginparams)) {
1790 $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
1795 return new external_function_parameters(
1796 array(
1797 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1798 'plugindata' => new external_single_structure(
1799 $pluginsubmissionparams
1806 * Save a student submission for a single assignment
1808 * @param int $assignmentid The id of the assignment
1809 * @param array $plugindata - The submitted data for plugins
1810 * @return array of warnings to indicate any errors
1811 * @since Moodle 2.6
1813 public static function save_submission($assignmentid, $plugindata) {
1814 global $CFG, $USER;
1816 $params = self::validate_parameters(self::save_submission_parameters(),
1817 array('assignmentid' => $assignmentid,
1818 'plugindata' => $plugindata));
1820 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1822 $notices = array();
1824 $assignment->update_effective_access($USER->id);
1825 if (!$assignment->submissions_open($USER->id)) {
1826 $notices[] = get_string('duedatereached', 'assign');
1827 } else {
1828 $submissiondata = (object)$params['plugindata'];
1829 $assignment->save_submission($submissiondata, $notices);
1832 $warnings = array();
1833 foreach ($notices as $notice) {
1834 $warnings[] = self::generate_warning($params['assignmentid'],
1835 'couldnotsavesubmission',
1836 $notice);
1839 return $warnings;
1843 * Describes the return value for save_submission
1845 * @return external_single_structure
1846 * @since Moodle 2.6
1848 public static function save_submission_returns() {
1849 return new external_warnings();
1853 * Describes the parameters for save_grade
1854 * @return external_function_parameters
1855 * @since Moodle 2.6
1857 public static function save_grade_parameters() {
1858 global $CFG;
1859 require_once("$CFG->dirroot/grade/grading/lib.php");
1860 $instance = new assign(null, null, null);
1861 $pluginfeedbackparams = array();
1863 foreach ($instance->get_feedback_plugins() as $plugin) {
1864 if ($plugin->is_visible()) {
1865 $pluginparams = $plugin->get_external_parameters();
1866 if (!empty($pluginparams)) {
1867 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1872 $advancedgradingdata = array();
1873 $methods = array_keys(grading_manager::available_methods(false));
1874 foreach ($methods as $method) {
1875 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1876 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1877 if (!empty($details)) {
1878 $items = array();
1879 foreach ($details as $key => $value) {
1880 $value->required = VALUE_OPTIONAL;
1881 unset($value->content->keys['id']);
1882 $items[$key] = new external_multiple_structure (new external_single_structure(
1883 array(
1884 'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1885 'fillings' => $value
1889 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1893 return new external_function_parameters(
1894 array(
1895 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1896 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1897 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'),
1898 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1899 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
1900 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1901 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1902 'to all members ' .
1903 'of the group (for group assignments).'),
1904 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()),
1905 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1906 VALUE_DEFAULT, array())
1912 * Save a student grade for a single assignment.
1914 * @param int $assignmentid The id of the assignment
1915 * @param int $userid The id of the user
1916 * @param float $grade The grade (ignored if the assignment uses advanced grading)
1917 * @param int $attemptnumber The attempt number
1918 * @param bool $addattempt Allow another attempt
1919 * @param string $workflowstate New workflow state
1920 * @param bool $applytoall Apply the grade to all members of the group
1921 * @param array $plugindata Custom data used by plugins
1922 * @param array $advancedgradingdata Advanced grading data
1923 * @return null
1924 * @since Moodle 2.6
1926 public static function save_grade($assignmentid,
1927 $userid,
1928 $grade,
1929 $attemptnumber,
1930 $addattempt,
1931 $workflowstate,
1932 $applytoall,
1933 $plugindata = array(),
1934 $advancedgradingdata = array()) {
1935 global $CFG, $USER;
1937 $params = self::validate_parameters(self::save_grade_parameters(),
1938 array('assignmentid' => $assignmentid,
1939 'userid' => $userid,
1940 'grade' => $grade,
1941 'attemptnumber' => $attemptnumber,
1942 'workflowstate' => $workflowstate,
1943 'addattempt' => $addattempt,
1944 'applytoall' => $applytoall,
1945 'plugindata' => $plugindata,
1946 'advancedgradingdata' => $advancedgradingdata));
1948 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1950 $gradedata = (object)$params['plugindata'];
1952 $gradedata->addattempt = $params['addattempt'];
1953 $gradedata->attemptnumber = $params['attemptnumber'];
1954 $gradedata->workflowstate = $params['workflowstate'];
1955 $gradedata->applytoall = $params['applytoall'];
1956 $gradedata->grade = $params['grade'];
1958 if (!empty($params['advancedgradingdata'])) {
1959 $advancedgrading = array();
1960 $criteria = reset($params['advancedgradingdata']);
1961 foreach ($criteria as $key => $criterion) {
1962 $details = array();
1963 foreach ($criterion as $value) {
1964 foreach ($value['fillings'] as $filling) {
1965 $details[$value['criterionid']] = $filling;
1968 $advancedgrading[$key] = $details;
1970 $gradedata->advancedgrading = $advancedgrading;
1973 $assignment->save_grade($params['userid'], $gradedata);
1975 return null;
1979 * Describes the return value for save_grade
1981 * @return external_single_structure
1982 * @since Moodle 2.6
1984 public static function save_grade_returns() {
1985 return null;
1989 * Describes the parameters for save_grades
1990 * @return external_function_parameters
1991 * @since Moodle 2.7
1993 public static function save_grades_parameters() {
1994 global $CFG;
1995 require_once("$CFG->dirroot/grade/grading/lib.php");
1996 $instance = new assign(null, null, null);
1997 $pluginfeedbackparams = array();
1999 foreach ($instance->get_feedback_plugins() as $plugin) {
2000 if ($plugin->is_visible()) {
2001 $pluginparams = $plugin->get_external_parameters();
2002 if (!empty($pluginparams)) {
2003 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
2008 $advancedgradingdata = array();
2009 $methods = array_keys(grading_manager::available_methods(false));
2010 foreach ($methods as $method) {
2011 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
2012 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
2013 if (!empty($details)) {
2014 $items = array();
2015 foreach ($details as $key => $value) {
2016 $value->required = VALUE_OPTIONAL;
2017 unset($value->content->keys['id']);
2018 $items[$key] = new external_multiple_structure (new external_single_structure(
2019 array(
2020 'criterionid' => new external_value(PARAM_INT, 'criterion id'),
2021 'fillings' => $value
2025 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
2029 return new external_function_parameters(
2030 array(
2031 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
2032 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
2033 'to all members ' .
2034 'of the group (for group assignments).'),
2035 'grades' => new external_multiple_structure(
2036 new external_single_structure(
2037 array (
2038 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
2039 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '.
2040 'Ignored if advanced grading used'),
2041 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
2042 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'),
2043 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
2044 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data',
2045 VALUE_DEFAULT, array()),
2046 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
2047 VALUE_DEFAULT, array())
2056 * Save multiple student grades for a single assignment.
2058 * @param int $assignmentid The id of the assignment
2059 * @param boolean $applytoall If set to true and this is a team assignment,
2060 * apply the grade to all members of the group
2061 * @param array $grades grade data for one or more students that includes
2062 * userid - The id of the student being graded
2063 * grade - The grade (ignored if the assignment uses advanced grading)
2064 * attemptnumber - The attempt number
2065 * addattempt - Allow another attempt
2066 * workflowstate - New workflow state
2067 * plugindata - Custom data used by plugins
2068 * advancedgradingdata - Optional Advanced grading data
2069 * @throws invalid_parameter_exception if multiple grades are supplied for
2070 * a team assignment that has $applytoall set to true
2071 * @return null
2072 * @since Moodle 2.7
2074 public static function save_grades($assignmentid, $applytoall, $grades) {
2075 global $CFG, $USER;
2077 $params = self::validate_parameters(self::save_grades_parameters(),
2078 array('assignmentid' => $assignmentid,
2079 'applytoall' => $applytoall,
2080 'grades' => $grades));
2082 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
2084 if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
2085 // Check that only 1 user per submission group is provided.
2086 $groupids = array();
2087 foreach ($params['grades'] as $gradeinfo) {
2088 $group = $assignment->get_submission_group($gradeinfo['userid']);
2089 if (in_array($group->id, $groupids)) {
2090 throw new invalid_parameter_exception('Multiple grades for the same team have been supplied '
2091 .' this is not permitted when the applytoall flag is set');
2092 } else {
2093 $groupids[] = $group->id;
2098 foreach ($params['grades'] as $gradeinfo) {
2099 $gradedata = (object)$gradeinfo['plugindata'];
2100 $gradedata->addattempt = $gradeinfo['addattempt'];
2101 $gradedata->attemptnumber = $gradeinfo['attemptnumber'];
2102 $gradedata->workflowstate = $gradeinfo['workflowstate'];
2103 $gradedata->applytoall = $params['applytoall'];
2104 $gradedata->grade = $gradeinfo['grade'];
2106 if (!empty($gradeinfo['advancedgradingdata'])) {
2107 $advancedgrading = array();
2108 $criteria = reset($gradeinfo['advancedgradingdata']);
2109 foreach ($criteria as $key => $criterion) {
2110 $details = array();
2111 foreach ($criterion as $value) {
2112 foreach ($value['fillings'] as $filling) {
2113 $details[$value['criterionid']] = $filling;
2116 $advancedgrading[$key] = $details;
2118 $gradedata->advancedgrading = $advancedgrading;
2120 $assignment->save_grade($gradeinfo['userid'], $gradedata);
2123 return null;
2127 * Describes the return value for save_grades
2129 * @return external_single_structure
2130 * @since Moodle 2.7
2132 public static function save_grades_returns() {
2133 return null;
2137 * Describes the parameters for copy_previous_attempt
2138 * @return external_function_parameters
2139 * @since Moodle 2.6
2141 public static function copy_previous_attempt_parameters() {
2142 return new external_function_parameters(
2143 array(
2144 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
2150 * Copy a students previous attempt to a new attempt.
2152 * @param int $assignmentid
2153 * @return array of warnings to indicate any errors.
2154 * @since Moodle 2.6
2156 public static function copy_previous_attempt($assignmentid) {
2158 $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
2159 array('assignmentid' => $assignmentid));
2161 list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
2163 $notices = array();
2165 $assignment->copy_previous_attempt($notices);
2167 $warnings = array();
2168 foreach ($notices as $notice) {
2169 $warnings[] = self::generate_warning($assignmentid,
2170 'couldnotcopyprevioussubmission',
2171 $notice);
2174 return $warnings;
2178 * Describes the return value for save_submission
2180 * @return external_single_structure
2181 * @since Moodle 2.6
2183 public static function copy_previous_attempt_returns() {
2184 return new external_warnings();
2188 * Returns description of method parameters
2190 * @return external_function_parameters
2191 * @since Moodle 3.0
2193 public static function view_grading_table_parameters() {
2194 return new external_function_parameters(
2195 array(
2196 'assignid' => new external_value(PARAM_INT, 'assign instance id')
2202 * Trigger the grading_table_viewed event.
2204 * @param int $assignid the assign instance id
2205 * @return array of warnings and status result
2206 * @since Moodle 3.0
2207 * @throws moodle_exception
2209 public static function view_grading_table($assignid) {
2211 $params = self::validate_parameters(self::view_grading_table_parameters(),
2212 array(
2213 'assignid' => $assignid
2215 $warnings = array();
2217 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2219 $assign->require_view_grades();
2220 \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger();
2222 $result = array();
2223 $result['status'] = true;
2224 $result['warnings'] = $warnings;
2225 return $result;
2229 * Returns description of method result value
2231 * @return external_description
2232 * @since Moodle 3.0
2234 public static function view_grading_table_returns() {
2235 return new external_single_structure(
2236 array(
2237 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2238 'warnings' => new external_warnings()
2244 * Describes the parameters for view_submission_status.
2246 * @return external_function_parameters
2247 * @since Moodle 3.1
2249 public static function view_submission_status_parameters() {
2250 return new external_function_parameters (
2251 array(
2252 'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2258 * Trigger the submission status viewed event.
2260 * @param int $assignid assign instance id
2261 * @return array of warnings and status result
2262 * @since Moodle 3.1
2264 public static function view_submission_status($assignid) {
2266 $warnings = array();
2267 $params = array(
2268 'assignid' => $assignid,
2270 $params = self::validate_parameters(self::view_submission_status_parameters(), $params);
2272 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2274 \mod_assign\event\submission_status_viewed::create_from_assign($assign)->trigger();
2276 $result = array();
2277 $result['status'] = true;
2278 $result['warnings'] = $warnings;
2279 return $result;
2283 * Describes the view_submission_status return value.
2285 * @return external_single_structure
2286 * @since Moodle 3.1
2288 public static function view_submission_status_returns() {
2289 return new external_single_structure(
2290 array(
2291 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2292 'warnings' => new external_warnings(),
2298 * Describes the parameters for get_submission_status.
2300 * @return external_function_parameters
2301 * @since Moodle 3.1
2303 public static function get_submission_status_parameters() {
2304 return new external_function_parameters (
2305 array(
2306 'assignid' => new external_value(PARAM_INT, 'assignment instance id'),
2307 'userid' => new external_value(PARAM_INT, 'user id (empty for current user)', VALUE_DEFAULT, 0),
2308 'groupid' => new external_value(PARAM_INT, 'filter by users in group (used for generating the grading summary).
2309 0 for all groups information, any other empty value will calculate currrent group.', VALUE_DEFAULT, 0),
2315 * Returns information about an assignment submission status for a given user.
2317 * @param int $assignid assignment instance id
2318 * @param int $userid user id (empty for current user)
2319 * @param int $groupid filter by users in group id (used for generating the grading summary). Use 0 for all groups information.
2320 * @return array of warnings and grading, status, feedback and previous attempts information
2321 * @since Moodle 3.1
2322 * @throws required_capability_exception
2324 public static function get_submission_status($assignid, $userid = 0, $groupid = 0) {
2325 global $USER;
2327 $warnings = array();
2329 $params = array(
2330 'assignid' => $assignid,
2331 'userid' => $userid,
2332 'groupid' => $groupid,
2334 $params = self::validate_parameters(self::get_submission_status_parameters(), $params);
2336 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2338 // Default value for userid.
2339 if (empty($params['userid'])) {
2340 $params['userid'] = $USER->id;
2342 $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2343 core_user::require_active_user($user);
2345 if (!$assign->can_view_submission($user->id)) {
2346 throw new required_capability_exception($context, 'mod/assign:viewgrades', 'nopermission', '');
2349 $assign->update_effective_access($user->id);
2351 $gradingsummary = $lastattempt = $feedback = $previousattempts = null;
2353 // Get the renderable since it contais all the info we need.
2354 if (!empty($params['groupid'])) {
2355 $groupid = $params['groupid'];
2356 // Determine is the group is visible to user.
2357 if (!groups_group_visible($groupid, $course, $cm)) {
2358 throw new moodle_exception('notingroup');
2360 } else {
2361 // A null group means that following functions will calculate the current group.
2362 // A groupid set to 0 means all groups.
2363 $groupid = ($params['groupid'] == 0) ? 0 : null;
2365 if ($assign->can_view_grades($groupid)) {
2366 $gradingsummary = $assign->get_assign_grading_summary_renderable($groupid);
2369 // Retrieve the rest of the renderable objects.
2370 if (has_capability('mod/assign:viewownsubmissionsummary', $context, $user, false)) {
2371 // The user can view the submission summary.
2372 $lastattempt = $assign->get_assign_submission_status_renderable($user, true);
2375 $feedback = $assign->get_assign_feedback_status_renderable($user);
2377 $previousattempts = $assign->get_assign_attempt_history_renderable($user);
2379 // Now, build the result.
2380 $result = array();
2382 // First of all, grading summary, this is suitable for teachers/managers.
2383 if ($gradingsummary) {
2384 $result['gradingsummary'] = $gradingsummary;
2386 // Show the grader's identity if 'Hide Grader' is disabled or has the 'Show Hidden Grader' capability.
2387 $showgradername = (has_capability('mod/assign:showhiddengrader', $context) or
2388 !$assign->is_hidden_grader());
2390 // Did we submit anything?
2391 if ($lastattempt) {
2392 $submissionplugins = $assign->get_submission_plugins();
2394 if (empty($lastattempt->submission)) {
2395 unset($lastattempt->submission);
2396 } else {
2397 $lastattempt->submission->plugins = self::get_plugins_data($assign, $submissionplugins, $lastattempt->submission);
2400 if (empty($lastattempt->teamsubmission)) {
2401 unset($lastattempt->teamsubmission);
2402 } else {
2403 $lastattempt->teamsubmission->plugins = self::get_plugins_data($assign, $submissionplugins,
2404 $lastattempt->teamsubmission);
2407 // We need to change the type of some of the structures retrieved from the renderable.
2408 if (!empty($lastattempt->submissiongroup)) {
2409 $lastattempt->submissiongroup = $lastattempt->submissiongroup->id;
2410 } else {
2411 unset($lastattempt->submissiongroup);
2414 if (!empty($lastattempt->usergroups)) {
2415 $lastattempt->usergroups = array_keys($lastattempt->usergroups);
2417 // We cannot use array_keys here.
2418 if (!empty($lastattempt->submissiongroupmemberswhoneedtosubmit)) {
2419 $lastattempt->submissiongroupmemberswhoneedtosubmit = array_map(
2420 function($e){
2421 return $e->id;
2423 $lastattempt->submissiongroupmemberswhoneedtosubmit);
2426 // Can edit its own submission?
2427 $lastattempt->caneditowner = has_capability('mod/assign:submit', $context, $user, false)
2428 && $assign->submissions_open($user->id) && $assign->is_any_submission_plugin_enabled();
2430 $result['lastattempt'] = $lastattempt;
2433 // The feedback for our latest submission.
2434 if ($feedback) {
2435 if ($feedback->grade) {
2436 if (!$showgradername) {
2437 $feedback->grade->grader = -1;
2439 $feedbackplugins = $assign->get_feedback_plugins();
2440 $feedback->plugins = self::get_plugins_data($assign, $feedbackplugins, $feedback->grade);
2441 } else {
2442 unset($feedback->plugins);
2443 unset($feedback->grade);
2446 $result['feedback'] = $feedback;
2449 // Retrieve only previous attempts.
2450 if ($previousattempts and count($previousattempts->submissions) > 1) {
2451 // Don't show the last one because it is the current submission.
2452 array_pop($previousattempts->submissions);
2454 // Show newest to oldest.
2455 $previousattempts->submissions = array_reverse($previousattempts->submissions);
2457 foreach ($previousattempts->submissions as $i => $submission) {
2458 $attempt = array();
2460 $grade = null;
2461 foreach ($previousattempts->grades as $onegrade) {
2462 if ($onegrade->attemptnumber == $submission->attemptnumber) {
2463 $grade = $onegrade;
2464 break;
2468 $attempt['attemptnumber'] = $submission->attemptnumber;
2470 if ($submission) {
2471 $submission->plugins = self::get_plugins_data($assign, $previousattempts->submissionplugins, $submission);
2472 $attempt['submission'] = $submission;
2475 if ($grade) {
2476 // From object to id.
2477 if (!$showgradername) {
2478 $grade->grader = -1;
2479 } else {
2480 $grade->grader = $grade->grader->id;
2483 $feedbackplugins = self::get_plugins_data($assign, $previousattempts->feedbackplugins, $grade);
2485 $attempt['grade'] = $grade;
2486 $attempt['feedbackplugins'] = $feedbackplugins;
2488 $result['previousattempts'][] = $attempt;
2492 // Send back some assignment data as well.
2493 $instance = $assign->get_instance();
2494 $assignmentdata = [];
2495 $attachments = [];
2496 if ($assign->should_provide_intro_attachments($user->id)) {
2497 $attachments['intro'] = external_util::get_area_files($context->id, 'mod_assign',
2498 ASSIGN_INTROATTACHMENT_FILEAREA, 0);
2500 if ($instance->activity && ($lastattempt || $assign->submissions_open($user->id, true))) {
2501 list($assignmentdata['activity'], $assignmentdata['activityformat']) = external_format_text($instance->activity,
2502 $instance->activityformat, $context->id, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0);
2503 $attachments['activity'] = external_util::get_area_files($context->id, 'mod_assign',
2504 ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0);
2506 if (!empty($attachments)) {
2507 $assignmentdata['attachments'] = $attachments;
2509 $result['assignmentdata'] = $assignmentdata;
2511 $result['warnings'] = $warnings;
2512 return $result;
2516 * Describes the get_submission_status return value.
2518 * @return external_single_structure
2519 * @since Moodle 3.1
2521 public static function get_submission_status_returns() {
2522 return new external_single_structure(
2523 array(
2524 'gradingsummary' => new external_single_structure(
2525 array(
2526 'participantcount' => new external_value(PARAM_INT, 'Number of users who can submit.'),
2527 'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'),
2528 'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'),
2529 'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'),
2530 'submissionssubmittedcount' => new external_value(PARAM_INT, 'Number of submissions in submitted status.'),
2531 'submissionsneedgradingcount' => new external_value(PARAM_INT, 'Number of submissions that need grading.'),
2532 'warnofungroupedusers' => new external_value(PARAM_ALPHA, 'Whether we need to warn people that there
2533 are users without groups (\'warningrequired\'), warn
2534 people there are users who will submit in the default
2535 group (\'warningoptional\') or no warning (\'\').'),
2536 ), 'Grading information.', VALUE_OPTIONAL
2538 'lastattempt' => new external_single_structure(
2539 array(
2540 'submission' => self::get_submission_structure(VALUE_OPTIONAL),
2541 'teamsubmission' => self::get_submission_structure(VALUE_OPTIONAL),
2542 'submissiongroup' => new external_value(PARAM_INT, 'The submission group id (for group submissions only).',
2543 VALUE_OPTIONAL),
2544 'submissiongroupmemberswhoneedtosubmit' => new external_multiple_structure(
2545 new external_value(PARAM_INT, 'USER id.'),
2546 'List of users who still need to submit (for group submissions only).',
2547 VALUE_OPTIONAL
2549 'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'),
2550 'locked' => new external_value(PARAM_BOOL, 'Whether new submissions are locked.'),
2551 'graded' => new external_value(PARAM_BOOL, 'Whether the submission is graded.'),
2552 'canedit' => new external_value(PARAM_BOOL, 'Whether the user can edit the current submission.'),
2553 'caneditowner' => new external_value(PARAM_BOOL, 'Whether the owner of the submission can edit it.'),
2554 'cansubmit' => new external_value(PARAM_BOOL, 'Whether the user can submit.'),
2555 'extensionduedate' => new external_value(PARAM_INT, 'Extension due date.'),
2556 'timelimit' => new external_value(PARAM_INT, 'Time limit for submission.', VALUE_OPTIONAL),
2557 'blindmarking' => new external_value(PARAM_BOOL, 'Whether blind marking is enabled.'),
2558 'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.'),
2559 'usergroups' => new external_multiple_structure(
2560 new external_value(PARAM_INT, 'Group id.'), 'User groups in the course.'
2562 ), 'Last attempt information.', VALUE_OPTIONAL
2564 'feedback' => new external_single_structure(
2565 array(
2566 'grade' => self::get_grade_structure(VALUE_OPTIONAL),
2567 'gradefordisplay' => new external_value(PARAM_RAW, 'Grade rendered into a format suitable for display.'),
2568 'gradeddate' => new external_value(PARAM_INT, 'The date the user was graded.'),
2569 'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'Plugins info.', VALUE_OPTIONAL),
2570 ), 'Feedback for the last attempt.', VALUE_OPTIONAL
2572 'previousattempts' => new external_multiple_structure(
2573 new external_single_structure(
2574 array(
2575 'attemptnumber' => new external_value(PARAM_INT, 'Attempt number.'),
2576 'submission' => self::get_submission_structure(VALUE_OPTIONAL),
2577 'grade' => self::get_grade_structure(VALUE_OPTIONAL),
2578 'feedbackplugins' => new external_multiple_structure(self::get_plugin_structure(), 'Feedback info.',
2579 VALUE_OPTIONAL),
2581 ), 'List all the previous attempts did by the user.', VALUE_OPTIONAL
2583 'assignmentdata' => new external_single_structure([
2584 'attachments' => new external_single_structure([
2585 'intro' => new external_files('Intro attachments files', VALUE_OPTIONAL),
2586 'activity' => new external_files('Activity attachments files', VALUE_OPTIONAL),
2587 ], 'Intro and activity attachments', VALUE_OPTIONAL),
2588 'activity' => new external_value(PARAM_RAW, 'Text of activity', VALUE_OPTIONAL),
2589 'activityformat' => new external_format_value('activity', VALUE_OPTIONAL),
2590 ], 'Extra information about assignment', VALUE_OPTIONAL),
2591 'warnings' => new external_warnings(),
2597 * Returns description of method parameters
2599 * @return external_function_parameters
2600 * @since Moodle 3.1
2602 public static function list_participants_parameters() {
2603 return new external_function_parameters(
2604 array(
2605 'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2606 'groupid' => new external_value(PARAM_INT, 'group id'),
2607 'filter' => new external_value(PARAM_RAW, 'search string to filter the results'),
2608 'skip' => new external_value(PARAM_INT, 'number of records to skip', VALUE_DEFAULT, 0),
2609 'limit' => new external_value(PARAM_INT, 'maximum number of records to return', VALUE_DEFAULT, 0),
2610 'onlyids' => new external_value(PARAM_BOOL, 'Do not return all user fields', VALUE_DEFAULT, false),
2611 'includeenrolments' => new external_value(PARAM_BOOL, 'Do return courses where the user is enrolled',
2612 VALUE_DEFAULT, true),
2613 'tablesort' => new external_value(PARAM_BOOL, 'Apply current user table sorting preferences.',
2614 VALUE_DEFAULT, false)
2620 * Retrieves the list of students to be graded for the assignment.
2622 * @param int $assignid the assign instance id
2623 * @param int $groupid the current group id
2624 * @param string $filter search string to filter the results.
2625 * @param int $skip Number of records to skip
2626 * @param int $limit Maximum number of records to return
2627 * @param bool $onlyids Only return user ids.
2628 * @param bool $includeenrolments Return courses where the user is enrolled.
2629 * @param bool $tablesort Apply current user table sorting params from the grading table.
2630 * @return array of warnings and status result
2631 * @since Moodle 3.1
2632 * @throws moodle_exception
2634 public static function list_participants($assignid, $groupid, $filter, $skip,
2635 $limit, $onlyids, $includeenrolments, $tablesort) {
2636 global $DB, $CFG;
2637 require_once($CFG->dirroot . "/mod/assign/locallib.php");
2638 require_once($CFG->dirroot . "/user/lib.php");
2640 $params = self::validate_parameters(self::list_participants_parameters(),
2641 array(
2642 'assignid' => $assignid,
2643 'groupid' => $groupid,
2644 'filter' => $filter,
2645 'skip' => $skip,
2646 'limit' => $limit,
2647 'onlyids' => $onlyids,
2648 'includeenrolments' => $includeenrolments,
2649 'tablesort' => $tablesort
2651 $warnings = array();
2653 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2655 require_capability('mod/assign:view', $context);
2657 $assign->require_view_grades();
2659 $participants = array();
2660 if (groups_group_visible($params['groupid'], $course, $cm)) {
2661 $participants = $assign->list_participants_with_filter_status_and_group($params['groupid'], $params['tablesort']);
2664 $userfields = user_get_default_fields();
2665 if (!$params['includeenrolments']) {
2666 // Remove enrolled courses from users fields to be returned.
2667 $key = array_search('enrolledcourses', $userfields);
2668 if ($key !== false) {
2669 unset($userfields[$key]);
2670 } else {
2671 throw new moodle_exception('invaliduserfield', 'error', '', 'enrolledcourses');
2675 $result = array();
2676 $index = 0;
2677 foreach ($participants as $record) {
2678 // Preserve the fullname set by the assignment.
2679 $fullname = $record->fullname;
2680 $searchable = $fullname;
2681 $match = false;
2682 if (empty($filter)) {
2683 $match = true;
2684 } else {
2685 $filter = core_text::strtolower($filter);
2686 $value = core_text::strtolower($searchable);
2687 if (is_string($value) && (core_text::strpos($value, $filter) !== false)) {
2688 $match = true;
2691 if ($match) {
2692 $index++;
2693 if ($index <= $params['skip']) {
2694 continue;
2696 if (($params['limit'] > 0) && (($index - $params['skip']) > $params['limit'])) {
2697 break;
2699 // Now we do the expensive lookup of user details because we completed the filtering.
2700 if (!$assign->is_blind_marking() && !$params['onlyids']) {
2701 $userdetails = user_get_user_details($record, $course, $userfields);
2702 } else {
2703 $userdetails = array('id' => $record->id);
2705 $userdetails['fullname'] = $fullname;
2706 $userdetails['submitted'] = $record->submitted;
2707 $userdetails['requiregrading'] = $record->requiregrading;
2708 $userdetails['grantedextension'] = $record->grantedextension;
2709 $userdetails['submissionstatus'] = $record->submissionstatus;
2710 if (!empty($record->groupid)) {
2711 $userdetails['groupid'] = $record->groupid;
2713 if (!empty($record->groupname)) {
2714 $userdetails['groupname'] = $record->groupname;
2716 // Unique id is required for blind marking.
2717 $userdetails['recordid'] = -1;
2718 if (!empty($record->recordid)) {
2719 $userdetails['recordid'] = $record->recordid;
2722 $result[] = $userdetails;
2725 return $result;
2729 * Returns the description of the results of the mod_assign_external::list_participants() method.
2731 * @return external_description
2732 * @since Moodle 3.1
2734 public static function list_participants_returns() {
2735 // Get user description.
2736 $userdesc = core_user_external::user_description();
2737 // List unneeded properties.
2738 $unneededproperties = [
2739 'auth', 'confirmed', 'lang', 'calendartype', 'theme', 'timezone', 'mailformat'
2741 // Remove unneeded properties for consistency with the previous version.
2742 foreach ($unneededproperties as $prop) {
2743 unset($userdesc->keys[$prop]);
2746 // Override property attributes for consistency with the previous version.
2747 $userdesc->keys['fullname']->type = PARAM_NOTAGS;
2748 $userdesc->keys['profileimageurlsmall']->required = VALUE_OPTIONAL;
2749 $userdesc->keys['profileimageurl']->required = VALUE_OPTIONAL;
2750 $userdesc->keys['email']->desc = 'Email address';
2751 $userdesc->keys['idnumber']->desc = 'The idnumber of the user';
2752 $userdesc->keys['recordid'] = new external_value(PARAM_INT, 'record id');
2754 // Define other keys.
2755 $otherkeys = [
2756 'groups' => new external_multiple_structure(
2757 new external_single_structure(
2759 'id' => new external_value(PARAM_INT, 'group id'),
2760 'name' => new external_value(PARAM_RAW, 'group name'),
2761 'description' => new external_value(PARAM_RAW, 'group description'),
2763 ), 'user groups', VALUE_OPTIONAL
2765 'roles' => new external_multiple_structure(
2766 new external_single_structure(
2768 'roleid' => new external_value(PARAM_INT, 'role id'),
2769 'name' => new external_value(PARAM_RAW, 'role name'),
2770 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
2771 'sortorder' => new external_value(PARAM_INT, 'role sortorder')
2773 ), 'user roles', VALUE_OPTIONAL
2775 'enrolledcourses' => new external_multiple_structure(
2776 new external_single_structure(
2778 'id' => new external_value(PARAM_INT, 'Id of the course'),
2779 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
2780 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
2782 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL
2784 'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'),
2785 'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'),
2786 'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'),
2787 'submissionstatus' => new external_value(PARAM_ALPHA, 'The submission status (new, draft, reopened or submitted).
2788 Empty when not submitted.', VALUE_OPTIONAL),
2789 'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL),
2790 'groupname' => new external_value(PARAM_NOTAGS, 'for group assignments this is the group name', VALUE_OPTIONAL),
2793 // Merge keys.
2794 $userdesc->keys = array_merge($userdesc->keys, $otherkeys);
2795 return new external_multiple_structure($userdesc);
2799 * Returns description of method parameters
2801 * @return external_function_parameters
2802 * @since Moodle 3.1
2804 public static function get_participant_parameters() {
2805 return new external_function_parameters(
2806 array(
2807 'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2808 'userid' => new external_value(PARAM_INT, 'user id'),
2809 'embeduser' => new external_value(PARAM_BOOL, 'user id', VALUE_DEFAULT, false),
2815 * Get the user participating in the given assignment. An error with code 'usernotincourse'
2816 * is thrown is the user isn't a participant of the given assignment.
2818 * @param int $assignid the assign instance id
2819 * @param int $userid the user id
2820 * @param bool $embeduser return user details (only applicable if not blind marking)
2821 * @return array of warnings and status result
2822 * @since Moodle 3.1
2823 * @throws moodle_exception
2825 public static function get_participant($assignid, $userid, $embeduser) {
2826 global $DB, $CFG;
2827 require_once($CFG->dirroot . "/mod/assign/locallib.php");
2828 require_once($CFG->dirroot . "/user/lib.php");
2830 $params = self::validate_parameters(self::get_participant_parameters(), array(
2831 'assignid' => $assignid,
2832 'userid' => $userid,
2833 'embeduser' => $embeduser
2836 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2837 $assign->require_view_grades();
2839 $participant = $assign->get_participant($params['userid']);
2841 // Update assign with override information.
2842 $assign->update_effective_access($params['userid']);
2844 if (!$participant) {
2845 // No participant found so we can return early.
2846 throw new moodle_exception('usernotincourse');
2849 $return = array(
2850 'id' => $participant->id,
2851 'fullname' => $participant->fullname,
2852 'submitted' => $participant->submitted,
2853 'requiregrading' => $participant->requiregrading,
2854 'grantedextension' => $participant->grantedextension,
2855 'submissionstatus' => $participant->submissionstatus,
2856 'blindmarking' => $assign->is_blind_marking(),
2857 'allowsubmissionsfromdate' => $assign->get_instance($userid)->allowsubmissionsfromdate,
2858 'duedate' => $assign->get_instance($userid)->duedate,
2859 'cutoffdate' => $assign->get_instance($userid)->cutoffdate,
2860 'duedatestr' => userdate($assign->get_instance($userid)->duedate, get_string('strftimedatetime', 'langconfig')),
2863 if (!empty($participant->groupid)) {
2864 $return['groupid'] = $participant->groupid;
2866 if (!empty($participant->groupname)) {
2867 $return['groupname'] = $participant->groupname;
2870 // Skip the expensive lookup of user detail if we're blind marking or the caller
2871 // hasn't asked for user details to be embedded.
2872 if (!$assign->is_blind_marking() && $embeduser) {
2873 if ($userdetails = user_get_user_details($participant, $course)) {
2874 $return['user'] = $userdetails;
2878 return $return;
2882 * Returns description of method result value
2884 * @return external_description
2885 * @since Moodle 3.1
2887 public static function get_participant_returns() {
2888 $userdescription = core_user_external::user_description();
2889 $userdescription->default = [];
2890 $userdescription->required = VALUE_OPTIONAL;
2892 return new external_single_structure(array(
2893 'id' => new external_value(PARAM_INT, 'ID of the user'),
2894 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
2895 'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'),
2896 'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'),
2897 'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'),
2898 'blindmarking' => new external_value(PARAM_BOOL, 'is blind marking enabled for this assignment'),
2899 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allowsubmissionsfromdate for the user'),
2900 'duedate' => new external_value(PARAM_INT, 'duedate for the user'),
2901 'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user'),
2902 'duedatestr' => new external_value(PARAM_TEXT, 'duedate for the user'),
2903 'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL),
2904 'groupname' => new external_value(PARAM_NOTAGS, 'for group assignments this is the group name', VALUE_OPTIONAL),
2905 'submissionstatus' => new external_value(PARAM_ALPHA, 'The submission status (new, draft, reopened or submitted).
2906 Empty when not submitted.', VALUE_OPTIONAL),
2907 'user' => $userdescription,
2912 * Describes the parameters for view_assign.
2914 * @return external_function_parameters
2915 * @since Moodle 3.2
2917 public static function view_assign_parameters() {
2918 return new external_function_parameters (
2919 array(
2920 'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2926 * Update the module completion status.
2928 * @param int $assignid assign instance id
2929 * @return array of warnings and status result
2930 * @since Moodle 3.2
2932 public static function view_assign($assignid) {
2933 $warnings = array();
2934 $params = array(
2935 'assignid' => $assignid,
2937 $params = self::validate_parameters(self::view_assign_parameters(), $params);
2939 list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2941 $assign->set_module_viewed();
2943 $result = array();
2944 $result['status'] = true;
2945 $result['warnings'] = $warnings;
2946 return $result;
2950 * Describes the view_assign return value.
2952 * @return external_single_structure
2953 * @since Moodle 3.2
2955 public static function view_assign_returns() {
2956 return new external_single_structure(
2957 array(
2958 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2959 'warnings' => new external_warnings(),