Merge branch 'wip-mdl-41266' of https://github.com/rajeshtaneja/moodle
[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, ' .
316                      'm.course, ' .
317                      'm.nosubmissions, ' .
318                      'm.submissiondrafts, ' .
319                      'm.sendnotifications, '.
320                      'm.sendlatenotifications, ' .
321                      'm.sendstudentnotifications, ' .
322                      'm.duedate, ' .
323                      'm.allowsubmissionsfromdate, '.
324                      'm.grade, ' .
325                      'm.timemodified, '.
326                      'm.completionsubmit, ' .
327                      'm.cutoffdate, ' .
328                      'm.teamsubmission, ' .
329                      'm.requireallteammemberssubmit, '.
330                      'm.teamsubmissiongroupingid, ' .
331                      'm.blindmarking, ' .
332                      'm.revealidentities, ' .
333                      'm.attemptreopenmethod, '.
334                      'm.maxattempts, ' .
335                      'm.markingworkflow, ' .
336                      'm.markingallocation, ' .
337                      'm.requiresubmissionstatement';
338         $coursearray = array();
339         foreach ($courses as $id => $course) {
340             $assignmentarray = array();
341             // Get a list of assignments for the course.
342             if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
343                 foreach ($modules as $module) {
344                     $context = context_module::instance($module->id);
345                     try {
346                         self::validate_context($context);
347                         require_capability('mod/assign:view', $context);
348                     } catch (Exception $e) {
349                         $warnings[] = array(
350                             'item' => 'module',
351                             'itemid' => $module->id,
352                             'warningcode' => '1',
353                             'message' => 'No access rights in module context'
354                         );
355                         continue;
356                     }
357                     $configrecords = $DB->get_recordset('assign_plugin_config', array('assignment' => $module->assignmentid));
358                     $configarray = array();
359                     foreach ($configrecords as $configrecord) {
360                         $configarray[] = array(
361                             'id' => $configrecord->id,
362                             'assignment' => $configrecord->assignment,
363                             'plugin' => $configrecord->plugin,
364                             'subtype' => $configrecord->subtype,
365                             'name' => $configrecord->name,
366                             'value' => $configrecord->value
367                         );
368                     }
369                     $configrecords->close();
371                     $assignmentarray[]= array(
372                         'id' => $module->assignmentid,
373                         'cmid' => $module->id,
374                         'course' => $module->course,
375                         'name' => $module->name,
376                         'nosubmissions' => $module->nosubmissions,
377                         'submissiondrafts' => $module->submissiondrafts,
378                         'sendnotifications' => $module->sendnotifications,
379                         'sendlatenotifications' => $module->sendlatenotifications,
380                         'sendstudentnotifications' => $module->sendstudentnotifications,
381                         'duedate' => $module->duedate,
382                         'allowsubmissionsfromdate' => $module->allowsubmissionsfromdate,
383                         'grade' => $module->grade,
384                         'timemodified' => $module->timemodified,
385                         'completionsubmit' => $module->completionsubmit,
386                         'cutoffdate' => $module->cutoffdate,
387                         'teamsubmission' => $module->teamsubmission,
388                         'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
389                         'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
390                         'blindmarking' => $module->blindmarking,
391                         'revealidentities' => $module->revealidentities,
392                         'attemptreopenmethod' => $module->attemptreopenmethod,
393                         'maxattempts' => $module->maxattempts,
394                         'markingworkflow' => $module->markingworkflow,
395                         'markingallocation' => $module->markingallocation,
396                         'requiresubmissionstatement' => $module->requiresubmissionstatement,
397                         'configs' => $configarray
398                     );
399                 }
400             }
401             $coursearray[]= array(
402                 'id' => $courses[$id]->id,
403                 'fullname' => $courses[$id]->fullname,
404                 'shortname' => $courses[$id]->shortname,
405                 'timemodified' => $courses[$id]->timemodified,
406                 'assignments' => $assignmentarray
407             );
408         }
410         $result = array(
411             'courses' => $coursearray,
412             'warnings' => $warnings
413         );
414         return $result;
415     }
417     /**
418      * Creates an assignment external_single_structure
419      *
420      * @return external_single_structure
421      * @since Moodle 2.4
422      */
423     private static function get_assignments_assignment_structure() {
424         return new external_single_structure(
425             array(
426                 'id' => new external_value(PARAM_INT, 'assignment id'),
427                 'cmid' => new external_value(PARAM_INT, 'course module id'),
428                 'course' => new external_value(PARAM_INT, 'course id'),
429                 'name' => new external_value(PARAM_TEXT, 'assignment name'),
430                 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
431                 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
432                 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
433                 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
434                 'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'),
435                 'duedate' => new external_value(PARAM_INT, 'assignment due date'),
436                 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
437                 'grade' => new external_value(PARAM_INT, 'grade type'),
438                 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
439                 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
440                 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
441                 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
442                 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
443                 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
444                 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
445                 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
446                 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
447                 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
448                 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
449                 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
450                 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
451                 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings')
452             ), 'assignment information object');
453     }
455     /**
456      * Creates an assign_plugin_config external_single_structure
457      *
458      * @return external_single_structure
459      * @since Moodle 2.4
460      */
461     private static function get_assignments_config_structure() {
462         return new external_single_structure(
463             array(
464                 'id' => new external_value(PARAM_INT, 'assign_plugin_config id'),
465                 'assignment' => new external_value(PARAM_INT, 'assignment id'),
466                 'plugin' => new external_value(PARAM_TEXT, 'plugin'),
467                 'subtype' => new external_value(PARAM_TEXT, 'subtype'),
468                 'name' => new external_value(PARAM_TEXT, 'name'),
469                 'value' => new external_value(PARAM_TEXT, 'value')
470             ), 'assignment configuration object'
471         );
472     }
474     /**
475      * Creates a course external_single_structure
476      *
477      * @return external_single_structure
478      * @since Moodle 2.4
479      */
480     private static function get_assignments_course_structure() {
481         return new external_single_structure(
482             array(
483                 'id' => new external_value(PARAM_INT, 'course id'),
484                 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
485                 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
486                 'timemodified' => new external_value(PARAM_INT, 'last time modified'),
487                 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
488               ), 'course information object'
489         );
490     }
492     /**
493      * Describes the return value for get_assignments
494      *
495      * @return external_single_structure
496      * @since Moodle 2.4
497      */
498     public static function get_assignments_returns() {
499         return new external_single_structure(
500             array(
501                 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
502                 'warnings'  => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
503                     'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
504                     'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
505             )
506         );
507     }
509     /**
510      * Describes the parameters for get_submissions
511      *
512      * @return external_external_function_parameters
513      * @since Moodle 2.5
514      */
515     public static function get_submissions_parameters() {
516         return new external_function_parameters(
517             array(
518                 'assignmentids' => new external_multiple_structure(
519                     new external_value(PARAM_INT, 'assignment id'),
520                     '1 or more assignment ids',
521                     VALUE_REQUIRED),
522                 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
523                 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
524                 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
525             )
526         );
527     }
529     /**
530      * Returns submissions for the requested assignment ids
531      *
532      * @param int[] $assignmentids
533      * @param string $status only return submissions with this status
534      * @param int $since only return submissions with timemodified >= since
535      * @param int $before only return submissions with timemodified <= before
536      * @return array of submissions for each requested assignment
537      * @since Moodle 2.5
538      */
539     public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
540         global $DB, $CFG;
541         require_once("$CFG->dirroot/mod/assign/locallib.php");
542         $params = self::validate_parameters(self::get_submissions_parameters(),
543                         array('assignmentids' => $assignmentids,
544                               'status' => $status,
545                               'since' => $since,
546                               'before' => $before));
548         $warnings = array();
549         $assignments = array();
551         // Check the user is allowed to get the submissions for the assignments requested.
552         $placeholders = array();
553         list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
554         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
555                "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
556         $placeholders['modname'] = 'assign';
557         $cms = $DB->get_records_sql($sql, $placeholders);
558         $assigns = array();
559         foreach ($cms as $cm) {
560             try {
561                 $context = context_module::instance($cm->id);
562                 self::validate_context($context);
563                 require_capability('mod/assign:grade', $context);
564                 $assign = new assign($context, null, null);
565                 $assigns[] = $assign;
566             } catch (Exception $e) {
567                 $warnings[] = array(
568                     'item' => 'assignment',
569                     'itemid' => $cm->instance,
570                     'warningcode' => '1',
571                     'message' => 'No access rights in module context'
572                 );
573             }
574         }
576         foreach ($assigns as $assign) {
577             $submissions = array();
578             $submissionplugins = $assign->get_submission_plugins();
579             $placeholders = array('assignid1' => $assign->get_instance()->id,
580                                   'assignid2' => $assign->get_instance()->id);
582             $submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
583                                      FROM {assign_submission} mxs
584                                      WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid';
586             $sql = "SELECT mas.id, mas.assignment,mas.userid,".
587                    "mas.timecreated,mas.timemodified,mas.status,mas.groupid,mas.attemptnumber ".
588                    "FROM {assign_submission} mas ".
589                    "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
590                    "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
592             if (!empty($params['status'])) {
593                 $placeholders['status'] = $params['status'];
594                 $sql = $sql." AND mas.status = :status";
595             }
596             if (!empty($params['before'])) {
597                 $placeholders['since'] = $params['since'];
598                 $placeholders['before'] = $params['before'];
599                 $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
600             } else {
601                 $placeholders['since'] = $params['since'];
602                 $sql = $sql." AND mas.timemodified >= :since";
603             }
605             $submissionrecords = $DB->get_records_sql($sql, $placeholders);
607             if (!empty($submissionrecords)) {
608                 $fs = get_file_storage();
609                 foreach ($submissionrecords as $submissionrecord) {
610                     $submission = array(
611                         'id' => $submissionrecord->id,
612                         'userid' => $submissionrecord->userid,
613                         'timecreated' => $submissionrecord->timecreated,
614                         'timemodified' => $submissionrecord->timemodified,
615                         'status' => $submissionrecord->status,
616                         'attemptnumber' => $submissionrecord->attemptnumber,
617                         'groupid' => $submissionrecord->groupid
618                     );
619                     foreach ($submissionplugins as $submissionplugin) {
620                         $plugin = array(
621                             'name' => $submissionplugin->get_name(),
622                             'type' => $submissionplugin->get_type()
623                         );
624                         // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
625                         $component = $submissionplugin->get_subtype().'_'.$submissionplugin->get_type();
627                         $fileareas = $submissionplugin->get_file_areas();
628                         foreach ($fileareas as $filearea => $name) {
629                             $fileareainfo = array('area' => $filearea);
630                             $files = $fs->get_area_files(
631                                 $assign->get_context()->id,
632                                 $component,
633                                 $filearea,
634                                 $submissionrecord->id,
635                                 "timemodified",
636                                 false
637                             );
638                             foreach ($files as $file) {
639                                 $filepath = array('filepath' => $file->get_filepath().$file->get_filename());
640                                 $fileareainfo['files'][] = $filepath;
641                             }
642                             $plugin['fileareas'][] = $fileareainfo;
643                         }
645                         $editorfields = $submissionplugin->get_editor_fields();
646                         foreach ($editorfields as $name => $description) {
647                             $editorfieldinfo = array(
648                                 'name' => $name,
649                                 'description' => $description,
650                                 'text' => $submissionplugin->get_editor_text($name, $submissionrecord->id),
651                                 'format' => $submissionplugin->get_editor_format($name, $submissionrecord->id)
652                             );
653                             $plugin['editorfields'][] = $editorfieldinfo;
654                         }
656                         $submission['plugins'][] = $plugin;
657                     }
658                     $submissions[] = $submission;
659                 }
660             } else {
661                 $warnings[] = array(
662                     'item' => 'module',
663                     'itemid' => $assign->get_instance()->id,
664                     'warningcode' => '3',
665                     'message' => 'No submissions found'
666                 );
667             }
669             $assignments[] = array(
670                 'assignmentid' => $assign->get_instance()->id,
671                 'submissions' => $submissions
672             );
674         }
676         $result = array(
677             'assignments' => $assignments,
678             'warnings' => $warnings
679         );
680         return $result;
681     }
683     /**
684      * Creates an assign_submissions external_single_structure
685      *
686      * @return external_single_structure
687      * @since Moodle 2.5
688      */
689     private static function get_submissions_structure() {
690         return new external_single_structure(
691             array (
692                 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
693                 'submissions' => new external_multiple_structure(
694                     new external_single_structure(
695                         array(
696                             'id' => new external_value(PARAM_INT, 'submission id'),
697                             'userid' => new external_value(PARAM_INT, 'student id'),
698                             'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
699                             'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
700                             'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
701                             'status' => new external_value(PARAM_TEXT, 'submission status'),
702                             'groupid' => new external_value(PARAM_INT, 'group id'),
703                             'plugins' => new external_multiple_structure(
704                                 new external_single_structure(
705                                     array(
706                                         'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
707                                         'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
708                                         'fileareas' => new external_multiple_structure(
709                                             new external_single_structure(
710                                                 array (
711                                                     'area' => new external_value (PARAM_TEXT, 'file area'),
712                                                     'files' => new external_multiple_structure(
713                                                         new external_single_structure(
714                                                             array (
715                                                                 'filepath' => new external_value (PARAM_TEXT, 'file path')
716                                                             )
717                                                         ), 'files', VALUE_OPTIONAL
718                                                     )
719                                                 )
720                                             ), 'fileareas', VALUE_OPTIONAL
721                                         ),
722                                         'editorfields' => new external_multiple_structure(
723                                             new external_single_structure(
724                                                 array(
725                                                     'name' => new external_value(PARAM_TEXT, 'field name'),
726                                                     'description' => new external_value(PARAM_TEXT, 'field description'),
727                                                     'text' => new external_value (PARAM_RAW, 'field value'),
728                                                     'format' => new external_format_value ('text')
729                                                 )
730                                             )
731                                             , 'editorfields', VALUE_OPTIONAL
732                                         )
733                                     )
734                                 )
735                                 , 'plugins', VALUE_OPTIONAL
736                             )
737                         )
738                     )
739                 )
740             )
741         );
742     }
744     /**
745      * Describes the get_submissions return value
746      *
747      * @return external_single_structure
748      * @since Moodle 2.5
749      */
750     public static function get_submissions_returns() {
751         return new external_single_structure(
752             array(
753                 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
754                 'warnings' => new external_warnings()
755             )
756         );
757     }
759     /**
760      * Describes the parameters for set_user_flags
761      * @return external_function_parameters
762      * @since  Moodle 2.6
763      */
764     public static function set_user_flags_parameters() {
765         return new external_function_parameters(
766             array(
767                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
768                 'userflags' => new external_multiple_structure(
769                     new external_single_structure(
770                         array(
771                             'userid'           => new external_value(PARAM_INT, 'student id'),
772                             'locked'           => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL),
773                             'mailed'           => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL),
774                             'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL),
775                             'workflowstate'    => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
776                             'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL)
777                         )
778                     )
779                 )
780             )
781         );
782     }
784     /**
785      * Create or update user_flags records
786      *
787      * @param int $assignmentid the assignment for which the userflags are created or updated
788      * @param array $userflags  An array of userflags to create or update
789      * @return array containing success or failure information for each record
790      * @since Moodle 2.6
791      */
792     public static function set_user_flags($assignmentid, $userflags = array()) {
793         global $CFG, $DB;
794         require_once($CFG->dirroot . "/mod/assign/locallib.php");
796         $params = self::validate_parameters(self::set_user_flags_parameters(),
797                                             array('assignmentid' => $assignmentid,
798                                                   'userflags' => $userflags));
800         // Load assignment if it exists and if the user has the capability.
801         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
802         $context = context_module::instance($cm->id);
803         self::validate_context($context);
804         require_capability('mod/assign:grade', $context);
805         $assign = new assign($context, null, null);
807         $results = array();
808         foreach ($params['userflags'] as $userflag) {
809             $success = true;
810             $result = array();
812             $record = $assign->get_user_flags($userflag['userid'], false);
813             if ($record) {
814                 if (isset($userflag['locked'])) {
815                     $record->locked = $userflag['locked'];
816                 }
817                 if (isset($userflag['mailed'])) {
818                     $record->mailed = $userflag['mailed'];
819                 }
820                 if (isset($userflag['extensionduedate'])) {
821                     $record->extensionduedate = $userflag['extensionduedate'];
822                 }
823                 if (isset($userflag['workflowstate'])) {
824                     $record->workflowstate = $userflag['workflowstate'];
825                 }
826                 if (isset($userflag['allocatedmarker'])) {
827                     $record->allocatedmarker = $userflag['allocatedmarker'];
828                 }
829                 if ($assign->update_user_flags($record)) {
830                     $result['id'] = $record->id;
831                     $result['userid'] = $userflag['userid'];
832                 } else {
833                     $result['id'] = $record->id;
834                     $result['userid'] = $userflag['userid'];
835                     $result['errormessage'] = 'Record created but values could not be set';
836                 }
837             } else {
838                 $record = $assign->get_user_flags($userflag['userid'], true);
839                 $setfields = isset($userflag['locked'])
840                              || isset($userflag['mailed'])
841                              || isset($userflag['extensionduedate'])
842                              || isset($userflag['workflowstate'])
843                              || isset($userflag['allocatedmarker']);
844                 if ($record) {
845                     if ($setfields) {
846                         if (isset($userflag['locked'])) {
847                             $record->locked = $userflag['locked'];
848                         }
849                         if (isset($userflag['mailed'])) {
850                             $record->mailed = $userflag['mailed'];
851                         }
852                         if (isset($userflag['extensionduedate'])) {
853                             $record->extensionduedate = $userflag['extensionduedate'];
854                         }
855                         if (isset($userflag['workflowstate'])) {
856                             $record->workflowstate = $userflag['workflowstate'];
857                         }
858                         if (isset($userflag['allocatedmarker'])) {
859                             $record->allocatedmarker = $userflag['allocatedmarker'];
860                         }
861                         if ($assign->update_user_flags($record)) {
862                             $result['id'] = $record->id;
863                             $result['userid'] = $userflag['userid'];
864                         } else {
865                             $result['id'] = $record->id;
866                             $result['userid'] = $userflag['userid'];
867                             $result['errormessage'] = 'Record created but values could not be set';
868                         }
869                     } else {
870                         $result['id'] = $record->id;
871                         $result['userid'] = $userflag['userid'];
872                     }
873                 } else {
874                     $result['id'] = -1;
875                     $result['userid'] = $userflag['userid'];
876                     $result['errormessage'] = 'Record could not be created';
877                 }
878             }
880             $results[] = $result;
881         }
882         return $results;
883     }
885     /**
886      * Describes the set_user_flags return value
887      * @return external_multiple_structure
888      * @since  Moodle 2.6
889      */
890     public static function set_user_flags_returns() {
891         return new external_multiple_structure(
892             new external_single_structure(
893                 array(
894                     'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'),
895                     'userid' => new external_value(PARAM_INT, 'userid of record'),
896                     'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL)
897                 )
898             )
899         );
900     }
902     /**
903      * Describes the parameters for get_user_flags
904      * @return external_function_parameters
905      * @since  Moodle 2.6
906      */
907     public static function get_user_flags_parameters() {
908         return new external_function_parameters(
909             array(
910                 'assignmentids' => new external_multiple_structure(
911                     new external_value(PARAM_INT, 'assignment id'),
912                     '1 or more assignment ids',
913                     VALUE_REQUIRED)
914             )
915         );
916     }
918     /**
919      * Returns user flag information from assign_user_flags for the requested assignment ids
920      * @param int[] $assignmentids
921      * @return array of user flag records for each requested assignment
922      * @since  Moodle 2.6
923      */
924     public static function get_user_flags($assignmentids) {
925         global $DB;
926         $params = self::validate_parameters(self::get_user_flags_parameters(),
927                         array('assignmentids' => $assignmentids));
929         $assignments = array();
930         $warnings = array();
931         $requestedassignmentids = $params['assignmentids'];
933         // Check the user is allowed to get the user flags for the assignments requested.
934         $placeholders = array();
935         list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
936         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
937                "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
938         $placeholders['modname'] = 'assign';
939         $cms = $DB->get_records_sql($sql, $placeholders);
940         foreach ($cms as $cm) {
941             try {
942                 $context = context_module::instance($cm->id);
943                 self::validate_context($context);
944                 require_capability('mod/assign:grade', $context);
945             } catch (Exception $e) {
946                 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
947                 $warning = array();
948                 $warning['item'] = 'assignment';
949                 $warning['itemid'] = $cm->instance;
950                 $warning['warningcode'] = '1';
951                 $warning['message'] = 'No access rights in module context';
952                 $warnings[] = $warning;
953             }
954         }
956         // Create the query and populate an array of assign_user_flags records from the recordset results.
957         if (count ($requestedassignmentids) > 0) {
958             $placeholders = array();
959             list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
961             $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
962                    "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
963                    "FROM {assign_user_flags} auf ".
964                    "WHERE auf.assignment ".$inorequalsql.
965                    " ORDER BY auf.assignment, auf.id";
967             $rs = $DB->get_recordset_sql($sql, $placeholders);
968             $currentassignmentid = null;
969             $assignment = null;
970             foreach ($rs as $rd) {
971                 $userflag = array();
972                 $userflag['id'] = $rd->id;
973                 $userflag['userid'] = $rd->userid;
974                 $userflag['locked'] = $rd->locked;
975                 $userflag['mailed'] = $rd->mailed;
976                 $userflag['extensionduedate'] = $rd->extensionduedate;
977                 $userflag['workflowstate'] = $rd->workflowstate;
978                 $userflag['allocatedmarker'] = $rd->allocatedmarker;
980                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
981                     if (!is_null($assignment)) {
982                         $assignments[] = $assignment;
983                     }
984                     $assignment = array();
985                     $assignment['assignmentid'] = $rd->assignment;
986                     $assignment['userflags'] = array();
987                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
988                 }
989                 $assignment['userflags'][] = $userflag;
991                 $currentassignmentid = $rd->assignment;
992             }
993             if (!is_null($assignment)) {
994                 $assignments[] = $assignment;
995             }
996             $rs->close();
998         }
1000         foreach ($requestedassignmentids as $assignmentid) {
1001             $warning = array();
1002             $warning['item'] = 'assignment';
1003             $warning['itemid'] = $assignmentid;
1004             $warning['warningcode'] = '3';
1005             $warning['message'] = 'No user flags found';
1006             $warnings[] = $warning;
1007         }
1009         $result = array();
1010         $result['assignments'] = $assignments;
1011         $result['warnings'] = $warnings;
1012         return $result;
1013     }
1015     /**
1016      * Creates an assign_user_flags external_single_structure
1017      * @return external_single_structure
1018      * @since  Moodle 2.6
1019      */
1020     private static function assign_user_flags() {
1021         return new external_single_structure(
1022             array (
1023                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1024                 'userflags'   => new external_multiple_structure(new external_single_structure(
1025                         array(
1026                             'id'               => new external_value(PARAM_INT, 'user flag id'),
1027                             'userid'           => new external_value(PARAM_INT, 'student id'),
1028                             'locked'           => new external_value(PARAM_INT, 'locked'),
1029                             'mailed'           => new external_value(PARAM_INT, 'mailed'),
1030                             'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
1031                             'workflowstate'    => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
1032                             'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker')
1033                         )
1034                     )
1035                 )
1036             )
1037         );
1038     }
1040     /**
1041      * Describes the get_user_flags return value
1042      * @return external_single_structure
1043      * @since  Moodle 2.6
1044      */
1045     public static function get_user_flags_returns() {
1046         return new external_single_structure(
1047             array(
1048                 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
1049                 'warnings'      => new external_warnings('item is always \'assignment\'',
1050                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1051                     'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
1052             )
1053         );
1054     }
1056     /**
1057      * Describes the parameters for get_user_mappings
1058      * @return external_function_parameters
1059      * @since  Moodle 2.6
1060      */
1061     public static function get_user_mappings_parameters() {
1062         return new external_function_parameters(
1063             array(
1064                 'assignmentids' => new external_multiple_structure(
1065                     new external_value(PARAM_INT, 'assignment id'),
1066                     '1 or more assignment ids',
1067                     VALUE_REQUIRED)
1068             )
1069         );
1070     }
1072     /**
1073      * Returns user mapping information from assign_user_mapping for the requested assignment ids
1074      * @param int[] $assignmentids
1075      * @return array of user mapping records for each requested assignment
1076      * @since  Moodle 2.6
1077      */
1078     public static function get_user_mappings($assignmentids) {
1079         global $DB;
1080         $params = self::validate_parameters(self::get_user_mappings_parameters(),
1081                         array('assignmentids' => $assignmentids));
1083         $assignments = array();
1084         $warnings = array();
1085         $requestedassignmentids = $params['assignmentids'];
1087         // Check the user is allowed to get the mappings for the assignments requested.
1088         $placeholders = array();
1089         list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1090         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1091                "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1092         $placeholders['modname'] = 'assign';
1093         $cms = $DB->get_records_sql($sql, $placeholders);
1094         foreach ($cms as $cm) {
1095             try {
1096                 $context = context_module::instance($cm->id);
1097                 self::validate_context($context);
1098                 require_capability('mod/assign:revealidentities', $context);
1099             } catch (Exception $e) {
1100                 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1101                 $warning = array();
1102                 $warning['item'] = 'assignment';
1103                 $warning['itemid'] = $cm->instance;
1104                 $warning['warningcode'] = '1';
1105                 $warning['message'] = 'No access rights in module context';
1106                 $warnings[] = $warning;
1107             }
1108         }
1110         // Create the query and populate an array of assign_user_mapping records from the recordset results.
1111         if (count ($requestedassignmentids) > 0) {
1112             $placeholders = array();
1113             list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1115             $sql = "SELECT aum.id,aum.assignment,aum.userid ".
1116                    "FROM {assign_user_mapping} aum ".
1117                    "WHERE aum.assignment ".$inorequalsql.
1118                    " ORDER BY aum.assignment, aum.id";
1120             $rs = $DB->get_recordset_sql($sql, $placeholders);
1121             $currentassignmentid = null;
1122             $assignment = null;
1123             foreach ($rs as $rd) {
1124                 $mapping = array();
1125                 $mapping['id'] = $rd->id;
1126                 $mapping['userid'] = $rd->userid;
1128                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1129                     if (!is_null($assignment)) {
1130                         $assignments[] = $assignment;
1131                     }
1132                     $assignment = array();
1133                     $assignment['assignmentid'] = $rd->assignment;
1134                     $assignment['mappings'] = array();
1135                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1136                 }
1137                 $assignment['mappings'][] = $mapping;
1139                 $currentassignmentid = $rd->assignment;
1140             }
1141             if (!is_null($assignment)) {
1142                 $assignments[] = $assignment;
1143             }
1144             $rs->close();
1146         }
1148         foreach ($requestedassignmentids as $assignmentid) {
1149             $warning = array();
1150             $warning['item'] = 'assignment';
1151             $warning['itemid'] = $assignmentid;
1152             $warning['warningcode'] = '3';
1153             $warning['message'] = 'No mappings found';
1154             $warnings[] = $warning;
1155         }
1157         $result = array();
1158         $result['assignments'] = $assignments;
1159         $result['warnings'] = $warnings;
1160         return $result;
1161     }
1163     /**
1164      * Creates an assign_user_mappings external_single_structure
1165      * @return external_single_structure
1166      * @since  Moodle 2.6
1167      */
1168     private static function assign_user_mappings() {
1169         return new external_single_structure(
1170             array (
1171                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1172                 'mappings'   => new external_multiple_structure(new external_single_structure(
1173                         array(
1174                             'id'     => new external_value(PARAM_INT, 'user mapping id'),
1175                             'userid' => new external_value(PARAM_INT, 'student id')
1176                         )
1177                     )
1178                 )
1179             )
1180         );
1181     }
1183     /**
1184      * Describes the get_user_mappings return value
1185      * @return external_single_structure
1186      * @since  Moodle 2.6
1187      */
1188     public static function get_user_mappings_returns() {
1189         return new external_single_structure(
1190             array(
1191                 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
1192                 'warnings'      => new external_warnings('item is always \'assignment\'',
1193                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1194                     'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
1195             )
1196         );
1197     }
1199     /**
1200      * Describes the parameters for lock_submissions
1201      * @return external_external_function_parameters
1202      * @since  Moodle 2.6
1203      */
1204     public static function lock_submissions_parameters() {
1205         return new external_function_parameters(
1206             array(
1207                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1208                 'userids' => new external_multiple_structure(
1209                     new external_value(PARAM_INT, 'user id'),
1210                     '1 or more user ids',
1211                     VALUE_REQUIRED),
1212             )
1213         );
1214     }
1216     /**
1217      * Locks (prevent updates to) submissions in this assignment.
1218      *
1219      * @param int $assignmentid The id of the assignment
1220      * @param array $userids Array of user ids to lock
1221      * @return array of warnings for each submission that could not be locked.
1222      * @since Moodle 2.6
1223      */
1224     public static function lock_submissions($assignmentid, $userids) {
1225         global $CFG;
1226         require_once("$CFG->dirroot/mod/assign/locallib.php");
1228         $params = self::validate_parameters(self::lock_submissions_parameters(),
1229                         array('assignmentid' => $assignmentid,
1230                               'userids' => $userids));
1232         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1233         $context = context_module::instance($cm->id);
1234         self::validate_context($context);
1236         $assignment = new assign($context, $cm, null);
1238         $warnings = array();
1239         foreach ($params['userids'] as $userid) {
1240             if (!$assignment->lock_submission($userid)) {
1241                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1242                 $warnings[] = self::generate_warning($params['assignmentid'],
1243                                                      'couldnotlock',
1244                                                      $detail);
1245             }
1246         }
1248         return $warnings;
1249     }
1251     /**
1252      * Describes the return value for lock_submissions
1253      *
1254      * @return external_single_structure
1255      * @since Moodle 2.6
1256      */
1257     public static function lock_submissions_returns() {
1258         return new external_multiple_structure(
1259            new external_warnings()
1260         );
1261     }
1263     /**
1264      * Describes the parameters for revert_submissions_to_draft
1265      * @return external_external_function_parameters
1266      * @since  Moodle 2.6
1267      */
1268     public static function revert_submissions_to_draft_parameters() {
1269         return new external_function_parameters(
1270             array(
1271                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1272                 'userids' => new external_multiple_structure(
1273                     new external_value(PARAM_INT, 'user id'),
1274                     '1 or more user ids',
1275                     VALUE_REQUIRED),
1276             )
1277         );
1278     }
1280     /**
1281      * Reverts a list of user submissions to draft for a single assignment.
1282      *
1283      * @param int $assignmentid The id of the assignment
1284      * @param array $userids Array of user ids to revert
1285      * @return array of warnings for each submission that could not be reverted.
1286      * @since Moodle 2.6
1287      */
1288     public static function revert_submissions_to_draft($assignmentid, $userids) {
1289         global $CFG;
1290         require_once("$CFG->dirroot/mod/assign/locallib.php");
1292         $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
1293                         array('assignmentid' => $assignmentid,
1294                               'userids' => $userids));
1296         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1297         $context = context_module::instance($cm->id);
1298         self::validate_context($context);
1300         $assignment = new assign($context, $cm, null);
1302         $warnings = array();
1303         foreach ($params['userids'] as $userid) {
1304             if (!$assignment->revert_to_draft($userid)) {
1305                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1306                 $warnings[] = self::generate_warning($params['assignmentid'],
1307                                                      'couldnotrevert',
1308                                                      $detail);
1309             }
1310         }
1312         return $warnings;
1313     }
1315     /**
1316      * Describes the return value for revert_submissions_to_draft
1317      *
1318      * @return external_single_structure
1319      * @since Moodle 2.6
1320      */
1321     public static function revert_submissions_to_draft_returns() {
1322         return new external_multiple_structure(
1323            new external_warnings()
1324         );
1325     }
1327     /**
1328      * Describes the parameters for unlock_submissions
1329      * @return external_external_function_parameters
1330      * @since  Moodle 2.6
1331      */
1332     public static function unlock_submissions_parameters() {
1333         return new external_function_parameters(
1334             array(
1335                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1336                 'userids' => new external_multiple_structure(
1337                     new external_value(PARAM_INT, 'user id'),
1338                     '1 or more user ids',
1339                     VALUE_REQUIRED),
1340             )
1341         );
1342     }
1344     /**
1345      * Locks (prevent updates to) submissions in this assignment.
1346      *
1347      * @param int $assignmentid The id of the assignment
1348      * @param array $userids Array of user ids to lock
1349      * @return array of warnings for each submission that could not be locked.
1350      * @since Moodle 2.6
1351      */
1352     public static function unlock_submissions($assignmentid, $userids) {
1353         global $CFG;
1354         require_once("$CFG->dirroot/mod/assign/locallib.php");
1356         $params = self::validate_parameters(self::unlock_submissions_parameters(),
1357                         array('assignmentid' => $assignmentid,
1358                               'userids' => $userids));
1360         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1361         $context = context_module::instance($cm->id);
1362         self::validate_context($context);
1364         $assignment = new assign($context, $cm, null);
1366         $warnings = array();
1367         foreach ($params['userids'] as $userid) {
1368             if (!$assignment->unlock_submission($userid)) {
1369                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1370                 $warnings[] = self::generate_warning($params['assignmentid'],
1371                                                      'couldnotunlock',
1372                                                      $detail);
1373             }
1374         }
1376         return $warnings;
1377     }
1379     /**
1380      * Describes the return value for unlock_submissions
1381      *
1382      * @return external_single_structure
1383      * @since Moodle 2.6
1384      */
1385     public static function unlock_submissions_returns() {
1386         return new external_multiple_structure(
1387            new external_warnings()
1388         );
1389     }
1391     /**
1392      * Describes the parameters for submit_for_grading
1393      * @return external_external_function_parameters
1394      * @since  Moodle 2.6
1395      */
1396     public static function submit_for_grading_parameters() {
1397         return new external_function_parameters(
1398             array(
1399                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1400                 'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement')
1401             )
1402         );
1403     }
1405     /**
1406      * Submit the logged in users assignment for grading.
1407      *
1408      * @param int $assignmentid The id of the assignment
1409      * @return array of warnings to indicate any errors.
1410      * @since Moodle 2.6
1411      */
1412     public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
1413         global $CFG, $USER;
1414         require_once("$CFG->dirroot/mod/assign/locallib.php");
1416         $params = self::validate_parameters(self::submit_for_grading_parameters(),
1417                                             array('assignmentid' => $assignmentid,
1418                                                   'acceptsubmissionstatement' => $acceptsubmissionstatement));
1420         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1421         $context = context_module::instance($cm->id);
1422         self::validate_context($context);
1424         $assignment = new assign($context, $cm, null);
1426         $warnings = array();
1427         $data = new stdClass();
1428         $data->submissionstatement = $params['acceptsubmissionstatement'];
1429         $notices = array();
1431         if (!$assignment->submit_for_grading($data, $notices)) {
1432             $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices);
1433             $warnings[] = self::generate_warning($params['assignmentid'],
1434                                                  'couldnotsubmitforgrading',
1435                                                  $detail);
1436         }
1438         return $warnings;
1439     }
1441     /**
1442      * Describes the return value for submit_for_grading
1443      *
1444      * @return external_single_structure
1445      * @since Moodle 2.6
1446      */
1447     public static function submit_for_grading_returns() {
1448         return new external_multiple_structure(
1449            new external_warnings()
1450         );
1451     }
1453     /**
1454      * Describes the parameters for save_user_extensions
1455      * @return external_external_function_parameters
1456      * @since  Moodle 2.6
1457      */
1458     public static function save_user_extensions_parameters() {
1459         return new external_function_parameters(
1460             array(
1461                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1462                 'userids' => new external_multiple_structure(
1463                     new external_value(PARAM_INT, 'user id'),
1464                     '1 or more user ids',
1465                     VALUE_REQUIRED),
1466                 'dates' => new external_multiple_structure(
1467                     new external_value(PARAM_INT, 'dates'),
1468                     '1 or more extension dates (timestamp)',
1469                     VALUE_REQUIRED),
1470             )
1471         );
1472     }
1474     /**
1475      * Grant extension dates to students for an assignment.
1476      *
1477      * @param int $assignmentid The id of the assignment
1478      * @param array $userids Array of user ids to grant extensions to
1479      * @param array $dates Array of extension dates
1480      * @return array of warnings for each extension date that could not be granted
1481      * @since Moodle 2.6
1482      */
1483     public static function save_user_extensions($assignmentid, $userids, $dates) {
1484         global $CFG;
1485         require_once("$CFG->dirroot/mod/assign/locallib.php");
1487         $params = self::validate_parameters(self::save_user_extensions_parameters(),
1488                         array('assignmentid' => $assignmentid,
1489                               'userids' => $userids,
1490                               'dates' => $dates));
1492         if (count($params['userids']) != count($params['dates'])) {
1493             $detail = 'Length of userids and dates parameters differ.';
1494             $warnings[] = self::generate_warning($params['assignmentid'],
1495                                                  'invalidparameters',
1496                                                  $detail);
1498             return $warnings;
1499         }
1501         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1502         $context = context_module::instance($cm->id);
1503         self::validate_context($context);
1505         $assignment = new assign($context, $cm, null);
1507         $warnings = array();
1508         foreach ($params['userids'] as $idx => $userid) {
1509             $duedate = $params['dates'][$idx];
1510             if (!$assignment->save_user_extension($userid, $duedate)) {
1511                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate;
1512                 $warnings[] = self::generate_warning($params['assignmentid'],
1513                                                      'couldnotgrantextensions',
1514                                                      $detail);
1515             }
1516         }
1518         return $warnings;
1519     }
1521     /**
1522      * Describes the return value for save_user_extensions
1523      *
1524      * @return external_single_structure
1525      * @since Moodle 2.6
1526      */
1527     public static function save_user_extensions_returns() {
1528         return new external_multiple_structure(
1529            new external_warnings()
1530         );
1531     }
1533     /**
1534      * Describes the parameters for reveal_identities
1535      * @return external_external_function_parameters
1536      * @since  Moodle 2.6
1537      */
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')
1542             )
1543         );
1544     }
1546     /**
1547      * Reveal the identities of anonymous students to markers for a single assignment.
1548      *
1549      * @param int $assignmentid The id of the assignment
1550      * @return array of warnings to indicate any errors.
1551      * @since Moodle 2.6
1552      */
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);
1572         }
1574         return $warnings;
1575     }
1577     /**
1578      * Describes the return value for reveal_identities
1579      *
1580      * @return external_single_structure
1581      * @since Moodle 2.6
1582      */
1583     public static function reveal_identities_returns() {
1584         return new external_multiple_structure(
1585            new external_warnings()
1586         );
1587     }
1589     /**
1590      * Describes the parameters for save_submission
1591      * @return external_external_function_parameters
1592      * @since  Moodle 2.6
1593      */
1594     public static function save_submission_parameters() {
1595         global $CFG;
1596         require_once("$CFG->dirroot/mod/assign/locallib.php");
1597         $instance = new assign(null, null, null);
1598         $pluginsubmissionparams = array();
1600         foreach ($instance->get_submission_plugins() as $plugin) {
1601             $pluginparams = $plugin->get_external_parameters();
1602             if (!empty($pluginparams)) {
1603                 $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
1604             }
1605         }
1607         return new external_function_parameters(
1608             array(
1609                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1610                 'plugindata' => new external_single_structure(
1611                     $pluginsubmissionparams
1612                 )
1613             )
1614         );
1615     }
1617     /**
1618      * Save a student submission for a single assignment
1619      *
1620      * @param int $assignmentid The id of the assignment
1621      * @param array $plugindata - The submitted data for plugins
1622      * @return array of warnings to indicate any errors
1623      * @since Moodle 2.6
1624      */
1625     public static function save_submission($assignmentid, $plugindata) {
1626         global $CFG, $USER;
1627         require_once("$CFG->dirroot/mod/assign/locallib.php");
1629         $params = self::validate_parameters(self::save_submission_parameters(),
1630                                             array('assignmentid' => $assignmentid,
1631                                                   'plugindata' => $plugindata));
1633         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1634         $context = context_module::instance($cm->id);
1635         self::validate_context($context);
1637         $assignment = new assign($context, $cm, null);
1639         $notices = array();
1641         $submissiondata = (object)$params['plugindata'];
1643         $assignment->save_submission($submissiondata, $notices);
1645         $warnings = array();
1646         foreach ($notices as $notice) {
1647             $warnings[] = self::generate_warning($params['assignmentid'],
1648                                                  'couldnotsavesubmission',
1649                                                  $notice);
1650         }
1652         return $warnings;
1653     }
1655     /**
1656      * Describes the return value for save_submission
1657      *
1658      * @return external_single_structure
1659      * @since Moodle 2.6
1660      */
1661     public static function save_submission_returns() {
1662         return new external_multiple_structure(
1663            new external_warnings()
1664         );
1665     }
1667     /**
1668      * Describes the parameters for save_grade
1669      * @return external_external_function_parameters
1670      * @since  Moodle 2.6
1671      */
1672     public static function save_grade_parameters() {
1673         global $CFG;
1674         require_once("$CFG->dirroot/mod/assign/locallib.php");
1675         require_once("$CFG->dirroot/grade/grading/lib.php");
1676         $instance = new assign(null, null, null);
1677         $pluginfeedbackparams = array();
1679         foreach ($instance->get_feedback_plugins() as $plugin) {
1680             $pluginparams = $plugin->get_external_parameters();
1681             if (!empty($pluginparams)) {
1682                 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1683             }
1684         }
1686         $advancedgradingdata = array();
1687         $methods = array_keys(grading_manager::available_methods(false));
1688         foreach ($methods as $method) {
1689             require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1690             $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1691             if (!empty($details)) {
1692                 $items = array();
1693                 foreach ($details as $key => $value) {
1694                     $value->required = VALUE_OPTIONAL;
1695                     unset($value->content->keys['id']);
1696                     $items[$key] = new external_multiple_structure (new external_single_structure(
1697                         array(
1698                             'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1699                             'fillings' => $value
1700                         )
1701                     ));
1702                 }
1703                 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1704             }
1705         }
1707         return new external_function_parameters(
1708             array(
1709                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1710                 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1711                 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'),
1712                 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1713                 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
1714                 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1715                 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1716                                                                'to all members ' .
1717                                                                'of the group (for group assignments).'),
1718                 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()),
1719                 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1720                                                                        VALUE_DEFAULT, array())
1721             )
1722         );
1723     }
1725     /**
1726      * Save a student grade for a single assignment.
1727      *
1728      * @param int $assignmentid The id of the assignment
1729      * @param int $userid The id of the user
1730      * @param float $grade The grade (ignored if the assignment uses advanced grading)
1731      * @param int $attemptnumber The attempt number
1732      * @param bool $addattempt Allow another attempt
1733      * @param string $workflowstate New workflow state
1734      * @param bool $applytoall Apply the grade to all members of the group
1735      * @param array $plugindata Custom data used by plugins
1736      * @param array $advancedgradingdata Advanced grading data
1737      * @return null
1738      * @since Moodle 2.6
1739      */
1740     public static function save_grade($assignmentid,
1741                                       $userid,
1742                                       $grade,
1743                                       $attemptnumber,
1744                                       $addattempt,
1745                                       $workflowstate,
1746                                       $applytoall,
1747                                       $plugindata = array(),
1748                                       $advancedgradingdata = array()) {
1749         global $CFG, $USER;
1750         require_once("$CFG->dirroot/mod/assign/locallib.php");
1752         $params = self::validate_parameters(self::save_grade_parameters(),
1753                                             array('assignmentid' => $assignmentid,
1754                                                   'userid' => $userid,
1755                                                   'grade' => $grade,
1756                                                   'attemptnumber' => $attemptnumber,
1757                                                   'workflowstate' => $workflowstate,
1758                                                   'addattempt' => $addattempt,
1759                                                   'applytoall' => $applytoall,
1760                                                   'plugindata' => $plugindata,
1761                                                   'advancedgradingdata' => $advancedgradingdata));
1763         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1764         $context = context_module::instance($cm->id);
1765         self::validate_context($context);
1767         $assignment = new assign($context, $cm, null);
1769         $gradedata = (object)$params['plugindata'];
1771         $gradedata->addattempt = $params['addattempt'];
1772         $gradedata->attemptnumber = $params['attemptnumber'];
1773         $gradedata->workflowstate = $params['workflowstate'];
1774         $gradedata->applytoall = $params['applytoall'];
1775         $gradedata->grade = $params['grade'];
1777         if (!empty($params['advancedgradingdata'])) {
1778             $advancedgrading = array();
1779             $criteria = reset($params['advancedgradingdata']);
1780             foreach ($criteria as $key => $criterion) {
1781                 $details = array();
1782                 foreach ($criterion as $value) {
1783                     foreach ($value['fillings'] as $filling) {
1784                         $details[$value['criterionid']] = $filling;
1785                     }
1786                 }
1787                 $advancedgrading[$key] = $details;
1788             }
1789             $gradedata->advancedgrading = $advancedgrading;
1790         }
1792         $assignment->save_grade($params['userid'], $gradedata);
1794         return null;
1795     }
1797     /**
1798      * Describes the return value for save_grade
1799      *
1800      * @return external_single_structure
1801      * @since Moodle 2.6
1802      */
1803     public static function save_grade_returns() {
1804         return null;
1805     }
1807     /**
1808      * Describes the parameters for save_grades
1809      * @return external_external_function_parameters
1810      * @since  Moodle 2.7
1811      */
1812     public static function save_grades_parameters() {
1813         global $CFG;
1814         require_once("$CFG->dirroot/mod/assign/locallib.php");
1815         require_once("$CFG->dirroot/grade/grading/lib.php");
1816         $instance = new assign(null, null, null);
1817         $pluginfeedbackparams = array();
1819         foreach ($instance->get_feedback_plugins() as $plugin) {
1820             $pluginparams = $plugin->get_external_parameters();
1821             if (!empty($pluginparams)) {
1822                 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1823             }
1824         }
1826         $advancedgradingdata = array();
1827         $methods = array_keys(grading_manager::available_methods(false));
1828         foreach ($methods as $method) {
1829             require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1830             $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1831             if (!empty($details)) {
1832                 $items = array();
1833                 foreach ($details as $key => $value) {
1834                     $value->required = VALUE_OPTIONAL;
1835                     unset($value->content->keys['id']);
1836                     $items[$key] = new external_multiple_structure (new external_single_structure(
1837                         array(
1838                             'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1839                             'fillings' => $value
1840                         )
1841                     ));
1842                 }
1843                 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1844             }
1845         }
1847         return new external_function_parameters(
1848             array(
1849                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1850                 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1851                                                                'to all members ' .
1852                                                                'of the group (for group assignments).'),
1853                 'grades' => new external_multiple_structure(
1854                     new external_single_structure(
1855                         array (
1856                             'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1857                             'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '.
1858                                                                        'Ignored if advanced grading used'),
1859                             'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1860                             'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'),
1861                             'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1862                             'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data',
1863                                                                           VALUE_DEFAULT, array()),
1864                             'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1865                                                                                    VALUE_DEFAULT, array())
1866                         )
1867                     )
1868                 )
1869             )
1870         );
1871     }
1873     /**
1874      * Save multiple student grades for a single assignment.
1875      *
1876      * @param int $assignmentid The id of the assignment
1877      * @param boolean $applytoall If set to true and this is a team assignment,
1878      * apply the grade to all members of the group
1879      * @param array $grades grade data for one or more students that includes
1880      *                  userid - The id of the student being graded
1881      *                  grade - The grade (ignored if the assignment uses advanced grading)
1882      *                  attemptnumber - The attempt number
1883      *                  addattempt - Allow another attempt
1884      *                  workflowstate - New workflow state
1885      *                  plugindata - Custom data used by plugins
1886      *                  advancedgradingdata - Optional Advanced grading data
1887      * @throws invalid_parameter_exception if multiple grades are supplied for
1888      * a team assignment that has $applytoall set to true
1889      * @return null
1890      * @since Moodle 2.7
1891      */
1892     public static function save_grades($assignmentid, $applytoall = false, $grades) {
1893         global $CFG, $USER;
1894         require_once("$CFG->dirroot/mod/assign/locallib.php");
1896         $params = self::validate_parameters(self::save_grades_parameters(),
1897                                             array('assignmentid' => $assignmentid,
1898                                                   'applytoall' => $applytoall,
1899                                                   'grades' => $grades));
1901         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1902         $context = context_module::instance($cm->id);
1903         self::validate_context($context);
1904         $assignment = new assign($context, $cm, null);
1906         if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
1907             // Check that only 1 user per submission group is provided.
1908             $groupids = array();
1909             foreach ($params['grades'] as $gradeinfo) {
1910                 $group = $assignment->get_submission_group($gradeinfo['userid']);
1911                 if (in_array($group->id, $groupids)) {
1912                     throw new invalid_parameter_exception('Multiple grades for the same team have been supplied '
1913                                                           .' this is not permitted when the applytoall flag is set');
1914                 } else {
1915                     $groupids[] = $group->id;
1916                 }
1917             }
1918         }
1920         foreach ($params['grades'] as $gradeinfo) {
1921             $gradedata = (object)$gradeinfo['plugindata'];
1922             $gradedata->addattempt = $gradeinfo['addattempt'];
1923             $gradedata->attemptnumber = $gradeinfo['attemptnumber'];
1924             $gradedata->workflowstate = $gradeinfo['workflowstate'];
1925             $gradedata->applytoall = $params['applytoall'];
1926             $gradedata->grade = $gradeinfo['grade'];
1928             if (!empty($gradeinfo['advancedgradingdata'])) {
1929                 $advancedgrading = array();
1930                 $criteria = reset($gradeinfo['advancedgradingdata']);
1931                 foreach ($criteria as $key => $criterion) {
1932                     $details = array();
1933                     foreach ($criterion as $value) {
1934                         foreach ($value['fillings'] as $filling) {
1935                             $details[$value['criterionid']] = $filling;
1936                         }
1937                     }
1938                     $advancedgrading[$key] = $details;
1939                 }
1940                 $gradedata->advancedgrading = $advancedgrading;
1941             }
1942             $assignment->save_grade($gradeinfo['userid'], $gradedata);
1943         }
1945         return null;
1946     }
1948     /**
1949      * Describes the return value for save_grades
1950      *
1951      * @return external_single_structure
1952      * @since Moodle 2.7
1953      */
1954     public static function save_grades_returns() {
1955         return null;
1956     }
1958     /**
1959      * Describes the parameters for copy_previous_attempt
1960      * @return external_external_function_parameters
1961      * @since  Moodle 2.6
1962      */
1963     public static function copy_previous_attempt_parameters() {
1964         return new external_function_parameters(
1965             array(
1966                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1967             )
1968         );
1969     }
1971     /**
1972      * Copy a students previous attempt to a new attempt.
1973      *
1974      * @param int $assignmentid
1975      * @return array of warnings to indicate any errors.
1976      * @since Moodle 2.6
1977      */
1978     public static function copy_previous_attempt($assignmentid) {
1979         global $CFG, $USER;
1980         require_once("$CFG->dirroot/mod/assign/locallib.php");
1982         $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
1983                                             array('assignmentid' => $assignmentid));
1985         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
1986         $context = context_module::instance($cm->id);
1987         self::validate_context($context);
1989         $assignment = new assign($context, $cm, null);
1991         $notices = array();
1993         $assignment->copy_previous_attempt($submissiondata, $notices);
1995         $warnings = array();
1996         foreach ($notices as $notice) {
1997             $warnings[] = self::generate_warning($assignmentid,
1998                                                  'couldnotcopyprevioussubmission',
1999                                                  $notice);
2000         }
2002         return $warnings;
2003     }
2005     /**
2006      * Describes the return value for save_submission
2007      *
2008      * @return external_single_structure
2009      * @since Moodle 2.6
2010      */
2011     public static function copy_previous_attempt_returns() {
2012         return new external_multiple_structure(
2013            new external_warnings()
2014         );
2015     }