4a679d9f1a49528b8fb19d59437bde19bcba2e91
[moodle.git] / mod / assign / externallib.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * External assign API
19  *
20  * @package    mod_assign
21  * @since      Moodle 2.4
22  * @copyright  2012 Paul Charsley
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
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
34  */
35 class mod_assign_external extends external_api {
37     /**
38      * Generate a warning in a standard structure for a known failure.
39      *
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
44      */
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.'
56         );
58         $message = $warningmessages[$warningcode];
59         if (empty($message)) {
60             $message = 'Unknown warning type.';
61         }
63         return array('item'=>$detail,
64                      'itemid'=>$assignmentid,
65                      'warningcode'=>$warningcode,
66                      'message'=>$message);
67     }
69     /**
70      * Describes the parameters for get_grades
71      * @return external_external_function_parameters
72      * @since  Moodle 2.4
73      */
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)
84             )
85         );
86     }
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
94      */
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;
125             }
126         }
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);
132             list($inorequalsql2, $placeholders2) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
134             $grademaxattempt = 'SELECT mxg.userid, MAX(mxg.attemptnumber) AS maxattempt
135                                 FROM {assign_grades} mxg
136                                 WHERE mxg.assignment ' . $inorequalsql2 . ' GROUP BY mxg.userid';
138             $sql = "SELECT ag.id,ag.assignment,ag.userid,ag.timecreated,ag.timemodified,".
139                    "ag.grader,ag.grade,ag.attemptnumber ".
140                    "FROM {assign_grades} ag ".
141                    "JOIN ( " . $grademaxattempt . " ) gmx ON ag.userid = gmx.userid".
142                    " WHERE ag.assignment ".$inorequalsql.
143                    " AND ag.timemodified  >= :since".
144                    " AND ag.attemptnumber = gmx.maxattempt" .
145                    " ORDER BY ag.assignment, ag.id";
146             $placeholders['since'] = $params['since'];
147             // Combine the parameters.
148             $placeholders += $placeholders2;
149             $rs = $DB->get_recordset_sql($sql, $placeholders);
150             $currentassignmentid = null;
151             $assignment = null;
152             foreach ($rs as $rd) {
153                 $grade = array();
154                 $grade['id'] = $rd->id;
155                 $grade['userid'] = $rd->userid;
156                 $grade['timecreated'] = $rd->timecreated;
157                 $grade['timemodified'] = $rd->timemodified;
158                 $grade['grader'] = $rd->grader;
159                 $grade['attemptnumber'] = $rd->attemptnumber;
160                 $grade['grade'] = (string)$rd->grade;
162                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
163                     if (!is_null($assignment)) {
164                         $assignments[] = $assignment;
165                     }
166                     $assignment = array();
167                     $assignment['assignmentid'] = $rd->assignment;
168                     $assignment['grades'] = array();
169                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
170                 }
171                 $assignment['grades'][] = $grade;
173                 $currentassignmentid = $rd->assignment;
174             }
175             if (!is_null($assignment)) {
176                 $assignments[] = $assignment;
177             }
178             $rs->close();
179         }
180         foreach ($requestedassignmentids as $assignmentid) {
181             $warning = array();
182             $warning['item'] = 'assignment';
183             $warning['itemid'] = $assignmentid;
184             $warning['warningcode'] = '3';
185             $warning['message'] = 'No grades found';
186             $warnings[] = $warning;
187         }
189         $result = array();
190         $result['assignments'] = $assignments;
191         $result['warnings'] = $warnings;
192         return $result;
193     }
195     /**
196      * Creates an assign_grades external_single_structure
197      * @return external_single_structure
198      * @since  Moodle 2.4
199      */
200     private static function assign_grades() {
201         return new external_single_structure(
202             array (
203                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
204                 'grades'   => new external_multiple_structure(new external_single_structure(
205                         array(
206                             'id'            => new external_value(PARAM_INT, 'grade id'),
207                             'userid'        => new external_value(PARAM_INT, 'student id'),
208                             'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
209                             'timecreated'   => new external_value(PARAM_INT, 'grade creation time'),
210                             'timemodified'  => new external_value(PARAM_INT, 'grade last modified time'),
211                             'grader'        => new external_value(PARAM_INT, 'grader'),
212                             'grade'         => new external_value(PARAM_TEXT, 'grade')
213                         )
214                     )
215                 )
216             )
217         );
218     }
220     /**
221      * Describes the get_grades return value
222      * @return external_single_structure
223      * @since  Moodle 2.4
224      */
225     public static function get_grades_returns() {
226         return new external_single_structure(
227             array(
228                 'assignments' => new external_multiple_structure(self::assign_grades(), 'list of assignment grade information'),
229                 'warnings'      => new external_warnings('item is always \'assignment\'',
230                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
231                     'errorcode can be 3 (no grades found) or 1 (no permission to get grades)')
232             )
233         );
234     }
236     /**
237      * Returns description of method parameters
238      *
239      * @return external_function_parameters
240      * @since  Moodle 2.4
241      */
242     public static function get_assignments_parameters() {
243         return new external_function_parameters(
244             array(
245                 'courseids' => new external_multiple_structure(
246                     new external_value(PARAM_INT, 'course id'),
247                     '0 or more course ids',
248                     VALUE_DEFAULT, array()
249                 ),
250                 'capabilities'  => new external_multiple_structure(
251                     new external_value(PARAM_CAPABILITY, 'capability'),
252                     'list of capabilities used to filter courses',
253                     VALUE_DEFAULT, array()
254                 )
255             )
256         );
257     }
259     /**
260      * Returns an array of courses the user is enrolled in, and for each course all of the assignments that the user can
261      * view within that course.
262      *
263      * @param array $courseids An optional array of course ids. If provided only assignments within the given course
264      * will be returned. If the user is not enrolled in a given course a warning will be generated and returned.
265      * @param array $capabilities An array of additional capability checks you wish to be made on the course context.
266      * @return An array of courses and warnings.
267      * @since  Moodle 2.4
268      */
269     public static function get_assignments($courseids = array(), $capabilities = array()) {
270         global $USER, $DB;
272         $params = self::validate_parameters(
273             self::get_assignments_parameters(),
274             array('courseids' => $courseids, 'capabilities' => $capabilities)
275         );
277         $warnings = array();
278         $fields = 'sortorder,shortname,fullname,timemodified';
279         $courses = enrol_get_users_courses($USER->id, true, $fields);
280         // Used to test for ids that have been requested but can't be returned.
281         if (count($params['courseids']) > 0) {
282             foreach ($params['courseids'] as $courseid) {
283                 if (!in_array($courseid, array_keys($courses))) {
284                     unset($courses[$courseid]);
285                     $warnings[] = array(
286                         'item' => 'course',
287                         'itemid' => $courseid,
288                         'warningcode' => '2',
289                         'message' => 'User is not enrolled or does not have requested capability'
290                     );
291                 }
292             }
293         }
294         foreach ($courses as $id => $course) {
295             if (count($params['courseids']) > 0 && !in_array($id, $params['courseids'])) {
296                 unset($courses[$id]);
297             }
298             $context = context_course::instance($id);
299             try {
300                 self::validate_context($context);
301             } catch (Exception $e) {
302                 unset($courses[$id]);
303                 $warnings[] = array(
304                     'item' => 'course',
305                     'itemid' => $id,
306                     'warningcode' => '1',
307                     'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
308                 );
309                 continue;
310             }
311             if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) {
312                 unset($courses[$id]);
313             }
314         }
315         $extrafields='m.id as assignmentid, m.course, m.nosubmissions, m.submissiondrafts, m.sendnotifications, '.
316                      'm.sendlatenotifications, m.duedate, m.allowsubmissionsfromdate, m.grade, m.timemodified, '.
317                      'm.completionsubmit, m.cutoffdate, m.teamsubmission, m.requireallteammemberssubmit, '.
318                      'm.teamsubmissiongroupingid, m.blindmarking, m.revealidentities, m.attemptreopenmethod, '.
319                      'm.maxattempts, m.markingworkflow, m.markingallocation, m.requiresubmissionstatement';
320         $coursearray = array();
321         foreach ($courses as $id => $course) {
322             $assignmentarray = array();
323             // Get a list of assignments for the course.
324             if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
325                 foreach ($modules as $module) {
326                     $context = context_module::instance($module->id);
327                     try {
328                         self::validate_context($context);
329                         require_capability('mod/assign:view', $context);
330                     } catch (Exception $e) {
331                         $warnings[] = array(
332                             'item' => 'module',
333                             'itemid' => $module->id,
334                             'warningcode' => '1',
335                             'message' => 'No access rights in module context'
336                         );
337                         continue;
338                     }
339                     $configrecords = $DB->get_recordset('assign_plugin_config', array('assignment' => $module->assignmentid));
340                     $configarray = array();
341                     foreach ($configrecords as $configrecord) {
342                         $configarray[] = array(
343                             'id' => $configrecord->id,
344                             'assignment' => $configrecord->assignment,
345                             'plugin' => $configrecord->plugin,
346                             'subtype' => $configrecord->subtype,
347                             'name' => $configrecord->name,
348                             'value' => $configrecord->value
349                         );
350                     }
351                     $configrecords->close();
353                     $assignmentarray[]= array(
354                         'id' => $module->assignmentid,
355                         'cmid' => $module->id,
356                         'course' => $module->course,
357                         'name' => $module->name,
358                         'nosubmissions' => $module->nosubmissions,
359                         'submissiondrafts' => $module->submissiondrafts,
360                         'sendnotifications' => $module->sendnotifications,
361                         'sendlatenotifications' => $module->sendlatenotifications,
362                         'duedate' => $module->duedate,
363                         'allowsubmissionsfromdate' => $module->allowsubmissionsfromdate,
364                         'grade' => $module->grade,
365                         'timemodified' => $module->timemodified,
366                         'completionsubmit' => $module->completionsubmit,
367                         'cutoffdate' => $module->cutoffdate,
368                         'teamsubmission' => $module->teamsubmission,
369                         'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
370                         'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
371                         'blindmarking' => $module->blindmarking,
372                         'revealidentities' => $module->revealidentities,
373                         'attemptreopenmethod' => $module->attemptreopenmethod,
374                         'maxattempts' => $module->maxattempts,
375                         'markingworkflow' => $module->markingworkflow,
376                         'markingallocation' => $module->markingallocation,
377                         'requiresubmissionstatement' => $module->requiresubmissionstatement,
378                         'configs' => $configarray
379                     );
380                 }
381             }
382             $coursearray[]= array(
383                 'id' => $courses[$id]->id,
384                 'fullname' => $courses[$id]->fullname,
385                 'shortname' => $courses[$id]->shortname,
386                 'timemodified' => $courses[$id]->timemodified,
387                 'assignments' => $assignmentarray
388             );
389         }
391         $result = array(
392             'courses' => $coursearray,
393             'warnings' => $warnings
394         );
395         return $result;
396     }
398     /**
399      * Creates an assignment external_single_structure
400      *
401      * @return external_single_structure
402      * @since Moodle 2.4
403      */
404     private static function get_assignments_assignment_structure() {
405         return new external_single_structure(
406             array(
407                 'id' => new external_value(PARAM_INT, 'assignment id'),
408                 'cmid' => new external_value(PARAM_INT, 'course module id'),
409                 'course' => new external_value(PARAM_INT, 'course id'),
410                 'name' => new external_value(PARAM_TEXT, 'assignment name'),
411                 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
412                 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
413                 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
414                 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
415                 'duedate' => new external_value(PARAM_INT, 'assignment due date'),
416                 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
417                 'grade' => new external_value(PARAM_INT, 'grade type'),
418                 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
419                 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
420                 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
421                 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
422                 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
423                 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
424                 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
425                 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
426                 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
427                 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
428                 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
429                 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
430                 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
431                 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings')
432             ), 'assignment information object');
433     }
435     /**
436      * Creates an assign_plugin_config external_single_structure
437      *
438      * @return external_single_structure
439      * @since Moodle 2.4
440      */
441     private static function get_assignments_config_structure() {
442         return new external_single_structure(
443             array(
444                 'id' => new external_value(PARAM_INT, 'assign_plugin_config id'),
445                 'assignment' => new external_value(PARAM_INT, 'assignment id'),
446                 'plugin' => new external_value(PARAM_TEXT, 'plugin'),
447                 'subtype' => new external_value(PARAM_TEXT, 'subtype'),
448                 'name' => new external_value(PARAM_TEXT, 'name'),
449                 'value' => new external_value(PARAM_TEXT, 'value')
450             ), 'assignment configuration object'
451         );
452     }
454     /**
455      * Creates a course external_single_structure
456      *
457      * @return external_single_structure
458      * @since Moodle 2.4
459      */
460     private static function get_assignments_course_structure() {
461         return new external_single_structure(
462             array(
463                 'id' => new external_value(PARAM_INT, 'course id'),
464                 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
465                 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
466                 'timemodified' => new external_value(PARAM_INT, 'last time modified'),
467                 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
468               ), 'course information object'
469         );
470     }
472     /**
473      * Describes the return value for get_assignments
474      *
475      * @return external_single_structure
476      * @since Moodle 2.4
477      */
478     public static function get_assignments_returns() {
479         return new external_single_structure(
480             array(
481                 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
482                 'warnings'  => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
483                     'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
484                     'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
485             )
486         );
487     }
489     /**
490      * Describes the parameters for get_submissions
491      *
492      * @return external_external_function_parameters
493      * @since Moodle 2.5
494      */
495     public static function get_submissions_parameters() {
496         return new external_function_parameters(
497             array(
498                 'assignmentids' => new external_multiple_structure(
499                     new external_value(PARAM_INT, 'assignment id'),
500                     '1 or more assignment ids',
501                     VALUE_REQUIRED),
502                 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
503                 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
504                 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
505             )
506         );
507     }
509     /**
510      * Returns submissions for the requested assignment ids
511      *
512      * @param int[] $assignmentids
513      * @param string $status only return submissions with this status
514      * @param int $since only return submissions with timemodified >= since
515      * @param int $before only return submissions with timemodified <= before
516      * @return array of submissions for each requested assignment
517      * @since Moodle 2.5
518      */
519     public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
520         global $DB, $CFG;
521         require_once("$CFG->dirroot/mod/assign/locallib.php");
522         $params = self::validate_parameters(self::get_submissions_parameters(),
523                         array('assignmentids' => $assignmentids,
524                               'status' => $status,
525                               'since' => $since,
526                               'before' => $before));
528         $warnings = array();
529         $assignments = array();
531         // Check the user is allowed to get the submissions for the assignments requested.
532         $placeholders = array();
533         list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
534         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
535                "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
536         $placeholders['modname'] = 'assign';
537         $cms = $DB->get_records_sql($sql, $placeholders);
538         $assigns = array();
539         foreach ($cms as $cm) {
540             try {
541                 $context = context_module::instance($cm->id);
542                 self::validate_context($context);
543                 require_capability('mod/assign:grade', $context);
544                 $assign = new assign($context, null, null);
545                 $assigns[] = $assign;
546             } catch (Exception $e) {
547                 $warnings[] = array(
548                     'item' => 'assignment',
549                     'itemid' => $cm->instance,
550                     'warningcode' => '1',
551                     'message' => 'No access rights in module context'
552                 );
553             }
554         }
556         foreach ($assigns as $assign) {
557             $submissions = array();
558             $submissionplugins = $assign->get_submission_plugins();
559             $placeholders = array('assignid1' => $assign->get_instance()->id,
560                                   'assignid2' => $assign->get_instance()->id);
562             $submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
563                                      FROM {assign_submission} mxs
564                                      WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid';
566             $sql = "SELECT mas.id, mas.assignment,mas.userid,".
567                    "mas.timecreated,mas.timemodified,mas.status,mas.groupid,mas.attemptnumber ".
568                    "FROM {assign_submission} mas ".
569                    "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
570                    "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
572             if (!empty($params['status'])) {
573                 $placeholders['status'] = $params['status'];
574                 $sql = $sql." AND mas.status = :status";
575             }
576             if (!empty($params['before'])) {
577                 $placeholders['since'] = $params['since'];
578                 $placeholders['before'] = $params['before'];
579                 $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
580             } else {
581                 $placeholders['since'] = $params['since'];
582                 $sql = $sql." AND mas.timemodified >= :since";
583             }
585             $submissionrecords = $DB->get_records_sql($sql, $placeholders);
587             if (!empty($submissionrecords)) {
588                 $fs = get_file_storage();
589                 foreach ($submissionrecords as $submissionrecord) {
590                     $submission = array(
591                         'id' => $submissionrecord->id,
592                         'userid' => $submissionrecord->userid,
593                         'timecreated' => $submissionrecord->timecreated,
594                         'timemodified' => $submissionrecord->timemodified,
595                         'status' => $submissionrecord->status,
596                         'attemptnumber' => $submissionrecord->attemptnumber,
597                         'groupid' => $submissionrecord->groupid
598                     );
599                     foreach ($submissionplugins as $submissionplugin) {
600                         $plugin = array(
601                             'name' => $submissionplugin->get_name(),
602                             'type' => $submissionplugin->get_type()
603                         );
604                         // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
605                         $component = $submissionplugin->get_subtype().'_'.$submissionplugin->get_type();
607                         $fileareas = $submissionplugin->get_file_areas();
608                         foreach ($fileareas as $filearea => $name) {
609                             $fileareainfo = array('area' => $filearea);
610                             $files = $fs->get_area_files(
611                                 $assign->get_context()->id,
612                                 $component,
613                                 $filearea,
614                                 $submissionrecord->id,
615                                 "timemodified",
616                                 false
617                             );
618                             foreach ($files as $file) {
619                                 $filepath = array('filepath' => $file->get_filepath().$file->get_filename());
620                                 $fileareainfo['files'][] = $filepath;
621                             }
622                             $plugin['fileareas'][] = $fileareainfo;
623                         }
625                         $editorfields = $submissionplugin->get_editor_fields();
626                         foreach ($editorfields as $name => $description) {
627                             $editorfieldinfo = array(
628                                 'name' => $name,
629                                 'description' => $description,
630                                 'text' => $submissionplugin->get_editor_text($name, $submissionrecord->id),
631                                 'format' => $submissionplugin->get_editor_format($name, $submissionrecord->id)
632                             );
633                             $plugin['editorfields'][] = $editorfieldinfo;
634                         }
636                         $submission['plugins'][] = $plugin;
637                     }
638                     $submissions[] = $submission;
639                 }
640             } else {
641                 $warnings[] = array(
642                     'item' => 'module',
643                     'itemid' => $assign->get_instance()->id,
644                     'warningcode' => '3',
645                     'message' => 'No submissions found'
646                 );
647             }
649             $assignments[] = array(
650                 'assignmentid' => $assign->get_instance()->id,
651                 'submissions' => $submissions
652             );
654         }
656         $result = array(
657             'assignments' => $assignments,
658             'warnings' => $warnings
659         );
660         return $result;
661     }
663     /**
664      * Creates an assign_submissions external_single_structure
665      *
666      * @return external_single_structure
667      * @since Moodle 2.5
668      */
669     private static function get_submissions_structure() {
670         return new external_single_structure(
671             array (
672                 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
673                 'submissions' => new external_multiple_structure(
674                     new external_single_structure(
675                         array(
676                             'id' => new external_value(PARAM_INT, 'submission id'),
677                             'userid' => new external_value(PARAM_INT, 'student id'),
678                             'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
679                             'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
680                             'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
681                             'status' => new external_value(PARAM_TEXT, 'submission status'),
682                             'groupid' => new external_value(PARAM_INT, 'group id'),
683                             'plugins' => new external_multiple_structure(
684                                 new external_single_structure(
685                                     array(
686                                         'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
687                                         'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
688                                         'fileareas' => new external_multiple_structure(
689                                             new external_single_structure(
690                                                 array (
691                                                     'area' => new external_value (PARAM_TEXT, 'file area'),
692                                                     'files' => new external_multiple_structure(
693                                                         new external_single_structure(
694                                                             array (
695                                                                 'filepath' => new external_value (PARAM_TEXT, 'file path')
696                                                             )
697                                                         ), 'files', VALUE_OPTIONAL
698                                                     )
699                                                 )
700                                             ), 'fileareas', VALUE_OPTIONAL
701                                         ),
702                                         'editorfields' => new external_multiple_structure(
703                                             new external_single_structure(
704                                                 array(
705                                                     'name' => new external_value(PARAM_TEXT, 'field name'),
706                                                     'description' => new external_value(PARAM_TEXT, 'field description'),
707                                                     'text' => new external_value (PARAM_RAW, 'field value'),
708                                                     'format' => new external_format_value ('text')
709                                                 )
710                                             )
711                                             , 'editorfields', VALUE_OPTIONAL
712                                         )
713                                     )
714                                 )
715                                 , 'plugins', VALUE_OPTIONAL
716                             )
717                         )
718                     )
719                 )
720             )
721         );
722     }
724     /**
725      * Describes the get_submissions return value
726      *
727      * @return external_single_structure
728      * @since Moodle 2.5
729      */
730     public static function get_submissions_returns() {
731         return new external_single_structure(
732             array(
733                 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
734                 'warnings' => new external_warnings()
735             )
736         );
737     }
739     /**
740      * Describes the parameters for get_user_flags
741      * @return external_function_parameters
742      * @since  Moodle 2.6
743      */
744     public static function get_user_flags_parameters() {
745         return new external_function_parameters(
746             array(
747                 'assignmentids' => new external_multiple_structure(
748                     new external_value(PARAM_INT, 'assignment id'),
749                     '1 or more assignment ids',
750                     VALUE_REQUIRED)
751             )
752         );
753     }
755     /**
756      * Returns user flag information from assign_user_flags for the requested assignment ids
757      * @param int[] $assignmentids
758      * @return array of user flag records for each requested assignment
759      * @since  Moodle 2.6
760      */
761     public static function get_user_flags($assignmentids) {
762         global $DB;
763         $params = self::validate_parameters(self::get_user_flags_parameters(),
764                         array('assignmentids' => $assignmentids));
766         $assignments = array();
767         $warnings = array();
768         $requestedassignmentids = $params['assignmentids'];
770         // Check the user is allowed to get the user flags for the assignments requested.
771         $placeholders = array();
772         list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
773         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
774                "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
775         $placeholders['modname'] = 'assign';
776         $cms = $DB->get_records_sql($sql, $placeholders);
777         foreach ($cms as $cm) {
778             try {
779                 $context = context_module::instance($cm->id);
780                 self::validate_context($context);
781                 require_capability('mod/assign:grade', $context);
782             } catch (Exception $e) {
783                 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
784                 $warning = array();
785                 $warning['item'] = 'assignment';
786                 $warning['itemid'] = $cm->instance;
787                 $warning['warningcode'] = '1';
788                 $warning['message'] = 'No access rights in module context';
789                 $warnings[] = $warning;
790             }
791         }
793         // Create the query and populate an array of assign_user_flags records from the recordset results.
794         if (count ($requestedassignmentids) > 0) {
795             $placeholders = array();
796             list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
798             $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
799                    "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
800                    "FROM {assign_user_flags} auf ".
801                    "WHERE auf.assignment ".$inorequalsql.
802                    " ORDER BY auf.assignment, auf.id";
804             $rs = $DB->get_recordset_sql($sql, $placeholders);
805             $currentassignmentid = null;
806             $assignment = null;
807             foreach ($rs as $rd) {
808                 $userflag = array();
809                 $userflag['id'] = $rd->id;
810                 $userflag['userid'] = $rd->userid;
811                 $userflag['locked'] = $rd->locked;
812                 $userflag['mailed'] = $rd->mailed;
813                 $userflag['extensionduedate'] = $rd->extensionduedate;
814                 $userflag['workflowstate'] = $rd->workflowstate;
815                 $userflag['allocatedmarker'] = $rd->allocatedmarker;
817                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
818                     if (!is_null($assignment)) {
819                         $assignments[] = $assignment;
820                     }
821                     $assignment = array();
822                     $assignment['assignmentid'] = $rd->assignment;
823                     $assignment['userflags'] = array();
824                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
825                 }
826                 $assignment['userflags'][] = $userflag;
828                 $currentassignmentid = $rd->assignment;
829             }
830             if (!is_null($assignment)) {
831                 $assignments[] = $assignment;
832             }
833             $rs->close();
835         }
837         foreach ($requestedassignmentids as $assignmentid) {
838             $warning = array();
839             $warning['item'] = 'assignment';
840             $warning['itemid'] = $assignmentid;
841             $warning['warningcode'] = '3';
842             $warning['message'] = 'No user flags found';
843             $warnings[] = $warning;
844         }
846         $result = array();
847         $result['assignments'] = $assignments;
848         $result['warnings'] = $warnings;
849         return $result;
850     }
852     /**
853      * Creates an assign_user_flags external_single_structure
854      * @return external_single_structure
855      * @since  Moodle 2.6
856      */
857     private static function assign_user_flags() {
858         return new external_single_structure(
859             array (
860                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
861                 'userflags'   => new external_multiple_structure(new external_single_structure(
862                         array(
863                             'id'               => new external_value(PARAM_INT, 'user flag id'),
864                             'userid'           => new external_value(PARAM_INT, 'student id'),
865                             'locked'           => new external_value(PARAM_INT, 'locked'),
866                             'mailed'           => new external_value(PARAM_INT, 'mailed'),
867                             'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
868                             'workflowstate'    => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
869                             'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker')
870                         )
871                     )
872                 )
873             )
874         );
875     }
877     /**
878      * Describes the get_user_flags return value
879      * @return external_single_structure
880      * @since  Moodle 2.6
881      */
882     public static function get_user_flags_returns() {
883         return new external_single_structure(
884             array(
885                 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
886                 'warnings'      => new external_warnings('item is always \'assignment\'',
887                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
888                     'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
889             )
890         );
891     }
893     /**
894      * Describes the parameters for get_user_mappings
895      * @return external_function_parameters
896      * @since  Moodle 2.6
897      */
898     public static function get_user_mappings_parameters() {
899         return new external_function_parameters(
900             array(
901                 'assignmentids' => new external_multiple_structure(
902                     new external_value(PARAM_INT, 'assignment id'),
903                     '1 or more assignment ids',
904                     VALUE_REQUIRED)
905             )
906         );
907     }
909     /**
910      * Returns user mapping information from assign_user_mapping for the requested assignment ids
911      * @param int[] $assignmentids
912      * @return array of user mapping records for each requested assignment
913      * @since  Moodle 2.6
914      */
915     public static function get_user_mappings($assignmentids) {
916         global $DB;
917         $params = self::validate_parameters(self::get_user_mappings_parameters(),
918                         array('assignmentids' => $assignmentids));
920         $assignments = array();
921         $warnings = array();
922         $requestedassignmentids = $params['assignmentids'];
924         // Check the user is allowed to get the mappings for the assignments requested.
925         $placeholders = array();
926         list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
927         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
928                "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
929         $placeholders['modname'] = 'assign';
930         $cms = $DB->get_records_sql($sql, $placeholders);
931         foreach ($cms as $cm) {
932             try {
933                 $context = context_module::instance($cm->id);
934                 self::validate_context($context);
935                 require_capability('mod/assign:revealidentities', $context);
936             } catch (Exception $e) {
937                 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
938                 $warning = array();
939                 $warning['item'] = 'assignment';
940                 $warning['itemid'] = $cm->instance;
941                 $warning['warningcode'] = '1';
942                 $warning['message'] = 'No access rights in module context';
943                 $warnings[] = $warning;
944             }
945         }
947         // Create the query and populate an array of assign_user_mapping records from the recordset results.
948         if (count ($requestedassignmentids) > 0) {
949             $placeholders = array();
950             list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
952             $sql = "SELECT aum.id,aum.assignment,aum.userid ".
953                    "FROM {assign_user_mapping} aum ".
954                    "WHERE aum.assignment ".$inorequalsql.
955                    " ORDER BY aum.assignment, aum.id";
957             $rs = $DB->get_recordset_sql($sql, $placeholders);
958             $currentassignmentid = null;
959             $assignment = null;
960             foreach ($rs as $rd) {
961                 $mapping = array();
962                 $mapping['id'] = $rd->id;
963                 $mapping['userid'] = $rd->userid;
965                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
966                     if (!is_null($assignment)) {
967                         $assignments[] = $assignment;
968                     }
969                     $assignment = array();
970                     $assignment['assignmentid'] = $rd->assignment;
971                     $assignment['mappings'] = array();
972                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
973                 }
974                 $assignment['mappings'][] = $mapping;
976                 $currentassignmentid = $rd->assignment;
977             }
978             if (!is_null($assignment)) {
979                 $assignments[] = $assignment;
980             }
981             $rs->close();
983         }
985         foreach ($requestedassignmentids as $assignmentid) {
986             $warning = array();
987             $warning['item'] = 'assignment';
988             $warning['itemid'] = $assignmentid;
989             $warning['warningcode'] = '3';
990             $warning['message'] = 'No mappings found';
991             $warnings[] = $warning;
992         }
994         $result = array();
995         $result['assignments'] = $assignments;
996         $result['warnings'] = $warnings;
997         return $result;
998     }
1000     /**
1001      * Creates an assign_user_mappings external_single_structure
1002      * @return external_single_structure
1003      * @since  Moodle 2.6
1004      */
1005     private static function assign_user_mappings() {
1006         return new external_single_structure(
1007             array (
1008                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1009                 'mappings'   => new external_multiple_structure(new external_single_structure(
1010                         array(
1011                             'id'     => new external_value(PARAM_INT, 'user mapping id'),
1012                             'userid' => new external_value(PARAM_INT, 'student id')
1013                         )
1014                     )
1015                 )
1016             )
1017         );
1018     }
1020     /**
1021      * Describes the get_user_mappings return value
1022      * @return external_single_structure
1023      * @since  Moodle 2.6
1024      */
1025     public static function get_user_mappings_returns() {
1026         return new external_single_structure(
1027             array(
1028                 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
1029                 'warnings'      => new external_warnings('item is always \'assignment\'',
1030                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1031                     'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
1032             )
1033         );
1034     }
1036     /**
1037      * Describes the parameters for lock_submissions
1038      * @return external_external_function_parameters
1039      * @since  Moodle 2.6
1040      */
1041     public static function lock_submissions_parameters() {
1042         return new external_function_parameters(
1043             array(
1044                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1045                 'userids' => new external_multiple_structure(
1046                     new external_value(PARAM_INT, 'user id'),
1047                     '1 or more user ids',
1048                     VALUE_REQUIRED),
1049             )
1050         );
1051     }
1053     /**
1054      * Locks (prevent updates to) submissions in this assignment.
1055      *
1056      * @param int $assignmentid The id of the assignment
1057      * @param array $userids Array of user ids to lock
1058      * @return array of warnings for each submission that could not be locked.
1059      * @since Moodle 2.6
1060      */
1061     public static function lock_submissions($assignmentid, $userids) {
1062         global $CFG;
1063         require_once("$CFG->dirroot/mod/assign/locallib.php");
1065         $params = self::validate_parameters(self::lock_submissions_parameters(),
1066                         array('assignmentid' => $assignmentid,
1067                               'userids' => $userids));
1069         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1070         $context = context_module::instance($cm->id);
1072         $assignment = new assign($context, $cm, null);
1074         $warnings = array();
1075         foreach ($userids as $userid) {
1076             if (!$assignment->lock_submission($userid)) {
1077                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $assignmentid;
1078                 $warnings[] = self::generate_warning($assignmentid,
1079                                                      'couldnotlock',
1080                                                      $detail);
1081             }
1082         }
1084         return $warnings;
1085     }
1087     /**
1088      * Describes the return value for lock_submissions
1089      *
1090      * @return external_single_structure
1091      * @since Moodle 2.6
1092      */
1093     public static function lock_submissions_returns() {
1094         return new external_multiple_structure(
1095            new external_warnings()
1096         );
1097     }
1099     /**
1100      * Describes the parameters for revert_submissions_to_draft
1101      * @return external_external_function_parameters
1102      * @since  Moodle 2.6
1103      */
1104     public static function revert_submissions_to_draft_parameters() {
1105         return new external_function_parameters(
1106             array(
1107                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1108                 'userids' => new external_multiple_structure(
1109                     new external_value(PARAM_INT, 'user id'),
1110                     '1 or more user ids',
1111                     VALUE_REQUIRED),
1112             )
1113         );
1114     }
1116     /**
1117      * Reverts a list of user submissions to draft for a single assignment.
1118      *
1119      * @param int $assignmentid The id of the assignment
1120      * @param array $userids Array of user ids to revert
1121      * @return array of warnings for each submission that could not be reverted.
1122      * @since Moodle 2.6
1123      */
1124     public static function revert_submissions_to_draft($assignmentid, $userids) {
1125         global $CFG;
1126         require_once("$CFG->dirroot/mod/assign/locallib.php");
1128         $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
1129                         array('assignmentid' => $assignmentid,
1130                               'userids' => $userids));
1132         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1133         $context = context_module::instance($cm->id);
1135         $assignment = new assign($context, $cm, null);
1137         $warnings = array();
1138         foreach ($userids as $userid) {
1139             if (!$assignment->revert_to_draft($userid)) {
1140                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $assignmentid;
1141                 $warnings[] = self::generate_warning($assignmentid,
1142                                                      'couldnotrevert',
1143                                                      $detail);
1144             }
1145         }
1147         return $warnings;
1148     }
1150     /**
1151      * Describes the return value for revert_submissions_to_draft
1152      *
1153      * @return external_single_structure
1154      * @since Moodle 2.6
1155      */
1156     public static function revert_submissions_to_draft_returns() {
1157         return new external_multiple_structure(
1158            new external_warnings()
1159         );
1160     }
1162     /**
1163      * Describes the parameters for unlock_submissions
1164      * @return external_external_function_parameters
1165      * @since  Moodle 2.6
1166      */
1167     public static function unlock_submissions_parameters() {
1168         return new external_function_parameters(
1169             array(
1170                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1171                 'userids' => new external_multiple_structure(
1172                     new external_value(PARAM_INT, 'user id'),
1173                     '1 or more user ids',
1174                     VALUE_REQUIRED),
1175             )
1176         );
1177     }
1179     /**
1180      * Locks (prevent updates to) submissions in this assignment.
1181      *
1182      * @param int $assignmentid The id of the assignment
1183      * @param array $userids Array of user ids to lock
1184      * @return array of warnings for each submission that could not be locked.
1185      * @since Moodle 2.6
1186      */
1187     public static function unlock_submissions($assignmentid, $userids) {
1188         global $CFG;
1189         require_once("$CFG->dirroot/mod/assign/locallib.php");
1191         $params = self::validate_parameters(self::unlock_submissions_parameters(),
1192                         array('assignmentid' => $assignmentid,
1193                               'userids' => $userids));
1195         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1196         $context = context_module::instance($cm->id);
1198         $assignment = new assign($context, $cm, null);
1200         $warnings = array();
1201         foreach ($userids as $userid) {
1202             if (!$assignment->unlock_submission($userid)) {
1203                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $assignmentid;
1204                 $warnings[] = self::generate_warning($assignmentid,
1205                                                      'couldnotunlock',
1206                                                      $detail);
1207             }
1208         }
1210         return $warnings;
1211     }
1213     /**
1214      * Describes the return value for unlock_submissions
1215      *
1216      * @return external_single_structure
1217      * @since Moodle 2.6
1218      */
1219     public static function unlock_submissions_returns() {
1220         return new external_multiple_structure(
1221            new external_warnings()
1222         );
1223     }
1225     /**
1226      * Describes the parameters for unlock_submissions
1227      * @return external_external_function_parameters
1228      * @since  Moodle 2.6
1229      */
1230     public static function submit_for_grading_parameters() {
1231         return new external_function_parameters(
1232             array(
1233                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
1234             )
1235         );
1236     }
1238     /**
1239      * Submit the logged in users assignment for grading.
1240      *
1241      * @param int $assignmentid The id of the assignment
1242      * @return array of warnings to indicate any errors.
1243      * @since Moodle 2.6
1244      */
1245     public static function submit_for_grading($assignmentid) {
1246         global $CFG, $USER;
1247         require_once("$CFG->dirroot/mod/assign/locallib.php");
1249         $params = self::validate_parameters(self::submit_for_grading_parameters(),
1250                                             array('assignmentid' => $assignmentid));
1252         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1253         $context = context_module::instance($cm->id);
1255         $assignment = new assign($context, $cm, null);
1257         $warnings = array();
1258         if (!$assignment->submit_for_grading()) {
1259             $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $assignmentid;
1260             $warnings[] = self::generate_warning($assignmentid,
1261                                                  'couldnotsubmitforgrading',
1262                                                  $detail);
1263         }
1265         return $warnings;
1266     }
1268     /**
1269      * Describes the return value for submit_for_grading
1270      *
1271      * @return external_single_structure
1272      * @since Moodle 2.6
1273      */
1274     public static function submit_for_grading_returns() {
1275         return new external_multiple_structure(
1276            new external_warnings()
1277         );
1278     }
1280     /**
1281      * Describes the parameters for save_user_extensions
1282      * @return external_external_function_parameters
1283      * @since  Moodle 2.6
1284      */
1285     public static function save_user_extensions_parameters() {
1286         return new external_function_parameters(
1287             array(
1288                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1289                 'userids' => new external_multiple_structure(
1290                     new external_value(PARAM_INT, 'user id'),
1291                     '1 or more user ids',
1292                     VALUE_REQUIRED),
1293                 'dates' => new external_multiple_structure(
1294                     new external_value(PARAM_INT, 'dates'),
1295                     '1 or more extension dates (timestamp)',
1296                     VALUE_REQUIRED),
1297             )
1298         );
1299     }
1301     /**
1302      * Grant extension dates to students for an assignment.
1303      *
1304      * @param int $assignmentid The id of the assignment
1305      * @param array $userids Array of user ids to grant extensions to
1306      * @param array $dates Array of extension dates
1307      * @return array of warnings for each extension date that could not be granted
1308      * @since Moodle 2.6
1309      */
1310     public static function save_user_extensions($assignmentid, $userids, $dates) {
1311         global $CFG;
1312         require_once("$CFG->dirroot/mod/assign/locallib.php");
1314         $params = self::validate_parameters(self::save_user_extensions_parameters(),
1315                         array('assignmentid' => $assignmentid,
1316                               'userids' => $userids,
1317                               'dates' => $dates));
1319         if (count($userids) != count($dates)) {
1320             $detail = 'Length of userids and dates parameters differ.';
1321             $warnings[] = self::generate_warning($assignmentid,
1322                                                  'invalidparameters',
1323                                                  $detail);
1325             return $warnings;
1326         }
1328         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1329         $context = context_module::instance($cm->id);
1331         $assignment = new assign($context, $cm, null);
1333         $warnings = array();
1334         foreach ($userids as $idx => $userid) {
1335             $duedate = $dates[$idx];
1336             if (!$assignment->save_user_extension($userid, $duedate)) {
1337                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $assignmentid . ', Extension date: ' . $duedate;
1338                 $warnings[] = self::generate_warning($assignmentid,
1339                                                      'couldnotgrantextensions',
1340                                                      $detail);
1341             }
1342         }
1344         return $warnings;
1345     }
1347     /**
1348      * Describes the return value for save_user_extensions
1349      *
1350      * @return external_single_structure
1351      * @since Moodle 2.6
1352      */
1353     public static function save_user_extensions_returns() {
1354         return new external_multiple_structure(
1355            new external_warnings()
1356         );
1357     }
1359     /**
1360      * Describes the parameters for reveal_identities
1361      * @return external_external_function_parameters
1362      * @since  Moodle 2.6
1363      */
1364     public static function reveal_identities_parameters() {
1365         return new external_function_parameters(
1366             array(
1367                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
1368             )
1369         );
1370     }
1372     /**
1373      * Reveal the identities of anonymous students to markers for a single assignment.
1374      *
1375      * @param int $assignmentid The id of the assignment
1376      * @return array of warnings to indicate any errors.
1377      * @since Moodle 2.6
1378      */
1379     public static function reveal_identities($assignmentid) {
1380         global $CFG, $USER;
1381         require_once("$CFG->dirroot/mod/assign/locallib.php");
1383         $params = self::validate_parameters(self::reveal_identities_parameters(),
1384                                             array('assignmentid' => $assignmentid));
1386         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1387         $context = context_module::instance($cm->id);
1389         $assignment = new assign($context, $cm, null);
1391         $warnings = array();
1392         if (!$assignment->reveal_identities()) {
1393             $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $assignmentid;
1394             $warnings[] = self::generate_warning($assignmentid,
1395                                                  'couldnotrevealidentities',
1396                                                  $detail);
1397         }
1399         return $warnings;
1400     }
1402     /**
1403      * Describes the return value for reveal_identities
1404      *
1405      * @return external_single_structure
1406      * @since Moodle 2.6
1407      */
1408     public static function reveal_identities_returns() {
1409         return new external_multiple_structure(
1410            new external_warnings()
1411         );
1412     }
1414     /**
1415      * Describes the parameters for save_submission
1416      * @return external_external_function_parameters
1417      * @since  Moodle 2.6
1418      */
1419     public static function save_submission_parameters() {
1420         global $CFG;
1421         require_once("$CFG->dirroot/mod/assign/locallib.php");
1422         $instance = new assign(null, null, null);
1423         $pluginsubmissionparams = array();
1425         foreach ($instance->get_submission_plugins() as $plugin) {
1426             $pluginparams = $plugin->get_external_parameters();
1427             if (!empty($pluginparams)) {
1428                 $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
1429             }
1430         }
1432         return new external_function_parameters(
1433             array(
1434                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1435                 'plugindata' => new external_single_structure(
1436                     $pluginsubmissionparams
1437                 )
1438             )
1439         );
1440     }
1442     /**
1443      * Save a student submission for a single assignment
1444      *
1445      * @param int $assignmentid The id of the assignment
1446      * @param array $plugindata - The submitted data for plugins
1447      * @return array of warnings to indicate any errors
1448      * @since Moodle 2.6
1449      */
1450     public static function save_submission($assignmentid, $plugindata) {
1451         global $CFG, $USER;
1452         require_once("$CFG->dirroot/mod/assign/locallib.php");
1454         $params = self::validate_parameters(self::save_submission_parameters(),
1455                                             array('assignmentid' => $assignmentid,
1456                                                   'plugindata' => $plugindata));
1458         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1459         $context = context_module::instance($cm->id);
1461         $assignment = new assign($context, $cm, null);
1463         $notices = array();
1465         $submissiondata = (object)$plugindata;
1467         $assignment->save_submission($submissiondata, $notices);
1469         $warnings = array();
1470         foreach ($notices as $notice) {
1471             $warnings[] = self::generate_warning($assignmentid,
1472                                                  'couldnotsavesubmission',
1473                                                  $notice);
1474         }
1476         return $warnings;
1477     }
1479     /**
1480      * Describes the return value for save_submission
1481      *
1482      * @return external_single_structure
1483      * @since Moodle 2.6
1484      */
1485     public static function save_submission_returns() {
1486         return new external_multiple_structure(
1487            new external_warnings()
1488         );
1489     }
1491     /**
1492      * Describes the parameters for save_grade
1493      * @return external_external_function_parameters
1494      * @since  Moodle 2.6
1495      */
1496     public static function save_grade_parameters() {
1497         global $CFG;
1498         require_once("$CFG->dirroot/mod/assign/locallib.php");
1499         $instance = new assign(null, null, null);
1500         $pluginfeedbackparams = array();
1502         foreach ($instance->get_feedback_plugins() as $plugin) {
1503             $pluginparams = $plugin->get_external_parameters();
1504             if (!empty($pluginparams)) {
1505                 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1506             }
1507         }
1509         return new external_function_parameters(
1510             array(
1511                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1512                 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1513                 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user'),
1514                 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1515                 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
1516                 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1517                 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1518                                                                'to all members ' .
1519                                                                'of the group (for group assignments).'),
1520                 'plugindata' => new external_single_structure(
1521                     $pluginfeedbackparams
1522                 )
1523             )
1524         );
1525     }
1527     /**
1528      * Save a student grade for a single assignment.
1529      *
1530      * @param int $assignmentid The id of the assignment
1531      * @param int $userid The id of the user
1532      * @param float $grade The grade
1533      * @param int $attemptnumber The attempt number
1534      * @param bool $addattempt Allow another attempt
1535      * @param string $workflowstate New workflow state
1536      * @param bool $applytoall Apply the grade to all members of the group
1537      * @param array $plugindata Custom data used by plugins
1538      * @return null
1539      * @since Moodle 2.6
1540      */
1541     public static function save_grade($assignmentid,
1542                                       $userid,
1543                                       $grade,
1544                                       $attemptnumber,
1545                                       $addattempt,
1546                                       $workflowstate,
1547                                       $applytoall,
1548                                       $plugindata) {
1549         global $CFG, $USER;
1550         require_once("$CFG->dirroot/mod/assign/locallib.php");
1552         $params = self::validate_parameters(self::save_grade_parameters(),
1553                                             array('assignmentid' => $assignmentid,
1554                                                   'userid' => $userid,
1555                                                   'grade' => $grade,
1556                                                   'attemptnumber' => $attemptnumber,
1557                                                   'workflowstate' => $workflowstate,
1558                                                   'addattempt' => $addattempt,
1559                                                   'applytoall' => $applytoall,
1560                                                   'plugindata' => $plugindata));
1562         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1563         $context = context_module::instance($cm->id);
1565         $assignment = new assign($context, $cm, null);
1567         $gradedata = (object)$plugindata;
1569         $gradedata->addattempt = $addattempt;
1570         $gradedata->attemptnumber = $attemptnumber;
1571         $gradedata->workflowstate = $workflowstate;
1572         $gradedata->applytoall = $applytoall;
1573         $gradedata->grade = $grade;
1575         $assignment->save_grade($userid, $gradedata);
1577         return null;
1578     }
1580     /**
1581      * Describes the return value for save_grade
1582      *
1583      * @return external_single_structure
1584      * @since Moodle 2.6
1585      */
1586     public static function save_grade_returns() {
1587         return null;
1588     }
1590     /**
1591      * Describes the parameters for copy_previous_attempt
1592      * @return external_external_function_parameters
1593      * @since  Moodle 2.6
1594      */
1595     public static function copy_previous_attempt_parameters() {
1596         return new external_function_parameters(
1597             array(
1598                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1599             )
1600         );
1601     }
1603     /**
1604      * Copy a students previous attempt to a new attempt.
1605      *
1606      * @param int $assignmentid
1607      * @return array of warnings to indicate any errors.
1608      * @since Moodle 2.6
1609      */
1610     public static function copy_previous_attempt($assignmentid) {
1611         global $CFG, $USER;
1612         require_once("$CFG->dirroot/mod/assign/locallib.php");
1614         $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
1615                                             array('assignmentid' => $assignmentid));
1617         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1618         $context = context_module::instance($cm->id);
1620         $assignment = new assign($context, $cm, null);
1622         $notices = array();
1624         $assignment->copy_previous_attempt($submissiondata, $notices);
1626         $warnings = array();
1627         foreach ($notices as $notice) {
1628             $warnings[] = self::generate_warning($assignmentid,
1629                                                  'couldnotcopyprevioussubmission',
1630                                                  $notice);
1631         }
1633         return $warnings;
1634     }
1636     /**
1637      * Describes the return value for save_submission
1638      *
1639      * @return external_single_structure
1640      * @since Moodle 2.6
1641      */
1642     public static function copy_previous_attempt_returns() {
1643         return new external_multiple_structure(
1644            new external_warnings()
1645         );
1646     }