MDL-52901 mod_assign: Check due dates in external save_submission
[moodle.git] / mod / assign / externallib.php
blob06920abc03c96bee6fb7abed10a69b0bbd376319
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;
274 $params = self::validate_parameters(
275 self::get_assignments_parameters(),
276 array('courseids' => $courseids, 'capabilities' => $capabilities)
279 $warnings = array();
280 $fields = 'sortorder,shortname,fullname,timemodified';
281 $courses = enrol_get_users_courses($USER->id, true, $fields);
282 // Used to test for ids that have been requested but can't be returned.
283 if (count($params['courseids']) > 0) {
284 foreach ($params['courseids'] as $courseid) {
285 if (!in_array($courseid, array_keys($courses))) {
286 unset($courses[$courseid]);
287 $warnings[] = array(
288 'item' => 'course',
289 'itemid' => $courseid,
290 'warningcode' => '2',
291 'message' => 'User is not enrolled or does not have requested capability'
296 foreach ($courses as $id => $course) {
297 if (count($params['courseids']) > 0 && !in_array($id, $params['courseids'])) {
298 unset($courses[$id]);
300 $context = context_course::instance($id);
301 try {
302 self::validate_context($context);
303 } catch (Exception $e) {
304 unset($courses[$id]);
305 $warnings[] = array(
306 'item' => 'course',
307 'itemid' => $id,
308 'warningcode' => '1',
309 'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
311 continue;
313 if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) {
314 unset($courses[$id]);
317 $extrafields='m.id as assignmentid, ' .
318 'm.course, ' .
319 'm.nosubmissions, ' .
320 'm.submissiondrafts, ' .
321 'm.sendnotifications, '.
322 'm.sendlatenotifications, ' .
323 'm.sendstudentnotifications, ' .
324 'm.duedate, ' .
325 'm.allowsubmissionsfromdate, '.
326 'm.grade, ' .
327 'm.timemodified, '.
328 'm.completionsubmit, ' .
329 'm.cutoffdate, ' .
330 'm.teamsubmission, ' .
331 'm.requireallteammemberssubmit, '.
332 'm.teamsubmissiongroupingid, ' .
333 'm.blindmarking, ' .
334 'm.revealidentities, ' .
335 'm.attemptreopenmethod, '.
336 'm.maxattempts, ' .
337 'm.markingworkflow, ' .
338 'm.markingallocation, ' .
339 'm.requiresubmissionstatement';
340 $coursearray = array();
341 foreach ($courses as $id => $course) {
342 $assignmentarray = array();
343 // Get a list of assignments for the course.
344 if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
345 foreach ($modules as $module) {
346 $context = context_module::instance($module->id);
347 try {
348 self::validate_context($context);
349 require_capability('mod/assign:view', $context);
350 } catch (Exception $e) {
351 $warnings[] = array(
352 'item' => 'module',
353 'itemid' => $module->id,
354 'warningcode' => '1',
355 'message' => 'No access rights in module context'
357 continue;
359 $configrecords = $DB->get_recordset('assign_plugin_config', array('assignment' => $module->assignmentid));
360 $configarray = array();
361 foreach ($configrecords as $configrecord) {
362 $configarray[] = array(
363 'id' => $configrecord->id,
364 'assignment' => $configrecord->assignment,
365 'plugin' => $configrecord->plugin,
366 'subtype' => $configrecord->subtype,
367 'name' => $configrecord->name,
368 'value' => $configrecord->value
371 $configrecords->close();
373 $assignmentarray[]= array(
374 'id' => $module->assignmentid,
375 'cmid' => $module->id,
376 'course' => $module->course,
377 'name' => $module->name,
378 'nosubmissions' => $module->nosubmissions,
379 'submissiondrafts' => $module->submissiondrafts,
380 'sendnotifications' => $module->sendnotifications,
381 'sendlatenotifications' => $module->sendlatenotifications,
382 'sendstudentnotifications' => $module->sendstudentnotifications,
383 'duedate' => $module->duedate,
384 'allowsubmissionsfromdate' => $module->allowsubmissionsfromdate,
385 'grade' => $module->grade,
386 'timemodified' => $module->timemodified,
387 'completionsubmit' => $module->completionsubmit,
388 'cutoffdate' => $module->cutoffdate,
389 'teamsubmission' => $module->teamsubmission,
390 'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
391 'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
392 'blindmarking' => $module->blindmarking,
393 'revealidentities' => $module->revealidentities,
394 'attemptreopenmethod' => $module->attemptreopenmethod,
395 'maxattempts' => $module->maxattempts,
396 'markingworkflow' => $module->markingworkflow,
397 'markingallocation' => $module->markingallocation,
398 'requiresubmissionstatement' => $module->requiresubmissionstatement,
399 'configs' => $configarray
403 $coursearray[]= array(
404 'id' => $courses[$id]->id,
405 'fullname' => $courses[$id]->fullname,
406 'shortname' => $courses[$id]->shortname,
407 'timemodified' => $courses[$id]->timemodified,
408 'assignments' => $assignmentarray
412 $result = array(
413 'courses' => $coursearray,
414 'warnings' => $warnings
416 return $result;
420 * Creates an assignment external_single_structure
422 * @return external_single_structure
423 * @since Moodle 2.4
425 private static function get_assignments_assignment_structure() {
426 return new external_single_structure(
427 array(
428 'id' => new external_value(PARAM_INT, 'assignment id'),
429 'cmid' => new external_value(PARAM_INT, 'course module id'),
430 'course' => new external_value(PARAM_INT, 'course id'),
431 'name' => new external_value(PARAM_TEXT, 'assignment name'),
432 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
433 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
434 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
435 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
436 'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'),
437 'duedate' => new external_value(PARAM_INT, 'assignment due date'),
438 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
439 'grade' => new external_value(PARAM_INT, 'grade type'),
440 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
441 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
442 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
443 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
444 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
445 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
446 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
447 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
448 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
449 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
450 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
451 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
452 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
453 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings')
454 ), 'assignment information object');
458 * Creates an assign_plugin_config external_single_structure
460 * @return external_single_structure
461 * @since Moodle 2.4
463 private static function get_assignments_config_structure() {
464 return new external_single_structure(
465 array(
466 'id' => new external_value(PARAM_INT, 'assign_plugin_config id'),
467 'assignment' => new external_value(PARAM_INT, 'assignment id'),
468 'plugin' => new external_value(PARAM_TEXT, 'plugin'),
469 'subtype' => new external_value(PARAM_TEXT, 'subtype'),
470 'name' => new external_value(PARAM_TEXT, 'name'),
471 'value' => new external_value(PARAM_TEXT, 'value')
472 ), 'assignment configuration object'
477 * Creates a course external_single_structure
479 * @return external_single_structure
480 * @since Moodle 2.4
482 private static function get_assignments_course_structure() {
483 return new external_single_structure(
484 array(
485 'id' => new external_value(PARAM_INT, 'course id'),
486 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
487 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
488 'timemodified' => new external_value(PARAM_INT, 'last time modified'),
489 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
490 ), 'course information object'
495 * Describes the return value for get_assignments
497 * @return external_single_structure
498 * @since Moodle 2.4
500 public static function get_assignments_returns() {
501 return new external_single_structure(
502 array(
503 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
504 'warnings' => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
505 'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
506 'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
512 * Describes the parameters for get_submissions
514 * @return external_external_function_parameters
515 * @since Moodle 2.5
517 public static function get_submissions_parameters() {
518 return new external_function_parameters(
519 array(
520 'assignmentids' => new external_multiple_structure(
521 new external_value(PARAM_INT, 'assignment id'),
522 '1 or more assignment ids',
523 VALUE_REQUIRED),
524 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
525 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
526 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
532 * Returns submissions for the requested assignment ids
534 * @param int[] $assignmentids
535 * @param string $status only return submissions with this status
536 * @param int $since only return submissions with timemodified >= since
537 * @param int $before only return submissions with timemodified <= before
538 * @return array of submissions for each requested assignment
539 * @since Moodle 2.5
541 public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
542 global $DB, $CFG;
543 require_once("$CFG->dirroot/mod/assign/locallib.php");
544 $params = self::validate_parameters(self::get_submissions_parameters(),
545 array('assignmentids' => $assignmentids,
546 'status' => $status,
547 'since' => $since,
548 'before' => $before));
550 $warnings = array();
551 $assignments = array();
553 // Check the user is allowed to get the submissions for the assignments requested.
554 $placeholders = array();
555 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
556 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
557 "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
558 $placeholders['modname'] = 'assign';
559 $cms = $DB->get_records_sql($sql, $placeholders);
560 $assigns = array();
561 foreach ($cms as $cm) {
562 try {
563 $context = context_module::instance($cm->id);
564 self::validate_context($context);
565 require_capability('mod/assign:grade', $context);
566 $assign = new assign($context, null, null);
567 $assigns[] = $assign;
568 } catch (Exception $e) {
569 $warnings[] = array(
570 'item' => 'assignment',
571 'itemid' => $cm->instance,
572 'warningcode' => '1',
573 'message' => 'No access rights in module context'
578 foreach ($assigns as $assign) {
579 $submissions = array();
580 $submissionplugins = $assign->get_submission_plugins();
581 $placeholders = array('assignid1' => $assign->get_instance()->id,
582 'assignid2' => $assign->get_instance()->id);
584 $submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
585 FROM {assign_submission} mxs
586 WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid';
588 $sql = "SELECT mas.id, mas.assignment,mas.userid,".
589 "mas.timecreated,mas.timemodified,mas.status,mas.groupid,mas.attemptnumber ".
590 "FROM {assign_submission} mas ".
591 "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
592 "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
594 if (!empty($params['status'])) {
595 $placeholders['status'] = $params['status'];
596 $sql = $sql." AND mas.status = :status";
598 if (!empty($params['before'])) {
599 $placeholders['since'] = $params['since'];
600 $placeholders['before'] = $params['before'];
601 $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
602 } else {
603 $placeholders['since'] = $params['since'];
604 $sql = $sql." AND mas.timemodified >= :since";
607 $submissionrecords = $DB->get_records_sql($sql, $placeholders);
609 if (!empty($submissionrecords)) {
610 $fs = get_file_storage();
611 foreach ($submissionrecords as $submissionrecord) {
612 $submission = array(
613 'id' => $submissionrecord->id,
614 'userid' => $submissionrecord->userid,
615 'timecreated' => $submissionrecord->timecreated,
616 'timemodified' => $submissionrecord->timemodified,
617 'status' => $submissionrecord->status,
618 'attemptnumber' => $submissionrecord->attemptnumber,
619 'groupid' => $submissionrecord->groupid
621 foreach ($submissionplugins as $submissionplugin) {
622 $plugin = array(
623 'name' => $submissionplugin->get_name(),
624 'type' => $submissionplugin->get_type()
626 // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
627 $component = $submissionplugin->get_subtype().'_'.$submissionplugin->get_type();
629 $fileareas = $submissionplugin->get_file_areas();
630 foreach ($fileareas as $filearea => $name) {
631 $fileareainfo = array('area' => $filearea);
632 $files = $fs->get_area_files(
633 $assign->get_context()->id,
634 $component,
635 $filearea,
636 $submissionrecord->id,
637 "timemodified",
638 false
640 foreach ($files as $file) {
641 $filepath = $file->get_filepath().$file->get_filename();
642 $fileurl = file_encode_url($CFG->wwwroot . '/webservice/pluginfile.php', '/' . $assign->get_context()->id .
643 '/' . $component. '/'. $filearea . '/' . $submissionrecord->id . $filepath);
644 $fileinfo = array(
645 'filepath' => $filepath,
646 'fileurl' => $fileurl
648 $fileareainfo['files'][] = $fileinfo;
650 $plugin['fileareas'][] = $fileareainfo;
653 $editorfields = $submissionplugin->get_editor_fields();
654 foreach ($editorfields as $name => $description) {
655 $editorfieldinfo = array(
656 'name' => $name,
657 'description' => $description,
658 'text' => $submissionplugin->get_editor_text($name, $submissionrecord->id),
659 'format' => $submissionplugin->get_editor_format($name, $submissionrecord->id)
661 $plugin['editorfields'][] = $editorfieldinfo;
664 $submission['plugins'][] = $plugin;
666 $submissions[] = $submission;
668 } else {
669 $warnings[] = array(
670 'item' => 'module',
671 'itemid' => $assign->get_instance()->id,
672 'warningcode' => '3',
673 'message' => 'No submissions found'
677 $assignments[] = array(
678 'assignmentid' => $assign->get_instance()->id,
679 'submissions' => $submissions
684 $result = array(
685 'assignments' => $assignments,
686 'warnings' => $warnings
688 return $result;
692 * Creates an assign_submissions external_single_structure
694 * @return external_single_structure
695 * @since Moodle 2.5
697 private static function get_submissions_structure() {
698 return new external_single_structure(
699 array (
700 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
701 'submissions' => new external_multiple_structure(
702 new external_single_structure(
703 array(
704 'id' => new external_value(PARAM_INT, 'submission id'),
705 'userid' => new external_value(PARAM_INT, 'student id'),
706 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
707 'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
708 'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
709 'status' => new external_value(PARAM_TEXT, 'submission status'),
710 'groupid' => new external_value(PARAM_INT, 'group id'),
711 'plugins' => new external_multiple_structure(
712 new external_single_structure(
713 array(
714 'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
715 'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
716 'fileareas' => new external_multiple_structure(
717 new external_single_structure(
718 array (
719 'area' => new external_value (PARAM_TEXT, 'file area'),
720 'files' => new external_multiple_structure(
721 new external_single_structure(
722 array (
723 'filepath' => new external_value (PARAM_TEXT, 'file path'),
724 'fileurl' => new external_value (PARAM_URL, 'file download url',
725 VALUE_OPTIONAL)
727 ), 'files', VALUE_OPTIONAL
730 ), 'fileareas', VALUE_OPTIONAL
732 'editorfields' => new external_multiple_structure(
733 new external_single_structure(
734 array(
735 'name' => new external_value(PARAM_TEXT, 'field name'),
736 'description' => new external_value(PARAM_TEXT, 'field description'),
737 'text' => new external_value (PARAM_RAW, 'field value'),
738 'format' => new external_format_value ('text')
741 , 'editorfields', VALUE_OPTIONAL
745 , 'plugins', VALUE_OPTIONAL
755 * Describes the get_submissions return value
757 * @return external_single_structure
758 * @since Moodle 2.5
760 public static function get_submissions_returns() {
761 return new external_single_structure(
762 array(
763 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
764 'warnings' => new external_warnings()
770 * Describes the parameters for set_user_flags
771 * @return external_function_parameters
772 * @since Moodle 2.6
774 public static function set_user_flags_parameters() {
775 return new external_function_parameters(
776 array(
777 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
778 'userflags' => new external_multiple_structure(
779 new external_single_structure(
780 array(
781 'userid' => new external_value(PARAM_INT, 'student id'),
782 'locked' => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL),
783 'mailed' => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL),
784 'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL),
785 'workflowstate' => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
786 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL)
795 * Create or update user_flags records
797 * @param int $assignmentid the assignment for which the userflags are created or updated
798 * @param array $userflags An array of userflags to create or update
799 * @return array containing success or failure information for each record
800 * @since Moodle 2.6
802 public static function set_user_flags($assignmentid, $userflags = array()) {
803 global $CFG, $DB;
804 require_once($CFG->dirroot . "/mod/assign/locallib.php");
806 $params = self::validate_parameters(self::set_user_flags_parameters(),
807 array('assignmentid' => $assignmentid,
808 'userflags' => $userflags));
810 // Load assignment if it exists and if the user has the capability.
811 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
812 $context = context_module::instance($cm->id);
813 self::validate_context($context);
814 require_capability('mod/assign:grade', $context);
815 $assign = new assign($context, null, null);
817 $results = array();
818 foreach ($params['userflags'] as $userflag) {
819 $success = true;
820 $result = array();
822 $record = $assign->get_user_flags($userflag['userid'], false);
823 if ($record) {
824 if (isset($userflag['locked'])) {
825 $record->locked = $userflag['locked'];
827 if (isset($userflag['mailed'])) {
828 $record->mailed = $userflag['mailed'];
830 if (isset($userflag['extensionduedate'])) {
831 $record->extensionduedate = $userflag['extensionduedate'];
833 if (isset($userflag['workflowstate'])) {
834 $record->workflowstate = $userflag['workflowstate'];
836 if (isset($userflag['allocatedmarker'])) {
837 $record->allocatedmarker = $userflag['allocatedmarker'];
839 if ($assign->update_user_flags($record)) {
840 $result['id'] = $record->id;
841 $result['userid'] = $userflag['userid'];
842 } else {
843 $result['id'] = $record->id;
844 $result['userid'] = $userflag['userid'];
845 $result['errormessage'] = 'Record created but values could not be set';
847 } else {
848 $record = $assign->get_user_flags($userflag['userid'], true);
849 $setfields = isset($userflag['locked'])
850 || isset($userflag['mailed'])
851 || isset($userflag['extensionduedate'])
852 || isset($userflag['workflowstate'])
853 || isset($userflag['allocatedmarker']);
854 if ($record) {
855 if ($setfields) {
856 if (isset($userflag['locked'])) {
857 $record->locked = $userflag['locked'];
859 if (isset($userflag['mailed'])) {
860 $record->mailed = $userflag['mailed'];
862 if (isset($userflag['extensionduedate'])) {
863 $record->extensionduedate = $userflag['extensionduedate'];
865 if (isset($userflag['workflowstate'])) {
866 $record->workflowstate = $userflag['workflowstate'];
868 if (isset($userflag['allocatedmarker'])) {
869 $record->allocatedmarker = $userflag['allocatedmarker'];
871 if ($assign->update_user_flags($record)) {
872 $result['id'] = $record->id;
873 $result['userid'] = $userflag['userid'];
874 } else {
875 $result['id'] = $record->id;
876 $result['userid'] = $userflag['userid'];
877 $result['errormessage'] = 'Record created but values could not be set';
879 } else {
880 $result['id'] = $record->id;
881 $result['userid'] = $userflag['userid'];
883 } else {
884 $result['id'] = -1;
885 $result['userid'] = $userflag['userid'];
886 $result['errormessage'] = 'Record could not be created';
890 $results[] = $result;
892 return $results;
896 * Describes the set_user_flags return value
897 * @return external_multiple_structure
898 * @since Moodle 2.6
900 public static function set_user_flags_returns() {
901 return new external_multiple_structure(
902 new external_single_structure(
903 array(
904 'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'),
905 'userid' => new external_value(PARAM_INT, 'userid of record'),
906 'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL)
913 * Describes the parameters for get_user_flags
914 * @return external_function_parameters
915 * @since Moodle 2.6
917 public static function get_user_flags_parameters() {
918 return new external_function_parameters(
919 array(
920 'assignmentids' => new external_multiple_structure(
921 new external_value(PARAM_INT, 'assignment id'),
922 '1 or more assignment ids',
923 VALUE_REQUIRED)
929 * Returns user flag information from assign_user_flags for the requested assignment ids
930 * @param int[] $assignmentids
931 * @return array of user flag records for each requested assignment
932 * @since Moodle 2.6
934 public static function get_user_flags($assignmentids) {
935 global $DB;
936 $params = self::validate_parameters(self::get_user_flags_parameters(),
937 array('assignmentids' => $assignmentids));
939 $assignments = array();
940 $warnings = array();
941 $requestedassignmentids = $params['assignmentids'];
943 // Check the user is allowed to get the user flags for the assignments requested.
944 $placeholders = array();
945 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
946 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
947 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
948 $placeholders['modname'] = 'assign';
949 $cms = $DB->get_records_sql($sql, $placeholders);
950 foreach ($cms as $cm) {
951 try {
952 $context = context_module::instance($cm->id);
953 self::validate_context($context);
954 require_capability('mod/assign:grade', $context);
955 } catch (Exception $e) {
956 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
957 $warning = array();
958 $warning['item'] = 'assignment';
959 $warning['itemid'] = $cm->instance;
960 $warning['warningcode'] = '1';
961 $warning['message'] = 'No access rights in module context';
962 $warnings[] = $warning;
966 // Create the query and populate an array of assign_user_flags records from the recordset results.
967 if (count ($requestedassignmentids) > 0) {
968 $placeholders = array();
969 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
971 $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
972 "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
973 "FROM {assign_user_flags} auf ".
974 "WHERE auf.assignment ".$inorequalsql.
975 " ORDER BY auf.assignment, auf.id";
977 $rs = $DB->get_recordset_sql($sql, $placeholders);
978 $currentassignmentid = null;
979 $assignment = null;
980 foreach ($rs as $rd) {
981 $userflag = array();
982 $userflag['id'] = $rd->id;
983 $userflag['userid'] = $rd->userid;
984 $userflag['locked'] = $rd->locked;
985 $userflag['mailed'] = $rd->mailed;
986 $userflag['extensionduedate'] = $rd->extensionduedate;
987 $userflag['workflowstate'] = $rd->workflowstate;
988 $userflag['allocatedmarker'] = $rd->allocatedmarker;
990 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
991 if (!is_null($assignment)) {
992 $assignments[] = $assignment;
994 $assignment = array();
995 $assignment['assignmentid'] = $rd->assignment;
996 $assignment['userflags'] = array();
997 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
999 $assignment['userflags'][] = $userflag;
1001 $currentassignmentid = $rd->assignment;
1003 if (!is_null($assignment)) {
1004 $assignments[] = $assignment;
1006 $rs->close();
1010 foreach ($requestedassignmentids as $assignmentid) {
1011 $warning = array();
1012 $warning['item'] = 'assignment';
1013 $warning['itemid'] = $assignmentid;
1014 $warning['warningcode'] = '3';
1015 $warning['message'] = 'No user flags found';
1016 $warnings[] = $warning;
1019 $result = array();
1020 $result['assignments'] = $assignments;
1021 $result['warnings'] = $warnings;
1022 return $result;
1026 * Creates an assign_user_flags external_single_structure
1027 * @return external_single_structure
1028 * @since Moodle 2.6
1030 private static function assign_user_flags() {
1031 return new external_single_structure(
1032 array (
1033 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
1034 'userflags' => new external_multiple_structure(new external_single_structure(
1035 array(
1036 'id' => new external_value(PARAM_INT, 'user flag id'),
1037 'userid' => new external_value(PARAM_INT, 'student id'),
1038 'locked' => new external_value(PARAM_INT, 'locked'),
1039 'mailed' => new external_value(PARAM_INT, 'mailed'),
1040 'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
1041 'workflowstate' => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
1042 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker')
1051 * Describes the get_user_flags return value
1052 * @return external_single_structure
1053 * @since Moodle 2.6
1055 public static function get_user_flags_returns() {
1056 return new external_single_structure(
1057 array(
1058 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
1059 'warnings' => new external_warnings('item is always \'assignment\'',
1060 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1061 'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
1067 * Describes the parameters for get_user_mappings
1068 * @return external_function_parameters
1069 * @since Moodle 2.6
1071 public static function get_user_mappings_parameters() {
1072 return new external_function_parameters(
1073 array(
1074 'assignmentids' => new external_multiple_structure(
1075 new external_value(PARAM_INT, 'assignment id'),
1076 '1 or more assignment ids',
1077 VALUE_REQUIRED)
1083 * Returns user mapping information from assign_user_mapping for the requested assignment ids
1084 * @param int[] $assignmentids
1085 * @return array of user mapping records for each requested assignment
1086 * @since Moodle 2.6
1088 public static function get_user_mappings($assignmentids) {
1089 global $DB;
1090 $params = self::validate_parameters(self::get_user_mappings_parameters(),
1091 array('assignmentids' => $assignmentids));
1093 $assignments = array();
1094 $warnings = array();
1095 $requestedassignmentids = $params['assignmentids'];
1097 // Check the user is allowed to get the mappings for the assignments requested.
1098 $placeholders = array();
1099 list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1100 $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1101 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1102 $placeholders['modname'] = 'assign';
1103 $cms = $DB->get_records_sql($sql, $placeholders);
1104 foreach ($cms as $cm) {
1105 try {
1106 $context = context_module::instance($cm->id);
1107 self::validate_context($context);
1108 require_capability('mod/assign:revealidentities', $context);
1109 } catch (Exception $e) {
1110 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1111 $warning = array();
1112 $warning['item'] = 'assignment';
1113 $warning['itemid'] = $cm->instance;
1114 $warning['warningcode'] = '1';
1115 $warning['message'] = 'No access rights in module context';
1116 $warnings[] = $warning;
1120 // Create the query and populate an array of assign_user_mapping records from the recordset results.
1121 if (count ($requestedassignmentids) > 0) {
1122 $placeholders = array();
1123 list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1125 $sql = "SELECT aum.id,aum.assignment,aum.userid ".
1126 "FROM {assign_user_mapping} aum ".
1127 "WHERE aum.assignment ".$inorequalsql.
1128 " ORDER BY aum.assignment, aum.id";
1130 $rs = $DB->get_recordset_sql($sql, $placeholders);
1131 $currentassignmentid = null;
1132 $assignment = null;
1133 foreach ($rs as $rd) {
1134 $mapping = array();
1135 $mapping['id'] = $rd->id;
1136 $mapping['userid'] = $rd->userid;
1138 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1139 if (!is_null($assignment)) {
1140 $assignments[] = $assignment;
1142 $assignment = array();
1143 $assignment['assignmentid'] = $rd->assignment;
1144 $assignment['mappings'] = array();
1145 $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1147 $assignment['mappings'][] = $mapping;
1149 $currentassignmentid = $rd->assignment;
1151 if (!is_null($assignment)) {
1152 $assignments[] = $assignment;
1154 $rs->close();
1158 foreach ($requestedassignmentids as $assignmentid) {
1159 $warning = array();
1160 $warning['item'] = 'assignment';
1161 $warning['itemid'] = $assignmentid;
1162 $warning['warningcode'] = '3';
1163 $warning['message'] = 'No mappings found';
1164 $warnings[] = $warning;
1167 $result = array();
1168 $result['assignments'] = $assignments;
1169 $result['warnings'] = $warnings;
1170 return $result;
1174 * Creates an assign_user_mappings external_single_structure
1175 * @return external_single_structure
1176 * @since Moodle 2.6
1178 private static function assign_user_mappings() {
1179 return new external_single_structure(
1180 array (
1181 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
1182 'mappings' => new external_multiple_structure(new external_single_structure(
1183 array(
1184 'id' => new external_value(PARAM_INT, 'user mapping id'),
1185 'userid' => new external_value(PARAM_INT, 'student id')
1194 * Describes the get_user_mappings return value
1195 * @return external_single_structure
1196 * @since Moodle 2.6
1198 public static function get_user_mappings_returns() {
1199 return new external_single_structure(
1200 array(
1201 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
1202 'warnings' => new external_warnings('item is always \'assignment\'',
1203 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1204 'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
1210 * Describes the parameters for lock_submissions
1211 * @return external_external_function_parameters
1212 * @since Moodle 2.6
1214 public static function lock_submissions_parameters() {
1215 return new external_function_parameters(
1216 array(
1217 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1218 'userids' => new external_multiple_structure(
1219 new external_value(PARAM_INT, 'user id'),
1220 '1 or more user ids',
1221 VALUE_REQUIRED),
1227 * Locks (prevent updates to) submissions in this assignment.
1229 * @param int $assignmentid The id of the assignment
1230 * @param array $userids Array of user ids to lock
1231 * @return array of warnings for each submission that could not be locked.
1232 * @since Moodle 2.6
1234 public static function lock_submissions($assignmentid, $userids) {
1235 global $CFG;
1236 require_once("$CFG->dirroot/mod/assign/locallib.php");
1238 $params = self::validate_parameters(self::lock_submissions_parameters(),
1239 array('assignmentid' => $assignmentid,
1240 'userids' => $userids));
1242 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1243 $context = context_module::instance($cm->id);
1244 self::validate_context($context);
1246 $assignment = new assign($context, $cm, null);
1248 $warnings = array();
1249 foreach ($params['userids'] as $userid) {
1250 if (!$assignment->lock_submission($userid)) {
1251 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1252 $warnings[] = self::generate_warning($params['assignmentid'],
1253 'couldnotlock',
1254 $detail);
1258 return $warnings;
1262 * Describes the return value for lock_submissions
1264 * @return external_single_structure
1265 * @since Moodle 2.6
1267 public static function lock_submissions_returns() {
1268 return new external_warnings();
1272 * Describes the parameters for revert_submissions_to_draft
1273 * @return external_external_function_parameters
1274 * @since Moodle 2.6
1276 public static function revert_submissions_to_draft_parameters() {
1277 return new external_function_parameters(
1278 array(
1279 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1280 'userids' => new external_multiple_structure(
1281 new external_value(PARAM_INT, 'user id'),
1282 '1 or more user ids',
1283 VALUE_REQUIRED),
1289 * Reverts a list of user submissions to draft for a single assignment.
1291 * @param int $assignmentid The id of the assignment
1292 * @param array $userids Array of user ids to revert
1293 * @return array of warnings for each submission that could not be reverted.
1294 * @since Moodle 2.6
1296 public static function revert_submissions_to_draft($assignmentid, $userids) {
1297 global $CFG;
1298 require_once("$CFG->dirroot/mod/assign/locallib.php");
1300 $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
1301 array('assignmentid' => $assignmentid,
1302 'userids' => $userids));
1304 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1305 $context = context_module::instance($cm->id);
1306 self::validate_context($context);
1308 $assignment = new assign($context, $cm, null);
1310 $warnings = array();
1311 foreach ($params['userids'] as $userid) {
1312 if (!$assignment->revert_to_draft($userid)) {
1313 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1314 $warnings[] = self::generate_warning($params['assignmentid'],
1315 'couldnotrevert',
1316 $detail);
1320 return $warnings;
1324 * Describes the return value for revert_submissions_to_draft
1326 * @return external_single_structure
1327 * @since Moodle 2.6
1329 public static function revert_submissions_to_draft_returns() {
1330 return new external_warnings();
1334 * Describes the parameters for unlock_submissions
1335 * @return external_external_function_parameters
1336 * @since Moodle 2.6
1338 public static function unlock_submissions_parameters() {
1339 return new external_function_parameters(
1340 array(
1341 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1342 'userids' => new external_multiple_structure(
1343 new external_value(PARAM_INT, 'user id'),
1344 '1 or more user ids',
1345 VALUE_REQUIRED),
1351 * Locks (prevent updates to) submissions in this assignment.
1353 * @param int $assignmentid The id of the assignment
1354 * @param array $userids Array of user ids to lock
1355 * @return array of warnings for each submission that could not be locked.
1356 * @since Moodle 2.6
1358 public static function unlock_submissions($assignmentid, $userids) {
1359 global $CFG;
1360 require_once("$CFG->dirroot/mod/assign/locallib.php");
1362 $params = self::validate_parameters(self::unlock_submissions_parameters(),
1363 array('assignmentid' => $assignmentid,
1364 'userids' => $userids));
1366 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1367 $context = context_module::instance($cm->id);
1368 self::validate_context($context);
1370 $assignment = new assign($context, $cm, null);
1372 $warnings = array();
1373 foreach ($params['userids'] as $userid) {
1374 if (!$assignment->unlock_submission($userid)) {
1375 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1376 $warnings[] = self::generate_warning($params['assignmentid'],
1377 'couldnotunlock',
1378 $detail);
1382 return $warnings;
1386 * Describes the return value for unlock_submissions
1388 * @return external_single_structure
1389 * @since Moodle 2.6
1391 public static function unlock_submissions_returns() {
1392 return new external_warnings();
1396 * Describes the parameters for submit_for_grading
1397 * @return external_external_function_parameters
1398 * @since Moodle 2.6
1400 public static function submit_for_grading_parameters() {
1401 return new external_function_parameters(
1402 array(
1403 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1404 'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement')
1410 * Submit the logged in users assignment for grading.
1412 * @param int $assignmentid The id of the assignment
1413 * @return array of warnings to indicate any errors.
1414 * @since Moodle 2.6
1416 public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
1417 global $CFG, $USER;
1418 require_once("$CFG->dirroot/mod/assign/locallib.php");
1420 $params = self::validate_parameters(self::submit_for_grading_parameters(),
1421 array('assignmentid' => $assignmentid,
1422 'acceptsubmissionstatement' => $acceptsubmissionstatement));
1424 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1425 $context = context_module::instance($cm->id);
1426 self::validate_context($context);
1428 $assignment = new assign($context, $cm, null);
1430 $warnings = array();
1431 $data = new stdClass();
1432 $data->submissionstatement = $params['acceptsubmissionstatement'];
1433 $notices = array();
1435 if (!$assignment->submit_for_grading($data, $notices)) {
1436 $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices);
1437 $warnings[] = self::generate_warning($params['assignmentid'],
1438 'couldnotsubmitforgrading',
1439 $detail);
1442 return $warnings;
1446 * Describes the return value for submit_for_grading
1448 * @return external_single_structure
1449 * @since Moodle 2.6
1451 public static function submit_for_grading_returns() {
1452 return new external_warnings();
1456 * Describes the parameters for save_user_extensions
1457 * @return external_external_function_parameters
1458 * @since Moodle 2.6
1460 public static function save_user_extensions_parameters() {
1461 return new external_function_parameters(
1462 array(
1463 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1464 'userids' => new external_multiple_structure(
1465 new external_value(PARAM_INT, 'user id'),
1466 '1 or more user ids',
1467 VALUE_REQUIRED),
1468 'dates' => new external_multiple_structure(
1469 new external_value(PARAM_INT, 'dates'),
1470 '1 or more extension dates (timestamp)',
1471 VALUE_REQUIRED),
1477 * Grant extension dates to students for an assignment.
1479 * @param int $assignmentid The id of the assignment
1480 * @param array $userids Array of user ids to grant extensions to
1481 * @param array $dates Array of extension dates
1482 * @return array of warnings for each extension date that could not be granted
1483 * @since Moodle 2.6
1485 public static function save_user_extensions($assignmentid, $userids, $dates) {
1486 global $CFG;
1487 require_once("$CFG->dirroot/mod/assign/locallib.php");
1489 $params = self::validate_parameters(self::save_user_extensions_parameters(),
1490 array('assignmentid' => $assignmentid,
1491 'userids' => $userids,
1492 'dates' => $dates));
1494 if (count($params['userids']) != count($params['dates'])) {
1495 $detail = 'Length of userids and dates parameters differ.';
1496 $warnings[] = self::generate_warning($params['assignmentid'],
1497 'invalidparameters',
1498 $detail);
1500 return $warnings;
1503 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1504 $context = context_module::instance($cm->id);
1505 self::validate_context($context);
1507 $assignment = new assign($context, $cm, null);
1509 $warnings = array();
1510 foreach ($params['userids'] as $idx => $userid) {
1511 $duedate = $params['dates'][$idx];
1512 if (!$assignment->save_user_extension($userid, $duedate)) {
1513 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate;
1514 $warnings[] = self::generate_warning($params['assignmentid'],
1515 'couldnotgrantextensions',
1516 $detail);
1520 return $warnings;
1524 * Describes the return value for save_user_extensions
1526 * @return external_single_structure
1527 * @since Moodle 2.6
1529 public static function save_user_extensions_returns() {
1530 return new external_warnings();
1534 * Describes the parameters for reveal_identities
1535 * @return external_external_function_parameters
1536 * @since Moodle 2.6
1538 public static function reveal_identities_parameters() {
1539 return new external_function_parameters(
1540 array(
1541 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
1547 * Reveal the identities of anonymous students to markers for a single assignment.
1549 * @param int $assignmentid The id of the assignment
1550 * @return array of warnings to indicate any errors.
1551 * @since Moodle 2.6
1553 public static function reveal_identities($assignmentid) {
1554 global $CFG, $USER;
1555 require_once("$CFG->dirroot/mod/assign/locallib.php");
1557 $params = self::validate_parameters(self::reveal_identities_parameters(),
1558 array('assignmentid' => $assignmentid));
1560 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1561 $context = context_module::instance($cm->id);
1562 self::validate_context($context);
1564 $assignment = new assign($context, $cm, null);
1566 $warnings = array();
1567 if (!$assignment->reveal_identities()) {
1568 $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'];
1569 $warnings[] = self::generate_warning($params['assignmentid'],
1570 'couldnotrevealidentities',
1571 $detail);
1574 return $warnings;
1578 * Describes the return value for reveal_identities
1580 * @return external_single_structure
1581 * @since Moodle 2.6
1583 public static function reveal_identities_returns() {
1584 return new external_warnings();
1588 * Describes the parameters for save_submission
1589 * @return external_external_function_parameters
1590 * @since Moodle 2.6
1592 public static function save_submission_parameters() {
1593 global $CFG;
1594 require_once("$CFG->dirroot/mod/assign/locallib.php");
1595 $instance = new assign(null, null, null);
1596 $pluginsubmissionparams = array();
1598 foreach ($instance->get_submission_plugins() as $plugin) {
1599 $pluginparams = $plugin->get_external_parameters();
1600 if (!empty($pluginparams)) {
1601 $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
1605 return new external_function_parameters(
1606 array(
1607 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1608 'plugindata' => new external_single_structure(
1609 $pluginsubmissionparams
1616 * Save a student submission for a single assignment
1618 * @param int $assignmentid The id of the assignment
1619 * @param array $plugindata - The submitted data for plugins
1620 * @return array of warnings to indicate any errors
1621 * @since Moodle 2.6
1623 public static function save_submission($assignmentid, $plugindata) {
1624 global $CFG, $USER;
1625 require_once("$CFG->dirroot/mod/assign/locallib.php");
1627 $params = self::validate_parameters(self::save_submission_parameters(),
1628 array('assignmentid' => $assignmentid,
1629 'plugindata' => $plugindata));
1631 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1632 $context = context_module::instance($cm->id);
1633 self::validate_context($context);
1635 $assignment = new assign($context, $cm, null);
1637 $notices = array();
1639 if (!$assignment->submissions_open($USER->id)) {
1640 $notices[] = get_string('duedatereached', 'assign');
1641 } else {
1642 $submissiondata = (object)$params['plugindata'];
1643 $assignment->save_submission($submissiondata, $notices);
1646 $warnings = array();
1647 foreach ($notices as $notice) {
1648 $warnings[] = self::generate_warning($params['assignmentid'],
1649 'couldnotsavesubmission',
1650 $notice);
1653 return $warnings;
1657 * Describes the return value for save_submission
1659 * @return external_single_structure
1660 * @since Moodle 2.6
1662 public static function save_submission_returns() {
1663 return new external_warnings();
1667 * Describes the parameters for save_grade
1668 * @return external_external_function_parameters
1669 * @since Moodle 2.6
1671 public static function save_grade_parameters() {
1672 global $CFG;
1673 require_once("$CFG->dirroot/mod/assign/locallib.php");
1674 require_once("$CFG->dirroot/grade/grading/lib.php");
1675 $instance = new assign(null, null, null);
1676 $pluginfeedbackparams = array();
1678 foreach ($instance->get_feedback_plugins() as $plugin) {
1679 $pluginparams = $plugin->get_external_parameters();
1680 if (!empty($pluginparams)) {
1681 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1685 $advancedgradingdata = array();
1686 $methods = array_keys(grading_manager::available_methods(false));
1687 foreach ($methods as $method) {
1688 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1689 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1690 if (!empty($details)) {
1691 $items = array();
1692 foreach ($details as $key => $value) {
1693 $value->required = VALUE_OPTIONAL;
1694 unset($value->content->keys['id']);
1695 $items[$key] = new external_multiple_structure (new external_single_structure(
1696 array(
1697 'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1698 'fillings' => $value
1702 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1706 return new external_function_parameters(
1707 array(
1708 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1709 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1710 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'),
1711 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1712 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
1713 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1714 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1715 'to all members ' .
1716 'of the group (for group assignments).'),
1717 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()),
1718 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1719 VALUE_DEFAULT, array())
1725 * Save a student grade for a single assignment.
1727 * @param int $assignmentid The id of the assignment
1728 * @param int $userid The id of the user
1729 * @param float $grade The grade (ignored if the assignment uses advanced grading)
1730 * @param int $attemptnumber The attempt number
1731 * @param bool $addattempt Allow another attempt
1732 * @param string $workflowstate New workflow state
1733 * @param bool $applytoall Apply the grade to all members of the group
1734 * @param array $plugindata Custom data used by plugins
1735 * @param array $advancedgradingdata Advanced grading data
1736 * @return null
1737 * @since Moodle 2.6
1739 public static function save_grade($assignmentid,
1740 $userid,
1741 $grade,
1742 $attemptnumber,
1743 $addattempt,
1744 $workflowstate,
1745 $applytoall,
1746 $plugindata = array(),
1747 $advancedgradingdata = array()) {
1748 global $CFG, $USER;
1749 require_once("$CFG->dirroot/mod/assign/locallib.php");
1751 $params = self::validate_parameters(self::save_grade_parameters(),
1752 array('assignmentid' => $assignmentid,
1753 'userid' => $userid,
1754 'grade' => $grade,
1755 'attemptnumber' => $attemptnumber,
1756 'workflowstate' => $workflowstate,
1757 'addattempt' => $addattempt,
1758 'applytoall' => $applytoall,
1759 'plugindata' => $plugindata,
1760 'advancedgradingdata' => $advancedgradingdata));
1762 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1763 $context = context_module::instance($cm->id);
1764 self::validate_context($context);
1766 $assignment = new assign($context, $cm, null);
1768 $gradedata = (object)$params['plugindata'];
1770 $gradedata->addattempt = $params['addattempt'];
1771 $gradedata->attemptnumber = $params['attemptnumber'];
1772 $gradedata->workflowstate = $params['workflowstate'];
1773 $gradedata->applytoall = $params['applytoall'];
1774 $gradedata->grade = $params['grade'];
1776 if (!empty($params['advancedgradingdata'])) {
1777 $advancedgrading = array();
1778 $criteria = reset($params['advancedgradingdata']);
1779 foreach ($criteria as $key => $criterion) {
1780 $details = array();
1781 foreach ($criterion as $value) {
1782 foreach ($value['fillings'] as $filling) {
1783 $details[$value['criterionid']] = $filling;
1786 $advancedgrading[$key] = $details;
1788 $gradedata->advancedgrading = $advancedgrading;
1791 $assignment->save_grade($params['userid'], $gradedata);
1793 return null;
1797 * Describes the return value for save_grade
1799 * @return external_single_structure
1800 * @since Moodle 2.6
1802 public static function save_grade_returns() {
1803 return null;
1807 * Describes the parameters for save_grades
1808 * @return external_external_function_parameters
1809 * @since Moodle 2.7
1811 public static function save_grades_parameters() {
1812 global $CFG;
1813 require_once("$CFG->dirroot/mod/assign/locallib.php");
1814 require_once("$CFG->dirroot/grade/grading/lib.php");
1815 $instance = new assign(null, null, null);
1816 $pluginfeedbackparams = array();
1818 foreach ($instance->get_feedback_plugins() as $plugin) {
1819 $pluginparams = $plugin->get_external_parameters();
1820 if (!empty($pluginparams)) {
1821 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1825 $advancedgradingdata = array();
1826 $methods = array_keys(grading_manager::available_methods(false));
1827 foreach ($methods as $method) {
1828 require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1829 $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1830 if (!empty($details)) {
1831 $items = array();
1832 foreach ($details as $key => $value) {
1833 $value->required = VALUE_OPTIONAL;
1834 unset($value->content->keys['id']);
1835 $items[$key] = new external_multiple_structure (new external_single_structure(
1836 array(
1837 'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1838 'fillings' => $value
1842 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1846 return new external_function_parameters(
1847 array(
1848 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1849 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1850 'to all members ' .
1851 'of the group (for group assignments).'),
1852 'grades' => new external_multiple_structure(
1853 new external_single_structure(
1854 array (
1855 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1856 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '.
1857 'Ignored if advanced grading used'),
1858 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1859 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'),
1860 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1861 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data',
1862 VALUE_DEFAULT, array()),
1863 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1864 VALUE_DEFAULT, array())
1873 * Save multiple student grades for a single assignment.
1875 * @param int $assignmentid The id of the assignment
1876 * @param boolean $applytoall If set to true and this is a team assignment,
1877 * apply the grade to all members of the group
1878 * @param array $grades grade data for one or more students that includes
1879 * userid - The id of the student being graded
1880 * grade - The grade (ignored if the assignment uses advanced grading)
1881 * attemptnumber - The attempt number
1882 * addattempt - Allow another attempt
1883 * workflowstate - New workflow state
1884 * plugindata - Custom data used by plugins
1885 * advancedgradingdata - Optional Advanced grading data
1886 * @throws invalid_parameter_exception if multiple grades are supplied for
1887 * a team assignment that has $applytoall set to true
1888 * @return null
1889 * @since Moodle 2.7
1891 public static function save_grades($assignmentid, $applytoall = false, $grades) {
1892 global $CFG, $USER;
1893 require_once("$CFG->dirroot/mod/assign/locallib.php");
1895 $params = self::validate_parameters(self::save_grades_parameters(),
1896 array('assignmentid' => $assignmentid,
1897 'applytoall' => $applytoall,
1898 'grades' => $grades));
1900 $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1901 $context = context_module::instance($cm->id);
1902 self::validate_context($context);
1903 $assignment = new assign($context, $cm, null);
1905 if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
1906 // Check that only 1 user per submission group is provided.
1907 $groupids = array();
1908 foreach ($params['grades'] as $gradeinfo) {
1909 $group = $assignment->get_submission_group($gradeinfo['userid']);
1910 if (in_array($group->id, $groupids)) {
1911 throw new invalid_parameter_exception('Multiple grades for the same team have been supplied '
1912 .' this is not permitted when the applytoall flag is set');
1913 } else {
1914 $groupids[] = $group->id;
1919 foreach ($params['grades'] as $gradeinfo) {
1920 $gradedata = (object)$gradeinfo['plugindata'];
1921 $gradedata->addattempt = $gradeinfo['addattempt'];
1922 $gradedata->attemptnumber = $gradeinfo['attemptnumber'];
1923 $gradedata->workflowstate = $gradeinfo['workflowstate'];
1924 $gradedata->applytoall = $params['applytoall'];
1925 $gradedata->grade = $gradeinfo['grade'];
1927 if (!empty($gradeinfo['advancedgradingdata'])) {
1928 $advancedgrading = array();
1929 $criteria = reset($gradeinfo['advancedgradingdata']);
1930 foreach ($criteria as $key => $criterion) {
1931 $details = array();
1932 foreach ($criterion as $value) {
1933 foreach ($value['fillings'] as $filling) {
1934 $details[$value['criterionid']] = $filling;
1937 $advancedgrading[$key] = $details;
1939 $gradedata->advancedgrading = $advancedgrading;
1941 $assignment->save_grade($gradeinfo['userid'], $gradedata);
1944 return null;
1948 * Describes the return value for save_grades
1950 * @return external_single_structure
1951 * @since Moodle 2.7
1953 public static function save_grades_returns() {
1954 return null;
1958 * Describes the parameters for copy_previous_attempt
1959 * @return external_external_function_parameters
1960 * @since Moodle 2.6
1962 public static function copy_previous_attempt_parameters() {
1963 return new external_function_parameters(
1964 array(
1965 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1971 * Copy a students previous attempt to a new attempt.
1973 * @param int $assignmentid
1974 * @return array of warnings to indicate any errors.
1975 * @since Moodle 2.6
1977 public static function copy_previous_attempt($assignmentid) {
1978 global $CFG, $USER;
1979 require_once("$CFG->dirroot/mod/assign/locallib.php");
1981 $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
1982 array('assignmentid' => $assignmentid));
1984 $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1985 $context = context_module::instance($cm->id);
1986 self::validate_context($context);
1988 $assignment = new assign($context, $cm, null);
1990 $notices = array();
1992 $assignment->copy_previous_attempt($submissiondata, $notices);
1994 $warnings = array();
1995 foreach ($notices as $notice) {
1996 $warnings[] = self::generate_warning($assignmentid,
1997 'couldnotcopyprevioussubmission',
1998 $notice);
2001 return $warnings;
2005 * Describes the return value for save_submission
2007 * @return external_single_structure
2008 * @since Moodle 2.6
2010 public static function copy_previous_attempt_returns() {
2011 return new external_warnings();