Merge branch 'MDL-53548-master' of https://github.com/sammarshallou/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);
133             $sql = "SELECT ag.id,
134                            ag.assignment,
135                            ag.userid,
136                            ag.timecreated,
137                            ag.timemodified,
138                            ag.grader,
139                            ag.grade,
140                            ag.attemptnumber
141                       FROM {assign_grades} ag, {assign_submission} s
142                      WHERE s.assignment $inorequalsql
143                        AND s.userid = ag.userid
144                        AND s.latest = 1
145                        AND s.attemptnumber = ag.attemptnumber
146                        AND ag.timemodified  >= :since
147                        AND ag.assignment = s.assignment
148                   ORDER BY ag.assignment, ag.id";
150             $placeholders['since'] = $params['since'];
151             $rs = $DB->get_recordset_sql($sql, $placeholders);
152             $currentassignmentid = null;
153             $assignment = null;
154             foreach ($rs as $rd) {
155                 $grade = array();
156                 $grade['id'] = $rd->id;
157                 $grade['userid'] = $rd->userid;
158                 $grade['timecreated'] = $rd->timecreated;
159                 $grade['timemodified'] = $rd->timemodified;
160                 $grade['grader'] = $rd->grader;
161                 $grade['attemptnumber'] = $rd->attemptnumber;
162                 $grade['grade'] = (string)$rd->grade;
164                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
165                     if (!is_null($assignment)) {
166                         $assignments[] = $assignment;
167                     }
168                     $assignment = array();
169                     $assignment['assignmentid'] = $rd->assignment;
170                     $assignment['grades'] = array();
171                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
172                 }
173                 $assignment['grades'][] = $grade;
175                 $currentassignmentid = $rd->assignment;
176             }
177             if (!is_null($assignment)) {
178                 $assignments[] = $assignment;
179             }
180             $rs->close();
181         }
182         foreach ($requestedassignmentids as $assignmentid) {
183             $warning = array();
184             $warning['item'] = 'assignment';
185             $warning['itemid'] = $assignmentid;
186             $warning['warningcode'] = '3';
187             $warning['message'] = 'No grades found';
188             $warnings[] = $warning;
189         }
191         $result = array();
192         $result['assignments'] = $assignments;
193         $result['warnings'] = $warnings;
194         return $result;
195     }
197     /**
198      * Creates an assign_grades external_single_structure
199      * @return external_single_structure
200      * @since  Moodle 2.4
201      */
202     private static function assign_grades() {
203         return new external_single_structure(
204             array (
205                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
206                 'grades'   => new external_multiple_structure(new external_single_structure(
207                         array(
208                             'id'            => new external_value(PARAM_INT, 'grade id'),
209                             'userid'        => new external_value(PARAM_INT, 'student id'),
210                             'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
211                             'timecreated'   => new external_value(PARAM_INT, 'grade creation time'),
212                             'timemodified'  => new external_value(PARAM_INT, 'grade last modified time'),
213                             'grader'        => new external_value(PARAM_INT, 'grader'),
214                             'grade'         => new external_value(PARAM_TEXT, 'grade')
215                         )
216                     )
217                 )
218             )
219         );
220     }
222     /**
223      * Describes the get_grades return value
224      * @return external_single_structure
225      * @since  Moodle 2.4
226      */
227     public static function get_grades_returns() {
228         return new external_single_structure(
229             array(
230                 'assignments' => new external_multiple_structure(self::assign_grades(), 'list of assignment grade information'),
231                 'warnings'      => new external_warnings('item is always \'assignment\'',
232                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
233                     'errorcode can be 3 (no grades found) or 1 (no permission to get grades)')
234             )
235         );
236     }
238     /**
239      * Returns description of method parameters
240      *
241      * @return external_function_parameters
242      * @since  Moodle 2.4
243      */
244     public static function get_assignments_parameters() {
245         return new external_function_parameters(
246             array(
247                 'courseids' => new external_multiple_structure(
248                     new external_value(PARAM_INT, 'course id, empty for retrieving all the courses where the user is enroled in'),
249                     '0 or more course ids',
250                     VALUE_DEFAULT, array()
251                 ),
252                 'capabilities'  => new external_multiple_structure(
253                     new external_value(PARAM_CAPABILITY, 'capability'),
254                     'list of capabilities used to filter courses',
255                     VALUE_DEFAULT, array()
256                 ),
257                 'includenotenrolledcourses' => new external_value(PARAM_BOOL, 'whether to return courses that the user can see
258                                                                     even if is not enroled in. This requires the parameter courseids
259                                                                     to not be empty.', VALUE_DEFAULT, false)
260             )
261         );
262     }
264     /**
265      * Returns an array of courses the user is enrolled, and for each course all of the assignments that the user can
266      * view within that course.
267      *
268      * @param array $courseids An optional array of course ids. If provided only assignments within the given course
269      * will be returned. If the user is not enrolled in or can't view a given course a warning will be generated and returned.
270      * @param array $capabilities An array of additional capability checks you wish to be made on the course context.
271      * @param bool $includenotenrolledcourses Wheter to return courses that the user can see even if is not enroled in.
272      * This requires the parameter $courseids to not be empty.
273      * @return An array of courses and warnings.
274      * @since  Moodle 2.4
275      */
276     public static function get_assignments($courseids = array(), $capabilities = array(), $includenotenrolledcourses = false) {
277         global $USER, $DB, $CFG;
278         require_once("$CFG->dirroot/mod/assign/locallib.php");
280         $params = self::validate_parameters(
281             self::get_assignments_parameters(),
282             array(
283                 'courseids' => $courseids,
284                 'capabilities' => $capabilities,
285                 'includenotenrolledcourses' => $includenotenrolledcourses
286             )
287         );
289         $warnings = array();
290         $courses = array();
291         $fields = 'sortorder,shortname,fullname,timemodified';
293         // If the courseids list is empty, we return only the courses where the user is enrolled in.
294         if (empty($params['courseids'])) {
295             $courses = enrol_get_users_courses($USER->id, true, $fields);
296             $courseids = array_keys($courses);
297         } else if ($includenotenrolledcourses) {
298             // In this case, we don't have to check here for enrolmnents. Maybe the user can see the course even if is not enrolled.
299             $courseids = $params['courseids'];
300         } else {
301             // We need to check for enrolments.
302             $mycourses = enrol_get_users_courses($USER->id, true, $fields);
303             $mycourseids = array_keys($mycourses);
305             foreach ($params['courseids'] as $courseid) {
306                 if (!in_array($courseid, $mycourseids)) {
307                     unset($courses[$courseid]);
308                     $warnings[] = array(
309                         'item' => 'course',
310                         'itemid' => $courseid,
311                         'warningcode' => '2',
312                         'message' => 'User is not enrolled or does not have requested capability'
313                     );
314                 } else {
315                     $courses[$courseid] = $mycourses[$courseid];
316                 }
317             }
318             $courseids = array_keys($courses);
319         }
321         foreach ($courseids as $cid) {
323             try {
324                 $context = context_course::instance($cid);
325                 self::validate_context($context);
327                 // Check if this course was already loaded (by enrol_get_users_courses).
328                 if (!isset($courses[$cid])) {
329                     $courses[$cid] = get_course($cid);
330                 }
331             } catch (Exception $e) {
332                 unset($courses[$cid]);
333                 $warnings[] = array(
334                     'item' => 'course',
335                     'itemid' => $cid,
336                     'warningcode' => '1',
337                     'message' => 'No access rights in course context '.$e->getMessage()
338                 );
339                 continue;
340             }
341             if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) {
342                 unset($courses[$cid]);
343             }
344         }
345         $extrafields='m.id as assignmentid, ' .
346                      'm.course, ' .
347                      'm.nosubmissions, ' .
348                      'm.submissiondrafts, ' .
349                      'm.sendnotifications, '.
350                      'm.sendlatenotifications, ' .
351                      'm.sendstudentnotifications, ' .
352                      'm.duedate, ' .
353                      'm.allowsubmissionsfromdate, '.
354                      'm.grade, ' .
355                      'm.timemodified, '.
356                      'm.completionsubmit, ' .
357                      'm.cutoffdate, ' .
358                      'm.teamsubmission, ' .
359                      'm.requireallteammemberssubmit, '.
360                      'm.teamsubmissiongroupingid, ' .
361                      'm.blindmarking, ' .
362                      'm.revealidentities, ' .
363                      'm.attemptreopenmethod, '.
364                      'm.maxattempts, ' .
365                      'm.markingworkflow, ' .
366                      'm.markingallocation, ' .
367                      'm.requiresubmissionstatement, '.
368                      'm.intro, '.
369                      'm.introformat';
370         $coursearray = array();
371         foreach ($courses as $id => $course) {
372             $assignmentarray = array();
373             // Get a list of assignments for the course.
374             if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
375                 foreach ($modules as $module) {
376                     $context = context_module::instance($module->id);
377                     try {
378                         self::validate_context($context);
379                         require_capability('mod/assign:view', $context);
380                     } catch (Exception $e) {
381                         $warnings[] = array(
382                             'item' => 'module',
383                             'itemid' => $module->id,
384                             'warningcode' => '1',
385                             'message' => 'No access rights in module context'
386                         );
387                         continue;
388                     }
389                     $configrecords = $DB->get_recordset('assign_plugin_config', array('assignment' => $module->assignmentid));
390                     $configarray = array();
391                     foreach ($configrecords as $configrecord) {
392                         $configarray[] = array(
393                             'id' => $configrecord->id,
394                             'assignment' => $configrecord->assignment,
395                             'plugin' => $configrecord->plugin,
396                             'subtype' => $configrecord->subtype,
397                             'name' => $configrecord->name,
398                             'value' => $configrecord->value
399                         );
400                     }
401                     $configrecords->close();
403                     $assignment = array(
404                         'id' => $module->assignmentid,
405                         'cmid' => $module->id,
406                         'course' => $module->course,
407                         'name' => $module->name,
408                         'nosubmissions' => $module->nosubmissions,
409                         'submissiondrafts' => $module->submissiondrafts,
410                         'sendnotifications' => $module->sendnotifications,
411                         'sendlatenotifications' => $module->sendlatenotifications,
412                         'sendstudentnotifications' => $module->sendstudentnotifications,
413                         'duedate' => $module->duedate,
414                         'allowsubmissionsfromdate' => $module->allowsubmissionsfromdate,
415                         'grade' => $module->grade,
416                         'timemodified' => $module->timemodified,
417                         'completionsubmit' => $module->completionsubmit,
418                         'cutoffdate' => $module->cutoffdate,
419                         'teamsubmission' => $module->teamsubmission,
420                         'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
421                         'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
422                         'blindmarking' => $module->blindmarking,
423                         'revealidentities' => $module->revealidentities,
424                         'attemptreopenmethod' => $module->attemptreopenmethod,
425                         'maxattempts' => $module->maxattempts,
426                         'markingworkflow' => $module->markingworkflow,
427                         'markingallocation' => $module->markingallocation,
428                         'requiresubmissionstatement' => $module->requiresubmissionstatement,
429                         'configs' => $configarray
430                     );
432                     // Return or not intro and file attachments depending on the plugin settings.
433                     $assign = new assign($context, null, null);
435                     if ($assign->show_intro()) {
437                         list($assignment['intro'], $assignment['introformat']) = external_format_text($module->intro,
438                             $module->introformat, $context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0);
440                         $fs = get_file_storage();
441                         if ($files = $fs->get_area_files($context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA,
442                                                             0, 'timemodified', false)) {
444                             $assignment['introattachments'] = array();
445                             foreach ($files as $file) {
446                                 $filename = $file->get_filename();
448                                 $assignment['introattachments'][] = array(
449                                     'filename' => $filename,
450                                     'mimetype' => $file->get_mimetype(),
451                                     'fileurl'  => moodle_url::make_webservice_pluginfile_url(
452                                         $context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0, '/', $filename)->out(false)
453                                 );
454                             }
455                         }
456                     }
458                     $assignmentarray[] = $assignment;
459                 }
460             }
461             $coursearray[]= array(
462                 'id' => $courses[$id]->id,
463                 'fullname' => $courses[$id]->fullname,
464                 'shortname' => $courses[$id]->shortname,
465                 'timemodified' => $courses[$id]->timemodified,
466                 'assignments' => $assignmentarray
467             );
468         }
470         $result = array(
471             'courses' => $coursearray,
472             'warnings' => $warnings
473         );
474         return $result;
475     }
477     /**
478      * Creates an assignment external_single_structure
479      *
480      * @return external_single_structure
481      * @since Moodle 2.4
482      */
483     private static function get_assignments_assignment_structure() {
484         return new external_single_structure(
485             array(
486                 'id' => new external_value(PARAM_INT, 'assignment id'),
487                 'cmid' => new external_value(PARAM_INT, 'course module id'),
488                 'course' => new external_value(PARAM_INT, 'course id'),
489                 'name' => new external_value(PARAM_TEXT, 'assignment name'),
490                 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
491                 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
492                 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
493                 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
494                 'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'),
495                 'duedate' => new external_value(PARAM_INT, 'assignment due date'),
496                 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
497                 'grade' => new external_value(PARAM_INT, 'grade type'),
498                 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
499                 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
500                 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
501                 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
502                 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
503                 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
504                 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
505                 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
506                 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
507                 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
508                 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
509                 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
510                 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
511                 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings'),
512                 'intro' => new external_value(PARAM_RAW,
513                     'assignment intro, not allways returned because it deppends on the activity configuration', VALUE_OPTIONAL),
514                 'introformat' => new external_format_value('intro', VALUE_OPTIONAL),
515                 'introattachments' => new external_multiple_structure(
516                     new external_single_structure(
517                         array (
518                             'filename' => new external_value(PARAM_FILE, 'file name'),
519                             'mimetype' => new external_value(PARAM_RAW, 'mime type'),
520                             'fileurl'  => new external_value(PARAM_URL, 'file download url')
521                         )
522                     ), 'intro attachments files', VALUE_OPTIONAL
523                 )
524             ), 'assignment information object');
525     }
527     /**
528      * Creates an assign_plugin_config external_single_structure
529      *
530      * @return external_single_structure
531      * @since Moodle 2.4
532      */
533     private static function get_assignments_config_structure() {
534         return new external_single_structure(
535             array(
536                 'id' => new external_value(PARAM_INT, 'assign_plugin_config id'),
537                 'assignment' => new external_value(PARAM_INT, 'assignment id'),
538                 'plugin' => new external_value(PARAM_TEXT, 'plugin'),
539                 'subtype' => new external_value(PARAM_TEXT, 'subtype'),
540                 'name' => new external_value(PARAM_TEXT, 'name'),
541                 'value' => new external_value(PARAM_TEXT, 'value')
542             ), 'assignment configuration object'
543         );
544     }
546     /**
547      * Creates a course external_single_structure
548      *
549      * @return external_single_structure
550      * @since Moodle 2.4
551      */
552     private static function get_assignments_course_structure() {
553         return new external_single_structure(
554             array(
555                 'id' => new external_value(PARAM_INT, 'course id'),
556                 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
557                 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
558                 'timemodified' => new external_value(PARAM_INT, 'last time modified'),
559                 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
560               ), 'course information object'
561         );
562     }
564     /**
565      * Describes the return value for get_assignments
566      *
567      * @return external_single_structure
568      * @since Moodle 2.4
569      */
570     public static function get_assignments_returns() {
571         return new external_single_structure(
572             array(
573                 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
574                 'warnings'  => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
575                     'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
576                     'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
577             )
578         );
579     }
581     /**
582      * Describes the parameters for get_submissions
583      *
584      * @return external_external_function_parameters
585      * @since Moodle 2.5
586      */
587     public static function get_submissions_parameters() {
588         return new external_function_parameters(
589             array(
590                 'assignmentids' => new external_multiple_structure(
591                     new external_value(PARAM_INT, 'assignment id'),
592                     '1 or more assignment ids',
593                     VALUE_REQUIRED),
594                 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
595                 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
596                 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
597             )
598         );
599     }
601     /**
602      * Returns submissions for the requested assignment ids
603      *
604      * @param int[] $assignmentids
605      * @param string $status only return submissions with this status
606      * @param int $since only return submissions with timemodified >= since
607      * @param int $before only return submissions with timemodified <= before
608      * @return array of submissions for each requested assignment
609      * @since Moodle 2.5
610      */
611     public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
612         global $DB, $CFG;
613         require_once("$CFG->dirroot/mod/assign/locallib.php");
614         $params = self::validate_parameters(self::get_submissions_parameters(),
615                         array('assignmentids' => $assignmentids,
616                               'status' => $status,
617                               'since' => $since,
618                               'before' => $before));
620         $warnings = array();
621         $assignments = array();
623         // Check the user is allowed to get the submissions for the assignments requested.
624         $placeholders = array();
625         list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
626         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
627                "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
628         $placeholders['modname'] = 'assign';
629         $cms = $DB->get_records_sql($sql, $placeholders);
630         $assigns = array();
631         foreach ($cms as $cm) {
632             try {
633                 $context = context_module::instance($cm->id);
634                 self::validate_context($context);
635                 require_capability('mod/assign:grade', $context);
636                 $assign = new assign($context, null, null);
637                 $assigns[] = $assign;
638             } catch (Exception $e) {
639                 $warnings[] = array(
640                     'item' => 'assignment',
641                     'itemid' => $cm->instance,
642                     'warningcode' => '1',
643                     'message' => 'No access rights in module context'
644                 );
645             }
646         }
648         foreach ($assigns as $assign) {
649             $submissions = array();
650             $submissionplugins = $assign->get_submission_plugins();
651             $placeholders = array('assignid1' => $assign->get_instance()->id,
652                                   'assignid2' => $assign->get_instance()->id);
654             $submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
655                                      FROM {assign_submission} mxs
656                                      WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid';
658             $sql = "SELECT mas.id, mas.assignment,mas.userid,".
659                    "mas.timecreated,mas.timemodified,mas.status,mas.groupid,mas.attemptnumber ".
660                    "FROM {assign_submission} mas ".
661                    "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
662                    "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
664             if (!empty($params['status'])) {
665                 $placeholders['status'] = $params['status'];
666                 $sql = $sql." AND mas.status = :status";
667             }
668             if (!empty($params['before'])) {
669                 $placeholders['since'] = $params['since'];
670                 $placeholders['before'] = $params['before'];
671                 $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
672             } else {
673                 $placeholders['since'] = $params['since'];
674                 $sql = $sql." AND mas.timemodified >= :since";
675             }
677             $submissionrecords = $DB->get_records_sql($sql, $placeholders);
679             if (!empty($submissionrecords)) {
680                 $fs = get_file_storage();
681                 foreach ($submissionrecords as $submissionrecord) {
682                     $submission = array(
683                         'id' => $submissionrecord->id,
684                         'userid' => $submissionrecord->userid,
685                         'timecreated' => $submissionrecord->timecreated,
686                         'timemodified' => $submissionrecord->timemodified,
687                         'status' => $submissionrecord->status,
688                         'attemptnumber' => $submissionrecord->attemptnumber,
689                         'groupid' => $submissionrecord->groupid
690                     );
691                     foreach ($submissionplugins as $submissionplugin) {
692                         $plugin = array(
693                             'name' => $submissionplugin->get_name(),
694                             'type' => $submissionplugin->get_type()
695                         );
696                         // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
697                         $component = $submissionplugin->get_subtype().'_'.$submissionplugin->get_type();
699                         $fileareas = $submissionplugin->get_file_areas();
700                         foreach ($fileareas as $filearea => $name) {
701                             $fileareainfo = array('area' => $filearea);
702                             $files = $fs->get_area_files(
703                                 $assign->get_context()->id,
704                                 $component,
705                                 $filearea,
706                                 $submissionrecord->id,
707                                 "timemodified",
708                                 false
709                             );
710                             foreach ($files as $file) {
711                                 $filepath = $file->get_filepath().$file->get_filename();
712                                 $fileurl = file_encode_url($CFG->wwwroot . '/webservice/pluginfile.php', '/' . $assign->get_context()->id .
713                                     '/' . $component. '/'. $filearea . '/' . $submissionrecord->id . $filepath);
714                                 $fileinfo = array(
715                                     'filepath' => $filepath,
716                                     'fileurl' => $fileurl
717                                     );
718                                 $fileareainfo['files'][] = $fileinfo;
719                             }
720                             $plugin['fileareas'][] = $fileareainfo;
721                         }
723                         $editorfields = $submissionplugin->get_editor_fields();
724                         foreach ($editorfields as $name => $description) {
725                             $editorfieldinfo = array(
726                                 'name' => $name,
727                                 'description' => $description,
728                                 'text' => $submissionplugin->get_editor_text($name, $submissionrecord->id),
729                                 'format' => $submissionplugin->get_editor_format($name, $submissionrecord->id)
730                             );
731                             $plugin['editorfields'][] = $editorfieldinfo;
732                         }
734                         $submission['plugins'][] = $plugin;
735                     }
736                     $submissions[] = $submission;
737                 }
738             } else {
739                 $warnings[] = array(
740                     'item' => 'module',
741                     'itemid' => $assign->get_instance()->id,
742                     'warningcode' => '3',
743                     'message' => 'No submissions found'
744                 );
745             }
747             $assignments[] = array(
748                 'assignmentid' => $assign->get_instance()->id,
749                 'submissions' => $submissions
750             );
752         }
754         $result = array(
755             'assignments' => $assignments,
756             'warnings' => $warnings
757         );
758         return $result;
759     }
761     /**
762      * Creates an assign_submissions external_single_structure
763      *
764      * @return external_single_structure
765      * @since Moodle 2.5
766      */
767     private static function get_submissions_structure() {
768         return new external_single_structure(
769             array (
770                 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
771                 'submissions' => new external_multiple_structure(
772                     new external_single_structure(
773                         array(
774                             'id' => new external_value(PARAM_INT, 'submission id'),
775                             'userid' => new external_value(PARAM_INT, 'student id'),
776                             'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
777                             'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
778                             'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
779                             'status' => new external_value(PARAM_TEXT, 'submission status'),
780                             'groupid' => new external_value(PARAM_INT, 'group id'),
781                             'plugins' => new external_multiple_structure(
782                                 new external_single_structure(
783                                     array(
784                                         'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
785                                         'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
786                                         'fileareas' => new external_multiple_structure(
787                                             new external_single_structure(
788                                                 array (
789                                                     'area' => new external_value (PARAM_TEXT, 'file area'),
790                                                     'files' => new external_multiple_structure(
791                                                         new external_single_structure(
792                                                             array (
793                                                                 'filepath' => new external_value (PARAM_TEXT, 'file path'),
794                                                                 'fileurl' => new external_value (PARAM_URL, 'file download url',
795                                                                     VALUE_OPTIONAL)
796                                                             )
797                                                         ), 'files', VALUE_OPTIONAL
798                                                     )
799                                                 )
800                                             ), 'fileareas', VALUE_OPTIONAL
801                                         ),
802                                         'editorfields' => new external_multiple_structure(
803                                             new external_single_structure(
804                                                 array(
805                                                     'name' => new external_value(PARAM_TEXT, 'field name'),
806                                                     'description' => new external_value(PARAM_TEXT, 'field description'),
807                                                     'text' => new external_value (PARAM_RAW, 'field value'),
808                                                     'format' => new external_format_value ('text')
809                                                 )
810                                             )
811                                             , 'editorfields', VALUE_OPTIONAL
812                                         )
813                                     )
814                                 )
815                                 , 'plugins', VALUE_OPTIONAL
816                             )
817                         )
818                     )
819                 )
820             )
821         );
822     }
824     /**
825      * Describes the get_submissions return value
826      *
827      * @return external_single_structure
828      * @since Moodle 2.5
829      */
830     public static function get_submissions_returns() {
831         return new external_single_structure(
832             array(
833                 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
834                 'warnings' => new external_warnings()
835             )
836         );
837     }
839     /**
840      * Describes the parameters for set_user_flags
841      * @return external_function_parameters
842      * @since  Moodle 2.6
843      */
844     public static function set_user_flags_parameters() {
845         return new external_function_parameters(
846             array(
847                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
848                 'userflags' => new external_multiple_structure(
849                     new external_single_structure(
850                         array(
851                             'userid'           => new external_value(PARAM_INT, 'student id'),
852                             'locked'           => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL),
853                             'mailed'           => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL),
854                             'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL),
855                             'workflowstate'    => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
856                             'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL)
857                         )
858                     )
859                 )
860             )
861         );
862     }
864     /**
865      * Create or update user_flags records
866      *
867      * @param int $assignmentid the assignment for which the userflags are created or updated
868      * @param array $userflags  An array of userflags to create or update
869      * @return array containing success or failure information for each record
870      * @since Moodle 2.6
871      */
872     public static function set_user_flags($assignmentid, $userflags = array()) {
873         global $CFG, $DB;
874         require_once($CFG->dirroot . "/mod/assign/locallib.php");
876         $params = self::validate_parameters(self::set_user_flags_parameters(),
877                                             array('assignmentid' => $assignmentid,
878                                                   'userflags' => $userflags));
880         // Load assignment if it exists and if the user has the capability.
881         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
882         $context = context_module::instance($cm->id);
883         self::validate_context($context);
884         require_capability('mod/assign:grade', $context);
885         $assign = new assign($context, null, null);
887         $results = array();
888         foreach ($params['userflags'] as $userflag) {
889             $success = true;
890             $result = array();
892             $record = $assign->get_user_flags($userflag['userid'], false);
893             if ($record) {
894                 if (isset($userflag['locked'])) {
895                     $record->locked = $userflag['locked'];
896                 }
897                 if (isset($userflag['mailed'])) {
898                     $record->mailed = $userflag['mailed'];
899                 }
900                 if (isset($userflag['extensionduedate'])) {
901                     $record->extensionduedate = $userflag['extensionduedate'];
902                 }
903                 if (isset($userflag['workflowstate'])) {
904                     $record->workflowstate = $userflag['workflowstate'];
905                 }
906                 if (isset($userflag['allocatedmarker'])) {
907                     $record->allocatedmarker = $userflag['allocatedmarker'];
908                 }
909                 if ($assign->update_user_flags($record)) {
910                     $result['id'] = $record->id;
911                     $result['userid'] = $userflag['userid'];
912                 } else {
913                     $result['id'] = $record->id;
914                     $result['userid'] = $userflag['userid'];
915                     $result['errormessage'] = 'Record created but values could not be set';
916                 }
917             } else {
918                 $record = $assign->get_user_flags($userflag['userid'], true);
919                 $setfields = isset($userflag['locked'])
920                              || isset($userflag['mailed'])
921                              || isset($userflag['extensionduedate'])
922                              || isset($userflag['workflowstate'])
923                              || isset($userflag['allocatedmarker']);
924                 if ($record) {
925                     if ($setfields) {
926                         if (isset($userflag['locked'])) {
927                             $record->locked = $userflag['locked'];
928                         }
929                         if (isset($userflag['mailed'])) {
930                             $record->mailed = $userflag['mailed'];
931                         }
932                         if (isset($userflag['extensionduedate'])) {
933                             $record->extensionduedate = $userflag['extensionduedate'];
934                         }
935                         if (isset($userflag['workflowstate'])) {
936                             $record->workflowstate = $userflag['workflowstate'];
937                         }
938                         if (isset($userflag['allocatedmarker'])) {
939                             $record->allocatedmarker = $userflag['allocatedmarker'];
940                         }
941                         if ($assign->update_user_flags($record)) {
942                             $result['id'] = $record->id;
943                             $result['userid'] = $userflag['userid'];
944                         } else {
945                             $result['id'] = $record->id;
946                             $result['userid'] = $userflag['userid'];
947                             $result['errormessage'] = 'Record created but values could not be set';
948                         }
949                     } else {
950                         $result['id'] = $record->id;
951                         $result['userid'] = $userflag['userid'];
952                     }
953                 } else {
954                     $result['id'] = -1;
955                     $result['userid'] = $userflag['userid'];
956                     $result['errormessage'] = 'Record could not be created';
957                 }
958             }
960             $results[] = $result;
961         }
962         return $results;
963     }
965     /**
966      * Describes the set_user_flags return value
967      * @return external_multiple_structure
968      * @since  Moodle 2.6
969      */
970     public static function set_user_flags_returns() {
971         return new external_multiple_structure(
972             new external_single_structure(
973                 array(
974                     'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'),
975                     'userid' => new external_value(PARAM_INT, 'userid of record'),
976                     'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL)
977                 )
978             )
979         );
980     }
982     /**
983      * Describes the parameters for get_user_flags
984      * @return external_function_parameters
985      * @since  Moodle 2.6
986      */
987     public static function get_user_flags_parameters() {
988         return new external_function_parameters(
989             array(
990                 'assignmentids' => new external_multiple_structure(
991                     new external_value(PARAM_INT, 'assignment id'),
992                     '1 or more assignment ids',
993                     VALUE_REQUIRED)
994             )
995         );
996     }
998     /**
999      * Returns user flag information from assign_user_flags for the requested assignment ids
1000      * @param int[] $assignmentids
1001      * @return array of user flag records for each requested assignment
1002      * @since  Moodle 2.6
1003      */
1004     public static function get_user_flags($assignmentids) {
1005         global $DB;
1006         $params = self::validate_parameters(self::get_user_flags_parameters(),
1007                         array('assignmentids' => $assignmentids));
1009         $assignments = array();
1010         $warnings = array();
1011         $requestedassignmentids = $params['assignmentids'];
1013         // Check the user is allowed to get the user flags for the assignments requested.
1014         $placeholders = array();
1015         list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1016         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1017                "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1018         $placeholders['modname'] = 'assign';
1019         $cms = $DB->get_records_sql($sql, $placeholders);
1020         foreach ($cms as $cm) {
1021             try {
1022                 $context = context_module::instance($cm->id);
1023                 self::validate_context($context);
1024                 require_capability('mod/assign:grade', $context);
1025             } catch (Exception $e) {
1026                 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1027                 $warning = array();
1028                 $warning['item'] = 'assignment';
1029                 $warning['itemid'] = $cm->instance;
1030                 $warning['warningcode'] = '1';
1031                 $warning['message'] = 'No access rights in module context';
1032                 $warnings[] = $warning;
1033             }
1034         }
1036         // Create the query and populate an array of assign_user_flags records from the recordset results.
1037         if (count ($requestedassignmentids) > 0) {
1038             $placeholders = array();
1039             list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1041             $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
1042                    "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
1043                    "FROM {assign_user_flags} auf ".
1044                    "WHERE auf.assignment ".$inorequalsql.
1045                    " ORDER BY auf.assignment, auf.id";
1047             $rs = $DB->get_recordset_sql($sql, $placeholders);
1048             $currentassignmentid = null;
1049             $assignment = null;
1050             foreach ($rs as $rd) {
1051                 $userflag = array();
1052                 $userflag['id'] = $rd->id;
1053                 $userflag['userid'] = $rd->userid;
1054                 $userflag['locked'] = $rd->locked;
1055                 $userflag['mailed'] = $rd->mailed;
1056                 $userflag['extensionduedate'] = $rd->extensionduedate;
1057                 $userflag['workflowstate'] = $rd->workflowstate;
1058                 $userflag['allocatedmarker'] = $rd->allocatedmarker;
1060                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1061                     if (!is_null($assignment)) {
1062                         $assignments[] = $assignment;
1063                     }
1064                     $assignment = array();
1065                     $assignment['assignmentid'] = $rd->assignment;
1066                     $assignment['userflags'] = array();
1067                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1068                 }
1069                 $assignment['userflags'][] = $userflag;
1071                 $currentassignmentid = $rd->assignment;
1072             }
1073             if (!is_null($assignment)) {
1074                 $assignments[] = $assignment;
1075             }
1076             $rs->close();
1078         }
1080         foreach ($requestedassignmentids as $assignmentid) {
1081             $warning = array();
1082             $warning['item'] = 'assignment';
1083             $warning['itemid'] = $assignmentid;
1084             $warning['warningcode'] = '3';
1085             $warning['message'] = 'No user flags found';
1086             $warnings[] = $warning;
1087         }
1089         $result = array();
1090         $result['assignments'] = $assignments;
1091         $result['warnings'] = $warnings;
1092         return $result;
1093     }
1095     /**
1096      * Creates an assign_user_flags external_single_structure
1097      * @return external_single_structure
1098      * @since  Moodle 2.6
1099      */
1100     private static function assign_user_flags() {
1101         return new external_single_structure(
1102             array (
1103                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1104                 'userflags'   => new external_multiple_structure(new external_single_structure(
1105                         array(
1106                             'id'               => new external_value(PARAM_INT, 'user flag id'),
1107                             'userid'           => new external_value(PARAM_INT, 'student id'),
1108                             'locked'           => new external_value(PARAM_INT, 'locked'),
1109                             'mailed'           => new external_value(PARAM_INT, 'mailed'),
1110                             'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
1111                             'workflowstate'    => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
1112                             'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker')
1113                         )
1114                     )
1115                 )
1116             )
1117         );
1118     }
1120     /**
1121      * Describes the get_user_flags return value
1122      * @return external_single_structure
1123      * @since  Moodle 2.6
1124      */
1125     public static function get_user_flags_returns() {
1126         return new external_single_structure(
1127             array(
1128                 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
1129                 'warnings'      => new external_warnings('item is always \'assignment\'',
1130                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1131                     'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
1132             )
1133         );
1134     }
1136     /**
1137      * Describes the parameters for get_user_mappings
1138      * @return external_function_parameters
1139      * @since  Moodle 2.6
1140      */
1141     public static function get_user_mappings_parameters() {
1142         return new external_function_parameters(
1143             array(
1144                 'assignmentids' => new external_multiple_structure(
1145                     new external_value(PARAM_INT, 'assignment id'),
1146                     '1 or more assignment ids',
1147                     VALUE_REQUIRED)
1148             )
1149         );
1150     }
1152     /**
1153      * Returns user mapping information from assign_user_mapping for the requested assignment ids
1154      * @param int[] $assignmentids
1155      * @return array of user mapping records for each requested assignment
1156      * @since  Moodle 2.6
1157      */
1158     public static function get_user_mappings($assignmentids) {
1159         global $DB;
1160         $params = self::validate_parameters(self::get_user_mappings_parameters(),
1161                         array('assignmentids' => $assignmentids));
1163         $assignments = array();
1164         $warnings = array();
1165         $requestedassignmentids = $params['assignmentids'];
1167         // Check the user is allowed to get the mappings for the assignments requested.
1168         $placeholders = array();
1169         list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1170         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1171                "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1172         $placeholders['modname'] = 'assign';
1173         $cms = $DB->get_records_sql($sql, $placeholders);
1174         foreach ($cms as $cm) {
1175             try {
1176                 $context = context_module::instance($cm->id);
1177                 self::validate_context($context);
1178                 require_capability('mod/assign:revealidentities', $context);
1179             } catch (Exception $e) {
1180                 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1181                 $warning = array();
1182                 $warning['item'] = 'assignment';
1183                 $warning['itemid'] = $cm->instance;
1184                 $warning['warningcode'] = '1';
1185                 $warning['message'] = 'No access rights in module context';
1186                 $warnings[] = $warning;
1187             }
1188         }
1190         // Create the query and populate an array of assign_user_mapping records from the recordset results.
1191         if (count ($requestedassignmentids) > 0) {
1192             $placeholders = array();
1193             list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1195             $sql = "SELECT aum.id,aum.assignment,aum.userid ".
1196                    "FROM {assign_user_mapping} aum ".
1197                    "WHERE aum.assignment ".$inorequalsql.
1198                    " ORDER BY aum.assignment, aum.id";
1200             $rs = $DB->get_recordset_sql($sql, $placeholders);
1201             $currentassignmentid = null;
1202             $assignment = null;
1203             foreach ($rs as $rd) {
1204                 $mapping = array();
1205                 $mapping['id'] = $rd->id;
1206                 $mapping['userid'] = $rd->userid;
1208                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1209                     if (!is_null($assignment)) {
1210                         $assignments[] = $assignment;
1211                     }
1212                     $assignment = array();
1213                     $assignment['assignmentid'] = $rd->assignment;
1214                     $assignment['mappings'] = array();
1215                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1216                 }
1217                 $assignment['mappings'][] = $mapping;
1219                 $currentassignmentid = $rd->assignment;
1220             }
1221             if (!is_null($assignment)) {
1222                 $assignments[] = $assignment;
1223             }
1224             $rs->close();
1226         }
1228         foreach ($requestedassignmentids as $assignmentid) {
1229             $warning = array();
1230             $warning['item'] = 'assignment';
1231             $warning['itemid'] = $assignmentid;
1232             $warning['warningcode'] = '3';
1233             $warning['message'] = 'No mappings found';
1234             $warnings[] = $warning;
1235         }
1237         $result = array();
1238         $result['assignments'] = $assignments;
1239         $result['warnings'] = $warnings;
1240         return $result;
1241     }
1243     /**
1244      * Creates an assign_user_mappings external_single_structure
1245      * @return external_single_structure
1246      * @since  Moodle 2.6
1247      */
1248     private static function assign_user_mappings() {
1249         return new external_single_structure(
1250             array (
1251                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1252                 'mappings'   => new external_multiple_structure(new external_single_structure(
1253                         array(
1254                             'id'     => new external_value(PARAM_INT, 'user mapping id'),
1255                             'userid' => new external_value(PARAM_INT, 'student id')
1256                         )
1257                     )
1258                 )
1259             )
1260         );
1261     }
1263     /**
1264      * Describes the get_user_mappings return value
1265      * @return external_single_structure
1266      * @since  Moodle 2.6
1267      */
1268     public static function get_user_mappings_returns() {
1269         return new external_single_structure(
1270             array(
1271                 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
1272                 'warnings'      => new external_warnings('item is always \'assignment\'',
1273                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1274                     'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
1275             )
1276         );
1277     }
1279     /**
1280      * Describes the parameters for lock_submissions
1281      * @return external_external_function_parameters
1282      * @since  Moodle 2.6
1283      */
1284     public static function lock_submissions_parameters() {
1285         return new external_function_parameters(
1286             array(
1287                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1288                 'userids' => new external_multiple_structure(
1289                     new external_value(PARAM_INT, 'user id'),
1290                     '1 or more user ids',
1291                     VALUE_REQUIRED),
1292             )
1293         );
1294     }
1296     /**
1297      * Locks (prevent updates to) submissions in this assignment.
1298      *
1299      * @param int $assignmentid The id of the assignment
1300      * @param array $userids Array of user ids to lock
1301      * @return array of warnings for each submission that could not be locked.
1302      * @since Moodle 2.6
1303      */
1304     public static function lock_submissions($assignmentid, $userids) {
1305         global $CFG;
1306         require_once("$CFG->dirroot/mod/assign/locallib.php");
1308         $params = self::validate_parameters(self::lock_submissions_parameters(),
1309                         array('assignmentid' => $assignmentid,
1310                               'userids' => $userids));
1312         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1313         $context = context_module::instance($cm->id);
1314         self::validate_context($context);
1316         $assignment = new assign($context, $cm, null);
1318         $warnings = array();
1319         foreach ($params['userids'] as $userid) {
1320             if (!$assignment->lock_submission($userid)) {
1321                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1322                 $warnings[] = self::generate_warning($params['assignmentid'],
1323                                                      'couldnotlock',
1324                                                      $detail);
1325             }
1326         }
1328         return $warnings;
1329     }
1331     /**
1332      * Describes the return value for lock_submissions
1333      *
1334      * @return external_single_structure
1335      * @since Moodle 2.6
1336      */
1337     public static function lock_submissions_returns() {
1338         return new external_warnings();
1339     }
1341     /**
1342      * Describes the parameters for revert_submissions_to_draft
1343      * @return external_external_function_parameters
1344      * @since  Moodle 2.6
1345      */
1346     public static function revert_submissions_to_draft_parameters() {
1347         return new external_function_parameters(
1348             array(
1349                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1350                 'userids' => new external_multiple_structure(
1351                     new external_value(PARAM_INT, 'user id'),
1352                     '1 or more user ids',
1353                     VALUE_REQUIRED),
1354             )
1355         );
1356     }
1358     /**
1359      * Reverts a list of user submissions to draft for a single assignment.
1360      *
1361      * @param int $assignmentid The id of the assignment
1362      * @param array $userids Array of user ids to revert
1363      * @return array of warnings for each submission that could not be reverted.
1364      * @since Moodle 2.6
1365      */
1366     public static function revert_submissions_to_draft($assignmentid, $userids) {
1367         global $CFG;
1368         require_once("$CFG->dirroot/mod/assign/locallib.php");
1370         $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
1371                         array('assignmentid' => $assignmentid,
1372                               'userids' => $userids));
1374         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1375         $context = context_module::instance($cm->id);
1376         self::validate_context($context);
1378         $assignment = new assign($context, $cm, null);
1380         $warnings = array();
1381         foreach ($params['userids'] as $userid) {
1382             if (!$assignment->revert_to_draft($userid)) {
1383                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1384                 $warnings[] = self::generate_warning($params['assignmentid'],
1385                                                      'couldnotrevert',
1386                                                      $detail);
1387             }
1388         }
1390         return $warnings;
1391     }
1393     /**
1394      * Describes the return value for revert_submissions_to_draft
1395      *
1396      * @return external_single_structure
1397      * @since Moodle 2.6
1398      */
1399     public static function revert_submissions_to_draft_returns() {
1400         return new external_warnings();
1401     }
1403     /**
1404      * Describes the parameters for unlock_submissions
1405      * @return external_external_function_parameters
1406      * @since  Moodle 2.6
1407      */
1408     public static function unlock_submissions_parameters() {
1409         return new external_function_parameters(
1410             array(
1411                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1412                 'userids' => new external_multiple_structure(
1413                     new external_value(PARAM_INT, 'user id'),
1414                     '1 or more user ids',
1415                     VALUE_REQUIRED),
1416             )
1417         );
1418     }
1420     /**
1421      * Locks (prevent updates to) submissions in this assignment.
1422      *
1423      * @param int $assignmentid The id of the assignment
1424      * @param array $userids Array of user ids to lock
1425      * @return array of warnings for each submission that could not be locked.
1426      * @since Moodle 2.6
1427      */
1428     public static function unlock_submissions($assignmentid, $userids) {
1429         global $CFG;
1430         require_once("$CFG->dirroot/mod/assign/locallib.php");
1432         $params = self::validate_parameters(self::unlock_submissions_parameters(),
1433                         array('assignmentid' => $assignmentid,
1434                               'userids' => $userids));
1436         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1437         $context = context_module::instance($cm->id);
1438         self::validate_context($context);
1440         $assignment = new assign($context, $cm, null);
1442         $warnings = array();
1443         foreach ($params['userids'] as $userid) {
1444             if (!$assignment->unlock_submission($userid)) {
1445                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1446                 $warnings[] = self::generate_warning($params['assignmentid'],
1447                                                      'couldnotunlock',
1448                                                      $detail);
1449             }
1450         }
1452         return $warnings;
1453     }
1455     /**
1456      * Describes the return value for unlock_submissions
1457      *
1458      * @return external_single_structure
1459      * @since Moodle 2.6
1460      */
1461     public static function unlock_submissions_returns() {
1462         return new external_warnings();
1463     }
1465     /**
1466      * Describes the parameters for submit_for_grading
1467      * @return external_external_function_parameters
1468      * @since  Moodle 2.6
1469      */
1470     public static function submit_for_grading_parameters() {
1471         return new external_function_parameters(
1472             array(
1473                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1474                 'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement')
1475             )
1476         );
1477     }
1479     /**
1480      * Submit the logged in users assignment for grading.
1481      *
1482      * @param int $assignmentid The id of the assignment
1483      * @return array of warnings to indicate any errors.
1484      * @since Moodle 2.6
1485      */
1486     public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
1487         global $CFG, $USER;
1488         require_once("$CFG->dirroot/mod/assign/locallib.php");
1490         $params = self::validate_parameters(self::submit_for_grading_parameters(),
1491                                             array('assignmentid' => $assignmentid,
1492                                                   'acceptsubmissionstatement' => $acceptsubmissionstatement));
1494         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1495         $context = context_module::instance($cm->id);
1496         self::validate_context($context);
1498         $assignment = new assign($context, $cm, null);
1500         $warnings = array();
1501         $data = new stdClass();
1502         $data->submissionstatement = $params['acceptsubmissionstatement'];
1503         $notices = array();
1505         if (!$assignment->submit_for_grading($data, $notices)) {
1506             $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices);
1507             $warnings[] = self::generate_warning($params['assignmentid'],
1508                                                  'couldnotsubmitforgrading',
1509                                                  $detail);
1510         }
1512         return $warnings;
1513     }
1515     /**
1516      * Describes the return value for submit_for_grading
1517      *
1518      * @return external_single_structure
1519      * @since Moodle 2.6
1520      */
1521     public static function submit_for_grading_returns() {
1522         return new external_warnings();
1523     }
1525     /**
1526      * Describes the parameters for save_user_extensions
1527      * @return external_external_function_parameters
1528      * @since  Moodle 2.6
1529      */
1530     public static function save_user_extensions_parameters() {
1531         return new external_function_parameters(
1532             array(
1533                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1534                 'userids' => new external_multiple_structure(
1535                     new external_value(PARAM_INT, 'user id'),
1536                     '1 or more user ids',
1537                     VALUE_REQUIRED),
1538                 'dates' => new external_multiple_structure(
1539                     new external_value(PARAM_INT, 'dates'),
1540                     '1 or more extension dates (timestamp)',
1541                     VALUE_REQUIRED),
1542             )
1543         );
1544     }
1546     /**
1547      * Grant extension dates to students for an assignment.
1548      *
1549      * @param int $assignmentid The id of the assignment
1550      * @param array $userids Array of user ids to grant extensions to
1551      * @param array $dates Array of extension dates
1552      * @return array of warnings for each extension date that could not be granted
1553      * @since Moodle 2.6
1554      */
1555     public static function save_user_extensions($assignmentid, $userids, $dates) {
1556         global $CFG;
1557         require_once("$CFG->dirroot/mod/assign/locallib.php");
1559         $params = self::validate_parameters(self::save_user_extensions_parameters(),
1560                         array('assignmentid' => $assignmentid,
1561                               'userids' => $userids,
1562                               'dates' => $dates));
1564         if (count($params['userids']) != count($params['dates'])) {
1565             $detail = 'Length of userids and dates parameters differ.';
1566             $warnings[] = self::generate_warning($params['assignmentid'],
1567                                                  'invalidparameters',
1568                                                  $detail);
1570             return $warnings;
1571         }
1573         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1574         $context = context_module::instance($cm->id);
1575         self::validate_context($context);
1577         $assignment = new assign($context, $cm, null);
1579         $warnings = array();
1580         foreach ($params['userids'] as $idx => $userid) {
1581             $duedate = $params['dates'][$idx];
1582             if (!$assignment->save_user_extension($userid, $duedate)) {
1583                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate;
1584                 $warnings[] = self::generate_warning($params['assignmentid'],
1585                                                      'couldnotgrantextensions',
1586                                                      $detail);
1587             }
1588         }
1590         return $warnings;
1591     }
1593     /**
1594      * Describes the return value for save_user_extensions
1595      *
1596      * @return external_single_structure
1597      * @since Moodle 2.6
1598      */
1599     public static function save_user_extensions_returns() {
1600         return new external_warnings();
1601     }
1603     /**
1604      * Describes the parameters for reveal_identities
1605      * @return external_external_function_parameters
1606      * @since  Moodle 2.6
1607      */
1608     public static function reveal_identities_parameters() {
1609         return new external_function_parameters(
1610             array(
1611                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
1612             )
1613         );
1614     }
1616     /**
1617      * Reveal the identities of anonymous students to markers for a single assignment.
1618      *
1619      * @param int $assignmentid The id of the assignment
1620      * @return array of warnings to indicate any errors.
1621      * @since Moodle 2.6
1622      */
1623     public static function reveal_identities($assignmentid) {
1624         global $CFG, $USER;
1625         require_once("$CFG->dirroot/mod/assign/locallib.php");
1627         $params = self::validate_parameters(self::reveal_identities_parameters(),
1628                                             array('assignmentid' => $assignmentid));
1630         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1631         $context = context_module::instance($cm->id);
1632         self::validate_context($context);
1634         $assignment = new assign($context, $cm, null);
1636         $warnings = array();
1637         if (!$assignment->reveal_identities()) {
1638             $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'];
1639             $warnings[] = self::generate_warning($params['assignmentid'],
1640                                                  'couldnotrevealidentities',
1641                                                  $detail);
1642         }
1644         return $warnings;
1645     }
1647     /**
1648      * Describes the return value for reveal_identities
1649      *
1650      * @return external_single_structure
1651      * @since Moodle 2.6
1652      */
1653     public static function reveal_identities_returns() {
1654         return new external_warnings();
1655     }
1657     /**
1658      * Describes the parameters for save_submission
1659      * @return external_external_function_parameters
1660      * @since  Moodle 2.6
1661      */
1662     public static function save_submission_parameters() {
1663         global $CFG;
1664         require_once("$CFG->dirroot/mod/assign/locallib.php");
1665         $instance = new assign(null, null, null);
1666         $pluginsubmissionparams = array();
1668         foreach ($instance->get_submission_plugins() as $plugin) {
1669             if ($plugin->is_visible()) {
1670                 $pluginparams = $plugin->get_external_parameters();
1671                 if (!empty($pluginparams)) {
1672                     $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
1673                 }
1674             }
1675         }
1677         return new external_function_parameters(
1678             array(
1679                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1680                 'plugindata' => new external_single_structure(
1681                     $pluginsubmissionparams
1682                 )
1683             )
1684         );
1685     }
1687     /**
1688      * Save a student submission for a single assignment
1689      *
1690      * @param int $assignmentid The id of the assignment
1691      * @param array $plugindata - The submitted data for plugins
1692      * @return array of warnings to indicate any errors
1693      * @since Moodle 2.6
1694      */
1695     public static function save_submission($assignmentid, $plugindata) {
1696         global $CFG, $USER;
1697         require_once("$CFG->dirroot/mod/assign/locallib.php");
1699         $params = self::validate_parameters(self::save_submission_parameters(),
1700                                             array('assignmentid' => $assignmentid,
1701                                                   'plugindata' => $plugindata));
1703         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1704         $context = context_module::instance($cm->id);
1705         self::validate_context($context);
1707         $assignment = new assign($context, $cm, null);
1709         $notices = array();
1711         if (!$assignment->submissions_open($USER->id)) {
1712             $notices[] = get_string('duedatereached', 'assign');
1713         } else {
1714             $submissiondata = (object)$params['plugindata'];
1715             $assignment->save_submission($submissiondata, $notices);
1716         }
1718         $warnings = array();
1719         foreach ($notices as $notice) {
1720             $warnings[] = self::generate_warning($params['assignmentid'],
1721                                                  'couldnotsavesubmission',
1722                                                  $notice);
1723         }
1725         return $warnings;
1726     }
1728     /**
1729      * Describes the return value for save_submission
1730      *
1731      * @return external_single_structure
1732      * @since Moodle 2.6
1733      */
1734     public static function save_submission_returns() {
1735         return new external_warnings();
1736     }
1738     /**
1739      * Describes the parameters for save_grade
1740      * @return external_external_function_parameters
1741      * @since  Moodle 2.6
1742      */
1743     public static function save_grade_parameters() {
1744         global $CFG;
1745         require_once("$CFG->dirroot/mod/assign/locallib.php");
1746         require_once("$CFG->dirroot/grade/grading/lib.php");
1747         $instance = new assign(null, null, null);
1748         $pluginfeedbackparams = array();
1750         foreach ($instance->get_feedback_plugins() as $plugin) {
1751             if ($plugin->is_visible()) {
1752                 $pluginparams = $plugin->get_external_parameters();
1753                 if (!empty($pluginparams)) {
1754                     $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1755                 }
1756             }
1757         }
1759         $advancedgradingdata = array();
1760         $methods = array_keys(grading_manager::available_methods(false));
1761         foreach ($methods as $method) {
1762             require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1763             $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1764             if (!empty($details)) {
1765                 $items = array();
1766                 foreach ($details as $key => $value) {
1767                     $value->required = VALUE_OPTIONAL;
1768                     unset($value->content->keys['id']);
1769                     $items[$key] = new external_multiple_structure (new external_single_structure(
1770                         array(
1771                             'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1772                             'fillings' => $value
1773                         )
1774                     ));
1775                 }
1776                 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1777             }
1778         }
1780         return new external_function_parameters(
1781             array(
1782                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1783                 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1784                 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'),
1785                 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1786                 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
1787                 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1788                 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1789                                                                'to all members ' .
1790                                                                'of the group (for group assignments).'),
1791                 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()),
1792                 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1793                                                                        VALUE_DEFAULT, array())
1794             )
1795         );
1796     }
1798     /**
1799      * Save a student grade for a single assignment.
1800      *
1801      * @param int $assignmentid The id of the assignment
1802      * @param int $userid The id of the user
1803      * @param float $grade The grade (ignored if the assignment uses advanced grading)
1804      * @param int $attemptnumber The attempt number
1805      * @param bool $addattempt Allow another attempt
1806      * @param string $workflowstate New workflow state
1807      * @param bool $applytoall Apply the grade to all members of the group
1808      * @param array $plugindata Custom data used by plugins
1809      * @param array $advancedgradingdata Advanced grading data
1810      * @return null
1811      * @since Moodle 2.6
1812      */
1813     public static function save_grade($assignmentid,
1814                                       $userid,
1815                                       $grade,
1816                                       $attemptnumber,
1817                                       $addattempt,
1818                                       $workflowstate,
1819                                       $applytoall,
1820                                       $plugindata = array(),
1821                                       $advancedgradingdata = array()) {
1822         global $CFG, $USER;
1823         require_once("$CFG->dirroot/mod/assign/locallib.php");
1825         $params = self::validate_parameters(self::save_grade_parameters(),
1826                                             array('assignmentid' => $assignmentid,
1827                                                   'userid' => $userid,
1828                                                   'grade' => $grade,
1829                                                   'attemptnumber' => $attemptnumber,
1830                                                   'workflowstate' => $workflowstate,
1831                                                   'addattempt' => $addattempt,
1832                                                   'applytoall' => $applytoall,
1833                                                   'plugindata' => $plugindata,
1834                                                   'advancedgradingdata' => $advancedgradingdata));
1836         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1837         $context = context_module::instance($cm->id);
1838         self::validate_context($context);
1840         $assignment = new assign($context, $cm, null);
1842         $gradedata = (object)$params['plugindata'];
1844         $gradedata->addattempt = $params['addattempt'];
1845         $gradedata->attemptnumber = $params['attemptnumber'];
1846         $gradedata->workflowstate = $params['workflowstate'];
1847         $gradedata->applytoall = $params['applytoall'];
1848         $gradedata->grade = $params['grade'];
1850         if (!empty($params['advancedgradingdata'])) {
1851             $advancedgrading = array();
1852             $criteria = reset($params['advancedgradingdata']);
1853             foreach ($criteria as $key => $criterion) {
1854                 $details = array();
1855                 foreach ($criterion as $value) {
1856                     foreach ($value['fillings'] as $filling) {
1857                         $details[$value['criterionid']] = $filling;
1858                     }
1859                 }
1860                 $advancedgrading[$key] = $details;
1861             }
1862             $gradedata->advancedgrading = $advancedgrading;
1863         }
1865         $assignment->save_grade($params['userid'], $gradedata);
1867         return null;
1868     }
1870     /**
1871      * Describes the return value for save_grade
1872      *
1873      * @return external_single_structure
1874      * @since Moodle 2.6
1875      */
1876     public static function save_grade_returns() {
1877         return null;
1878     }
1880     /**
1881      * Describes the parameters for save_grades
1882      * @return external_external_function_parameters
1883      * @since  Moodle 2.7
1884      */
1885     public static function save_grades_parameters() {
1886         global $CFG;
1887         require_once("$CFG->dirroot/mod/assign/locallib.php");
1888         require_once("$CFG->dirroot/grade/grading/lib.php");
1889         $instance = new assign(null, null, null);
1890         $pluginfeedbackparams = array();
1892         foreach ($instance->get_feedback_plugins() as $plugin) {
1893             if ($plugin->is_visible()) {
1894                 $pluginparams = $plugin->get_external_parameters();
1895                 if (!empty($pluginparams)) {
1896                     $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1897                 }
1898             }
1899         }
1901         $advancedgradingdata = array();
1902         $methods = array_keys(grading_manager::available_methods(false));
1903         foreach ($methods as $method) {
1904             require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1905             $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1906             if (!empty($details)) {
1907                 $items = array();
1908                 foreach ($details as $key => $value) {
1909                     $value->required = VALUE_OPTIONAL;
1910                     unset($value->content->keys['id']);
1911                     $items[$key] = new external_multiple_structure (new external_single_structure(
1912                         array(
1913                             'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1914                             'fillings' => $value
1915                         )
1916                     ));
1917                 }
1918                 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1919             }
1920         }
1922         return new external_function_parameters(
1923             array(
1924                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1925                 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1926                                                                'to all members ' .
1927                                                                'of the group (for group assignments).'),
1928                 'grades' => new external_multiple_structure(
1929                     new external_single_structure(
1930                         array (
1931                             'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1932                             'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '.
1933                                                                        'Ignored if advanced grading used'),
1934                             'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1935                             'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'),
1936                             'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1937                             'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data',
1938                                                                           VALUE_DEFAULT, array()),
1939                             'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1940                                                                                    VALUE_DEFAULT, array())
1941                         )
1942                     )
1943                 )
1944             )
1945         );
1946     }
1948     /**
1949      * Save multiple student grades for a single assignment.
1950      *
1951      * @param int $assignmentid The id of the assignment
1952      * @param boolean $applytoall If set to true and this is a team assignment,
1953      * apply the grade to all members of the group
1954      * @param array $grades grade data for one or more students that includes
1955      *                  userid - The id of the student being graded
1956      *                  grade - The grade (ignored if the assignment uses advanced grading)
1957      *                  attemptnumber - The attempt number
1958      *                  addattempt - Allow another attempt
1959      *                  workflowstate - New workflow state
1960      *                  plugindata - Custom data used by plugins
1961      *                  advancedgradingdata - Optional Advanced grading data
1962      * @throws invalid_parameter_exception if multiple grades are supplied for
1963      * a team assignment that has $applytoall set to true
1964      * @return null
1965      * @since Moodle 2.7
1966      */
1967     public static function save_grades($assignmentid, $applytoall = false, $grades) {
1968         global $CFG, $USER;
1969         require_once("$CFG->dirroot/mod/assign/locallib.php");
1971         $params = self::validate_parameters(self::save_grades_parameters(),
1972                                             array('assignmentid' => $assignmentid,
1973                                                   'applytoall' => $applytoall,
1974                                                   'grades' => $grades));
1976         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1977         $context = context_module::instance($cm->id);
1978         self::validate_context($context);
1979         $assignment = new assign($context, $cm, null);
1981         if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
1982             // Check that only 1 user per submission group is provided.
1983             $groupids = array();
1984             foreach ($params['grades'] as $gradeinfo) {
1985                 $group = $assignment->get_submission_group($gradeinfo['userid']);
1986                 if (in_array($group->id, $groupids)) {
1987                     throw new invalid_parameter_exception('Multiple grades for the same team have been supplied '
1988                                                           .' this is not permitted when the applytoall flag is set');
1989                 } else {
1990                     $groupids[] = $group->id;
1991                 }
1992             }
1993         }
1995         foreach ($params['grades'] as $gradeinfo) {
1996             $gradedata = (object)$gradeinfo['plugindata'];
1997             $gradedata->addattempt = $gradeinfo['addattempt'];
1998             $gradedata->attemptnumber = $gradeinfo['attemptnumber'];
1999             $gradedata->workflowstate = $gradeinfo['workflowstate'];
2000             $gradedata->applytoall = $params['applytoall'];
2001             $gradedata->grade = $gradeinfo['grade'];
2003             if (!empty($gradeinfo['advancedgradingdata'])) {
2004                 $advancedgrading = array();
2005                 $criteria = reset($gradeinfo['advancedgradingdata']);
2006                 foreach ($criteria as $key => $criterion) {
2007                     $details = array();
2008                     foreach ($criterion as $value) {
2009                         foreach ($value['fillings'] as $filling) {
2010                             $details[$value['criterionid']] = $filling;
2011                         }
2012                     }
2013                     $advancedgrading[$key] = $details;
2014                 }
2015                 $gradedata->advancedgrading = $advancedgrading;
2016             }
2017             $assignment->save_grade($gradeinfo['userid'], $gradedata);
2018         }
2020         return null;
2021     }
2023     /**
2024      * Describes the return value for save_grades
2025      *
2026      * @return external_single_structure
2027      * @since Moodle 2.7
2028      */
2029     public static function save_grades_returns() {
2030         return null;
2031     }
2033     /**
2034      * Describes the parameters for copy_previous_attempt
2035      * @return external_external_function_parameters
2036      * @since  Moodle 2.6
2037      */
2038     public static function copy_previous_attempt_parameters() {
2039         return new external_function_parameters(
2040             array(
2041                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
2042             )
2043         );
2044     }
2046     /**
2047      * Copy a students previous attempt to a new attempt.
2048      *
2049      * @param int $assignmentid
2050      * @return array of warnings to indicate any errors.
2051      * @since Moodle 2.6
2052      */
2053     public static function copy_previous_attempt($assignmentid) {
2054         global $CFG, $USER;
2055         require_once("$CFG->dirroot/mod/assign/locallib.php");
2057         $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
2058                                             array('assignmentid' => $assignmentid));
2060         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
2061         $context = context_module::instance($cm->id);
2062         self::validate_context($context);
2064         $assignment = new assign($context, $cm, null);
2066         $notices = array();
2068         $assignment->copy_previous_attempt($submissiondata, $notices);
2070         $warnings = array();
2071         foreach ($notices as $notice) {
2072             $warnings[] = self::generate_warning($assignmentid,
2073                                                  'couldnotcopyprevioussubmission',
2074                                                  $notice);
2075         }
2077         return $warnings;
2078     }
2080     /**
2081      * Describes the return value for save_submission
2082      *
2083      * @return external_single_structure
2084      * @since Moodle 2.6
2085      */
2086     public static function copy_previous_attempt_returns() {
2087         return new external_warnings();
2088     }
2090     /**
2091      * Returns description of method parameters
2092      *
2093      * @return external_function_parameters
2094      * @since Moodle 3.0
2095      */
2096     public static function view_grading_table_parameters() {
2097         return new external_function_parameters(
2098             array(
2099                 'assignid' => new external_value(PARAM_INT, 'assign instance id')
2100             )
2101         );
2102     }
2104     /**
2105      * Trigger the grading_table_viewed event.
2106      *
2107      * @param int $assignid the assign instance id
2108      * @return array of warnings and status result
2109      * @since Moodle 3.0
2110      * @throws moodle_exception
2111      */
2112     public static function view_grading_table($assignid) {
2113         global $DB, $CFG;
2114         require_once($CFG->dirroot . "/mod/assign/locallib.php");
2116         $params = self::validate_parameters(self::view_grading_table_parameters(),
2117                                             array(
2118                                                 'assignid' => $assignid
2119                                             ));
2120         $warnings = array();
2122         // Request and permission validation.
2123         $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
2124         list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
2126         $context = context_module::instance($cm->id);
2127         self::validate_context($context);
2129         require_capability('mod/assign:view', $context);
2131         $assign = new assign($context, null, null);
2132         $assign->require_view_grades();
2133         \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger();
2135         $result = array();
2136         $result['status'] = true;
2137         $result['warnings'] = $warnings;
2138         return $result;
2139     }
2141     /**
2142      * Returns description of method result value
2143      *
2144      * @return external_description
2145      * @since Moodle 3.0
2146      */
2147     public static function view_grading_table_returns() {
2148         return new external_single_structure(
2149             array(
2150                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2151                 'warnings' => new external_warnings()
2152             )
2153         );
2154     }
2155     /**
2156      * Describes the parameters for view_submission_status.
2157      *
2158      * @return external_external_function_parameters
2159      * @since Moodle 3.1
2160      */
2161     public static function view_submission_status_parameters() {
2162         return new external_function_parameters (
2163             array(
2164                 'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2165             )
2166         );
2167     }
2169     /**
2170      * Trigger the submission status viewed event.
2171      *
2172      * @param int $assignid assign instance id
2173      * @return array of warnings and status result
2174      * @since Moodle 3.1
2175      */
2176     public static function view_submission_status($assignid) {
2177         global $DB, $CFG;
2178         require_once("$CFG->dirroot/mod/assign/locallib.php");
2180         $warnings = array();
2181         $params = array(
2182             'assignid' => $assignid,
2183         );
2184         $params = self::validate_parameters(self::view_submission_status_parameters(), $params);
2186         // Request and permission validation.
2187         $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
2188         list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
2190         $context = context_module::instance($cm->id);
2191         // Please, note that is not required to check mod/assign:view because is done by validate_context->require_login.
2192         self::validate_context($context);
2194         $assign = new assign($context, $cm, $course);
2195         \mod_assign\event\submission_status_viewed::create_from_assign($assign)->trigger();
2197         $result = array();
2198         $result['status'] = true;
2199         $result['warnings'] = $warnings;
2200         return $result;
2201     }
2203     /**
2204      * Describes the view_submission_status return value.
2205      *
2206      * @return external_single_structure
2207      * @since Moodle 3.1
2208      */
2209     public static function view_submission_status_returns() {
2210         return new external_single_structure(
2211             array(
2212                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2213                 'warnings' => new external_warnings(),
2214             )
2215         );
2216     }