MDL-49796 assign: New external function view_grading_table
[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'),
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             )
258         );
259     }
261     /**
262      * Returns an array of courses the user is enrolled in, and for each course all of the assignments that the user can
263      * view within that course.
264      *
265      * @param array $courseids An optional array of course ids. If provided only assignments within the given course
266      * will be returned. If the user is not enrolled in a given course a warning will be generated and returned.
267      * @param array $capabilities An array of additional capability checks you wish to be made on the course context.
268      * @return An array of courses and warnings.
269      * @since  Moodle 2.4
270      */
271     public static function get_assignments($courseids = array(), $capabilities = array()) {
272         global $USER, $DB, $CFG;
273         require_once("$CFG->dirroot/mod/assign/locallib.php");
275         $params = self::validate_parameters(
276             self::get_assignments_parameters(),
277             array('courseids' => $courseids, 'capabilities' => $capabilities)
278         );
280         $warnings = array();
281         $fields = 'sortorder,shortname,fullname,timemodified';
282         $courses = enrol_get_users_courses($USER->id, true, $fields);
283         // Used to test for ids that have been requested but can't be returned.
284         if (count($params['courseids']) > 0) {
285             foreach ($params['courseids'] as $courseid) {
286                 if (!in_array($courseid, array_keys($courses))) {
287                     unset($courses[$courseid]);
288                     $warnings[] = array(
289                         'item' => 'course',
290                         'itemid' => $courseid,
291                         'warningcode' => '2',
292                         'message' => 'User is not enrolled or does not have requested capability'
293                     );
294                 }
295             }
296         }
297         foreach ($courses as $id => $course) {
298             if (count($params['courseids']) > 0 && !in_array($id, $params['courseids'])) {
299                 unset($courses[$id]);
300             }
301             $context = context_course::instance($id);
302             try {
303                 self::validate_context($context);
304             } catch (Exception $e) {
305                 unset($courses[$id]);
306                 $warnings[] = array(
307                     'item' => 'course',
308                     'itemid' => $id,
309                     'warningcode' => '1',
310                     'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
311                 );
312                 continue;
313             }
314             if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) {
315                 unset($courses[$id]);
316             }
317         }
318         $extrafields='m.id as assignmentid, ' .
319                      'm.course, ' .
320                      'm.nosubmissions, ' .
321                      'm.submissiondrafts, ' .
322                      'm.sendnotifications, '.
323                      'm.sendlatenotifications, ' .
324                      'm.sendstudentnotifications, ' .
325                      'm.duedate, ' .
326                      'm.allowsubmissionsfromdate, '.
327                      'm.grade, ' .
328                      'm.timemodified, '.
329                      'm.completionsubmit, ' .
330                      'm.cutoffdate, ' .
331                      'm.teamsubmission, ' .
332                      'm.requireallteammemberssubmit, '.
333                      'm.teamsubmissiongroupingid, ' .
334                      'm.blindmarking, ' .
335                      'm.revealidentities, ' .
336                      'm.attemptreopenmethod, '.
337                      'm.maxattempts, ' .
338                      'm.markingworkflow, ' .
339                      'm.markingallocation, ' .
340                      'm.requiresubmissionstatement, '.
341                      'm.intro, '.
342                      'm.introformat';
343         $coursearray = array();
344         foreach ($courses as $id => $course) {
345             $assignmentarray = array();
346             // Get a list of assignments for the course.
347             if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
348                 foreach ($modules as $module) {
349                     $context = context_module::instance($module->id);
350                     try {
351                         self::validate_context($context);
352                         require_capability('mod/assign:view', $context);
353                     } catch (Exception $e) {
354                         $warnings[] = array(
355                             'item' => 'module',
356                             'itemid' => $module->id,
357                             'warningcode' => '1',
358                             'message' => 'No access rights in module context'
359                         );
360                         continue;
361                     }
362                     $configrecords = $DB->get_recordset('assign_plugin_config', array('assignment' => $module->assignmentid));
363                     $configarray = array();
364                     foreach ($configrecords as $configrecord) {
365                         $configarray[] = array(
366                             'id' => $configrecord->id,
367                             'assignment' => $configrecord->assignment,
368                             'plugin' => $configrecord->plugin,
369                             'subtype' => $configrecord->subtype,
370                             'name' => $configrecord->name,
371                             'value' => $configrecord->value
372                         );
373                     }
374                     $configrecords->close();
376                     $assignment = array(
377                         'id' => $module->assignmentid,
378                         'cmid' => $module->id,
379                         'course' => $module->course,
380                         'name' => $module->name,
381                         'nosubmissions' => $module->nosubmissions,
382                         'submissiondrafts' => $module->submissiondrafts,
383                         'sendnotifications' => $module->sendnotifications,
384                         'sendlatenotifications' => $module->sendlatenotifications,
385                         'sendstudentnotifications' => $module->sendstudentnotifications,
386                         'duedate' => $module->duedate,
387                         'allowsubmissionsfromdate' => $module->allowsubmissionsfromdate,
388                         'grade' => $module->grade,
389                         'timemodified' => $module->timemodified,
390                         'completionsubmit' => $module->completionsubmit,
391                         'cutoffdate' => $module->cutoffdate,
392                         'teamsubmission' => $module->teamsubmission,
393                         'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
394                         'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
395                         'blindmarking' => $module->blindmarking,
396                         'revealidentities' => $module->revealidentities,
397                         'attemptreopenmethod' => $module->attemptreopenmethod,
398                         'maxattempts' => $module->maxattempts,
399                         'markingworkflow' => $module->markingworkflow,
400                         'markingallocation' => $module->markingallocation,
401                         'requiresubmissionstatement' => $module->requiresubmissionstatement,
402                         'configs' => $configarray
403                     );
405                     // Return or not intro and file attachments depending on the plugin settings.
406                     $assign = new assign($context, null, null);
408                     if ($assign->show_intro()) {
410                         list($assignment['intro'], $assignment['introformat']) = external_format_text($module->intro,
411                             $module->introformat, $context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0);
413                         $fs = get_file_storage();
414                         if ($files = $fs->get_area_files($context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA,
415                                                             0, 'timemodified', false)) {
417                             $assignment['introattachments'] = array();
418                             foreach ($files as $file) {
419                                 $filename = $file->get_filename();
421                                 $assignment['introattachments'][] = array(
422                                     'filename' => $filename,
423                                     'mimetype' => $file->get_mimetype(),
424                                     'fileurl'  => moodle_url::make_webservice_pluginfile_url(
425                                         $context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0, '/', $filename)->out(false)
426                                 );
427                             }
428                         }
429                     }
431                     $assignmentarray[] = $assignment;
432                 }
433             }
434             $coursearray[]= array(
435                 'id' => $courses[$id]->id,
436                 'fullname' => $courses[$id]->fullname,
437                 'shortname' => $courses[$id]->shortname,
438                 'timemodified' => $courses[$id]->timemodified,
439                 'assignments' => $assignmentarray
440             );
441         }
443         $result = array(
444             'courses' => $coursearray,
445             'warnings' => $warnings
446         );
447         return $result;
448     }
450     /**
451      * Creates an assignment external_single_structure
452      *
453      * @return external_single_structure
454      * @since Moodle 2.4
455      */
456     private static function get_assignments_assignment_structure() {
457         return new external_single_structure(
458             array(
459                 'id' => new external_value(PARAM_INT, 'assignment id'),
460                 'cmid' => new external_value(PARAM_INT, 'course module id'),
461                 'course' => new external_value(PARAM_INT, 'course id'),
462                 'name' => new external_value(PARAM_TEXT, 'assignment name'),
463                 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
464                 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
465                 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
466                 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
467                 'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'),
468                 'duedate' => new external_value(PARAM_INT, 'assignment due date'),
469                 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
470                 'grade' => new external_value(PARAM_INT, 'grade type'),
471                 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
472                 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
473                 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
474                 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
475                 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
476                 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
477                 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
478                 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
479                 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
480                 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
481                 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
482                 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
483                 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
484                 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings'),
485                 'intro' => new external_value(PARAM_RAW,
486                     'assignment intro, not allways returned because it deppends on the activity configuration', VALUE_OPTIONAL),
487                 'introformat' => new external_format_value('intro', VALUE_OPTIONAL),
488                 'introattachments' => new external_multiple_structure(
489                     new external_single_structure(
490                         array (
491                             'filename' => new external_value(PARAM_FILE, 'file name'),
492                             'mimetype' => new external_value(PARAM_RAW, 'mime type'),
493                             'fileurl'  => new external_value(PARAM_URL, 'file download url')
494                         )
495                     ), 'intro attachments files', VALUE_OPTIONAL
496                 )
497             ), 'assignment information object');
498     }
500     /**
501      * Creates an assign_plugin_config external_single_structure
502      *
503      * @return external_single_structure
504      * @since Moodle 2.4
505      */
506     private static function get_assignments_config_structure() {
507         return new external_single_structure(
508             array(
509                 'id' => new external_value(PARAM_INT, 'assign_plugin_config id'),
510                 'assignment' => new external_value(PARAM_INT, 'assignment id'),
511                 'plugin' => new external_value(PARAM_TEXT, 'plugin'),
512                 'subtype' => new external_value(PARAM_TEXT, 'subtype'),
513                 'name' => new external_value(PARAM_TEXT, 'name'),
514                 'value' => new external_value(PARAM_TEXT, 'value')
515             ), 'assignment configuration object'
516         );
517     }
519     /**
520      * Creates a course external_single_structure
521      *
522      * @return external_single_structure
523      * @since Moodle 2.4
524      */
525     private static function get_assignments_course_structure() {
526         return new external_single_structure(
527             array(
528                 'id' => new external_value(PARAM_INT, 'course id'),
529                 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
530                 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
531                 'timemodified' => new external_value(PARAM_INT, 'last time modified'),
532                 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
533               ), 'course information object'
534         );
535     }
537     /**
538      * Describes the return value for get_assignments
539      *
540      * @return external_single_structure
541      * @since Moodle 2.4
542      */
543     public static function get_assignments_returns() {
544         return new external_single_structure(
545             array(
546                 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
547                 'warnings'  => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
548                     'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
549                     'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
550             )
551         );
552     }
554     /**
555      * Describes the parameters for get_submissions
556      *
557      * @return external_external_function_parameters
558      * @since Moodle 2.5
559      */
560     public static function get_submissions_parameters() {
561         return new external_function_parameters(
562             array(
563                 'assignmentids' => new external_multiple_structure(
564                     new external_value(PARAM_INT, 'assignment id'),
565                     '1 or more assignment ids',
566                     VALUE_REQUIRED),
567                 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
568                 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
569                 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
570             )
571         );
572     }
574     /**
575      * Returns submissions for the requested assignment ids
576      *
577      * @param int[] $assignmentids
578      * @param string $status only return submissions with this status
579      * @param int $since only return submissions with timemodified >= since
580      * @param int $before only return submissions with timemodified <= before
581      * @return array of submissions for each requested assignment
582      * @since Moodle 2.5
583      */
584     public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
585         global $DB, $CFG;
586         require_once("$CFG->dirroot/mod/assign/locallib.php");
587         $params = self::validate_parameters(self::get_submissions_parameters(),
588                         array('assignmentids' => $assignmentids,
589                               'status' => $status,
590                               'since' => $since,
591                               'before' => $before));
593         $warnings = array();
594         $assignments = array();
596         // Check the user is allowed to get the submissions for the assignments requested.
597         $placeholders = array();
598         list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
599         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
600                "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
601         $placeholders['modname'] = 'assign';
602         $cms = $DB->get_records_sql($sql, $placeholders);
603         $assigns = array();
604         foreach ($cms as $cm) {
605             try {
606                 $context = context_module::instance($cm->id);
607                 self::validate_context($context);
608                 require_capability('mod/assign:grade', $context);
609                 $assign = new assign($context, null, null);
610                 $assigns[] = $assign;
611             } catch (Exception $e) {
612                 $warnings[] = array(
613                     'item' => 'assignment',
614                     'itemid' => $cm->instance,
615                     'warningcode' => '1',
616                     'message' => 'No access rights in module context'
617                 );
618             }
619         }
621         foreach ($assigns as $assign) {
622             $submissions = array();
623             $submissionplugins = $assign->get_submission_plugins();
624             $placeholders = array('assignid1' => $assign->get_instance()->id,
625                                   'assignid2' => $assign->get_instance()->id);
627             $submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
628                                      FROM {assign_submission} mxs
629                                      WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid';
631             $sql = "SELECT mas.id, mas.assignment,mas.userid,".
632                    "mas.timecreated,mas.timemodified,mas.status,mas.groupid,mas.attemptnumber ".
633                    "FROM {assign_submission} mas ".
634                    "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
635                    "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
637             if (!empty($params['status'])) {
638                 $placeholders['status'] = $params['status'];
639                 $sql = $sql." AND mas.status = :status";
640             }
641             if (!empty($params['before'])) {
642                 $placeholders['since'] = $params['since'];
643                 $placeholders['before'] = $params['before'];
644                 $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
645             } else {
646                 $placeholders['since'] = $params['since'];
647                 $sql = $sql." AND mas.timemodified >= :since";
648             }
650             $submissionrecords = $DB->get_records_sql($sql, $placeholders);
652             if (!empty($submissionrecords)) {
653                 $fs = get_file_storage();
654                 foreach ($submissionrecords as $submissionrecord) {
655                     $submission = array(
656                         'id' => $submissionrecord->id,
657                         'userid' => $submissionrecord->userid,
658                         'timecreated' => $submissionrecord->timecreated,
659                         'timemodified' => $submissionrecord->timemodified,
660                         'status' => $submissionrecord->status,
661                         'attemptnumber' => $submissionrecord->attemptnumber,
662                         'groupid' => $submissionrecord->groupid
663                     );
664                     foreach ($submissionplugins as $submissionplugin) {
665                         $plugin = array(
666                             'name' => $submissionplugin->get_name(),
667                             'type' => $submissionplugin->get_type()
668                         );
669                         // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
670                         $component = $submissionplugin->get_subtype().'_'.$submissionplugin->get_type();
672                         $fileareas = $submissionplugin->get_file_areas();
673                         foreach ($fileareas as $filearea => $name) {
674                             $fileareainfo = array('area' => $filearea);
675                             $files = $fs->get_area_files(
676                                 $assign->get_context()->id,
677                                 $component,
678                                 $filearea,
679                                 $submissionrecord->id,
680                                 "timemodified",
681                                 false
682                             );
683                             foreach ($files as $file) {
684                                 $filepath = $file->get_filepath().$file->get_filename();
685                                 $fileurl = file_encode_url($CFG->wwwroot . '/webservice/pluginfile.php', '/' . $assign->get_context()->id .
686                                     '/' . $component. '/'. $filearea . '/' . $submissionrecord->id . $filepath);
687                                 $fileinfo = array(
688                                     'filepath' => $filepath,
689                                     'fileurl' => $fileurl
690                                     );
691                                 $fileareainfo['files'][] = $fileinfo;
692                             }
693                             $plugin['fileareas'][] = $fileareainfo;
694                         }
696                         $editorfields = $submissionplugin->get_editor_fields();
697                         foreach ($editorfields as $name => $description) {
698                             $editorfieldinfo = array(
699                                 'name' => $name,
700                                 'description' => $description,
701                                 'text' => $submissionplugin->get_editor_text($name, $submissionrecord->id),
702                                 'format' => $submissionplugin->get_editor_format($name, $submissionrecord->id)
703                             );
704                             $plugin['editorfields'][] = $editorfieldinfo;
705                         }
707                         $submission['plugins'][] = $plugin;
708                     }
709                     $submissions[] = $submission;
710                 }
711             } else {
712                 $warnings[] = array(
713                     'item' => 'module',
714                     'itemid' => $assign->get_instance()->id,
715                     'warningcode' => '3',
716                     'message' => 'No submissions found'
717                 );
718             }
720             $assignments[] = array(
721                 'assignmentid' => $assign->get_instance()->id,
722                 'submissions' => $submissions
723             );
725         }
727         $result = array(
728             'assignments' => $assignments,
729             'warnings' => $warnings
730         );
731         return $result;
732     }
734     /**
735      * Creates an assign_submissions external_single_structure
736      *
737      * @return external_single_structure
738      * @since Moodle 2.5
739      */
740     private static function get_submissions_structure() {
741         return new external_single_structure(
742             array (
743                 'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
744                 'submissions' => new external_multiple_structure(
745                     new external_single_structure(
746                         array(
747                             'id' => new external_value(PARAM_INT, 'submission id'),
748                             'userid' => new external_value(PARAM_INT, 'student id'),
749                             'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
750                             'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
751                             'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
752                             'status' => new external_value(PARAM_TEXT, 'submission status'),
753                             'groupid' => new external_value(PARAM_INT, 'group id'),
754                             'plugins' => new external_multiple_structure(
755                                 new external_single_structure(
756                                     array(
757                                         'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
758                                         'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
759                                         'fileareas' => new external_multiple_structure(
760                                             new external_single_structure(
761                                                 array (
762                                                     'area' => new external_value (PARAM_TEXT, 'file area'),
763                                                     'files' => new external_multiple_structure(
764                                                         new external_single_structure(
765                                                             array (
766                                                                 'filepath' => new external_value (PARAM_TEXT, 'file path'),
767                                                                 'fileurl' => new external_value (PARAM_URL, 'file download url',
768                                                                     VALUE_OPTIONAL)
769                                                             )
770                                                         ), 'files', VALUE_OPTIONAL
771                                                     )
772                                                 )
773                                             ), 'fileareas', VALUE_OPTIONAL
774                                         ),
775                                         'editorfields' => new external_multiple_structure(
776                                             new external_single_structure(
777                                                 array(
778                                                     'name' => new external_value(PARAM_TEXT, 'field name'),
779                                                     'description' => new external_value(PARAM_TEXT, 'field description'),
780                                                     'text' => new external_value (PARAM_RAW, 'field value'),
781                                                     'format' => new external_format_value ('text')
782                                                 )
783                                             )
784                                             , 'editorfields', VALUE_OPTIONAL
785                                         )
786                                     )
787                                 )
788                                 , 'plugins', VALUE_OPTIONAL
789                             )
790                         )
791                     )
792                 )
793             )
794         );
795     }
797     /**
798      * Describes the get_submissions return value
799      *
800      * @return external_single_structure
801      * @since Moodle 2.5
802      */
803     public static function get_submissions_returns() {
804         return new external_single_structure(
805             array(
806                 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
807                 'warnings' => new external_warnings()
808             )
809         );
810     }
812     /**
813      * Describes the parameters for set_user_flags
814      * @return external_function_parameters
815      * @since  Moodle 2.6
816      */
817     public static function set_user_flags_parameters() {
818         return new external_function_parameters(
819             array(
820                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
821                 'userflags' => new external_multiple_structure(
822                     new external_single_structure(
823                         array(
824                             'userid'           => new external_value(PARAM_INT, 'student id'),
825                             'locked'           => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL),
826                             'mailed'           => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL),
827                             'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL),
828                             'workflowstate'    => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
829                             'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL)
830                         )
831                     )
832                 )
833             )
834         );
835     }
837     /**
838      * Create or update user_flags records
839      *
840      * @param int $assignmentid the assignment for which the userflags are created or updated
841      * @param array $userflags  An array of userflags to create or update
842      * @return array containing success or failure information for each record
843      * @since Moodle 2.6
844      */
845     public static function set_user_flags($assignmentid, $userflags = array()) {
846         global $CFG, $DB;
847         require_once($CFG->dirroot . "/mod/assign/locallib.php");
849         $params = self::validate_parameters(self::set_user_flags_parameters(),
850                                             array('assignmentid' => $assignmentid,
851                                                   'userflags' => $userflags));
853         // Load assignment if it exists and if the user has the capability.
854         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
855         $context = context_module::instance($cm->id);
856         self::validate_context($context);
857         require_capability('mod/assign:grade', $context);
858         $assign = new assign($context, null, null);
860         $results = array();
861         foreach ($params['userflags'] as $userflag) {
862             $success = true;
863             $result = array();
865             $record = $assign->get_user_flags($userflag['userid'], false);
866             if ($record) {
867                 if (isset($userflag['locked'])) {
868                     $record->locked = $userflag['locked'];
869                 }
870                 if (isset($userflag['mailed'])) {
871                     $record->mailed = $userflag['mailed'];
872                 }
873                 if (isset($userflag['extensionduedate'])) {
874                     $record->extensionduedate = $userflag['extensionduedate'];
875                 }
876                 if (isset($userflag['workflowstate'])) {
877                     $record->workflowstate = $userflag['workflowstate'];
878                 }
879                 if (isset($userflag['allocatedmarker'])) {
880                     $record->allocatedmarker = $userflag['allocatedmarker'];
881                 }
882                 if ($assign->update_user_flags($record)) {
883                     $result['id'] = $record->id;
884                     $result['userid'] = $userflag['userid'];
885                 } else {
886                     $result['id'] = $record->id;
887                     $result['userid'] = $userflag['userid'];
888                     $result['errormessage'] = 'Record created but values could not be set';
889                 }
890             } else {
891                 $record = $assign->get_user_flags($userflag['userid'], true);
892                 $setfields = isset($userflag['locked'])
893                              || isset($userflag['mailed'])
894                              || isset($userflag['extensionduedate'])
895                              || isset($userflag['workflowstate'])
896                              || isset($userflag['allocatedmarker']);
897                 if ($record) {
898                     if ($setfields) {
899                         if (isset($userflag['locked'])) {
900                             $record->locked = $userflag['locked'];
901                         }
902                         if (isset($userflag['mailed'])) {
903                             $record->mailed = $userflag['mailed'];
904                         }
905                         if (isset($userflag['extensionduedate'])) {
906                             $record->extensionduedate = $userflag['extensionduedate'];
907                         }
908                         if (isset($userflag['workflowstate'])) {
909                             $record->workflowstate = $userflag['workflowstate'];
910                         }
911                         if (isset($userflag['allocatedmarker'])) {
912                             $record->allocatedmarker = $userflag['allocatedmarker'];
913                         }
914                         if ($assign->update_user_flags($record)) {
915                             $result['id'] = $record->id;
916                             $result['userid'] = $userflag['userid'];
917                         } else {
918                             $result['id'] = $record->id;
919                             $result['userid'] = $userflag['userid'];
920                             $result['errormessage'] = 'Record created but values could not be set';
921                         }
922                     } else {
923                         $result['id'] = $record->id;
924                         $result['userid'] = $userflag['userid'];
925                     }
926                 } else {
927                     $result['id'] = -1;
928                     $result['userid'] = $userflag['userid'];
929                     $result['errormessage'] = 'Record could not be created';
930                 }
931             }
933             $results[] = $result;
934         }
935         return $results;
936     }
938     /**
939      * Describes the set_user_flags return value
940      * @return external_multiple_structure
941      * @since  Moodle 2.6
942      */
943     public static function set_user_flags_returns() {
944         return new external_multiple_structure(
945             new external_single_structure(
946                 array(
947                     'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'),
948                     'userid' => new external_value(PARAM_INT, 'userid of record'),
949                     'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL)
950                 )
951             )
952         );
953     }
955     /**
956      * Describes the parameters for get_user_flags
957      * @return external_function_parameters
958      * @since  Moodle 2.6
959      */
960     public static function get_user_flags_parameters() {
961         return new external_function_parameters(
962             array(
963                 'assignmentids' => new external_multiple_structure(
964                     new external_value(PARAM_INT, 'assignment id'),
965                     '1 or more assignment ids',
966                     VALUE_REQUIRED)
967             )
968         );
969     }
971     /**
972      * Returns user flag information from assign_user_flags for the requested assignment ids
973      * @param int[] $assignmentids
974      * @return array of user flag records for each requested assignment
975      * @since  Moodle 2.6
976      */
977     public static function get_user_flags($assignmentids) {
978         global $DB;
979         $params = self::validate_parameters(self::get_user_flags_parameters(),
980                         array('assignmentids' => $assignmentids));
982         $assignments = array();
983         $warnings = array();
984         $requestedassignmentids = $params['assignmentids'];
986         // Check the user is allowed to get the user flags for the assignments requested.
987         $placeholders = array();
988         list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
989         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
990                "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
991         $placeholders['modname'] = 'assign';
992         $cms = $DB->get_records_sql($sql, $placeholders);
993         foreach ($cms as $cm) {
994             try {
995                 $context = context_module::instance($cm->id);
996                 self::validate_context($context);
997                 require_capability('mod/assign:grade', $context);
998             } catch (Exception $e) {
999                 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1000                 $warning = array();
1001                 $warning['item'] = 'assignment';
1002                 $warning['itemid'] = $cm->instance;
1003                 $warning['warningcode'] = '1';
1004                 $warning['message'] = 'No access rights in module context';
1005                 $warnings[] = $warning;
1006             }
1007         }
1009         // Create the query and populate an array of assign_user_flags records from the recordset results.
1010         if (count ($requestedassignmentids) > 0) {
1011             $placeholders = array();
1012             list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1014             $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
1015                    "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
1016                    "FROM {assign_user_flags} auf ".
1017                    "WHERE auf.assignment ".$inorequalsql.
1018                    " ORDER BY auf.assignment, auf.id";
1020             $rs = $DB->get_recordset_sql($sql, $placeholders);
1021             $currentassignmentid = null;
1022             $assignment = null;
1023             foreach ($rs as $rd) {
1024                 $userflag = array();
1025                 $userflag['id'] = $rd->id;
1026                 $userflag['userid'] = $rd->userid;
1027                 $userflag['locked'] = $rd->locked;
1028                 $userflag['mailed'] = $rd->mailed;
1029                 $userflag['extensionduedate'] = $rd->extensionduedate;
1030                 $userflag['workflowstate'] = $rd->workflowstate;
1031                 $userflag['allocatedmarker'] = $rd->allocatedmarker;
1033                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1034                     if (!is_null($assignment)) {
1035                         $assignments[] = $assignment;
1036                     }
1037                     $assignment = array();
1038                     $assignment['assignmentid'] = $rd->assignment;
1039                     $assignment['userflags'] = array();
1040                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1041                 }
1042                 $assignment['userflags'][] = $userflag;
1044                 $currentassignmentid = $rd->assignment;
1045             }
1046             if (!is_null($assignment)) {
1047                 $assignments[] = $assignment;
1048             }
1049             $rs->close();
1051         }
1053         foreach ($requestedassignmentids as $assignmentid) {
1054             $warning = array();
1055             $warning['item'] = 'assignment';
1056             $warning['itemid'] = $assignmentid;
1057             $warning['warningcode'] = '3';
1058             $warning['message'] = 'No user flags found';
1059             $warnings[] = $warning;
1060         }
1062         $result = array();
1063         $result['assignments'] = $assignments;
1064         $result['warnings'] = $warnings;
1065         return $result;
1066     }
1068     /**
1069      * Creates an assign_user_flags external_single_structure
1070      * @return external_single_structure
1071      * @since  Moodle 2.6
1072      */
1073     private static function assign_user_flags() {
1074         return new external_single_structure(
1075             array (
1076                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1077                 'userflags'   => new external_multiple_structure(new external_single_structure(
1078                         array(
1079                             'id'               => new external_value(PARAM_INT, 'user flag id'),
1080                             'userid'           => new external_value(PARAM_INT, 'student id'),
1081                             'locked'           => new external_value(PARAM_INT, 'locked'),
1082                             'mailed'           => new external_value(PARAM_INT, 'mailed'),
1083                             'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
1084                             'workflowstate'    => new external_value(PARAM_TEXT, 'marking workflow state', VALUE_OPTIONAL),
1085                             'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker')
1086                         )
1087                     )
1088                 )
1089             )
1090         );
1091     }
1093     /**
1094      * Describes the get_user_flags return value
1095      * @return external_single_structure
1096      * @since  Moodle 2.6
1097      */
1098     public static function get_user_flags_returns() {
1099         return new external_single_structure(
1100             array(
1101                 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
1102                 'warnings'      => new external_warnings('item is always \'assignment\'',
1103                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1104                     'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
1105             )
1106         );
1107     }
1109     /**
1110      * Describes the parameters for get_user_mappings
1111      * @return external_function_parameters
1112      * @since  Moodle 2.6
1113      */
1114     public static function get_user_mappings_parameters() {
1115         return new external_function_parameters(
1116             array(
1117                 'assignmentids' => new external_multiple_structure(
1118                     new external_value(PARAM_INT, 'assignment id'),
1119                     '1 or more assignment ids',
1120                     VALUE_REQUIRED)
1121             )
1122         );
1123     }
1125     /**
1126      * Returns user mapping information from assign_user_mapping for the requested assignment ids
1127      * @param int[] $assignmentids
1128      * @return array of user mapping records for each requested assignment
1129      * @since  Moodle 2.6
1130      */
1131     public static function get_user_mappings($assignmentids) {
1132         global $DB;
1133         $params = self::validate_parameters(self::get_user_mappings_parameters(),
1134                         array('assignmentids' => $assignmentids));
1136         $assignments = array();
1137         $warnings = array();
1138         $requestedassignmentids = $params['assignmentids'];
1140         // Check the user is allowed to get the mappings for the assignments requested.
1141         $placeholders = array();
1142         list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1143         $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1144                "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1145         $placeholders['modname'] = 'assign';
1146         $cms = $DB->get_records_sql($sql, $placeholders);
1147         foreach ($cms as $cm) {
1148             try {
1149                 $context = context_module::instance($cm->id);
1150                 self::validate_context($context);
1151                 require_capability('mod/assign:revealidentities', $context);
1152             } catch (Exception $e) {
1153                 $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1154                 $warning = array();
1155                 $warning['item'] = 'assignment';
1156                 $warning['itemid'] = $cm->instance;
1157                 $warning['warningcode'] = '1';
1158                 $warning['message'] = 'No access rights in module context';
1159                 $warnings[] = $warning;
1160             }
1161         }
1163         // Create the query and populate an array of assign_user_mapping records from the recordset results.
1164         if (count ($requestedassignmentids) > 0) {
1165             $placeholders = array();
1166             list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1168             $sql = "SELECT aum.id,aum.assignment,aum.userid ".
1169                    "FROM {assign_user_mapping} aum ".
1170                    "WHERE aum.assignment ".$inorequalsql.
1171                    " ORDER BY aum.assignment, aum.id";
1173             $rs = $DB->get_recordset_sql($sql, $placeholders);
1174             $currentassignmentid = null;
1175             $assignment = null;
1176             foreach ($rs as $rd) {
1177                 $mapping = array();
1178                 $mapping['id'] = $rd->id;
1179                 $mapping['userid'] = $rd->userid;
1181                 if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1182                     if (!is_null($assignment)) {
1183                         $assignments[] = $assignment;
1184                     }
1185                     $assignment = array();
1186                     $assignment['assignmentid'] = $rd->assignment;
1187                     $assignment['mappings'] = array();
1188                     $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1189                 }
1190                 $assignment['mappings'][] = $mapping;
1192                 $currentassignmentid = $rd->assignment;
1193             }
1194             if (!is_null($assignment)) {
1195                 $assignments[] = $assignment;
1196             }
1197             $rs->close();
1199         }
1201         foreach ($requestedassignmentids as $assignmentid) {
1202             $warning = array();
1203             $warning['item'] = 'assignment';
1204             $warning['itemid'] = $assignmentid;
1205             $warning['warningcode'] = '3';
1206             $warning['message'] = 'No mappings found';
1207             $warnings[] = $warning;
1208         }
1210         $result = array();
1211         $result['assignments'] = $assignments;
1212         $result['warnings'] = $warnings;
1213         return $result;
1214     }
1216     /**
1217      * Creates an assign_user_mappings external_single_structure
1218      * @return external_single_structure
1219      * @since  Moodle 2.6
1220      */
1221     private static function assign_user_mappings() {
1222         return new external_single_structure(
1223             array (
1224                 'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1225                 'mappings'   => new external_multiple_structure(new external_single_structure(
1226                         array(
1227                             'id'     => new external_value(PARAM_INT, 'user mapping id'),
1228                             'userid' => new external_value(PARAM_INT, 'student id')
1229                         )
1230                     )
1231                 )
1232             )
1233         );
1234     }
1236     /**
1237      * Describes the get_user_mappings return value
1238      * @return external_single_structure
1239      * @since  Moodle 2.6
1240      */
1241     public static function get_user_mappings_returns() {
1242         return new external_single_structure(
1243             array(
1244                 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
1245                 'warnings'      => new external_warnings('item is always \'assignment\'',
1246                     'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1247                     'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
1248             )
1249         );
1250     }
1252     /**
1253      * Describes the parameters for lock_submissions
1254      * @return external_external_function_parameters
1255      * @since  Moodle 2.6
1256      */
1257     public static function lock_submissions_parameters() {
1258         return new external_function_parameters(
1259             array(
1260                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1261                 'userids' => new external_multiple_structure(
1262                     new external_value(PARAM_INT, 'user id'),
1263                     '1 or more user ids',
1264                     VALUE_REQUIRED),
1265             )
1266         );
1267     }
1269     /**
1270      * Locks (prevent updates to) submissions in this assignment.
1271      *
1272      * @param int $assignmentid The id of the assignment
1273      * @param array $userids Array of user ids to lock
1274      * @return array of warnings for each submission that could not be locked.
1275      * @since Moodle 2.6
1276      */
1277     public static function lock_submissions($assignmentid, $userids) {
1278         global $CFG;
1279         require_once("$CFG->dirroot/mod/assign/locallib.php");
1281         $params = self::validate_parameters(self::lock_submissions_parameters(),
1282                         array('assignmentid' => $assignmentid,
1283                               'userids' => $userids));
1285         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1286         $context = context_module::instance($cm->id);
1287         self::validate_context($context);
1289         $assignment = new assign($context, $cm, null);
1291         $warnings = array();
1292         foreach ($params['userids'] as $userid) {
1293             if (!$assignment->lock_submission($userid)) {
1294                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1295                 $warnings[] = self::generate_warning($params['assignmentid'],
1296                                                      'couldnotlock',
1297                                                      $detail);
1298             }
1299         }
1301         return $warnings;
1302     }
1304     /**
1305      * Describes the return value for lock_submissions
1306      *
1307      * @return external_single_structure
1308      * @since Moodle 2.6
1309      */
1310     public static function lock_submissions_returns() {
1311         return new external_warnings();
1312     }
1314     /**
1315      * Describes the parameters for revert_submissions_to_draft
1316      * @return external_external_function_parameters
1317      * @since  Moodle 2.6
1318      */
1319     public static function revert_submissions_to_draft_parameters() {
1320         return new external_function_parameters(
1321             array(
1322                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1323                 'userids' => new external_multiple_structure(
1324                     new external_value(PARAM_INT, 'user id'),
1325                     '1 or more user ids',
1326                     VALUE_REQUIRED),
1327             )
1328         );
1329     }
1331     /**
1332      * Reverts a list of user submissions to draft for a single assignment.
1333      *
1334      * @param int $assignmentid The id of the assignment
1335      * @param array $userids Array of user ids to revert
1336      * @return array of warnings for each submission that could not be reverted.
1337      * @since Moodle 2.6
1338      */
1339     public static function revert_submissions_to_draft($assignmentid, $userids) {
1340         global $CFG;
1341         require_once("$CFG->dirroot/mod/assign/locallib.php");
1343         $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
1344                         array('assignmentid' => $assignmentid,
1345                               'userids' => $userids));
1347         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1348         $context = context_module::instance($cm->id);
1349         self::validate_context($context);
1351         $assignment = new assign($context, $cm, null);
1353         $warnings = array();
1354         foreach ($params['userids'] as $userid) {
1355             if (!$assignment->revert_to_draft($userid)) {
1356                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1357                 $warnings[] = self::generate_warning($params['assignmentid'],
1358                                                      'couldnotrevert',
1359                                                      $detail);
1360             }
1361         }
1363         return $warnings;
1364     }
1366     /**
1367      * Describes the return value for revert_submissions_to_draft
1368      *
1369      * @return external_single_structure
1370      * @since Moodle 2.6
1371      */
1372     public static function revert_submissions_to_draft_returns() {
1373         return new external_warnings();
1374     }
1376     /**
1377      * Describes the parameters for unlock_submissions
1378      * @return external_external_function_parameters
1379      * @since  Moodle 2.6
1380      */
1381     public static function unlock_submissions_parameters() {
1382         return new external_function_parameters(
1383             array(
1384                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1385                 'userids' => new external_multiple_structure(
1386                     new external_value(PARAM_INT, 'user id'),
1387                     '1 or more user ids',
1388                     VALUE_REQUIRED),
1389             )
1390         );
1391     }
1393     /**
1394      * Locks (prevent updates to) submissions in this assignment.
1395      *
1396      * @param int $assignmentid The id of the assignment
1397      * @param array $userids Array of user ids to lock
1398      * @return array of warnings for each submission that could not be locked.
1399      * @since Moodle 2.6
1400      */
1401     public static function unlock_submissions($assignmentid, $userids) {
1402         global $CFG;
1403         require_once("$CFG->dirroot/mod/assign/locallib.php");
1405         $params = self::validate_parameters(self::unlock_submissions_parameters(),
1406                         array('assignmentid' => $assignmentid,
1407                               'userids' => $userids));
1409         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1410         $context = context_module::instance($cm->id);
1411         self::validate_context($context);
1413         $assignment = new assign($context, $cm, null);
1415         $warnings = array();
1416         foreach ($params['userids'] as $userid) {
1417             if (!$assignment->unlock_submission($userid)) {
1418                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1419                 $warnings[] = self::generate_warning($params['assignmentid'],
1420                                                      'couldnotunlock',
1421                                                      $detail);
1422             }
1423         }
1425         return $warnings;
1426     }
1428     /**
1429      * Describes the return value for unlock_submissions
1430      *
1431      * @return external_single_structure
1432      * @since Moodle 2.6
1433      */
1434     public static function unlock_submissions_returns() {
1435         return new external_warnings();
1436     }
1438     /**
1439      * Describes the parameters for submit_for_grading
1440      * @return external_external_function_parameters
1441      * @since  Moodle 2.6
1442      */
1443     public static function submit_for_grading_parameters() {
1444         return new external_function_parameters(
1445             array(
1446                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1447                 'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement')
1448             )
1449         );
1450     }
1452     /**
1453      * Submit the logged in users assignment for grading.
1454      *
1455      * @param int $assignmentid The id of the assignment
1456      * @return array of warnings to indicate any errors.
1457      * @since Moodle 2.6
1458      */
1459     public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
1460         global $CFG, $USER;
1461         require_once("$CFG->dirroot/mod/assign/locallib.php");
1463         $params = self::validate_parameters(self::submit_for_grading_parameters(),
1464                                             array('assignmentid' => $assignmentid,
1465                                                   'acceptsubmissionstatement' => $acceptsubmissionstatement));
1467         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1468         $context = context_module::instance($cm->id);
1469         self::validate_context($context);
1471         $assignment = new assign($context, $cm, null);
1473         $warnings = array();
1474         $data = new stdClass();
1475         $data->submissionstatement = $params['acceptsubmissionstatement'];
1476         $notices = array();
1478         if (!$assignment->submit_for_grading($data, $notices)) {
1479             $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices);
1480             $warnings[] = self::generate_warning($params['assignmentid'],
1481                                                  'couldnotsubmitforgrading',
1482                                                  $detail);
1483         }
1485         return $warnings;
1486     }
1488     /**
1489      * Describes the return value for submit_for_grading
1490      *
1491      * @return external_single_structure
1492      * @since Moodle 2.6
1493      */
1494     public static function submit_for_grading_returns() {
1495         return new external_warnings();
1496     }
1498     /**
1499      * Describes the parameters for save_user_extensions
1500      * @return external_external_function_parameters
1501      * @since  Moodle 2.6
1502      */
1503     public static function save_user_extensions_parameters() {
1504         return new external_function_parameters(
1505             array(
1506                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1507                 'userids' => new external_multiple_structure(
1508                     new external_value(PARAM_INT, 'user id'),
1509                     '1 or more user ids',
1510                     VALUE_REQUIRED),
1511                 'dates' => new external_multiple_structure(
1512                     new external_value(PARAM_INT, 'dates'),
1513                     '1 or more extension dates (timestamp)',
1514                     VALUE_REQUIRED),
1515             )
1516         );
1517     }
1519     /**
1520      * Grant extension dates to students for an assignment.
1521      *
1522      * @param int $assignmentid The id of the assignment
1523      * @param array $userids Array of user ids to grant extensions to
1524      * @param array $dates Array of extension dates
1525      * @return array of warnings for each extension date that could not be granted
1526      * @since Moodle 2.6
1527      */
1528     public static function save_user_extensions($assignmentid, $userids, $dates) {
1529         global $CFG;
1530         require_once("$CFG->dirroot/mod/assign/locallib.php");
1532         $params = self::validate_parameters(self::save_user_extensions_parameters(),
1533                         array('assignmentid' => $assignmentid,
1534                               'userids' => $userids,
1535                               'dates' => $dates));
1537         if (count($params['userids']) != count($params['dates'])) {
1538             $detail = 'Length of userids and dates parameters differ.';
1539             $warnings[] = self::generate_warning($params['assignmentid'],
1540                                                  'invalidparameters',
1541                                                  $detail);
1543             return $warnings;
1544         }
1546         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1547         $context = context_module::instance($cm->id);
1548         self::validate_context($context);
1550         $assignment = new assign($context, $cm, null);
1552         $warnings = array();
1553         foreach ($params['userids'] as $idx => $userid) {
1554             $duedate = $params['dates'][$idx];
1555             if (!$assignment->save_user_extension($userid, $duedate)) {
1556                 $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate;
1557                 $warnings[] = self::generate_warning($params['assignmentid'],
1558                                                      'couldnotgrantextensions',
1559                                                      $detail);
1560             }
1561         }
1563         return $warnings;
1564     }
1566     /**
1567      * Describes the return value for save_user_extensions
1568      *
1569      * @return external_single_structure
1570      * @since Moodle 2.6
1571      */
1572     public static function save_user_extensions_returns() {
1573         return new external_warnings();
1574     }
1576     /**
1577      * Describes the parameters for reveal_identities
1578      * @return external_external_function_parameters
1579      * @since  Moodle 2.6
1580      */
1581     public static function reveal_identities_parameters() {
1582         return new external_function_parameters(
1583             array(
1584                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
1585             )
1586         );
1587     }
1589     /**
1590      * Reveal the identities of anonymous students to markers for a single assignment.
1591      *
1592      * @param int $assignmentid The id of the assignment
1593      * @return array of warnings to indicate any errors.
1594      * @since Moodle 2.6
1595      */
1596     public static function reveal_identities($assignmentid) {
1597         global $CFG, $USER;
1598         require_once("$CFG->dirroot/mod/assign/locallib.php");
1600         $params = self::validate_parameters(self::reveal_identities_parameters(),
1601                                             array('assignmentid' => $assignmentid));
1603         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1604         $context = context_module::instance($cm->id);
1605         self::validate_context($context);
1607         $assignment = new assign($context, $cm, null);
1609         $warnings = array();
1610         if (!$assignment->reveal_identities()) {
1611             $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'];
1612             $warnings[] = self::generate_warning($params['assignmentid'],
1613                                                  'couldnotrevealidentities',
1614                                                  $detail);
1615         }
1617         return $warnings;
1618     }
1620     /**
1621      * Describes the return value for reveal_identities
1622      *
1623      * @return external_single_structure
1624      * @since Moodle 2.6
1625      */
1626     public static function reveal_identities_returns() {
1627         return new external_warnings();
1628     }
1630     /**
1631      * Describes the parameters for save_submission
1632      * @return external_external_function_parameters
1633      * @since  Moodle 2.6
1634      */
1635     public static function save_submission_parameters() {
1636         global $CFG;
1637         require_once("$CFG->dirroot/mod/assign/locallib.php");
1638         $instance = new assign(null, null, null);
1639         $pluginsubmissionparams = array();
1641         foreach ($instance->get_submission_plugins() as $plugin) {
1642             $pluginparams = $plugin->get_external_parameters();
1643             if (!empty($pluginparams)) {
1644                 $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
1645             }
1646         }
1648         return new external_function_parameters(
1649             array(
1650                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1651                 'plugindata' => new external_single_structure(
1652                     $pluginsubmissionparams
1653                 )
1654             )
1655         );
1656     }
1658     /**
1659      * Save a student submission for a single assignment
1660      *
1661      * @param int $assignmentid The id of the assignment
1662      * @param array $plugindata - The submitted data for plugins
1663      * @return array of warnings to indicate any errors
1664      * @since Moodle 2.6
1665      */
1666     public static function save_submission($assignmentid, $plugindata) {
1667         global $CFG, $USER;
1668         require_once("$CFG->dirroot/mod/assign/locallib.php");
1670         $params = self::validate_parameters(self::save_submission_parameters(),
1671                                             array('assignmentid' => $assignmentid,
1672                                                   'plugindata' => $plugindata));
1674         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1675         $context = context_module::instance($cm->id);
1676         self::validate_context($context);
1678         $assignment = new assign($context, $cm, null);
1680         $notices = array();
1682         $submissiondata = (object)$params['plugindata'];
1684         $assignment->save_submission($submissiondata, $notices);
1686         $warnings = array();
1687         foreach ($notices as $notice) {
1688             $warnings[] = self::generate_warning($params['assignmentid'],
1689                                                  'couldnotsavesubmission',
1690                                                  $notice);
1691         }
1693         return $warnings;
1694     }
1696     /**
1697      * Describes the return value for save_submission
1698      *
1699      * @return external_single_structure
1700      * @since Moodle 2.6
1701      */
1702     public static function save_submission_returns() {
1703         return new external_warnings();
1704     }
1706     /**
1707      * Describes the parameters for save_grade
1708      * @return external_external_function_parameters
1709      * @since  Moodle 2.6
1710      */
1711     public static function save_grade_parameters() {
1712         global $CFG;
1713         require_once("$CFG->dirroot/mod/assign/locallib.php");
1714         require_once("$CFG->dirroot/grade/grading/lib.php");
1715         $instance = new assign(null, null, null);
1716         $pluginfeedbackparams = array();
1718         foreach ($instance->get_feedback_plugins() as $plugin) {
1719             $pluginparams = $plugin->get_external_parameters();
1720             if (!empty($pluginparams)) {
1721                 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1722             }
1723         }
1725         $advancedgradingdata = array();
1726         $methods = array_keys(grading_manager::available_methods(false));
1727         foreach ($methods as $method) {
1728             require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1729             $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1730             if (!empty($details)) {
1731                 $items = array();
1732                 foreach ($details as $key => $value) {
1733                     $value->required = VALUE_OPTIONAL;
1734                     unset($value->content->keys['id']);
1735                     $items[$key] = new external_multiple_structure (new external_single_structure(
1736                         array(
1737                             'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1738                             'fillings' => $value
1739                         )
1740                     ));
1741                 }
1742                 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1743             }
1744         }
1746         return new external_function_parameters(
1747             array(
1748                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1749                 'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1750                 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'),
1751                 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1752                 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
1753                 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1754                 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1755                                                                'to all members ' .
1756                                                                'of the group (for group assignments).'),
1757                 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()),
1758                 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1759                                                                        VALUE_DEFAULT, array())
1760             )
1761         );
1762     }
1764     /**
1765      * Save a student grade for a single assignment.
1766      *
1767      * @param int $assignmentid The id of the assignment
1768      * @param int $userid The id of the user
1769      * @param float $grade The grade (ignored if the assignment uses advanced grading)
1770      * @param int $attemptnumber The attempt number
1771      * @param bool $addattempt Allow another attempt
1772      * @param string $workflowstate New workflow state
1773      * @param bool $applytoall Apply the grade to all members of the group
1774      * @param array $plugindata Custom data used by plugins
1775      * @param array $advancedgradingdata Advanced grading data
1776      * @return null
1777      * @since Moodle 2.6
1778      */
1779     public static function save_grade($assignmentid,
1780                                       $userid,
1781                                       $grade,
1782                                       $attemptnumber,
1783                                       $addattempt,
1784                                       $workflowstate,
1785                                       $applytoall,
1786                                       $plugindata = array(),
1787                                       $advancedgradingdata = array()) {
1788         global $CFG, $USER;
1789         require_once("$CFG->dirroot/mod/assign/locallib.php");
1791         $params = self::validate_parameters(self::save_grade_parameters(),
1792                                             array('assignmentid' => $assignmentid,
1793                                                   'userid' => $userid,
1794                                                   'grade' => $grade,
1795                                                   'attemptnumber' => $attemptnumber,
1796                                                   'workflowstate' => $workflowstate,
1797                                                   'addattempt' => $addattempt,
1798                                                   'applytoall' => $applytoall,
1799                                                   'plugindata' => $plugindata,
1800                                                   'advancedgradingdata' => $advancedgradingdata));
1802         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1803         $context = context_module::instance($cm->id);
1804         self::validate_context($context);
1806         $assignment = new assign($context, $cm, null);
1808         $gradedata = (object)$params['plugindata'];
1810         $gradedata->addattempt = $params['addattempt'];
1811         $gradedata->attemptnumber = $params['attemptnumber'];
1812         $gradedata->workflowstate = $params['workflowstate'];
1813         $gradedata->applytoall = $params['applytoall'];
1814         $gradedata->grade = $params['grade'];
1816         if (!empty($params['advancedgradingdata'])) {
1817             $advancedgrading = array();
1818             $criteria = reset($params['advancedgradingdata']);
1819             foreach ($criteria as $key => $criterion) {
1820                 $details = array();
1821                 foreach ($criterion as $value) {
1822                     foreach ($value['fillings'] as $filling) {
1823                         $details[$value['criterionid']] = $filling;
1824                     }
1825                 }
1826                 $advancedgrading[$key] = $details;
1827             }
1828             $gradedata->advancedgrading = $advancedgrading;
1829         }
1831         $assignment->save_grade($params['userid'], $gradedata);
1833         return null;
1834     }
1836     /**
1837      * Describes the return value for save_grade
1838      *
1839      * @return external_single_structure
1840      * @since Moodle 2.6
1841      */
1842     public static function save_grade_returns() {
1843         return null;
1844     }
1846     /**
1847      * Describes the parameters for save_grades
1848      * @return external_external_function_parameters
1849      * @since  Moodle 2.7
1850      */
1851     public static function save_grades_parameters() {
1852         global $CFG;
1853         require_once("$CFG->dirroot/mod/assign/locallib.php");
1854         require_once("$CFG->dirroot/grade/grading/lib.php");
1855         $instance = new assign(null, null, null);
1856         $pluginfeedbackparams = array();
1858         foreach ($instance->get_feedback_plugins() as $plugin) {
1859             $pluginparams = $plugin->get_external_parameters();
1860             if (!empty($pluginparams)) {
1861                 $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1862             }
1863         }
1865         $advancedgradingdata = array();
1866         $methods = array_keys(grading_manager::available_methods(false));
1867         foreach ($methods as $method) {
1868             require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1869             $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1870             if (!empty($details)) {
1871                 $items = array();
1872                 foreach ($details as $key => $value) {
1873                     $value->required = VALUE_OPTIONAL;
1874                     unset($value->content->keys['id']);
1875                     $items[$key] = new external_multiple_structure (new external_single_structure(
1876                         array(
1877                             'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1878                             'fillings' => $value
1879                         )
1880                     ));
1881                 }
1882                 $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1883             }
1884         }
1886         return new external_function_parameters(
1887             array(
1888                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1889                 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1890                                                                'to all members ' .
1891                                                                'of the group (for group assignments).'),
1892                 'grades' => new external_multiple_structure(
1893                     new external_single_structure(
1894                         array (
1895                             'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1896                             'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '.
1897                                                                        'Ignored if advanced grading used'),
1898                             'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1899                             'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'),
1900                             'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1901                             'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data',
1902                                                                           VALUE_DEFAULT, array()),
1903                             'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1904                                                                                    VALUE_DEFAULT, array())
1905                         )
1906                     )
1907                 )
1908             )
1909         );
1910     }
1912     /**
1913      * Save multiple student grades for a single assignment.
1914      *
1915      * @param int $assignmentid The id of the assignment
1916      * @param boolean $applytoall If set to true and this is a team assignment,
1917      * apply the grade to all members of the group
1918      * @param array $grades grade data for one or more students that includes
1919      *                  userid - The id of the student being graded
1920      *                  grade - The grade (ignored if the assignment uses advanced grading)
1921      *                  attemptnumber - The attempt number
1922      *                  addattempt - Allow another attempt
1923      *                  workflowstate - New workflow state
1924      *                  plugindata - Custom data used by plugins
1925      *                  advancedgradingdata - Optional Advanced grading data
1926      * @throws invalid_parameter_exception if multiple grades are supplied for
1927      * a team assignment that has $applytoall set to true
1928      * @return null
1929      * @since Moodle 2.7
1930      */
1931     public static function save_grades($assignmentid, $applytoall = false, $grades) {
1932         global $CFG, $USER;
1933         require_once("$CFG->dirroot/mod/assign/locallib.php");
1935         $params = self::validate_parameters(self::save_grades_parameters(),
1936                                             array('assignmentid' => $assignmentid,
1937                                                   'applytoall' => $applytoall,
1938                                                   'grades' => $grades));
1940         $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
1941         $context = context_module::instance($cm->id);
1942         self::validate_context($context);
1943         $assignment = new assign($context, $cm, null);
1945         if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
1946             // Check that only 1 user per submission group is provided.
1947             $groupids = array();
1948             foreach ($params['grades'] as $gradeinfo) {
1949                 $group = $assignment->get_submission_group($gradeinfo['userid']);
1950                 if (in_array($group->id, $groupids)) {
1951                     throw new invalid_parameter_exception('Multiple grades for the same team have been supplied '
1952                                                           .' this is not permitted when the applytoall flag is set');
1953                 } else {
1954                     $groupids[] = $group->id;
1955                 }
1956             }
1957         }
1959         foreach ($params['grades'] as $gradeinfo) {
1960             $gradedata = (object)$gradeinfo['plugindata'];
1961             $gradedata->addattempt = $gradeinfo['addattempt'];
1962             $gradedata->attemptnumber = $gradeinfo['attemptnumber'];
1963             $gradedata->workflowstate = $gradeinfo['workflowstate'];
1964             $gradedata->applytoall = $params['applytoall'];
1965             $gradedata->grade = $gradeinfo['grade'];
1967             if (!empty($gradeinfo['advancedgradingdata'])) {
1968                 $advancedgrading = array();
1969                 $criteria = reset($gradeinfo['advancedgradingdata']);
1970                 foreach ($criteria as $key => $criterion) {
1971                     $details = array();
1972                     foreach ($criterion as $value) {
1973                         foreach ($value['fillings'] as $filling) {
1974                             $details[$value['criterionid']] = $filling;
1975                         }
1976                     }
1977                     $advancedgrading[$key] = $details;
1978                 }
1979                 $gradedata->advancedgrading = $advancedgrading;
1980             }
1981             $assignment->save_grade($gradeinfo['userid'], $gradedata);
1982         }
1984         return null;
1985     }
1987     /**
1988      * Describes the return value for save_grades
1989      *
1990      * @return external_single_structure
1991      * @since Moodle 2.7
1992      */
1993     public static function save_grades_returns() {
1994         return null;
1995     }
1997     /**
1998      * Describes the parameters for copy_previous_attempt
1999      * @return external_external_function_parameters
2000      * @since  Moodle 2.6
2001      */
2002     public static function copy_previous_attempt_parameters() {
2003         return new external_function_parameters(
2004             array(
2005                 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
2006             )
2007         );
2008     }
2010     /**
2011      * Copy a students previous attempt to a new attempt.
2012      *
2013      * @param int $assignmentid
2014      * @return array of warnings to indicate any errors.
2015      * @since Moodle 2.6
2016      */
2017     public static function copy_previous_attempt($assignmentid) {
2018         global $CFG, $USER;
2019         require_once("$CFG->dirroot/mod/assign/locallib.php");
2021         $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
2022                                             array('assignmentid' => $assignmentid));
2024         $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
2025         $context = context_module::instance($cm->id);
2026         self::validate_context($context);
2028         $assignment = new assign($context, $cm, null);
2030         $notices = array();
2032         $assignment->copy_previous_attempt($submissiondata, $notices);
2034         $warnings = array();
2035         foreach ($notices as $notice) {
2036             $warnings[] = self::generate_warning($assignmentid,
2037                                                  'couldnotcopyprevioussubmission',
2038                                                  $notice);
2039         }
2041         return $warnings;
2042     }
2044     /**
2045      * Describes the return value for save_submission
2046      *
2047      * @return external_single_structure
2048      * @since Moodle 2.6
2049      */
2050     public static function copy_previous_attempt_returns() {
2051         return new external_warnings();
2052     }
2054     /**
2055      * Returns description of method parameters
2056      *
2057      * @return external_function_parameters
2058      * @since Moodle 3.0
2059      */
2060     public static function view_grading_table_parameters() {
2061         return new external_function_parameters(
2062             array(
2063                 'assignid' => new external_value(PARAM_INT, 'assign instance id')
2064             )
2065         );
2066     }
2068     /**
2069      * Simulate the web interface grading table view.
2070      *
2071      * @param int $assignid the assign instance id
2072      * @return array of warnings and status result
2073      * @since Moodle 3.0
2074      * @throws moodle_exception
2075      */
2076     public static function view_grading_table($assignid) {
2077         global $DB, $CFG;
2078         require_once($CFG->dirroot . "/mod/assign/locallib.php");
2080         $params = self::validate_parameters(self::view_grading_table_parameters(),
2081                                             array(
2082                                                 'assignid' => $assignid
2083                                             ));
2084         $warnings = array();
2086         // Request and permission validation.
2087         $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
2088         list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
2090         $context = context_module::instance($cm->id);
2091         self::validate_context($context);
2093         require_capability('mod/assign:view', $context);
2095         $assign = new assign($context, null, null);
2096         $assign->require_view_grades();
2097         \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger();
2099         $result = array();
2100         $result['status'] = true;
2101         $result['warnings'] = $warnings;
2102         return $result;
2103     }
2105     /**
2106      * Returns description of method result value
2107      *
2108      * @return external_description
2109      * @since Moodle 3.0
2110      */
2111     public static function view_grading_table_returns() {
2112         return new external_single_structure(
2113             array(
2114                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2115                 'warnings' => new external_warnings()
2116             )
2117         );
2118     }