MDL-50930 completion: New WS core_completion_mark_course_self_completed
[moodle.git] / mod / assign / externallib.php
blobcd7be3042ddf46486e173bd0717132102e5c63f5
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");
30 /**
31 * Assign functions
32 * @copyright 2012 Paul Charsley
33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 class mod_assign_external extends external_api {
37 /**
38 * Generate a warning in a standard structure for a known failure.
40 * @param int $assignmentid - The assignment
41 * @param string $warningcode - The key for the warning message
42 * @param string $detail - A description of the error
43 * @return array - Warning structure containing item, itemid, warningcode, message
45 private static function generate_warning($assignmentid, $warningcode, $detail) {
46 $warningmessages = array(
47 'couldnotlock'=>'Could not lock the submission for this user.',
48 'couldnotunlock'=>'Could not unlock the submission for this user.',
49 'couldnotsubmitforgrading'=>'Could not submit assignment for grading.',
50 'couldnotrevealidentities'=>'Could not reveal identities.',
51 'couldnotgrantextensions'=>'Could not grant submission date extensions.',
52 'couldnotrevert'=>'Could not revert submission to draft.',
53 'invalidparameters'=>'Invalid parameters.',
54 'couldnotsavesubmission'=>'Could not save submission.',
55 'couldnotsavegrade'=>'Could not save grade.'
58 $message = $warningmessages[$warningcode];
59 if (empty($message)) {
60 $message = 'Unknown warning type.';
63 return array('item'=>$detail,
64 'itemid'=>$assignmentid,
65 'warningcode'=>$warningcode,
66 'message'=>$message);
69 /**
70 * Describes the parameters for get_grades
71 * @return external_external_function_parameters
72 * @since Moodle 2.4
74 public static function get_grades_parameters() {
75 return new external_function_parameters(
76 array(
77 'assignmentids' => new external_multiple_structure(
78 new external_value(PARAM_INT, 'assignment id'),
79 '1 or more assignment ids',
80 VALUE_REQUIRED),
81 'since' => new external_value(PARAM_INT,
82 'timestamp, only return records where timemodified >= since',
83 VALUE_DEFAULT, 0)
88 /**
89 * Returns grade information from assign_grades for the requested assignment ids
90 * @param int[] $assignmentids
91 * @param int $since only return records with timemodified >= since
92 * @return array of grade records for each requested assignment
93 * @since Moodle 2.4
95 public static function get_grades($assignmentids, $since = 0) {
96 global $DB;
97 $params = self::validate_parameters(self::get_grades_parameters(),
98 array('assignmentids' => $assignmentids,
99 'since' => $since));
101 $assignments = array();
102 $warnings = array();
103 $requestedassignmentids = $params['assignmentids'];
105 // Check the user is allowed to get the grades for the assignments requested.
106 $placeholders = array();
107 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
108 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
109 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
110 $placeholders['modname'] = 'assign';
111 $cms = $DB->get_records_sql($sql, $placeholders);
112 foreach ($cms as $cm) {
113 try {
114 $context = context_module::instance($cm->id);
115 self::validate_context($context);
116 require_capability('mod/assign:grade', $context);
117 } catch (Exception $e) {
118 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
119 $warning = array();
120 $warning['item'] = 'assignment';
121 $warning['itemid'] = $cm->instance;
122 $warning['warningcode'] = '1';
123 $warning['message'] = 'No access rights in module context';
124 $warnings[] = $warning;
128 // Create the query and populate an array of grade records from the recordset results.
129 if (count ($requestedassignmentids) > 0) {
130 $placeholders = array();
131 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
133 $sql = "SELECT ag.id,
134 ag.assignment,
135 ag.userid,
136 ag.timecreated,
137 ag.timemodified,
138 ag.grader,
139 ag.grade,
140 ag.attemptnumber
141 FROM {assign_grades} ag, {assign_submission} s
142 WHERE s.assignment $inorequalsql
143 AND s.userid = ag.userid
144 AND s.latest = 1
145 AND s.attemptnumber = ag.attemptnumber
146 AND ag.timemodified >= :since
147 AND ag.assignment = s.assignment
148 ORDER BY ag.assignment, ag.id";
150 $placeholders['since'] = $params['since'];
151 $rs = $DB->get_recordset_sql($sql, $placeholders);
152 $currentassignmentid = null;
153 $assignment = null;
154 foreach ($rs as $rd) {
155 $grade = array();
156 $grade['id'] = $rd->id;
157 $grade['userid'] = $rd->userid;
158 $grade['timecreated'] = $rd->timecreated;
159 $grade['timemodified'] = $rd->timemodified;
160 $grade['grader'] = $rd->grader;
161 $grade['attemptnumber'] = $rd->attemptnumber;
162 $grade['grade'] = (string)$rd->grade;
164 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
165 if (!is_null($assignment)) {
166 $assignments[] = $assignment;
168 $assignment = array();
169 $assignment['assignmentid'] = $rd->assignment;
170 $assignment['grades'] = array();
171 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
173 $assignment['grades'][] = $grade;
175 $currentassignmentid = $rd->assignment;
177 if (!is_null($assignment)) {
178 $assignments[] = $assignment;
180 $rs->close();
182 foreach ($requestedassignmentids as $assignmentid) {
183 $warning = array();
184 $warning['item'] = 'assignment';
185 $warning['itemid'] = $assignmentid;
186 $warning['warningcode'] = '3';
187 $warning['message'] = 'No grades found';
188 $warnings[] = $warning;
191 $result = array();
192 $result['assignments'] = $assignments;
193 $result['warnings'] = $warnings;
194 return $result;
198 * Creates an assign_grades external_single_structure
199 * @return external_single_structure
200 * @since Moodle 2.4
202 private static function assign_grades() {
203 return new external_single_structure(
204 array (
205 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
206 'grades' => new external_multiple_structure(new external_single_structure(
207 array(
208 'id' => new external_value(PARAM_INT, 'grade id'),
209 'userid' => new external_value(PARAM_INT, 'student id'),
210 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
211 'timecreated' => new external_value(PARAM_INT, 'grade creation time'),
212 'timemodified' => new external_value(PARAM_INT, 'grade last modified time'),
213 'grader' => new external_value(PARAM_INT, 'grader'),
214 'grade' => new external_value(PARAM_TEXT, 'grade')
223 * Describes the get_grades return value
224 * @return external_single_structure
225 * @since Moodle 2.4
227 public static function get_grades_returns() {
228 return new external_single_structure(
229 array(
230 'assignments' => new external_multiple_structure(self::assign_grades(), 'list of assignment grade information'),
231 'warnings' => new external_warnings('item is always \'assignment\'',
232 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
233 'errorcode can be 3 (no grades found) or 1 (no permission to get grades)')
239 * Returns description of method parameters
241 * @return external_function_parameters
242 * @since Moodle 2.4
244 public static function get_assignments_parameters() {
245 return new external_function_parameters(
246 array(
247 'courseids' => new external_multiple_structure(
248 new external_value(PARAM_INT, 'course id'),
249 '0 or more course ids',
250 VALUE_DEFAULT, array()
252 'capabilities' => new external_multiple_structure(
253 new external_value(PARAM_CAPABILITY, 'capability'),
254 'list of capabilities used to filter courses',
255 VALUE_DEFAULT, array()
262 * Returns an array of courses the user is enrolled in, and for each course all of the assignments that the user can
263 * view within that course.
265 * @param array $courseids An optional array of course ids. If provided only assignments within the given course
266 * will be returned. If the user is not enrolled in a given course a warning will be generated and returned.
267 * @param array $capabilities An array of additional capability checks you wish to be made on the course context.
268 * @return An array of courses and warnings.
269 * @since Moodle 2.4
271 public static function get_assignments($courseids = array(), $capabilities = array()) {
272 global $USER, $DB, $CFG;
273 require_once("$CFG->dirroot/mod/assign/locallib.php");
275 $params = self::validate_parameters(
276 self::get_assignments_parameters(),
277 array('courseids' => $courseids, 'capabilities' => $capabilities)
280 $warnings = array();
281 $fields = 'sortorder,shortname,fullname,timemodified';
282 $courses = enrol_get_users_courses($USER->id, true, $fields);
283 // Used to test for ids that have been requested but can't be returned.
284 if (count($params['courseids']) > 0) {
285 foreach ($params['courseids'] as $courseid) {
286 if (!in_array($courseid, array_keys($courses))) {
287 unset($courses[$courseid]);
288 $warnings[] = array(
289 'item' => 'course',
290 'itemid' => $courseid,
291 'warningcode' => '2',
292 'message' => 'User is not enrolled or does not have requested capability'
297 foreach ($courses as $id => $course) {
298 if (count($params['courseids']) > 0 && !in_array($id, $params['courseids'])) {
299 unset($courses[$id]);
301 $context = context_course::instance($id);
302 try {
303 self::validate_context($context);
304 } catch (Exception $e) {
305 unset($courses[$id]);
306 $warnings[] = array(
307 'item' => 'course',
308 'itemid' => $id,
309 'warningcode' => '1',
310 'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
312 continue;
314 if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) {
315 unset($courses[$id]);
318 $extrafields='m.id as assignmentid, ' .
319 'm.course, ' .
320 'm.nosubmissions, ' .
321 'm.submissiondrafts, ' .
322 'm.sendnotifications, '.
323 'm.sendlatenotifications, ' .
324 'm.sendstudentnotifications, ' .
325 'm.duedate, ' .
326 'm.allowsubmissionsfromdate, '.
327 'm.grade, ' .
328 'm.timemodified, '.
329 'm.completionsubmit, ' .
330 'm.cutoffdate, ' .
331 'm.teamsubmission, ' .
332 'm.requireallteammemberssubmit, '.
333 'm.teamsubmissiongroupingid, ' .
334 'm.blindmarking, ' .
335 'm.revealidentities, ' .
336 'm.attemptreopenmethod, '.
337 'm.maxattempts, ' .
338 'm.markingworkflow, ' .
339 'm.markingallocation, ' .
340 'm.requiresubmissionstatement, '.
341 'm.intro, '.
342 'm.introformat';
343 $coursearray = array();
344 foreach ($courses as $id => $course) {
345 $assignmentarray = array();
346 // Get a list of assignments for the course.
347 if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
348 foreach ($modules as $module) {
349 $context = context_module::instance($module->id);
350 try {
351 self::validate_context($context);
352 require_capability('mod/assign:view', $context);
353 } catch (Exception $e) {
354 $warnings[] = array(
355 'item' => 'module',
356 'itemid' => $module->id,
357 'warningcode' => '1',
358 'message' => 'No access rights in module context'
360 continue;
362 $configrecords = $DB->get_recordset('assign_plugin_config', array('assignment' => $module->assignmentid));
363 $configarray = array();
364 foreach ($configrecords as $configrecord) {
365 $configarray[] = array(
366 'id' => $configrecord->id,
367 'assignment' => $configrecord->assignment,
368 'plugin' => $configrecord->plugin,
369 'subtype' => $configrecord->subtype,
370 'name' => $configrecord->name,
371 'value' => $configrecord->value
374 $configrecords->close();
376 $assignment = array(
377 'id' => $module->assignmentid,
378 'cmid' => $module->id,
379 'course' => $module->course,
380 'name' => $module->name,
381 'nosubmissions' => $module->nosubmissions,
382 'submissiondrafts' => $module->submissiondrafts,
383 'sendnotifications' => $module->sendnotifications,
384 'sendlatenotifications' => $module->sendlatenotifications,
385 'sendstudentnotifications' => $module->sendstudentnotifications,
386 'duedate' => $module->duedate,
387 'allowsubmissionsfromdate' => $module->allowsubmissionsfromdate,
388 'grade' => $module->grade,
389 'timemodified' => $module->timemodified,
390 'completionsubmit' => $module->completionsubmit,
391 'cutoffdate' => $module->cutoffdate,
392 'teamsubmission' => $module->teamsubmission,
393 'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
394 'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
395 'blindmarking' => $module->blindmarking,
396 'revealidentities' => $module->revealidentities,
397 'attemptreopenmethod' => $module->attemptreopenmethod,
398 'maxattempts' => $module->maxattempts,
399 'markingworkflow' => $module->markingworkflow,
400 'markingallocation' => $module->markingallocation,
401 'requiresubmissionstatement' => $module->requiresubmissionstatement,
402 'configs' => $configarray
405 // Return or not intro and file attachments depending on the plugin settings.
406 $assign = new assign($context, null, null);
408 if ($assign->show_intro()) {
410 list($assignment['intro'], $assignment['introformat']) = external_format_text($module->intro,
411 $module->introformat, $context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0);
413 $fs = get_file_storage();
414 if ($files = $fs->get_area_files($context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA,
415 0, 'timemodified', false)) {
417 $assignment['introattachments'] = array();
418 foreach ($files as $file) {
419 $filename = $file->get_filename();
421 $assignment['introattachments'][] = array(
422 'filename' => $filename,
423 'mimetype' => $file->get_mimetype(),
424 'fileurl' => moodle_url::make_webservice_pluginfile_url(
425 $context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0, '/', $filename)->out(false)
431 $assignmentarray[] = $assignment;
434 $coursearray[]= array(
435 'id' => $courses[$id]->id,
436 'fullname' => $courses[$id]->fullname,
437 'shortname' => $courses[$id]->shortname,
438 'timemodified' => $courses[$id]->timemodified,
439 'assignments' => $assignmentarray
443 $result = array(
444 'courses' => $coursearray,
445 'warnings' => $warnings
447 return $result;
451 * Creates an assignment external_single_structure
453 * @return external_single_structure
454 * @since Moodle 2.4
456 private static function get_assignments_assignment_structure() {
457 return new external_single_structure(
458 array(
459 'id' => new external_value(PARAM_INT, 'assignment id'),
460 'cmid' => new external_value(PARAM_INT, 'course module id'),
461 'course' => new external_value(PARAM_INT, 'course id'),
462 'name' => new external_value(PARAM_TEXT, 'assignment name'),
463 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
464 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
465 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
466 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
467 'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'),
468 'duedate' => new external_value(PARAM_INT, 'assignment due date'),
469 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
470 'grade' => new external_value(PARAM_INT, 'grade type'),
471 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
472 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
473 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
474 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
475 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
476 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
477 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
478 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
479 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
480 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
481 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
482 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
483 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
484 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings'),
485 'intro' => new external_value(PARAM_RAW,
486 'assignment intro, not allways returned because it deppends on the activity configuration', VALUE_OPTIONAL),
487 'introformat' => new external_format_value('intro', VALUE_OPTIONAL),
488 'introattachments' => new external_multiple_structure(
489 new external_single_structure(
490 array (
491 'filename' => new external_value(PARAM_FILE, 'file name'),
492 'mimetype' => new external_value(PARAM_RAW, 'mime type'),
493 'fileurl' => new external_value(PARAM_URL, 'file download url')
495 ), 'intro attachments files', VALUE_OPTIONAL
497 ), 'assignment information object');
501 * Creates an assign_plugin_config external_single_structure
503 * @return external_single_structure
504 * @since Moodle 2.4
506 private static function get_assignments_config_structure() {
507 return new external_single_structure(
508 array(
509 'id' => new external_value(PARAM_INT, 'assign_plugin_config id'),
510 'assignment' => new external_value(PARAM_INT, 'assignment id'),
511 'plugin' => new external_value(PARAM_TEXT, 'plugin'),
512 'subtype' => new external_value(PARAM_TEXT, 'subtype'),
513 'name' => new external_value(PARAM_TEXT, 'name'),
514 'value' => new external_value(PARAM_TEXT, 'value')
515 ), 'assignment configuration object'
520 * Creates a course external_single_structure
522 * @return external_single_structure
523 * @since Moodle 2.4
525 private static function get_assignments_course_structure() {
526 return new external_single_structure(
527 array(
528 'id' => new external_value(PARAM_INT, 'course id'),
529 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
530 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
531 'timemodified' => new external_value(PARAM_INT, 'last time modified'),
532 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
533 ), 'course information object'
538 * Describes the return value for get_assignments
540 * @return external_single_structure
541 * @since Moodle 2.4
543 public static function get_assignments_returns() {
544 return new external_single_structure(
545 array(
546 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
547 'warnings' => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
548 'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
549 'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
555 * Describes the parameters for get_submissions
557 * @return external_external_function_parameters
558 * @since Moodle 2.5
560 public static function get_submissions_parameters() {
561 return new external_function_parameters(
562 array(
563 'assignmentids' => new external_multiple_structure(
564 new external_value(PARAM_INT, 'assignment id'),
565 '1 or more assignment ids',
566 VALUE_REQUIRED),
567 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
568 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
569 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
575 * Returns submissions for the requested assignment ids
577 * @param int[] $assignmentids
578 * @param string $status only return submissions with this status
579 * @param int $since only return submissions with timemodified >= since
580 * @param int $before only return submissions with timemodified <= before
581 * @return array of submissions for each requested assignment
582 * @since Moodle 2.5
584 public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
585 global $DB, $CFG;
586 require_once("$CFG->dirroot/mod/assign/locallib.php");
587 $params = self::validate_parameters(self::get_submissions_parameters(),
588 array('assignmentids' => $assignmentids,
589 'status' => $status,
590 'since' => $since,
591 'before' => $before));
593 $warnings = array();
594 $assignments = array();
596 // Check the user is allowed to get the submissions for the assignments requested.
597 $placeholders = array();
598 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
599 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
600 "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
601 $placeholders['modname'] = 'assign';
602 $cms = $DB->get_records_sql($sql, $placeholders);
603 $assigns = array();
604 foreach ($cms as $cm) {
605 try {
606 $context = context_module::instance($cm->id);
607 self::validate_context($context);
608 require_capability('mod/assign:grade', $context);
609 $assign = new assign($context, null, null);
610 $assigns[] = $assign;
611 } catch (Exception $e) {
612 $warnings[] = array(
613 'item' => 'assignment',
614 'itemid' => $cm->instance,
615 'warningcode' => '1',
616 'message' => 'No access rights in module context'
621 foreach ($assigns as $assign) {
622 $submissions = array();
623 $submissionplugins = $assign->get_submission_plugins();
624 $placeholders = array('assignid1' => $assign->get_instance()->id,
625 'assignid2' => $assign->get_instance()->id);
627 $submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
628 FROM {assign_submission} mxs
629 WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid';
631 $sql = "SELECT mas.id, mas.assignment,mas.userid,".
632 "mas.timecreated,mas.timemodified,mas.status,mas.groupid,mas.attemptnumber ".
633 "FROM {assign_submission} mas ".
634 "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
635 "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
637 if (!empty($params['status'])) {
638 $placeholders['status'] = $params['status'];
639 $sql = $sql." AND mas.status = :status";
641 if (!empty($params['before'])) {
642 $placeholders['since'] = $params['since'];
643 $placeholders['before'] = $params['before'];
644 $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
645 } else {
646 $placeholders['since'] = $params['since'];
647 $sql = $sql." AND mas.timemodified >= :since";
650 $submissionrecords = $DB->get_records_sql($sql, $placeholders);
652 if (!empty($submissionrecords)) {
653 $fs = get_file_storage();
654 foreach ($submissionrecords as $submissionrecord) {
655 $submission = array(
656 'id' => $submissionrecord->id,
657 'userid' => $submissionrecord->userid,
658 'timecreated' => $submissionrecord->timecreated,
659 'timemodified' => $submissionrecord->timemodified,
660 'status' => $submissionrecord->status,
661 'attemptnumber' => $submissionrecord->attemptnumber,
662 'groupid' => $submissionrecord->groupid
664 foreach ($submissionplugins as $submissionplugin) {
665 $plugin = array(
666 'name' => $submissionplugin->get_name(),
667 'type' => $submissionplugin->get_type()
669 // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
670 $component = $submissionplugin->get_subtype().'_'.$submissionplugin->get_type();
672 $fileareas = $submissionplugin->get_file_areas();
673 foreach ($fileareas as $filearea => $name) {
674 $fileareainfo = array('area' => $filearea);
675 $files = $fs->get_area_files(
676 $assign->get_context()->id,
677 $component,
678 $filearea,
679 $submissionrecord->id,
680 "timemodified",
681 false
683 foreach ($files as $file) {
684 $filepath = $file->get_filepath().$file->get_filename();
685 $fileurl = file_encode_url($CFG->wwwroot . '/webservice/pluginfile.php', '/' . $assign->get_context()->id .
686 '/' . $component. '/'. $filearea . '/' . $submissionrecord->id . $filepath);
687 $fileinfo = array(
688 'filepath' => $filepath,
689 'fileurl' => $fileurl
691 $fileareainfo['files'][] = $fileinfo;
693 $plugin['fileareas'][] = $fileareainfo;
696 $editorfields = $submissionplugin->get_editor_fields();
697 foreach ($editorfields as $name => $description) {
698 $editorfieldinfo = array(
699 'name' => $name,
700 'description' => $description,
701 'text' => $submissionplugin->get_editor_text($name, $submissionrecord->id),
702 'format' => $submissionplugin->get_editor_format($name, $submissionrecord->id)
704 $plugin['editorfields'][] = $editorfieldinfo;
707 $submission['plugins'][] = $plugin;
709 $submissions[] = $submission;
711 } else {
712 $warnings[] = array(
713 'item' => 'module',
714 'itemid' => $assign->get_instance()->id,
715 'warningcode' => '3',
716 'message' => 'No submissions found'
720 $assignments[] = array(
721 'assignmentid' => $assign->get_instance()->id,
722 'submissions' => $submissions
727 $result = array(
728 'assignments' => $assignments,
729 'warnings' => $warnings
731 return $result;
735 * Creates an assign_submissions external_single_structure
737 * @return external_single_structure
738 * @since Moodle 2.5
740 private static function get_submissions_structure() {
741 return new external_single_structure(
742 array (
743 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
744 'submissions' => new external_multiple_structure(
745 new external_single_structure(
746 array(
747 'id' => new external_value(PARAM_INT, 'submission id'),
748 'userid' => new external_value(PARAM_INT, 'student id'),
749 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
750 'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
751 'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
752 'status' => new external_value(PARAM_TEXT, 'submission status'),
753 'groupid' => new external_value(PARAM_INT, 'group id'),
754 'plugins' => new external_multiple_structure(
755 new external_single_structure(
756 array(
757 'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
758 'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
759 'fileareas' => new external_multiple_structure(
760 new external_single_structure(
761 array (
762 'area' => new external_value (PARAM_TEXT, 'file area'),
763 'files' => new external_multiple_structure(
764 new external_single_structure(
765 array (
766 'filepath' => new external_value (PARAM_TEXT, 'file path'),
767 'fileurl' => new external_value (PARAM_URL, 'file download url',
768 VALUE_OPTIONAL)
770 ), 'files', VALUE_OPTIONAL
773 ), 'fileareas', VALUE_OPTIONAL
775 'editorfields' => new external_multiple_structure(
776 new external_single_structure(
777 array(
778 'name' => new external_value(PARAM_TEXT, 'field name'),
779 'description' => new external_value(PARAM_TEXT, 'field description'),
780 'text' => new external_value (PARAM_RAW, 'field value'),
781 'format' => new external_format_value ('text')
784 , 'editorfields', VALUE_OPTIONAL
788 , 'plugins', VALUE_OPTIONAL
798 * Describes the get_submissions return value
800 * @return external_single_structure
801 * @since Moodle 2.5
803 public static function get_submissions_returns() {
804 return new external_single_structure(
805 array(
806 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
807 'warnings' => new external_warnings()
813 * Describes the parameters for set_user_flags
814 * @return external_function_parameters
815 * @since Moodle 2.6
817 public static function set_user_flags_parameters() {
818 return new external_function_parameters(
819 array(
820 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
821 'userflags' => new external_multiple_structure(
822 new external_single_structure(
823 array(
824 'userid' => new external_value(PARAM_INT, 'student id'),
825 'locked' => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL),
826 'mailed' => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL),
827 'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL),
828 'workflowstate' => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
829 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL)
838 * Create or update user_flags records
840 * @param int $assignmentid the assignment for which the userflags are created or updated
841 * @param array $userflags An array of userflags to create or update
842 * @return array containing success or failure information for each record
843 * @since Moodle 2.6
845 public static function set_user_flags($assignmentid, $userflags = array()) {
846 global $CFG, $DB;
847 require_once($CFG->dirroot . "/mod/assign/locallib.php");
849 $params = self::validate_parameters(self::set_user_flags_parameters(),
850 array('assignmentid' => $assignmentid,
851 'userflags' => $userflags));
853 // Load assignment if it exists and if the user has the capability.
854 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
855 $context = context_module::instance($cm->id);
856 self::validate_context($context);
857 require_capability('mod/assign:grade', $context);
858 $assign = new assign($context, null, null);
860 $results = array();
861 foreach ($params['userflags'] as $userflag) {
862 $success = true;
863 $result = array();
865 $record = $assign->get_user_flags($userflag['userid'], false);
866 if ($record) {
867 if (isset($userflag['locked'])) {
868 $record->locked = $userflag['locked'];
870 if (isset($userflag['mailed'])) {
871 $record->mailed = $userflag['mailed'];
873 if (isset($userflag['extensionduedate'])) {
874 $record->extensionduedate = $userflag['extensionduedate'];
876 if (isset($userflag['workflowstate'])) {
877 $record->workflowstate = $userflag['workflowstate'];
879 if (isset($userflag['allocatedmarker'])) {
880 $record->allocatedmarker = $userflag['allocatedmarker'];
882 if ($assign->update_user_flags($record)) {
883 $result['id'] = $record->id;
884 $result['userid'] = $userflag['userid'];
885 } else {
886 $result['id'] = $record->id;
887 $result['userid'] = $userflag['userid'];
888 $result['errormessage'] = 'Record created but values could not be set';
890 } else {
891 $record = $assign->get_user_flags($userflag['userid'], true);
892 $setfields = isset($userflag['locked'])
893 || isset($userflag['mailed'])
894 || isset($userflag['extensionduedate'])
895 || isset($userflag['workflowstate'])
896 || isset($userflag['allocatedmarker']);
897 if ($record) {
898 if ($setfields) {
899 if (isset($userflag['locked'])) {
900 $record->locked = $userflag['locked'];
902 if (isset($userflag['mailed'])) {
903 $record->mailed = $userflag['mailed'];
905 if (isset($userflag['extensionduedate'])) {
906 $record->extensionduedate = $userflag['extensionduedate'];
908 if (isset($userflag['workflowstate'])) {
909 $record->workflowstate = $userflag['workflowstate'];
911 if (isset($userflag['allocatedmarker'])) {
912 $record->allocatedmarker = $userflag['allocatedmarker'];
914 if ($assign->update_user_flags($record)) {
915 $result['id'] = $record->id;
916 $result['userid'] = $userflag['userid'];
917 } else {
918 $result['id'] = $record->id;
919 $result['userid'] = $userflag['userid'];
920 $result['errormessage'] = 'Record created but values could not be set';
922 } else {
923 $result['id'] = $record->id;
924 $result['userid'] = $userflag['userid'];
926 } else {
927 $result['id'] = -1;
928 $result['userid'] = $userflag['userid'];
929 $result['errormessage'] = 'Record could not be created';
933 $results[] = $result;
935 return $results;
939 * Describes the set_user_flags return value
940 * @return external_multiple_structure
941 * @since Moodle 2.6
943 public static function set_user_flags_returns() {
944 return new external_multiple_structure(
945 new external_single_structure(
946 array(
947 'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'),
948 'userid' => new external_value(PARAM_INT, 'userid of record'),
949 'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL)
956 * Describes the parameters for get_user_flags
957 * @return external_function_parameters
958 * @since Moodle 2.6
960 public static function get_user_flags_parameters() {
961 return new external_function_parameters(
962 array(
963 'assignmentids' => new external_multiple_structure(
964 new external_value(PARAM_INT, 'assignment id'),
965 '1 or more assignment ids',
966 VALUE_REQUIRED)
972 * Returns user flag information from assign_user_flags for the requested assignment ids
973 * @param int[] $assignmentids
974 * @return array of user flag records for each requested assignment
975 * @since Moodle 2.6
977 public static function get_user_flags($assignmentids) {
978 global $DB;
979 $params = self::validate_parameters(self::get_user_flags_parameters(),
980 array('assignmentids' => $assignmentids));
982 $assignments = array();
983 $warnings = array();
984 $requestedassignmentids = $params['assignmentids'];
986 // Check the user is allowed to get the user flags for the assignments requested.
987 $placeholders = array();
988 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
989 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
990 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
991 $placeholders['modname'] = 'assign';
992 $cms = $DB->get_records_sql($sql, $placeholders);
993 foreach ($cms as $cm) {
994 try {
995 $context = context_module::instance($cm->id);
996 self::validate_context($context);
997 require_capability('mod/assign:grade', $context);
998 } catch (Exception $e) {
999 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1000 $warning = array();
1001 $warning['item'] = 'assignment';
1002 $warning['itemid'] = $cm->instance;
1003 $warning['warningcode'] = '1';
1004 $warning['message'] = 'No access rights in module context';
1005 $warnings[] = $warning;
1009 // Create the query and populate an array of assign_user_flags records from the recordset results.
1010 if (count ($requestedassignmentids) > 0) {
1011 $placeholders = array();
1012 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1014 $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
1015 "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
1016 "FROM {assign_user_flags} auf ".
1017 "WHERE auf.assignment ".$inorequalsql.
1018 " ORDER BY auf.assignment, auf.id";
1020 $rs = $DB->get_recordset_sql($sql, $placeholders);
1021 $currentassignmentid = null;
1022 $assignment = null;
1023 foreach ($rs as $rd) {
1024 $userflag = array();
1025 $userflag['id'] = $rd->id;
1026 $userflag['userid'] = $rd->userid;
1027 $userflag['locked'] = $rd->locked;
1028 $userflag['mailed'] = $rd->mailed;
1029 $userflag['extensionduedate'] = $rd->extensionduedate;
1030 $userflag['workflowstate'] = $rd->workflowstate;
1031 $userflag['allocatedmarker'] = $rd->allocatedmarker;
1033 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1034 if (!is_null($assignment)) {
1035 $assignments[] = $assignment;
1037 $assignment = array();
1038 $assignment['assignmentid'] = $rd->assignment;
1039 $assignment['userflags'] = array();
1040 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1042 $assignment['userflags'][] = $userflag;
1044 $currentassignmentid = $rd->assignment;
1046 if (!is_null($assignment)) {
1047 $assignments[] = $assignment;
1049 $rs->close();
1053 foreach ($requestedassignmentids as $assignmentid) {
1054 $warning = array();
1055 $warning['item'] = 'assignment';
1056 $warning['itemid'] = $assignmentid;
1057 $warning['warningcode'] = '3';
1058 $warning['message'] = 'No user flags found';
1059 $warnings[] = $warning;
1062 $result = array();
1063 $result['assignments'] = $assignments;
1064 $result['warnings'] = $warnings;
1065 return $result;
1069 * Creates an assign_user_flags external_single_structure
1070 * @return external_single_structure
1071 * @since Moodle 2.6
1073 private static function assign_user_flags() {
1074 return new external_single_structure(
1075 array (
1076 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
1077 'userflags' => new external_multiple_structure(new external_single_structure(
1078 array(
1079 'id' => new external_value(PARAM_INT, 'user flag id'),
1080 'userid' => new external_value(PARAM_INT, 'student id'),
1081 'locked' => new external_value(PARAM_INT, 'locked'),
1082 'mailed' => new external_value(PARAM_INT, 'mailed'),
1083 'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
1084 'workflowstate' => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
1085 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker')
1094 * Describes the get_user_flags return value
1095 * @return external_single_structure
1096 * @since Moodle 2.6
1098 public static function get_user_flags_returns() {
1099 return new external_single_structure(
1100 array(
1101 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
1102 'warnings' => new external_warnings('item is always \'assignment\'',
1103 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1104 'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
1110 * Describes the parameters for get_user_mappings
1111 * @return external_function_parameters
1112 * @since Moodle 2.6
1114 public static function get_user_mappings_parameters() {
1115 return new external_function_parameters(
1116 array(
1117 'assignmentids' => new external_multiple_structure(
1118 new external_value(PARAM_INT, 'assignment id'),
1119 '1 or more assignment ids',
1120 VALUE_REQUIRED)
1126 * Returns user mapping information from assign_user_mapping for the requested assignment ids
1127 * @param int[] $assignmentids
1128 * @return array of user mapping records for each requested assignment
1129 * @since Moodle 2.6
1131 public static function get_user_mappings($assignmentids) {
1132 global $DB;
1133 $params = self::validate_parameters(self::get_user_mappings_parameters(),
1134 array('assignmentids' => $assignmentids));
1136 $assignments = array();
1137 $warnings = array();
1138 $requestedassignmentids = $params['assignmentids'];
1140 // Check the user is allowed to get the mappings for the assignments requested.
1141 $placeholders = array();
1142 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1143 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1144 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1145 $placeholders['modname'] = 'assign';
1146 $cms = $DB->get_records_sql($sql, $placeholders);
1147 foreach ($cms as $cm) {
1148 try {
1149 $context = context_module::instance($cm->id);
1150 self::validate_context($context);
1151 require_capability('mod/assign:revealidentities', $context);
1152 } catch (Exception $e) {
1153 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1154 $warning = array();
1155 $warning['item'] = 'assignment';
1156 $warning['itemid'] = $cm->instance;
1157 $warning['warningcode'] = '1';
1158 $warning['message'] = 'No access rights in module context';
1159 $warnings[] = $warning;
1163 // Create the query and populate an array of assign_user_mapping records from the recordset results.
1164 if (count ($requestedassignmentids) > 0) {
1165 $placeholders = array();
1166 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1168 $sql = "SELECT aum.id,aum.assignment,aum.userid ".
1169 "FROM {assign_user_mapping} aum ".
1170 "WHERE aum.assignment ".$inorequalsql.
1171 " ORDER BY aum.assignment, aum.id";
1173 $rs = $DB->get_recordset_sql($sql, $placeholders);
1174 $currentassignmentid = null;
1175 $assignment = null;
1176 foreach ($rs as $rd) {
1177 $mapping = array();
1178 $mapping['id'] = $rd->id;
1179 $mapping['userid'] = $rd->userid;
1181 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1182 if (!is_null($assignment)) {
1183 $assignments[] = $assignment;
1185 $assignment = array();
1186 $assignment['assignmentid'] = $rd->assignment;
1187 $assignment['mappings'] = array();
1188 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1190 $assignment['mappings'][] = $mapping;
1192 $currentassignmentid = $rd->assignment;
1194 if (!is_null($assignment)) {
1195 $assignments[] = $assignment;
1197 $rs->close();
1201 foreach ($requestedassignmentids as $assignmentid) {
1202 $warning = array();
1203 $warning['item'] = 'assignment';
1204 $warning['itemid'] = $assignmentid;
1205 $warning['warningcode'] = '3';
1206 $warning['message'] = 'No mappings found';
1207 $warnings[] = $warning;
1210 $result = array();
1211 $result['assignments'] = $assignments;
1212 $result['warnings'] = $warnings;
1213 return $result;
1217 * Creates an assign_user_mappings external_single_structure
1218 * @return external_single_structure
1219 * @since Moodle 2.6
1221 private static function assign_user_mappings() {
1222 return new external_single_structure(
1223 array (
1224 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
1225 'mappings' => new external_multiple_structure(new external_single_structure(
1226 array(
1227 'id' => new external_value(PARAM_INT, 'user mapping id'),
1228 'userid' => new external_value(PARAM_INT, 'student id')
1237 * Describes the get_user_mappings return value
1238 * @return external_single_structure
1239 * @since Moodle 2.6
1241 public static function get_user_mappings_returns() {
1242 return new external_single_structure(
1243 array(
1244 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
1245 'warnings' => new external_warnings('item is always \'assignment\'',
1246 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1247 'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
1253 * Describes the parameters for lock_submissions
1254 * @return external_external_function_parameters
1255 * @since Moodle 2.6
1257 public static function lock_submissions_parameters() {
1258 return new external_function_parameters(
1259 array(
1260 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1261 'userids' => new external_multiple_structure(
1262 new external_value(PARAM_INT, 'user id'),
1263 '1 or more user ids',
1264 VALUE_REQUIRED),
1270 * Locks (prevent updates to) submissions in this assignment.
1272 * @param int $assignmentid The id of the assignment
1273 * @param array $userids Array of user ids to lock
1274 * @return array of warnings for each submission that could not be locked.
1275 * @since Moodle 2.6
1277 public static function lock_submissions($assignmentid, $userids) {
1278 global $CFG;
1279 require_once("$CFG->dirroot/mod/assign/locallib.php");
1281 $params = self::validate_parameters(self::lock_submissions_parameters(),
1282 array('assignmentid' => $assignmentid,
1283 'userids' => $userids));
1285 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1286 $context = context_module::instance($cm->id);
1287 self::validate_context($context);
1289 $assignment = new assign($context, $cm, null);
1291 $warnings = array();
1292 foreach ($params['userids'] as $userid) {
1293 if (!$assignment->lock_submission($userid)) {
1294 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1295 $warnings[] = self::generate_warning($params['assignmentid'],
1296 'couldnotlock',
1297 $detail);
1301 return $warnings;
1305 * Describes the return value for lock_submissions
1307 * @return external_single_structure
1308 * @since Moodle 2.6
1310 public static function lock_submissions_returns() {
1311 return new external_warnings();
1315 * Describes the parameters for revert_submissions_to_draft
1316 * @return external_external_function_parameters
1317 * @since Moodle 2.6
1319 public static function revert_submissions_to_draft_parameters() {
1320 return new external_function_parameters(
1321 array(
1322 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1323 'userids' => new external_multiple_structure(
1324 new external_value(PARAM_INT, 'user id'),
1325 '1 or more user ids',
1326 VALUE_REQUIRED),
1332 * Reverts a list of user submissions to draft for a single assignment.
1334 * @param int $assignmentid The id of the assignment
1335 * @param array $userids Array of user ids to revert
1336 * @return array of warnings for each submission that could not be reverted.
1337 * @since Moodle 2.6
1339 public static function revert_submissions_to_draft($assignmentid, $userids) {
1340 global $CFG;
1341 require_once("$CFG->dirroot/mod/assign/locallib.php");
1343 $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
1344 array('assignmentid' => $assignmentid,
1345 'userids' => $userids));
1347 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1348 $context = context_module::instance($cm->id);
1349 self::validate_context($context);
1351 $assignment = new assign($context, $cm, null);
1353 $warnings = array();
1354 foreach ($params['userids'] as $userid) {
1355 if (!$assignment->revert_to_draft($userid)) {
1356 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1357 $warnings[] = self::generate_warning($params['assignmentid'],
1358 'couldnotrevert',
1359 $detail);
1363 return $warnings;
1367 * Describes the return value for revert_submissions_to_draft
1369 * @return external_single_structure
1370 * @since Moodle 2.6
1372 public static function revert_submissions_to_draft_returns() {
1373 return new external_warnings();
1377 * Describes the parameters for unlock_submissions
1378 * @return external_external_function_parameters
1379 * @since Moodle 2.6
1381 public static function unlock_submissions_parameters() {
1382 return new external_function_parameters(
1383 array(
1384 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1385 'userids' => new external_multiple_structure(
1386 new external_value(PARAM_INT, 'user id'),
1387 '1 or more user ids',
1388 VALUE_REQUIRED),
1394 * Locks (prevent updates to) submissions in this assignment.
1396 * @param int $assignmentid The id of the assignment
1397 * @param array $userids Array of user ids to lock
1398 * @return array of warnings for each submission that could not be locked.
1399 * @since Moodle 2.6
1401 public static function unlock_submissions($assignmentid, $userids) {
1402 global $CFG;
1403 require_once("$CFG->dirroot/mod/assign/locallib.php");
1405 $params = self::validate_parameters(self::unlock_submissions_parameters(),
1406 array('assignmentid' => $assignmentid,
1407 'userids' => $userids));
1409 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1410 $context = context_module::instance($cm->id);
1411 self::validate_context($context);
1413 $assignment = new assign($context, $cm, null);
1415 $warnings = array();
1416 foreach ($params['userids'] as $userid) {
1417 if (!$assignment->unlock_submission($userid)) {
1418 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1419 $warnings[] = self::generate_warning($params['assignmentid'],
1420 'couldnotunlock',
1421 $detail);
1425 return $warnings;
1429 * Describes the return value for unlock_submissions
1431 * @return external_single_structure
1432 * @since Moodle 2.6
1434 public static function unlock_submissions_returns() {
1435 return new external_warnings();
1439 * Describes the parameters for submit_for_grading
1440 * @return external_external_function_parameters
1441 * @since Moodle 2.6
1443 public static function submit_for_grading_parameters() {
1444 return new external_function_parameters(
1445 array(
1446 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1447 'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement')
1453 * Submit the logged in users assignment for grading.
1455 * @param int $assignmentid The id of the assignment
1456 * @return array of warnings to indicate any errors.
1457 * @since Moodle 2.6
1459 public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
1460 global $CFG, $USER;
1461 require_once("$CFG->dirroot/mod/assign/locallib.php");
1463 $params = self::validate_parameters(self::submit_for_grading_parameters(),
1464 array('assignmentid' => $assignmentid,
1465 'acceptsubmissionstatement' => $acceptsubmissionstatement));
1467 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1468 $context = context_module::instance($cm->id);
1469 self::validate_context($context);
1471 $assignment = new assign($context, $cm, null);
1473 $warnings = array();
1474 $data = new stdClass();
1475 $data->submissionstatement = $params['acceptsubmissionstatement'];
1476 $notices = array();
1478 if (!$assignment->submit_for_grading($data, $notices)) {
1479 $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices);
1480 $warnings[] = self::generate_warning($params['assignmentid'],
1481 'couldnotsubmitforgrading',
1482 $detail);
1485 return $warnings;
1489 * Describes the return value for submit_for_grading
1491 * @return external_single_structure
1492 * @since Moodle 2.6
1494 public static function submit_for_grading_returns() {
1495 return new external_warnings();
1499 * Describes the parameters for save_user_extensions
1500 * @return external_external_function_parameters
1501 * @since Moodle 2.6
1503 public static function save_user_extensions_parameters() {
1504 return new external_function_parameters(
1505 array(
1506 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1507 'userids' => new external_multiple_structure(
1508 new external_value(PARAM_INT, 'user id'),
1509 '1 or more user ids',
1510 VALUE_REQUIRED),
1511 'dates' => new external_multiple_structure(
1512 new external_value(PARAM_INT, 'dates'),
1513 '1 or more extension dates (timestamp)',
1514 VALUE_REQUIRED),
1520 * Grant extension dates to students for an assignment.
1522 * @param int $assignmentid The id of the assignment
1523 * @param array $userids Array of user ids to grant extensions to
1524 * @param array $dates Array of extension dates
1525 * @return array of warnings for each extension date that could not be granted
1526 * @since Moodle 2.6
1528 public static function save_user_extensions($assignmentid, $userids, $dates) {
1529 global $CFG;
1530 require_once("$CFG->dirroot/mod/assign/locallib.php");
1532 $params = self::validate_parameters(self::save_user_extensions_parameters(),
1533 array('assignmentid' => $assignmentid,
1534 'userids' => $userids,
1535 'dates' => $dates));
1537 if (count($params['userids']) != count($params['dates'])) {
1538 $detail = 'Length of userids and dates parameters differ.';
1539 $warnings[] = self::generate_warning($params['assignmentid'],
1540 'invalidparameters',
1541 $detail);
1543 return $warnings;
1546 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1547 $context = context_module::instance($cm->id);
1548 self::validate_context($context);
1550 $assignment = new assign($context, $cm, null);
1552 $warnings = array();
1553 foreach ($params['userids'] as $idx => $userid) {
1554 $duedate = $params['dates'][$idx];
1555 if (!$assignment->save_user_extension($userid, $duedate)) {
1556 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate;
1557 $warnings[] = self::generate_warning($params['assignmentid'],
1558 'couldnotgrantextensions',
1559 $detail);
1563 return $warnings;
1567 * Describes the return value for save_user_extensions
1569 * @return external_single_structure
1570 * @since Moodle 2.6
1572 public static function save_user_extensions_returns() {
1573 return new external_warnings();
1577 * Describes the parameters for reveal_identities
1578 * @return external_external_function_parameters
1579 * @since Moodle 2.6
1581 public static function reveal_identities_parameters() {
1582 return new external_function_parameters(
1583 array(
1584 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
1590 * Reveal the identities of anonymous students to markers for a single assignment.
1592 * @param int $assignmentid The id of the assignment
1593 * @return array of warnings to indicate any errors.
1594 * @since Moodle 2.6
1596 public static function reveal_identities($assignmentid) {
1597 global $CFG, $USER;
1598 require_once("$CFG->dirroot/mod/assign/locallib.php");
1600 $params = self::validate_parameters(self::reveal_identities_parameters(),
1601 array('assignmentid' => $assignmentid));
1603 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1604 $context = context_module::instance($cm->id);
1605 self::validate_context($context);
1607 $assignment = new assign($context, $cm, null);
1609 $warnings = array();
1610 if (!$assignment->reveal_identities()) {
1611 $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'];
1612 $warnings[] = self::generate_warning($params['assignmentid'],
1613 'couldnotrevealidentities',
1614 $detail);
1617 return $warnings;
1621 * Describes the return value for reveal_identities
1623 * @return external_single_structure
1624 * @since Moodle 2.6
1626 public static function reveal_identities_returns() {
1627 return new external_warnings();
1631 * Describes the parameters for save_submission
1632 * @return external_external_function_parameters
1633 * @since Moodle 2.6
1635 public static function save_submission_parameters() {
1636 global $CFG;
1637 require_once("$CFG->dirroot/mod/assign/locallib.php");
1638 $instance = new assign(null, null, null);
1639 $pluginsubmissionparams = array();
1641 foreach ($instance->get_submission_plugins() as $plugin) {
1642 $pluginparams = $plugin->get_external_parameters();
1643 if (!empty($pluginparams)) {
1644 $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
1648 return new external_function_parameters(
1649 array(
1650 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1651 'plugindata' => new external_single_structure(
1652 $pluginsubmissionparams
1659 * Save a student submission for a single assignment
1661 * @param int $assignmentid The id of the assignment
1662 * @param array $plugindata - The submitted data for plugins
1663 * @return array of warnings to indicate any errors
1664 * @since Moodle 2.6
1666 public static function save_submission($assignmentid, $plugindata) {
1667 global $CFG, $USER;
1668 require_once("$CFG->dirroot/mod/assign/locallib.php");
1670 $params = self::validate_parameters(self::save_submission_parameters(),
1671 array('assignmentid' => $assignmentid,
1672 'plugindata' => $plugindata));
1674 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1675 $context = context_module::instance($cm->id);
1676 self::validate_context($context);
1678 $assignment = new assign($context, $cm, null);
1680 $notices = array();
1682 $submissiondata = (object)$params['plugindata'];
1684 $assignment->save_submission($submissiondata, $notices);
1686 $warnings = array();
1687 foreach ($notices as $notice) {
1688 $warnings[] = self::generate_warning($params['assignmentid'],
1689 'couldnotsavesubmission',
1690 $notice);
1693 return $warnings;
1697 * Describes the return value for save_submission
1699 * @return external_single_structure
1700 * @since Moodle 2.6
1702 public static function save_submission_returns() {
1703 return new external_warnings();
1707 * Describes the parameters for save_grade
1708 * @return external_external_function_parameters
1709 * @since Moodle 2.6
1711 public static function save_grade_parameters() {
1712 global $CFG;
1713 require_once("$CFG->dirroot/mod/assign/locallib.php");
1714 require_once("$CFG->dirroot/grade/grading/lib.php");
1715 $instance = new assign(null, null, null);
1716 $pluginfeedbackparams = array();
1718 foreach ($instance->get_feedback_plugins() as $plugin) {
1719 $pluginparams = $plugin->get_external_parameters();
1720 if (!empty($pluginparams)) {
1721 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1725 $advancedgradingdata = array();
1726 $methods = array_keys(grading_manager::available_methods(false));
1727 foreach ($methods as $method) {
1728 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1729 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1730 if (!empty($details)) {
1731 $items = array();
1732 foreach ($details as $key => $value) {
1733 $value->required = VALUE_OPTIONAL;
1734 unset($value->content->keys['id']);
1735 $items[$key] = new external_multiple_structure (new external_single_structure(
1736 array(
1737 'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1738 'fillings' => $value
1742 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1746 return new external_function_parameters(
1747 array(
1748 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1749 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1750 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'),
1751 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1752 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
1753 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1754 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1755 'to all members ' .
1756 'of the group (for group assignments).'),
1757 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()),
1758 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1759 VALUE_DEFAULT, array())
1765 * Save a student grade for a single assignment.
1767 * @param int $assignmentid The id of the assignment
1768 * @param int $userid The id of the user
1769 * @param float $grade The grade (ignored if the assignment uses advanced grading)
1770 * @param int $attemptnumber The attempt number
1771 * @param bool $addattempt Allow another attempt
1772 * @param string $workflowstate New workflow state
1773 * @param bool $applytoall Apply the grade to all members of the group
1774 * @param array $plugindata Custom data used by plugins
1775 * @param array $advancedgradingdata Advanced grading data
1776 * @return null
1777 * @since Moodle 2.6
1779 public static function save_grade($assignmentid,
1780 $userid,
1781 $grade,
1782 $attemptnumber,
1783 $addattempt,
1784 $workflowstate,
1785 $applytoall,
1786 $plugindata = array(),
1787 $advancedgradingdata = array()) {
1788 global $CFG, $USER;
1789 require_once("$CFG->dirroot/mod/assign/locallib.php");
1791 $params = self::validate_parameters(self::save_grade_parameters(),
1792 array('assignmentid' => $assignmentid,
1793 'userid' => $userid,
1794 'grade' => $grade,
1795 'attemptnumber' => $attemptnumber,
1796 'workflowstate' => $workflowstate,
1797 'addattempt' => $addattempt,
1798 'applytoall' => $applytoall,
1799 'plugindata' => $plugindata,
1800 'advancedgradingdata' => $advancedgradingdata));
1802 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1803 $context = context_module::instance($cm->id);
1804 self::validate_context($context);
1806 $assignment = new assign($context, $cm, null);
1808 $gradedata = (object)$params['plugindata'];
1810 $gradedata->addattempt = $params['addattempt'];
1811 $gradedata->attemptnumber = $params['attemptnumber'];
1812 $gradedata->workflowstate = $params['workflowstate'];
1813 $gradedata->applytoall = $params['applytoall'];
1814 $gradedata->grade = $params['grade'];
1816 if (!empty($params['advancedgradingdata'])) {
1817 $advancedgrading = array();
1818 $criteria = reset($params['advancedgradingdata']);
1819 foreach ($criteria as $key => $criterion) {
1820 $details = array();
1821 foreach ($criterion as $value) {
1822 foreach ($value['fillings'] as $filling) {
1823 $details[$value['criterionid']] = $filling;
1826 $advancedgrading[$key] = $details;
1828 $gradedata->advancedgrading = $advancedgrading;
1831 $assignment->save_grade($params['userid'], $gradedata);
1833 return null;
1837 * Describes the return value for save_grade
1839 * @return external_single_structure
1840 * @since Moodle 2.6
1842 public static function save_grade_returns() {
1843 return null;
1847 * Describes the parameters for save_grades
1848 * @return external_external_function_parameters
1849 * @since Moodle 2.7
1851 public static function save_grades_parameters() {
1852 global $CFG;
1853 require_once("$CFG->dirroot/mod/assign/locallib.php");
1854 require_once("$CFG->dirroot/grade/grading/lib.php");
1855 $instance = new assign(null, null, null);
1856 $pluginfeedbackparams = array();
1858 foreach ($instance->get_feedback_plugins() as $plugin) {
1859 $pluginparams = $plugin->get_external_parameters();
1860 if (!empty($pluginparams)) {
1861 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1865 $advancedgradingdata = array();
1866 $methods = array_keys(grading_manager::available_methods(false));
1867 foreach ($methods as $method) {
1868 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1869 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1870 if (!empty($details)) {
1871 $items = array();
1872 foreach ($details as $key => $value) {
1873 $value->required = VALUE_OPTIONAL;
1874 unset($value->content->keys['id']);
1875 $items[$key] = new external_multiple_structure (new external_single_structure(
1876 array(
1877 'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1878 'fillings' => $value
1882 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1886 return new external_function_parameters(
1887 array(
1888 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1889 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1890 'to all members ' .
1891 'of the group (for group assignments).'),
1892 'grades' => new external_multiple_structure(
1893 new external_single_structure(
1894 array (
1895 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1896 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '.
1897 '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 manual attempt reopen method'),
1900 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1901 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data',
1902 VALUE_DEFAULT, array()),
1903 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1904 VALUE_DEFAULT, array())
1913 * Save multiple student grades for a single assignment.
1915 * @param int $assignmentid The id of the assignment
1916 * @param boolean $applytoall If set to true and this is a team assignment,
1917 * apply the grade to all members of the group
1918 * @param array $grades grade data for one or more students that includes
1919 * userid - The id of the student being graded
1920 * grade - The grade (ignored if the assignment uses advanced grading)
1921 * attemptnumber - The attempt number
1922 * addattempt - Allow another attempt
1923 * workflowstate - New workflow state
1924 * plugindata - Custom data used by plugins
1925 * advancedgradingdata - Optional Advanced grading data
1926 * @throws invalid_parameter_exception if multiple grades are supplied for
1927 * a team assignment that has $applytoall set to true
1928 * @return null
1929 * @since Moodle 2.7
1931 public static function save_grades($assignmentid, $applytoall = false, $grades) {
1932 global $CFG, $USER;
1933 require_once("$CFG->dirroot/mod/assign/locallib.php");
1935 $params = self::validate_parameters(self::save_grades_parameters(),
1936 array('assignmentid' => $assignmentid,
1937 'applytoall' => $applytoall,
1938 'grades' => $grades));
1940 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1941 $context = context_module::instance($cm->id);
1942 self::validate_context($context);
1943 $assignment = new assign($context, $cm, null);
1945 if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
1946 // Check that only 1 user per submission group is provided.
1947 $groupids = array();
1948 foreach ($params['grades'] as $gradeinfo) {
1949 $group = $assignment->get_submission_group($gradeinfo['userid']);
1950 if (in_array($group->id, $groupids)) {
1951 throw new invalid_parameter_exception('Multiple grades for the same team have been supplied '
1952 .' this is not permitted when the applytoall flag is set');
1953 } else {
1954 $groupids[] = $group->id;
1959 foreach ($params['grades'] as $gradeinfo) {
1960 $gradedata = (object)$gradeinfo['plugindata'];
1961 $gradedata->addattempt = $gradeinfo['addattempt'];
1962 $gradedata->attemptnumber = $gradeinfo['attemptnumber'];
1963 $gradedata->workflowstate = $gradeinfo['workflowstate'];
1964 $gradedata->applytoall = $params['applytoall'];
1965 $gradedata->grade = $gradeinfo['grade'];
1967 if (!empty($gradeinfo['advancedgradingdata'])) {
1968 $advancedgrading = array();
1969 $criteria = reset($gradeinfo['advancedgradingdata']);
1970 foreach ($criteria as $key => $criterion) {
1971 $details = array();
1972 foreach ($criterion as $value) {
1973 foreach ($value['fillings'] as $filling) {
1974 $details[$value['criterionid']] = $filling;
1977 $advancedgrading[$key] = $details;
1979 $gradedata->advancedgrading = $advancedgrading;
1981 $assignment->save_grade($gradeinfo['userid'], $gradedata);
1984 return null;
1988 * Describes the return value for save_grades
1990 * @return external_single_structure
1991 * @since Moodle 2.7
1993 public static function save_grades_returns() {
1994 return null;
1998 * Describes the parameters for copy_previous_attempt
1999 * @return external_external_function_parameters
2000 * @since Moodle 2.6
2002 public static function copy_previous_attempt_parameters() {
2003 return new external_function_parameters(
2004 array(
2005 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
2011 * Copy a students previous attempt to a new attempt.
2013 * @param int $assignmentid
2014 * @return array of warnings to indicate any errors.
2015 * @since Moodle 2.6
2017 public static function copy_previous_attempt($assignmentid) {
2018 global $CFG, $USER;
2019 require_once("$CFG->dirroot/mod/assign/locallib.php");
2021 $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
2022 array('assignmentid' => $assignmentid));
2024 $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
2025 $context = context_module::instance($cm->id);
2026 self::validate_context($context);
2028 $assignment = new assign($context, $cm, null);
2030 $notices = array();
2032 $assignment->copy_previous_attempt($submissiondata, $notices);
2034 $warnings = array();
2035 foreach ($notices as $notice) {
2036 $warnings[] = self::generate_warning($assignmentid,
2037 'couldnotcopyprevioussubmission',
2038 $notice);
2041 return $warnings;
2045 * Describes the return value for save_submission
2047 * @return external_single_structure
2048 * @since Moodle 2.6
2050 public static function copy_previous_attempt_returns() {
2051 return new external_warnings();
2055 * Returns description of method parameters
2057 * @return external_function_parameters
2058 * @since Moodle 3.0
2060 public static function view_grading_table_parameters() {
2061 return new external_function_parameters(
2062 array(
2063 'assignid' => new external_value(PARAM_INT, 'assign instance id')
2069 * Trigger the grading_table_viewed event.
2071 * @param int $assignid the assign instance id
2072 * @return array of warnings and status result
2073 * @since Moodle 3.0
2074 * @throws moodle_exception
2076 public static function view_grading_table($assignid) {
2077 global $DB, $CFG;
2078 require_once($CFG->dirroot . "/mod/assign/locallib.php");
2080 $params = self::validate_parameters(self::view_grading_table_parameters(),
2081 array(
2082 'assignid' => $assignid
2084 $warnings = array();
2086 // Request and permission validation.
2087 $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
2088 list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
2090 $context = context_module::instance($cm->id);
2091 self::validate_context($context);
2093 require_capability('mod/assign:view', $context);
2095 $assign = new assign($context, null, null);
2096 $assign->require_view_grades();
2097 \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger();
2099 $result = array();
2100 $result['status'] = true;
2101 $result['warnings'] = $warnings;
2102 return $result;
2106 * Returns description of method result value
2108 * @return external_description
2109 * @since Moodle 3.0
2111 public static function view_grading_table_returns() {
2112 return new external_single_structure(
2113 array(
2114 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2115 'warnings' => new external_warnings()