Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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/>.
16
 
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
 */
25
 
26
use core_external\external_files;
27
use core_external\external_format_value;
28
use core_external\external_function_parameters;
29
use core_external\external_multiple_structure;
30
use core_external\external_single_structure;
31
use core_external\external_value;
32
use core_external\external_warnings;
33
use core_external\util as external_util;
34
 
35
defined('MOODLE_INTERNAL') || die;
36
 
37
require_once("$CFG->dirroot/user/externallib.php");
38
require_once("$CFG->dirroot/mod/assign/locallib.php");
39
 
40
/**
41
 * Assign functions
42
 * @copyright 2012 Paul Charsley
43
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44
 */
45
class mod_assign_external extends \mod_assign\external\external_api {
46
 
47
    /**
48
     * Describes the parameters for get_grades
49
     * @return external_function_parameters
50
     * @since  Moodle 2.4
51
     */
52
    public static function get_grades_parameters() {
53
        return new external_function_parameters(
54
            array(
55
                'assignmentids' => new external_multiple_structure(
56
                    new external_value(PARAM_INT, 'assignment id'),
57
                    '1 or more assignment ids',
58
                    VALUE_REQUIRED),
59
                'since' => new external_value(PARAM_INT,
60
                          'timestamp, only return records where timemodified >= since',
61
                          VALUE_DEFAULT, 0)
62
            )
63
        );
64
    }
65
 
66
    /**
67
     * Returns grade information from assign_grades for the requested assignment ids
68
     * @param int[] $assignmentids
69
     * @param int $since only return records with timemodified >= since
70
     * @return array of grade records for each requested assignment
71
     * @since  Moodle 2.4
72
     */
73
    public static function get_grades($assignmentids, $since = 0) {
74
        global $DB;
75
        $params = self::validate_parameters(self::get_grades_parameters(),
76
                        array('assignmentids' => $assignmentids,
77
                              'since' => $since));
78
 
79
        $assignments = array();
80
        $warnings = array();
81
        $requestedassignmentids = $params['assignmentids'];
82
 
83
        // Check the user is allowed to get the grades for the assignments requested.
84
        $placeholders = array();
85
        list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
86
        $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
87
               "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
88
        $placeholders['modname'] = 'assign';
89
        $cms = $DB->get_records_sql($sql, $placeholders);
90
        foreach ($cms as $cm) {
91
            try {
92
                $context = context_module::instance($cm->id);
93
                self::validate_context($context);
94
                $assign = new assign($context, null, null);
95
                $assign->require_view_grades();
96
            } catch (Exception $e) {
97
                $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
98
                $warning = array();
99
                $warning['item'] = 'assignment';
100
                $warning['itemid'] = $cm->instance;
101
                $warning['warningcode'] = '1';
102
                $warning['message'] = 'No access rights in module context';
103
                $warnings[] = $warning;
104
            }
105
        }
106
 
107
        // Create the query and populate an array of grade records from the recordset results.
108
        if (count ($requestedassignmentids) > 0) {
109
            $placeholders = array();
110
            list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
111
 
112
            $sql = "SELECT ag.id,
113
                           ag.assignment,
114
                           ag.userid,
115
                           ag.timecreated,
116
                           ag.timemodified,
117
                           ag.grader,
118
                           ag.grade,
119
                           ag.attemptnumber
120
                      FROM {assign_grades} ag, {assign_submission} s
121
                     WHERE s.assignment $inorequalsql
122
                       AND s.userid = ag.userid
123
                       AND s.latest = 1
124
                       AND s.attemptnumber = ag.attemptnumber
125
                       AND ag.timemodified  >= :since
126
                       AND ag.assignment = s.assignment
127
                  ORDER BY ag.assignment, ag.id";
128
 
129
            $placeholders['since'] = $params['since'];
130
            $rs = $DB->get_recordset_sql($sql, $placeholders);
131
            $currentassignmentid = null;
132
            $assignment = null;
133
            foreach ($rs as $rd) {
134
                $grade = array();
135
                $grade['id'] = $rd->id;
136
                $grade['userid'] = $rd->userid;
137
                $grade['timecreated'] = $rd->timecreated;
138
                $grade['timemodified'] = $rd->timemodified;
139
                $grade['grader'] = $rd->grader;
140
                $grade['attemptnumber'] = $rd->attemptnumber;
141
                $grade['grade'] = (string)$rd->grade;
142
 
143
                if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
144
                    if (!is_null($assignment)) {
145
                        $assignments[] = $assignment;
146
                    }
147
                    $assignment = array();
148
                    $assignment['assignmentid'] = $rd->assignment;
149
                    $assignment['grades'] = array();
150
                    $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
151
                }
152
                $assignment['grades'][] = $grade;
153
 
154
                $currentassignmentid = $rd->assignment;
155
            }
156
            if (!is_null($assignment)) {
157
                $assignments[] = $assignment;
158
            }
159
            $rs->close();
160
        }
161
        foreach ($requestedassignmentids as $assignmentid) {
162
            $warning = array();
163
            $warning['item'] = 'assignment';
164
            $warning['itemid'] = $assignmentid;
165
            $warning['warningcode'] = '3';
166
            $warning['message'] = 'No grades found';
167
            $warnings[] = $warning;
168
        }
169
 
170
        $result = array();
171
        $result['assignments'] = $assignments;
172
        $result['warnings'] = $warnings;
173
        return $result;
174
    }
175
 
176
    /**
177
     * Creates a grade single structure.
178
     *
179
     * @return external_single_structure a grade single structure.
180
     * @since  Moodle 3.1
181
     */
182
    private static function get_grade_structure($required = VALUE_REQUIRED) {
183
        return new external_single_structure(
184
            array(
185
                'id'                => new external_value(PARAM_INT, 'grade id'),
186
                'assignment'        => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
187
                'userid'            => new external_value(PARAM_INT, 'student id'),
188
                'attemptnumber'     => new external_value(PARAM_INT, 'attempt number'),
189
                'timecreated'       => new external_value(PARAM_INT, 'grade creation time'),
190
                'timemodified'      => new external_value(PARAM_INT, 'grade last modified time'),
191
                'grader'            => new external_value(PARAM_INT, 'grader, -1 if grader is hidden'),
192
                'grade'             => new external_value(PARAM_TEXT, 'grade'),
193
                'gradefordisplay'   => new external_value(PARAM_RAW, 'grade rendered into a format suitable for display',
194
                                                            VALUE_OPTIONAL),
195
            ), 'grade information', $required
196
        );
197
    }
198
 
199
    /**
200
     * Creates an assign_grades external_single_structure
201
     * @return external_single_structure
202
     * @since  Moodle 2.4
203
     */
204
    private static function assign_grades() {
205
        return new external_single_structure(
206
            array (
207
                'assignmentid'  => new external_value(PARAM_INT, 'assignment id'),
208
                'grades'        => new external_multiple_structure(self::get_grade_structure())
209
            )
210
        );
211
    }
212
 
213
    /**
214
     * Describes the get_grades return value
215
     * @return external_single_structure
216
     * @since  Moodle 2.4
217
     */
218
    public static function get_grades_returns() {
219
        return new external_single_structure(
220
            array(
221
                'assignments' => new external_multiple_structure(self::assign_grades(), 'list of assignment grade information'),
222
                'warnings'      => new external_warnings('item is always \'assignment\'',
223
                    'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
224
                    'errorcode can be 3 (no grades found) or 1 (no permission to get grades)')
225
            )
226
        );
227
    }
228
 
229
    /**
230
     * Returns description of method parameters
231
     *
232
     * @return external_function_parameters
233
     * @since  Moodle 2.4
234
     */
235
    public static function get_assignments_parameters() {
236
        return new external_function_parameters(
237
            array(
238
                'courseids' => new external_multiple_structure(
239
                    new external_value(PARAM_INT, 'course id, empty for retrieving all the courses where the user is enroled in'),
240
                    '0 or more course ids',
241
                    VALUE_DEFAULT, array()
242
                ),
243
                'capabilities'  => new external_multiple_structure(
244
                    new external_value(PARAM_CAPABILITY, 'capability'),
245
                    'list of capabilities used to filter courses',
246
                    VALUE_DEFAULT, array()
247
                ),
248
                'includenotenrolledcourses' => new external_value(PARAM_BOOL, 'whether to return courses that the user can see
249
                                                                    even if is not enroled in. This requires the parameter courseids
250
                                                                    to not be empty.', VALUE_DEFAULT, false)
251
            )
252
        );
253
    }
254
 
255
    /**
256
     * Returns an array of courses the user is enrolled, and for each course all of the assignments that the user can
257
     * view within that course.
258
     *
259
     * @param array $courseids An optional array of course ids. If provided only assignments within the given course
260
     * will be returned. If the user is not enrolled in or can't view a given course a warning will be generated and returned.
261
     * @param array $capabilities An array of additional capability checks you wish to be made on the course context.
262
     * @param bool $includenotenrolledcourses Wheter to return courses that the user can see even if is not enroled in.
263
     * This requires the parameter $courseids to not be empty.
264
     * @return An array of courses and warnings.
265
     * @since  Moodle 2.4
266
     */
267
    public static function get_assignments($courseids = array(), $capabilities = array(), $includenotenrolledcourses = false) {
268
        global $USER, $DB, $CFG;
269
 
270
        $params = self::validate_parameters(
271
            self::get_assignments_parameters(),
272
            array(
273
                'courseids' => $courseids,
274
                'capabilities' => $capabilities,
275
                'includenotenrolledcourses' => $includenotenrolledcourses
276
            )
277
        );
278
 
279
        $warnings = array();
280
        $courses = array();
281
        $fields = 'sortorder,shortname,fullname,timemodified';
282
 
283
        // If the courseids list is empty, we return only the courses where the user is enrolled in.
284
        if (empty($params['courseids'])) {
285
            $courses = enrol_get_users_courses($USER->id, true, $fields);
286
            $courseids = array_keys($courses);
287
        } else if ($includenotenrolledcourses) {
288
            // In this case, we don't have to check here for enrolmnents. Maybe the user can see the course even if is not enrolled.
289
            $courseids = $params['courseids'];
290
        } else {
291
            // We need to check for enrolments.
292
            $mycourses = enrol_get_users_courses($USER->id, true, $fields);
293
            $mycourseids = array_keys($mycourses);
294
 
295
            foreach ($params['courseids'] as $courseid) {
296
                if (!in_array($courseid, $mycourseids)) {
297
                    unset($courses[$courseid]);
298
                    $warnings[] = array(
299
                        'item' => 'course',
300
                        'itemid' => $courseid,
301
                        'warningcode' => '2',
302
                        'message' => 'User is not enrolled or does not have requested capability'
303
                    );
304
                } else {
305
                    $courses[$courseid] = $mycourses[$courseid];
306
                }
307
            }
308
            $courseids = array_keys($courses);
309
        }
310
 
311
        foreach ($courseids as $cid) {
312
 
313
            try {
314
                $context = context_course::instance($cid);
315
                self::validate_context($context);
316
 
317
                // Check if this course was already loaded (by enrol_get_users_courses).
318
                if (!isset($courses[$cid])) {
319
                    $courses[$cid] = get_course($cid);
320
                }
321
                $courses[$cid]->contextid = $context->id;
322
            } catch (Exception $e) {
323
                unset($courses[$cid]);
324
                $warnings[] = array(
325
                    'item' => 'course',
326
                    'itemid' => $cid,
327
                    'warningcode' => '1',
328
                    'message' => 'No access rights in course context '.$e->getMessage()
329
                );
330
                continue;
331
            }
332
            if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) {
333
                unset($courses[$cid]);
334
            }
335
        }
336
        $extrafields='m.id as assignmentid, ' .
337
                     'm.course, ' .
338
                     'm.nosubmissions, ' .
339
                     'm.submissiondrafts, ' .
340
                     'm.sendnotifications, '.
341
                     'm.sendlatenotifications, ' .
342
                     'm.sendstudentnotifications, ' .
343
                     'm.duedate, ' .
344
                     'm.allowsubmissionsfromdate, '.
345
                     'm.grade, ' .
346
                     'm.timemodified, '.
347
                     'm.completionsubmit, ' .
348
                     'm.cutoffdate, ' .
349
                     'm.gradingduedate, ' .
350
                     'm.teamsubmission, ' .
351
                     'm.requireallteammemberssubmit, '.
352
                     'm.teamsubmissiongroupingid, ' .
353
                     'm.blindmarking, ' .
354
                     'm.hidegrader, ' .
355
                     'm.revealidentities, ' .
356
                     'm.attemptreopenmethod, '.
357
                     'm.maxattempts, ' .
358
                     'm.markingworkflow, ' .
359
                     'm.markingallocation, ' .
360
                     'm.markinganonymous, ' .
361
                     'm.requiresubmissionstatement, '.
362
                     'm.preventsubmissionnotingroup, '.
363
                     'm.intro, '.
364
                     'm.introformat,' .
365
                     'm.activity,' .
366
                     'm.activityformat,' .
367
                     'm.timelimit,' .
368
                     'm.submissionattachments';
369
        $coursearray = array();
370
        foreach ($courses as $id => $course) {
371
            $assignmentarray = array();
372
            // Get a list of assignments for the course.
373
            if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
374
                foreach ($modules as $module) {
375
                    $context = context_module::instance($module->id);
376
                    try {
377
                        self::validate_context($context);
378
                        require_capability('mod/assign:view', $context);
379
                    } catch (Exception $e) {
380
                        $warnings[] = array(
381
                            'item' => 'module',
382
                            'itemid' => $module->id,
383
                            'warningcode' => '1',
384
                            'message' => 'No access rights in module context'
385
                        );
386
                        continue;
387
                    }
388
 
389
                    $assign = new assign($context, null, null);
390
                    // Update assign with override information.
391
                    $assign->update_effective_access($USER->id);
392
 
393
                    // Get configurations for only enabled plugins.
394
                    $plugins = $assign->get_submission_plugins();
395
                    $plugins = array_merge($plugins, $assign->get_feedback_plugins());
396
 
397
                    $configarray = array();
398
                    foreach ($plugins as $plugin) {
399
                        if ($plugin->is_enabled() && $plugin->is_visible()) {
400
                            $configrecords = $plugin->get_config_for_external();
401
                            foreach ($configrecords as $name => $value) {
402
                                $configarray[] = array(
403
                                    'plugin' => $plugin->get_type(),
404
                                    'subtype' => $plugin->get_subtype(),
405
                                    'name' => $name,
406
                                    'value' => $value
407
                                );
408
                            }
409
                        }
410
                    }
411
 
412
                    $assignment = array(
413
                        'id' => $module->assignmentid,
414
                        'cmid' => $module->id,
415
                        'course' => $module->course,
416
                        'name' => \core_external\util::format_string($module->name, $context),
417
                        'nosubmissions' => $module->nosubmissions,
418
                        'submissiondrafts' => $module->submissiondrafts,
419
                        'sendnotifications' => $module->sendnotifications,
420
                        'sendlatenotifications' => $module->sendlatenotifications,
421
                        'sendstudentnotifications' => $module->sendstudentnotifications,
422
                        'duedate' => $assign->get_instance()->duedate,
423
                        'allowsubmissionsfromdate' => $assign->get_instance()->allowsubmissionsfromdate,
424
                        'grade' => $module->grade,
425
                        'timemodified' => $module->timemodified,
426
                        'completionsubmit' => $module->completionsubmit,
427
                        'cutoffdate' => $assign->get_instance()->cutoffdate,
428
                        'gradingduedate' => $assign->get_instance()->gradingduedate,
429
                        'teamsubmission' => $module->teamsubmission,
430
                        'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
431
                        'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
432
                        'blindmarking' => $module->blindmarking,
433
                        'hidegrader' => $module->hidegrader,
434
                        'revealidentities' => $module->revealidentities,
435
                        'attemptreopenmethod' => $module->attemptreopenmethod,
436
                        'maxattempts' => $module->maxattempts,
437
                        'markingworkflow' => $module->markingworkflow,
438
                        'markingallocation' => $module->markingallocation,
439
                        'markinganonymous' => $module->markinganonymous,
440
                        'requiresubmissionstatement' => $module->requiresubmissionstatement,
441
                        'preventsubmissionnotingroup' => $module->preventsubmissionnotingroup,
442
                        'timelimit' => $module->timelimit,
443
                        'submissionattachments' => $module->submissionattachments,
444
                        'configs' => $configarray
445
                    );
446
 
447
                    // Return or not intro and file attachments depending on the plugin settings.
448
                    if ($assign->show_intro()) {
449
                        $options = array('noclean' => true);
450
                        [$assignment['intro'], $assignment['introformat']] = \core_external\util::format_text(
451
                            $module->intro,
452
                            $module->introformat,
453
                            $context,
454
                            'mod_assign',
455
                            'intro',
456
                            null,
457
                            $options
458
                        );
459
                        $assignment['introfiles'] = external_util::get_area_files($context->id, 'mod_assign', 'intro', false,
460
                                                                                    false);
461
                        if ($assign->should_provide_intro_attachments($USER->id)) {
462
                            $assignment['introattachments'] = external_util::get_area_files($context->id, 'mod_assign',
463
                                ASSIGN_INTROATTACHMENT_FILEAREA, 0);
464
                        }
465
                    }
466
 
467
                    if ($module->requiresubmissionstatement) {
468
                        // Submission statement is required, return the submission statement value.
469
                        $adminconfig = get_config('assign');
470
                        // Single submission.
471
                        if (!$module->teamsubmission) {
472
                            list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
473
                                \core_external\util::format_text($adminconfig->submissionstatement, FORMAT_MOODLE, $context->id,
474
                                    'mod_assign', '', 0);
475
                        } else { // Team submission.
476
                            // One user can submit for the whole team.
477
                            if (!empty($adminconfig->submissionstatementteamsubmission) && !$module->requireallteammemberssubmit) {
478
                                list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
479
                                    \core_external\util::format_text($adminconfig->submissionstatementteamsubmission,
480
                                        FORMAT_MOODLE, $context->id, 'mod_assign', '', 0);
481
                            } else if (!empty($adminconfig->submissionstatementteamsubmissionallsubmit) &&
482
                                $module->requireallteammemberssubmit) {
483
                                // All team members must submit.
484
                                list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
485
                                    \core_external\util::format_text($adminconfig->submissionstatementteamsubmissionallsubmit,
486
                                        FORMAT_MOODLE, $context->id, 'mod_assign', '', 0);
487
                            }
488
                        }
489
                    }
490
 
491
                    if ($module->activity && $assign->submissions_open($USER->id, true)) {
492
                        list($assignment['activity'], $assignment['activityformat']) = \core_external\util::format_text(
493
                            $module->activity,
494
                            $module->activityformat,
495
                            $context,
496
                            'mod_assign',
497
                            ASSIGN_ACTIVITYATTACHMENT_FILEAREA,
498
 
499
                        );
500
                        $assignment['activityattachments'] = external_util::get_area_files($context->id, 'mod_assign',
501
                            ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0);
502
                    }
503
 
504
                    $assignmentarray[] = $assignment;
505
                }
506
            }
507
            $coursearray[]= array(
508
                'id' => $courses[$id]->id,
509
                'fullname' => \core_external\util::format_string($courses[$id]->fullname, $course->contextid),
510
                'shortname' => \core_external\util::format_string($courses[$id]->shortname, $course->contextid),
511
                'timemodified' => $courses[$id]->timemodified,
512
                'assignments' => $assignmentarray
513
            );
514
        }
515
 
516
        $result = array(
517
            'courses' => $coursearray,
518
            'warnings' => $warnings
519
        );
520
        return $result;
521
    }
522
 
523
    /**
524
     * Creates an assignment external_single_structure
525
     *
526
     * @return external_single_structure
527
     * @since Moodle 2.4
528
     */
529
    private static function get_assignments_assignment_structure() {
530
        return new external_single_structure(
531
            array(
532
                'id' => new external_value(PARAM_INT, 'assignment id'),
533
                'cmid' => new external_value(PARAM_INT, 'course module id'),
534
                'course' => new external_value(PARAM_INT, 'course id'),
535
                'name' => new external_value(PARAM_RAW, 'assignment name'),
536
                'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
537
                'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
538
                'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
539
                'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
540
                'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'),
541
                'duedate' => new external_value(PARAM_INT, 'assignment due date'),
542
                'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
543
                'grade' => new external_value(PARAM_INT, 'grade type'),
544
                'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
545
                'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
546
                'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
547
                'gradingduedate' => new external_value(PARAM_INT, 'the expected date for marking the submissions'),
548
                'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
549
                'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
550
                'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
551
                'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
552
                'hidegrader' => new external_value(PARAM_INT, 'If enabled, hide grader to student'),
553
                'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
554
                'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
555
                'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
556
                'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
557
                'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
558
                'markinganonymous' => new external_value(PARAM_INT, 'enable marking anonymous'),
559
                'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
560
                'preventsubmissionnotingroup' => new external_value(PARAM_INT, 'Prevent submission not in group', VALUE_OPTIONAL),
561
                'submissionstatement' => new external_value(PARAM_RAW, 'Submission statement formatted.', VALUE_OPTIONAL),
562
                'submissionstatementformat' => new external_format_value('submissionstatement', VALUE_OPTIONAL),
563
                'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings'),
564
                'intro' => new external_value(PARAM_RAW,
565
                    'assignment intro, not allways returned because it deppends on the activity configuration', VALUE_OPTIONAL),
566
                'introformat' => new external_format_value('intro', VALUE_OPTIONAL),
567
                'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
568
                'introattachments' => new external_files('intro attachments files', VALUE_OPTIONAL),
569
                'activity' => new external_value(PARAM_RAW, 'Description of activity', VALUE_OPTIONAL),
570
                'activityformat' => new external_format_value('activity', VALUE_OPTIONAL),
571
                'activityattachments' => new external_files('Files from activity field', VALUE_OPTIONAL),
572
                'timelimit' => new external_value(PARAM_INT, 'Time limit to complete assigment', VALUE_OPTIONAL),
573
                'submissionattachments' => new external_value(PARAM_INT,
574
                    'Flag to only show files during submission', VALUE_OPTIONAL),
575
            ), 'assignment information object');
576
    }
577
 
578
    /**
579
     * Creates an assign_plugin_config external_single_structure
580
     *
581
     * @return external_single_structure
582
     * @since Moodle 2.4
583
     */
584
    private static function get_assignments_config_structure() {
585
        return new external_single_structure(
586
            array(
587
                'id' => new external_value(PARAM_INT, 'assign_plugin_config id', VALUE_OPTIONAL),
588
                'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
589
                'plugin' => new external_value(PARAM_TEXT, 'plugin'),
590
                'subtype' => new external_value(PARAM_TEXT, 'subtype'),
591
                'name' => new external_value(PARAM_TEXT, 'name'),
592
                'value' => new external_value(PARAM_TEXT, 'value')
593
            ), 'assignment configuration object'
594
        );
595
    }
596
 
597
    /**
598
     * Creates a course external_single_structure
599
     *
600
     * @return external_single_structure
601
     * @since Moodle 2.4
602
     */
603
    private static function get_assignments_course_structure() {
604
        return new external_single_structure(
605
            array(
606
                'id' => new external_value(PARAM_INT, 'course id'),
607
                'fullname' => new external_value(PARAM_RAW, 'course full name'),
608
                'shortname' => new external_value(PARAM_RAW, 'course short name'),
609
                'timemodified' => new external_value(PARAM_INT, 'last time modified'),
610
                'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
611
              ), 'course information object'
612
        );
613
    }
614
 
615
    /**
616
     * Describes the return value for get_assignments
617
     *
618
     * @return external_single_structure
619
     * @since Moodle 2.4
620
     */
621
    public static function get_assignments_returns() {
622
        return new external_single_structure(
623
            array(
624
                'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
625
                'warnings'  => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
626
                    'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
627
                    'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
628
            )
629
        );
630
    }
631
 
632
    /**
633
     * Return information (files and text fields) for the given plugins in the assignment.
634
     *
635
     * @param  assign $assign the assignment object
636
     * @param  array $assignplugins array of assignment plugins (submission or feedback)
637
     * @param  stdClass $item the item object (submission or grade)
638
     * @return array an array containing the plugins returned information
639
     */
640
    private static function get_plugins_data($assign, $assignplugins, $item) {
641
        global $CFG;
642
 
643
        $plugins = array();
644
        $fs = get_file_storage();
645
 
646
        foreach ($assignplugins as $assignplugin) {
647
 
648
            if (!$assignplugin->is_enabled() or !$assignplugin->is_visible()) {
649
                continue;
650
            }
651
 
652
            $plugin = array(
653
                'name' => $assignplugin->get_name(),
654
                'type' => $assignplugin->get_type()
655
            );
656
            // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
657
            $component = $assignplugin->get_subtype().'_'.$assignplugin->get_type();
658
 
659
            $fileareas = $assignplugin->get_file_areas();
660
            foreach ($fileareas as $filearea => $name) {
661
                $fileareainfo = array('area' => $filearea);
662
 
663
                $fileareainfo['files'] = external_util::get_area_files(
664
                    $assign->get_context()->id,
665
                    $component,
666
                    $filearea,
667
                    $item->id
668
                );
669
 
670
                $plugin['fileareas'][] = $fileareainfo;
671
            }
672
 
673
            $editorfields = $assignplugin->get_editor_fields();
674
            foreach ($editorfields as $name => $description) {
675
                $editorfieldinfo = array(
676
                    'name' => $name,
677
                    'description' => $description,
678
                    'text' => $assignplugin->get_editor_text($name, $item->id),
679
                    'format' => $assignplugin->get_editor_format($name, $item->id)
680
                );
681
 
682
                // Now format the text.
683
                foreach ($fileareas as $filearea => $name) {
684
                    list($editorfieldinfo['text'], $editorfieldinfo['format']) = \core_external\util::format_text(
685
                        $editorfieldinfo['text'], $editorfieldinfo['format'], $assign->get_context(),
686
                        $component, $filearea, $item->id);
687
                }
688
 
689
                $plugin['editorfields'][] = $editorfieldinfo;
690
            }
691
            $plugins[] = $plugin;
692
        }
693
        return $plugins;
694
    }
695
 
696
    /**
697
     * Describes the parameters for get_submissions
698
     *
699
     * @return external_function_parameters
700
     * @since Moodle 2.5
701
     */
702
    public static function get_submissions_parameters() {
703
        return new external_function_parameters(
704
            array(
705
                'assignmentids' => new external_multiple_structure(
706
                    new external_value(PARAM_INT, 'assignment id'),
707
                    '1 or more assignment ids',
708
                    VALUE_REQUIRED),
709
                'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
710
                'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
711
                'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
712
            )
713
        );
714
    }
715
 
716
    /**
717
     * Returns submissions for the requested assignment ids
718
     *
719
     * @param int[] $assignmentids
720
     * @param string $status only return submissions with this status
721
     * @param int $since only return submissions with timemodified >= since
722
     * @param int $before only return submissions with timemodified <= before
723
     * @return array of submissions for each requested assignment
724
     * @since Moodle 2.5
725
     */
726
    public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
727
        global $DB, $CFG;
728
 
729
        $params = self::validate_parameters(self::get_submissions_parameters(),
730
                        array('assignmentids' => $assignmentids,
731
                              'status' => $status,
732
                              'since' => $since,
733
                              'before' => $before));
734
 
735
        $warnings = array();
736
        $assignments = array();
737
 
738
        // Check the user is allowed to get the submissions for the assignments requested.
739
        $placeholders = array();
740
        list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
741
        $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
742
               "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
743
        $placeholders['modname'] = 'assign';
744
        $cms = $DB->get_records_sql($sql, $placeholders);
745
        $assigns = array();
746
        foreach ($cms as $cm) {
747
            try {
748
                $context = context_module::instance($cm->id);
749
                self::validate_context($context);
750
                $assign = new assign($context, null, null);
751
                $assign->require_view_grades();
752
                $assigns[] = $assign;
753
            } catch (Exception $e) {
754
                $warnings[] = array(
755
                    'item' => 'assignment',
756
                    'itemid' => $cm->instance,
757
                    'warningcode' => '1',
758
                    'message' => 'No access rights in module context'
759
                );
760
            }
761
        }
762
 
763
        foreach ($assigns as $assign) {
764
            $submissions = array();
765
            $placeholders = array('assignid1' => $assign->get_instance()->id,
766
                                  'assignid2' => $assign->get_instance()->id);
767
 
768
            $submissionmaxattempt = 'SELECT mxs.userid, mxs.groupid, MAX(mxs.attemptnumber) AS maxattempt
769
                                     FROM {assign_submission} mxs
770
                                     WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid, mxs.groupid';
771
 
772
            $sql = "SELECT mas.id, mas.assignment,mas.userid,".
773
                   "mas.timecreated,mas.timemodified,mas.timestarted,mas.status,mas.groupid,mas.attemptnumber ".
774
                   "FROM {assign_submission} mas ".
775
                   "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
776
                   "AND mas.groupid = smx.groupid ".
777
                   "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
778
 
779
            if (!empty($params['status'])) {
780
                $placeholders['status'] = $params['status'];
781
                $sql = $sql." AND mas.status = :status";
782
            }
783
            if (!empty($params['before'])) {
784
                $placeholders['since'] = $params['since'];
785
                $placeholders['before'] = $params['before'];
786
                $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
787
            } else {
788
                $placeholders['since'] = $params['since'];
789
                $sql = $sql." AND mas.timemodified >= :since";
790
            }
791
 
792
            $submissionrecords = $DB->get_records_sql($sql, $placeholders);
793
 
794
            if (!empty($submissionrecords)) {
795
                $submissionplugins = $assign->get_submission_plugins();
796
                foreach ($submissionrecords as $submissionrecord) {
797
                    $submission = array(
798
                        'id' => $submissionrecord->id,
799
                        'userid' => $submissionrecord->userid,
800
                        'timecreated' => $submissionrecord->timecreated,
801
                        'timemodified' => $submissionrecord->timemodified,
802
                        'timestarted' => $submissionrecord->timestarted,
803
                        'status' => $submissionrecord->status,
804
                        'attemptnumber' => $submissionrecord->attemptnumber,
805
                        'groupid' => $submissionrecord->groupid,
806
                        'plugins' => self::get_plugins_data($assign, $submissionplugins, $submissionrecord),
807
                        'gradingstatus' => $assign->get_grading_status($submissionrecord->userid)
808
                    );
809
 
810
                    if (($assign->get_instance()->teamsubmission
811
                        && $assign->can_view_group_submission($submissionrecord->groupid))
812
                        || (!$assign->get_instance()->teamsubmission
813
                        && $assign->can_view_submission($submissionrecord->userid))
814
                    ) {
815
                        $submissions[] = $submission;
816
                    }
817
                }
818
            } else {
819
                $warnings[] = array(
820
                    'item' => 'module',
821
                    'itemid' => $assign->get_instance()->id,
822
                    'warningcode' => '3',
823
                    'message' => 'No submissions found'
824
                );
825
            }
826
 
827
            $assignments[] = array(
828
                'assignmentid' => $assign->get_instance()->id,
829
                'submissions' => $submissions
830
            );
831
 
832
        }
833
 
834
        $result = array(
835
            'assignments' => $assignments,
836
            'warnings' => $warnings
837
        );
838
        return $result;
839
    }
840
 
841
    /**
842
     * Creates an assignment plugin structure.
843
     *
844
     * @return external_single_structure the plugin structure
845
     */
846
    private static function get_plugin_structure() {
847
        return new external_single_structure(
848
            array(
849
                'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
850
                'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
851
                'fileareas' => new external_multiple_structure(
852
                    new external_single_structure(
853
                        array (
854
                            'area' => new external_value (PARAM_TEXT, 'file area'),
855
                            'files' => new external_files('files', VALUE_OPTIONAL),
856
                        )
857
                    ), 'fileareas', VALUE_OPTIONAL
858
                ),
859
                'editorfields' => new external_multiple_structure(
860
                    new external_single_structure(
861
                        array(
862
                            'name' => new external_value(PARAM_TEXT, 'field name'),
863
                            'description' => new external_value(PARAM_RAW, 'field description'),
864
                            'text' => new external_value (PARAM_RAW, 'field value'),
865
                            'format' => new external_format_value ('text')
866
                        )
867
                    )
868
                    , 'editorfields', VALUE_OPTIONAL
869
                )
870
            )
871
        );
872
    }
873
 
874
    /**
875
     * Creates a submission structure.
876
     *
877
     * @return external_single_structure the submission structure
878
     */
879
    private static function get_submission_structure($required = VALUE_REQUIRED) {
880
        return new external_single_structure(
881
            array(
882
                'id' => new external_value(PARAM_INT, 'submission id'),
883
                'userid' => new external_value(PARAM_INT, 'student id'),
884
                'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
885
                'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
886
                'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
887
                'timestarted' => new external_value(PARAM_INT, 'submission start time', VALUE_OPTIONAL),
888
                'status' => new external_value(PARAM_TEXT, 'submission status'),
889
                'groupid' => new external_value(PARAM_INT, 'group id'),
890
                'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
891
                'latest' => new external_value(PARAM_INT, 'latest attempt', VALUE_OPTIONAL),
892
                'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'plugins', VALUE_OPTIONAL),
893
                'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.', VALUE_OPTIONAL),
894
            ), 'submission info', $required
895
        );
896
    }
897
 
898
    /**
899
     * Creates an assign_submissions external_single_structure
900
     *
901
     * @return external_single_structure
902
     * @since Moodle 2.5
903
     */
904
    private static function get_submissions_structure() {
905
        return new external_single_structure(
906
            array (
907
                'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
908
                'submissions' => new external_multiple_structure(self::get_submission_structure())
909
            )
910
        );
911
    }
912
 
913
    /**
914
     * Describes the get_submissions return value
915
     *
916
     * @return external_single_structure
917
     * @since Moodle 2.5
918
     */
919
    public static function get_submissions_returns() {
920
        return new external_single_structure(
921
            array(
922
                'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
923
                'warnings' => new external_warnings()
924
            )
925
        );
926
    }
927
 
928
    /**
929
     * Describes the parameters for set_user_flags
930
     * @return external_function_parameters
931
     * @since  Moodle 2.6
932
     */
933
    public static function set_user_flags_parameters() {
934
        return new external_function_parameters(
935
            array(
936
                'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
937
                'userflags' => new external_multiple_structure(
938
                    new external_single_structure(
939
                        array(
940
                            'userid'           => new external_value(PARAM_INT, 'student id'),
941
                            'locked'           => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL),
942
                            'mailed'           => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL),
943
                            'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL),
944
                            'workflowstate'    => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL),
945
                            'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL)
946
                        )
947
                    )
948
                )
949
            )
950
        );
951
    }
952
 
953
    /**
954
     * Create or update user_flags records
955
     *
956
     * @param int $assignmentid the assignment for which the userflags are created or updated
957
     * @param array $userflags  An array of userflags to create or update
958
     * @return array containing success or failure information for each record
959
     * @since Moodle 2.6
960
     */
961
    public static function set_user_flags($assignmentid, $userflags = array()) {
962
        global $CFG, $DB;
963
 
964
        $params = self::validate_parameters(self::set_user_flags_parameters(),
965
                                            array('assignmentid' => $assignmentid,
966
                                                  'userflags' => $userflags));
967
 
968
        // Load assignment if it exists and if the user has the capability.
969
        list($assign, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
970
        require_capability('mod/assign:grade', $context);
971
 
972
        $results = array();
973
        foreach ($params['userflags'] as $userflag) {
974
            $success = true;
975
            $result = array();
976
 
977
            $record = $assign->get_user_flags($userflag['userid'], false);
978
            if ($record) {
979
                if (isset($userflag['locked'])) {
980
                    $record->locked = $userflag['locked'];
981
                }
982
                if (isset($userflag['mailed'])) {
983
                    $record->mailed = $userflag['mailed'];
984
                }
985
                if (isset($userflag['extensionduedate'])) {
986
                    $record->extensionduedate = $userflag['extensionduedate'];
987
                }
988
                if (isset($userflag['workflowstate'])) {
989
                    $record->workflowstate = $userflag['workflowstate'];
990
                }
991
                if (isset($userflag['allocatedmarker'])) {
992
                    $record->allocatedmarker = $userflag['allocatedmarker'];
993
                }
994
                if ($assign->update_user_flags($record)) {
995
                    $result['id'] = $record->id;
996
                    $result['userid'] = $userflag['userid'];
997
                } else {
998
                    $result['id'] = $record->id;
999
                    $result['userid'] = $userflag['userid'];
1000
                    $result['errormessage'] = 'Record created but values could not be set';
1001
                }
1002
            } else {
1003
                $record = $assign->get_user_flags($userflag['userid'], true);
1004
                $setfields = isset($userflag['locked'])
1005
                             || isset($userflag['mailed'])
1006
                             || isset($userflag['extensionduedate'])
1007
                             || isset($userflag['workflowstate'])
1008
                             || isset($userflag['allocatedmarker']);
1009
                if ($record) {
1010
                    if ($setfields) {
1011
                        if (isset($userflag['locked'])) {
1012
                            $record->locked = $userflag['locked'];
1013
                        }
1014
                        if (isset($userflag['mailed'])) {
1015
                            $record->mailed = $userflag['mailed'];
1016
                        }
1017
                        if (isset($userflag['extensionduedate'])) {
1018
                            $record->extensionduedate = $userflag['extensionduedate'];
1019
                        }
1020
                        if (isset($userflag['workflowstate'])) {
1021
                            $record->workflowstate = $userflag['workflowstate'];
1022
                        }
1023
                        if (isset($userflag['allocatedmarker'])) {
1024
                            $record->allocatedmarker = $userflag['allocatedmarker'];
1025
                        }
1026
                        if ($assign->update_user_flags($record)) {
1027
                            $result['id'] = $record->id;
1028
                            $result['userid'] = $userflag['userid'];
1029
                        } else {
1030
                            $result['id'] = $record->id;
1031
                            $result['userid'] = $userflag['userid'];
1032
                            $result['errormessage'] = 'Record created but values could not be set';
1033
                        }
1034
                    } else {
1035
                        $result['id'] = $record->id;
1036
                        $result['userid'] = $userflag['userid'];
1037
                    }
1038
                } else {
1039
                    $result['id'] = -1;
1040
                    $result['userid'] = $userflag['userid'];
1041
                    $result['errormessage'] = 'Record could not be created';
1042
                }
1043
            }
1044
 
1045
            $results[] = $result;
1046
        }
1047
        return $results;
1048
    }
1049
 
1050
    /**
1051
     * Describes the set_user_flags return value
1052
     * @return external_multiple_structure
1053
     * @since  Moodle 2.6
1054
     */
1055
    public static function set_user_flags_returns() {
1056
        return new external_multiple_structure(
1057
            new external_single_structure(
1058
                array(
1059
                    'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'),
1060
                    'userid' => new external_value(PARAM_INT, 'userid of record'),
1061
                    'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL)
1062
                )
1063
            )
1064
        );
1065
    }
1066
 
1067
    /**
1068
     * Describes the parameters for get_user_flags
1069
     * @return external_function_parameters
1070
     * @since  Moodle 2.6
1071
     */
1072
    public static function get_user_flags_parameters() {
1073
        return new external_function_parameters(
1074
            array(
1075
                'assignmentids' => new external_multiple_structure(
1076
                    new external_value(PARAM_INT, 'assignment id'),
1077
                    '1 or more assignment ids',
1078
                    VALUE_REQUIRED)
1079
            )
1080
        );
1081
    }
1082
 
1083
    /**
1084
     * Returns user flag information from assign_user_flags for the requested assignment ids
1085
     * @param int[] $assignmentids
1086
     * @return array of user flag records for each requested assignment
1087
     * @since  Moodle 2.6
1088
     */
1089
    public static function get_user_flags($assignmentids) {
1090
        global $DB;
1091
        $params = self::validate_parameters(self::get_user_flags_parameters(),
1092
                        array('assignmentids' => $assignmentids));
1093
 
1094
        $assignments = array();
1095
        $warnings = array();
1096
        $requestedassignmentids = $params['assignmentids'];
1097
 
1098
        // Check the user is allowed to get the user flags for the assignments requested.
1099
        $placeholders = array();
1100
        list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1101
        $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1102
               "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1103
        $placeholders['modname'] = 'assign';
1104
        $cms = $DB->get_records_sql($sql, $placeholders);
1105
        foreach ($cms as $cm) {
1106
            try {
1107
                $context = context_module::instance($cm->id);
1108
                self::validate_context($context);
1109
                require_capability('mod/assign:grade', $context);
1110
            } catch (Exception $e) {
1111
                $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1112
                $warning = array();
1113
                $warning['item'] = 'assignment';
1114
                $warning['itemid'] = $cm->instance;
1115
                $warning['warningcode'] = '1';
1116
                $warning['message'] = 'No access rights in module context';
1117
                $warnings[] = $warning;
1118
            }
1119
        }
1120
 
1121
        // Create the query and populate an array of assign_user_flags records from the recordset results.
1122
        if (count ($requestedassignmentids) > 0) {
1123
            $placeholders = array();
1124
            list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1125
 
1126
            $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
1127
                   "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
1128
                   "FROM {assign_user_flags} auf ".
1129
                   "WHERE auf.assignment ".$inorequalsql.
1130
                   " ORDER BY auf.assignment, auf.id";
1131
 
1132
            $rs = $DB->get_recordset_sql($sql, $placeholders);
1133
            $currentassignmentid = null;
1134
            $assignment = null;
1135
            foreach ($rs as $rd) {
1136
                $userflag = array();
1137
                $userflag['id'] = $rd->id;
1138
                $userflag['userid'] = $rd->userid;
1139
                $userflag['locked'] = $rd->locked;
1140
                $userflag['mailed'] = $rd->mailed;
1141
                $userflag['extensionduedate'] = $rd->extensionduedate;
1142
                $userflag['workflowstate'] = $rd->workflowstate;
1143
                $userflag['allocatedmarker'] = $rd->allocatedmarker;
1144
 
1145
                if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1146
                    if (!is_null($assignment)) {
1147
                        $assignments[] = $assignment;
1148
                    }
1149
                    $assignment = array();
1150
                    $assignment['assignmentid'] = $rd->assignment;
1151
                    $assignment['userflags'] = array();
1152
                    $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1153
                }
1154
                $assignment['userflags'][] = $userflag;
1155
 
1156
                $currentassignmentid = $rd->assignment;
1157
            }
1158
            if (!is_null($assignment)) {
1159
                $assignments[] = $assignment;
1160
            }
1161
            $rs->close();
1162
 
1163
        }
1164
 
1165
        foreach ($requestedassignmentids as $assignmentid) {
1166
            $warning = array();
1167
            $warning['item'] = 'assignment';
1168
            $warning['itemid'] = $assignmentid;
1169
            $warning['warningcode'] = '3';
1170
            $warning['message'] = 'No user flags found';
1171
            $warnings[] = $warning;
1172
        }
1173
 
1174
        $result = array();
1175
        $result['assignments'] = $assignments;
1176
        $result['warnings'] = $warnings;
1177
        return $result;
1178
    }
1179
 
1180
    /**
1181
     * Creates an assign_user_flags external_single_structure
1182
     * @return external_single_structure
1183
     * @since  Moodle 2.6
1184
     */
1185
    private static function assign_user_flags() {
1186
        return new external_single_structure(
1187
            array (
1188
                'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1189
                'userflags'   => new external_multiple_structure(new external_single_structure(
1190
                        array(
1191
                            'id'               => new external_value(PARAM_INT, 'user flag id'),
1192
                            'userid'           => new external_value(PARAM_INT, 'student id'),
1193
                            'locked'           => new external_value(PARAM_INT, 'locked'),
1194
                            'mailed'           => new external_value(PARAM_INT, 'mailed'),
1195
                            'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
1196
                            'workflowstate'    => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL),
1197
                            'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker')
1198
                        )
1199
                    )
1200
                )
1201
            )
1202
        );
1203
    }
1204
 
1205
    /**
1206
     * Describes the get_user_flags return value
1207
     * @return external_single_structure
1208
     * @since  Moodle 2.6
1209
     */
1210
    public static function get_user_flags_returns() {
1211
        return new external_single_structure(
1212
            array(
1213
                'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
1214
                'warnings'      => new external_warnings('item is always \'assignment\'',
1215
                    'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1216
                    'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
1217
            )
1218
        );
1219
    }
1220
 
1221
    /**
1222
     * Describes the parameters for get_user_mappings
1223
     * @return external_function_parameters
1224
     * @since  Moodle 2.6
1225
     */
1226
    public static function get_user_mappings_parameters() {
1227
        return new external_function_parameters(
1228
            array(
1229
                'assignmentids' => new external_multiple_structure(
1230
                    new external_value(PARAM_INT, 'assignment id'),
1231
                    '1 or more assignment ids',
1232
                    VALUE_REQUIRED)
1233
            )
1234
        );
1235
    }
1236
 
1237
    /**
1238
     * Returns user mapping information from assign_user_mapping for the requested assignment ids
1239
     * @param int[] $assignmentids
1240
     * @return array of user mapping records for each requested assignment
1241
     * @since  Moodle 2.6
1242
     */
1243
    public static function get_user_mappings($assignmentids) {
1244
        global $DB;
1245
        $params = self::validate_parameters(self::get_user_mappings_parameters(),
1246
                        array('assignmentids' => $assignmentids));
1247
 
1248
        $assignments = array();
1249
        $warnings = array();
1250
        $requestedassignmentids = $params['assignmentids'];
1251
 
1252
        // Check the user is allowed to get the mappings for the assignments requested.
1253
        $placeholders = array();
1254
        list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1255
        $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
1256
               "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
1257
        $placeholders['modname'] = 'assign';
1258
        $cms = $DB->get_records_sql($sql, $placeholders);
1259
        foreach ($cms as $cm) {
1260
            try {
1261
                $context = context_module::instance($cm->id);
1262
                self::validate_context($context);
1263
                require_capability('mod/assign:revealidentities', $context);
1264
            } catch (Exception $e) {
1265
                $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
1266
                $warning = array();
1267
                $warning['item'] = 'assignment';
1268
                $warning['itemid'] = $cm->instance;
1269
                $warning['warningcode'] = '1';
1270
                $warning['message'] = 'No access rights in module context';
1271
                $warnings[] = $warning;
1272
            }
1273
        }
1274
 
1275
        // Create the query and populate an array of assign_user_mapping records from the recordset results.
1276
        if (count ($requestedassignmentids) > 0) {
1277
            $placeholders = array();
1278
            list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
1279
 
1280
            $sql = "SELECT aum.id,aum.assignment,aum.userid ".
1281
                   "FROM {assign_user_mapping} aum ".
1282
                   "WHERE aum.assignment ".$inorequalsql.
1283
                   " ORDER BY aum.assignment, aum.id";
1284
 
1285
            $rs = $DB->get_recordset_sql($sql, $placeholders);
1286
            $currentassignmentid = null;
1287
            $assignment = null;
1288
            foreach ($rs as $rd) {
1289
                $mapping = array();
1290
                $mapping['id'] = $rd->id;
1291
                $mapping['userid'] = $rd->userid;
1292
 
1293
                if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
1294
                    if (!is_null($assignment)) {
1295
                        $assignments[] = $assignment;
1296
                    }
1297
                    $assignment = array();
1298
                    $assignment['assignmentid'] = $rd->assignment;
1299
                    $assignment['mappings'] = array();
1300
                    $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
1301
                }
1302
                $assignment['mappings'][] = $mapping;
1303
 
1304
                $currentassignmentid = $rd->assignment;
1305
            }
1306
            if (!is_null($assignment)) {
1307
                $assignments[] = $assignment;
1308
            }
1309
            $rs->close();
1310
 
1311
        }
1312
 
1313
        foreach ($requestedassignmentids as $assignmentid) {
1314
            $warning = array();
1315
            $warning['item'] = 'assignment';
1316
            $warning['itemid'] = $assignmentid;
1317
            $warning['warningcode'] = '3';
1318
            $warning['message'] = 'No mappings found';
1319
            $warnings[] = $warning;
1320
        }
1321
 
1322
        $result = array();
1323
        $result['assignments'] = $assignments;
1324
        $result['warnings'] = $warnings;
1325
        return $result;
1326
    }
1327
 
1328
    /**
1329
     * Creates an assign_user_mappings external_single_structure
1330
     * @return external_single_structure
1331
     * @since  Moodle 2.6
1332
     */
1333
    private static function assign_user_mappings() {
1334
        return new external_single_structure(
1335
            array (
1336
                'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
1337
                'mappings'   => new external_multiple_structure(new external_single_structure(
1338
                        array(
1339
                            'id'     => new external_value(PARAM_INT, 'user mapping id'),
1340
                            'userid' => new external_value(PARAM_INT, 'student id')
1341
                        )
1342
                    )
1343
                )
1344
            )
1345
        );
1346
    }
1347
 
1348
    /**
1349
     * Describes the get_user_mappings return value
1350
     * @return external_single_structure
1351
     * @since  Moodle 2.6
1352
     */
1353
    public static function get_user_mappings_returns() {
1354
        return new external_single_structure(
1355
            array(
1356
                'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
1357
                'warnings'      => new external_warnings('item is always \'assignment\'',
1358
                    'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
1359
                    'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
1360
            )
1361
        );
1362
    }
1363
 
1364
    /**
1365
     * Describes the parameters for lock_submissions
1366
     * @return external_function_parameters
1367
     * @since  Moodle 2.6
1368
     */
1369
    public static function lock_submissions_parameters() {
1370
        return new external_function_parameters(
1371
            array(
1372
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1373
                'userids' => new external_multiple_structure(
1374
                    new external_value(PARAM_INT, 'user id'),
1375
                    '1 or more user ids',
1376
                    VALUE_REQUIRED),
1377
            )
1378
        );
1379
    }
1380
 
1381
    /**
1382
     * Locks (prevent updates to) submissions in this assignment.
1383
     *
1384
     * @param int $assignmentid The id of the assignment
1385
     * @param array $userids Array of user ids to lock
1386
     * @return array of warnings for each submission that could not be locked.
1387
     * @since Moodle 2.6
1388
     */
1389
    public static function lock_submissions($assignmentid, $userids) {
1390
        global $CFG;
1391
 
1392
        $params = self::validate_parameters(self::lock_submissions_parameters(),
1393
                        array('assignmentid' => $assignmentid,
1394
                              'userids' => $userids));
1395
 
1396
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1397
 
1398
        $warnings = array();
1399
        foreach ($params['userids'] as $userid) {
1400
            if (!$assignment->lock_submission($userid)) {
1401
                $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1402
                $warnings[] = self::generate_warning($params['assignmentid'],
1403
                                                     'couldnotlock',
1404
                                                     $detail);
1405
            }
1406
        }
1407
 
1408
        return $warnings;
1409
    }
1410
 
1411
    /**
1412
     * Describes the return value for lock_submissions
1413
     *
1414
     * @return external_single_structure
1415
     * @since Moodle 2.6
1416
     */
1417
    public static function lock_submissions_returns() {
1418
        return new external_warnings();
1419
    }
1420
 
1421
    /**
1422
     * Describes the parameters for revert_submissions_to_draft
1423
     * @return external_function_parameters
1424
     * @since  Moodle 2.6
1425
     */
1426
    public static function revert_submissions_to_draft_parameters() {
1427
        return new external_function_parameters(
1428
            array(
1429
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1430
                'userids' => new external_multiple_structure(
1431
                    new external_value(PARAM_INT, 'user id'),
1432
                    '1 or more user ids',
1433
                    VALUE_REQUIRED),
1434
            )
1435
        );
1436
    }
1437
 
1438
    /**
1439
     * Reverts a list of user submissions to draft for a single assignment.
1440
     *
1441
     * @param int $assignmentid The id of the assignment
1442
     * @param array $userids Array of user ids to revert
1443
     * @return array of warnings for each submission that could not be reverted.
1444
     * @since Moodle 2.6
1445
     */
1446
    public static function revert_submissions_to_draft($assignmentid, $userids) {
1447
        global $CFG;
1448
 
1449
        $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
1450
                        array('assignmentid' => $assignmentid,
1451
                              'userids' => $userids));
1452
 
1453
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1454
 
1455
        $warnings = array();
1456
        foreach ($params['userids'] as $userid) {
1457
            if (!$assignment->revert_to_draft($userid)) {
1458
                $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1459
                $warnings[] = self::generate_warning($params['assignmentid'],
1460
                                                     'couldnotrevert',
1461
                                                     $detail);
1462
            }
1463
        }
1464
 
1465
        return $warnings;
1466
    }
1467
 
1468
    /**
1469
     * Describes the return value for revert_submissions_to_draft
1470
     *
1471
     * @return external_single_structure
1472
     * @since Moodle 2.6
1473
     */
1474
    public static function revert_submissions_to_draft_returns() {
1475
        return new external_warnings();
1476
    }
1477
 
1478
    /**
1479
     * Describes the parameters for unlock_submissions
1480
     * @return external_function_parameters
1481
     * @since  Moodle 2.6
1482
     */
1483
    public static function unlock_submissions_parameters() {
1484
        return new external_function_parameters(
1485
            array(
1486
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1487
                'userids' => new external_multiple_structure(
1488
                    new external_value(PARAM_INT, 'user id'),
1489
                    '1 or more user ids',
1490
                    VALUE_REQUIRED),
1491
            )
1492
        );
1493
    }
1494
 
1495
    /**
1496
     * Locks (prevent updates to) submissions in this assignment.
1497
     *
1498
     * @param int $assignmentid The id of the assignment
1499
     * @param array $userids Array of user ids to lock
1500
     * @return array of warnings for each submission that could not be locked.
1501
     * @since Moodle 2.6
1502
     */
1503
    public static function unlock_submissions($assignmentid, $userids) {
1504
        global $CFG;
1505
 
1506
        $params = self::validate_parameters(self::unlock_submissions_parameters(),
1507
                        array('assignmentid' => $assignmentid,
1508
                              'userids' => $userids));
1509
 
1510
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1511
 
1512
        $warnings = array();
1513
        foreach ($params['userids'] as $userid) {
1514
            if (!$assignment->unlock_submission($userid)) {
1515
                $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
1516
                $warnings[] = self::generate_warning($params['assignmentid'],
1517
                                                     'couldnotunlock',
1518
                                                     $detail);
1519
            }
1520
        }
1521
 
1522
        return $warnings;
1523
    }
1524
 
1525
    /**
1526
     * Describes the return value for unlock_submissions
1527
     *
1528
     * @return external_single_structure
1529
     * @since Moodle 2.6
1530
     */
1531
    public static function unlock_submissions_returns() {
1532
        return new external_warnings();
1533
    }
1534
 
1535
    /**
1536
     * Describes the parameters for submit_grading_form webservice.
1537
     * @return external_function_parameters
1538
     * @since  Moodle 3.1
1539
     */
1540
    public static function submit_grading_form_parameters() {
1541
        return new external_function_parameters(
1542
            array(
1543
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1544
                'userid' => new external_value(PARAM_INT, 'The user id the submission belongs to'),
1545
                'jsonformdata' => new external_value(PARAM_RAW, 'The data from the grading form, encoded as a json array')
1546
            )
1547
        );
1548
    }
1549
 
1550
    /**
1551
     * Submit the logged in users assignment for grading.
1552
     *
1553
     * @param int $assignmentid The id of the assignment
1554
     * @param int $userid The id of the user the submission belongs to.
1555
     * @param string $jsonformdata The data from the form, encoded as a json array.
1556
     * @return array of warnings to indicate any errors.
1557
     * @since Moodle 3.1
1558
     */
1559
    public static function submit_grading_form($assignmentid, $userid, $jsonformdata) {
1560
        global $CFG, $USER;
1561
 
1562
        require_once($CFG->dirroot . '/mod/assign/locallib.php');
1563
        require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1564
 
1565
        $params = self::validate_parameters(self::submit_grading_form_parameters(),
1566
                                            array(
1567
                                                'assignmentid' => $assignmentid,
1568
                                                'userid' => $userid,
1569
                                                'jsonformdata' => $jsonformdata
1570
                                            ));
1571
 
1572
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1573
 
1574
        $serialiseddata = json_decode($params['jsonformdata']);
1575
 
1576
        $data = array();
1577
        parse_str($serialiseddata, $data);
1578
 
1579
        $warnings = array();
1580
 
1581
        $options = array(
1582
            'userid' => $params['userid'],
1583
            'attemptnumber' => $data['attemptnumber'],
1584
            'rownum' => 0,
1585
            'gradingpanel' => true
1586
        );
1587
 
1588
        if (WS_SERVER) {
1589
            // Assume form submission if coming from WS.
1590
            $USER->ignoresesskey = true;
1591
            $data['_qf__mod_assign_grade_form_'.$params['userid']] = 1;
1592
        }
1593
 
1594
        $customdata = (object) $data;
1595
        $formparams = array($assignment, $customdata, $options);
1596
 
1597
        // Data is injected into the form by the last param for the constructor.
1598
        $mform = new mod_assign_grade_form(null, $formparams, 'post', '', null, true, $data);
1599
        $validateddata = $mform->get_data();
1600
 
1601
        if ($validateddata) {
1602
            $assignment->save_grade($params['userid'], $validateddata);
1603
        } else {
1604
            $warnings[] = self::generate_warning($params['assignmentid'],
1605
                                                 'couldnotsavegrade',
1606
                                                 'Form validation failed.');
1607
        }
1608
 
1609
 
1610
        return $warnings;
1611
    }
1612
 
1613
    /**
1614
     * Describes the return for submit_grading_form
1615
     * @return external_function_parameters
1616
     * @since  Moodle 3.1
1617
     */
1618
    public static function submit_grading_form_returns() {
1619
        return new external_warnings();
1620
    }
1621
 
1622
    /**
1623
     * Describes the parameters for submit_for_grading
1624
     * @return external_function_parameters
1625
     * @since  Moodle 2.6
1626
     */
1627
    public static function submit_for_grading_parameters() {
1628
        return new external_function_parameters(
1629
            array(
1630
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1631
                'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement')
1632
            )
1633
        );
1634
    }
1635
 
1636
    /**
1637
     * Submit the logged in users assignment for grading.
1638
     *
1639
     * @param int $assignmentid The id of the assignment
1640
     * @return array of warnings to indicate any errors.
1641
     * @since Moodle 2.6
1642
     */
1643
    public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
1644
        global $CFG, $USER;
1645
 
1646
        $params = self::validate_parameters(self::submit_for_grading_parameters(),
1647
                                            array('assignmentid' => $assignmentid,
1648
                                                  'acceptsubmissionstatement' => $acceptsubmissionstatement));
1649
 
1650
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1651
 
1652
        $warnings = array();
1653
        $data = new stdClass();
1654
        $data->submissionstatement = $params['acceptsubmissionstatement'];
1655
        $notices = array();
1656
 
1657
        if (!$assignment->submit_for_grading($data, $notices)) {
1658
            $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices);
1659
            $warnings[] = self::generate_warning($params['assignmentid'],
1660
                                                 'couldnotsubmitforgrading',
1661
                                                 $detail);
1662
        }
1663
 
1664
        return $warnings;
1665
    }
1666
 
1667
    /**
1668
     * Describes the return value for submit_for_grading
1669
     *
1670
     * @return external_single_structure
1671
     * @since Moodle 2.6
1672
     */
1673
    public static function submit_for_grading_returns() {
1674
        return new external_warnings();
1675
    }
1676
 
1677
    /**
1678
     * Describes the parameters for save_user_extensions
1679
     * @return external_function_parameters
1680
     * @since  Moodle 2.6
1681
     */
1682
    public static function save_user_extensions_parameters() {
1683
        return new external_function_parameters(
1684
            array(
1685
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1686
                'userids' => new external_multiple_structure(
1687
                    new external_value(PARAM_INT, 'user id'),
1688
                    '1 or more user ids',
1689
                    VALUE_REQUIRED),
1690
                'dates' => new external_multiple_structure(
1691
                    new external_value(PARAM_INT, 'dates'),
1692
                    '1 or more extension dates (timestamp)',
1693
                    VALUE_REQUIRED),
1694
            )
1695
        );
1696
    }
1697
 
1698
    /**
1699
     * Grant extension dates to students for an assignment.
1700
     *
1701
     * @param int $assignmentid The id of the assignment
1702
     * @param array $userids Array of user ids to grant extensions to
1703
     * @param array $dates Array of extension dates
1704
     * @return array of warnings for each extension date that could not be granted
1705
     * @since Moodle 2.6
1706
     */
1707
    public static function save_user_extensions($assignmentid, $userids, $dates) {
1708
        global $CFG;
1709
 
1710
        $params = self::validate_parameters(self::save_user_extensions_parameters(),
1711
                        array('assignmentid' => $assignmentid,
1712
                              'userids' => $userids,
1713
                              'dates' => $dates));
1714
 
1715
        if (count($params['userids']) != count($params['dates'])) {
1716
            $detail = 'Length of userids and dates parameters differ.';
1717
            $warnings[] = self::generate_warning($params['assignmentid'],
1718
                                                 'invalidparameters',
1719
                                                 $detail);
1720
 
1721
            return $warnings;
1722
        }
1723
 
1724
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1725
 
1726
        $warnings = array();
1727
        foreach ($params['userids'] as $idx => $userid) {
1728
            $duedate = $params['dates'][$idx];
1729
            if (!$assignment->save_user_extension($userid, $duedate)) {
1730
                $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate;
1731
                $warnings[] = self::generate_warning($params['assignmentid'],
1732
                                                     'couldnotgrantextensions',
1733
                                                     $detail);
1734
            }
1735
        }
1736
 
1737
        return $warnings;
1738
    }
1739
 
1740
    /**
1741
     * Describes the return value for save_user_extensions
1742
     *
1743
     * @return external_single_structure
1744
     * @since Moodle 2.6
1745
     */
1746
    public static function save_user_extensions_returns() {
1747
        return new external_warnings();
1748
    }
1749
 
1750
    /**
1751
     * Describes the parameters for reveal_identities
1752
     * @return external_function_parameters
1753
     * @since  Moodle 2.6
1754
     */
1755
    public static function reveal_identities_parameters() {
1756
        return new external_function_parameters(
1757
            array(
1758
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
1759
            )
1760
        );
1761
    }
1762
 
1763
    /**
1764
     * Reveal the identities of anonymous students to markers for a single assignment.
1765
     *
1766
     * @param int $assignmentid The id of the assignment
1767
     * @return array of warnings to indicate any errors.
1768
     * @since Moodle 2.6
1769
     */
1770
    public static function reveal_identities($assignmentid) {
1771
        global $CFG, $USER;
1772
 
1773
        $params = self::validate_parameters(self::reveal_identities_parameters(),
1774
                                            array('assignmentid' => $assignmentid));
1775
 
1776
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1777
 
1778
        $warnings = array();
1779
        if (!$assignment->reveal_identities()) {
1780
            $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'];
1781
            $warnings[] = self::generate_warning($params['assignmentid'],
1782
                                                 'couldnotrevealidentities',
1783
                                                 $detail);
1784
        }
1785
 
1786
        return $warnings;
1787
    }
1788
 
1789
    /**
1790
     * Describes the return value for reveal_identities
1791
     *
1792
     * @return external_single_structure
1793
     * @since Moodle 2.6
1794
     */
1795
    public static function reveal_identities_returns() {
1796
        return new external_warnings();
1797
    }
1798
 
1799
    /**
1800
     * Describes the parameters for save_submission
1801
     * @return external_function_parameters
1802
     * @since  Moodle 2.6
1803
     */
1804
    public static function save_submission_parameters() {
1805
        global $CFG;
1806
        $instance = new assign(null, null, null);
1807
        $pluginsubmissionparams = array();
1808
 
1809
        foreach ($instance->get_submission_plugins() as $plugin) {
1810
            if ($plugin->is_visible()) {
1811
                $pluginparams = $plugin->get_external_parameters();
1812
                if (!empty($pluginparams)) {
1813
                    $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
1814
                }
1815
            }
1816
        }
1817
 
1818
        return new external_function_parameters(
1819
            array(
1820
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1821
                'plugindata' => new external_single_structure(
1822
                    $pluginsubmissionparams
1823
                )
1824
            )
1825
        );
1826
    }
1827
 
1828
    /**
1829
     * Save a student submission for a single assignment
1830
     *
1831
     * @param int $assignmentid The id of the assignment
1832
     * @param array $plugindata - The submitted data for plugins
1833
     * @return array of warnings to indicate any errors
1834
     * @since Moodle 2.6
1835
     */
1836
    public static function save_submission($assignmentid, $plugindata) {
1837
        global $CFG, $USER;
1838
 
1839
        $params = self::validate_parameters(self::save_submission_parameters(),
1840
                                            array('assignmentid' => $assignmentid,
1841
                                                  'plugindata' => $plugindata));
1842
 
1843
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1844
 
1845
        $notices = array();
1846
 
1847
        $assignment->update_effective_access($USER->id);
1848
        if (!$assignment->submissions_open($USER->id)) {
1849
            $notices[] = get_string('duedatereached', 'assign');
1850
        } else {
1851
            $submissiondata = (object)$params['plugindata'];
1852
            $assignment->save_submission($submissiondata, $notices);
1853
        }
1854
 
1855
        $warnings = array();
1856
        foreach ($notices as $notice) {
1857
            $warnings[] = self::generate_warning($params['assignmentid'],
1858
                                                 'couldnotsavesubmission',
1859
                                                 $notice);
1860
        }
1861
 
1862
        return $warnings;
1863
    }
1864
 
1865
    /**
1866
     * Describes the return value for save_submission
1867
     *
1868
     * @return external_single_structure
1869
     * @since Moodle 2.6
1870
     */
1871
    public static function save_submission_returns() {
1872
        return new external_warnings();
1873
    }
1874
 
1875
    /**
1876
     * Describes the parameters for save_grade
1877
     * @return external_function_parameters
1878
     * @since  Moodle 2.6
1879
     */
1880
    public static function save_grade_parameters() {
1881
        global $CFG;
1882
        require_once("$CFG->dirroot/grade/grading/lib.php");
1883
        $instance = new assign(null, null, null);
1884
        $pluginfeedbackparams = array();
1885
 
1886
        foreach ($instance->get_feedback_plugins() as $plugin) {
1887
            if ($plugin->is_visible()) {
1888
                $pluginparams = $plugin->get_external_parameters();
1889
                if (!empty($pluginparams)) {
1890
                    $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
1891
                }
1892
            }
1893
        }
1894
 
1895
        $advancedgradingdata = array();
1896
        $methods = array_keys(grading_manager::available_methods(false));
1897
        foreach ($methods as $method) {
1898
            require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
1899
            $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
1900
            if (!empty($details)) {
1901
                $items = array();
1902
                foreach ($details as $key => $value) {
1903
                    $value->required = VALUE_OPTIONAL;
1904
                    unset($value->content->keys['id']);
1905
                    $items[$key] = new external_multiple_structure (new external_single_structure(
1906
                        array(
1907
                            'criterionid' => new external_value(PARAM_INT, 'criterion id'),
1908
                            'fillings' => $value
1909
                        )
1910
                    ));
1911
                }
1912
                $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
1913
            }
1914
        }
1915
 
1916
        return new external_function_parameters(
1917
            array(
1918
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
1919
                'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
1920
                'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'),
1921
                'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
1922
                'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
1923
                'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
1924
                'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
1925
                                                               'to all members ' .
1926
                                                               'of the group (for group assignments).'),
1927
                'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()),
1928
                'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
1929
                                                                       VALUE_DEFAULT, array())
1930
            )
1931
        );
1932
    }
1933
 
1934
    /**
1935
     * Save a student grade for a single assignment.
1936
     *
1937
     * @param int $assignmentid The id of the assignment
1938
     * @param int $userid The id of the user
1939
     * @param float $grade The grade (ignored if the assignment uses advanced grading)
1940
     * @param int $attemptnumber The attempt number
1941
     * @param bool $addattempt Allow another attempt
1942
     * @param string $workflowstate New workflow state
1943
     * @param bool $applytoall Apply the grade to all members of the group
1944
     * @param array $plugindata Custom data used by plugins
1945
     * @param array $advancedgradingdata Advanced grading data
1946
     * @return null
1947
     * @since Moodle 2.6
1948
     */
1949
    public static function save_grade($assignmentid,
1950
                                      $userid,
1951
                                      $grade,
1952
                                      $attemptnumber,
1953
                                      $addattempt,
1954
                                      $workflowstate,
1955
                                      $applytoall,
1956
                                      $plugindata = array(),
1957
                                      $advancedgradingdata = array()) {
1958
        global $CFG, $USER;
1959
 
1960
        $params = self::validate_parameters(self::save_grade_parameters(),
1961
                                            array('assignmentid' => $assignmentid,
1962
                                                  'userid' => $userid,
1963
                                                  'grade' => $grade,
1964
                                                  'attemptnumber' => $attemptnumber,
1965
                                                  'workflowstate' => $workflowstate,
1966
                                                  'addattempt' => $addattempt,
1967
                                                  'applytoall' => $applytoall,
1968
                                                  'plugindata' => $plugindata,
1969
                                                  'advancedgradingdata' => $advancedgradingdata));
1970
 
1971
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
1972
 
1973
        $gradedata = (object)$params['plugindata'];
1974
 
1975
        $gradedata->addattempt = $params['addattempt'];
1976
        $gradedata->attemptnumber = $params['attemptnumber'];
1977
        $gradedata->workflowstate = $params['workflowstate'];
1978
        $gradedata->applytoall = $params['applytoall'];
1979
        $gradedata->grade = $params['grade'];
1980
 
1981
        if (!empty($params['advancedgradingdata'])) {
1982
            $advancedgrading = array();
1983
            $criteria = reset($params['advancedgradingdata']);
1984
            foreach ($criteria as $key => $criterion) {
1985
                $details = array();
1986
                foreach ($criterion as $value) {
1987
                    foreach ($value['fillings'] as $filling) {
1988
                        $details[$value['criterionid']] = $filling;
1989
                    }
1990
                }
1991
                $advancedgrading[$key] = $details;
1992
            }
1993
            $gradedata->advancedgrading = $advancedgrading;
1994
        }
1995
 
1996
        $assignment->save_grade($params['userid'], $gradedata);
1997
 
1998
        return null;
1999
    }
2000
 
2001
    /**
2002
     * Describes the return value for save_grade
2003
     *
2004
     * @return external_single_structure
2005
     * @since Moodle 2.6
2006
     */
2007
    public static function save_grade_returns() {
2008
        return null;
2009
    }
2010
 
2011
    /**
2012
     * Describes the parameters for save_grades
2013
     * @return external_function_parameters
2014
     * @since  Moodle 2.7
2015
     */
2016
    public static function save_grades_parameters() {
2017
        global $CFG;
2018
        require_once("$CFG->dirroot/grade/grading/lib.php");
2019
        $instance = new assign(null, null, null);
2020
        $pluginfeedbackparams = array();
2021
 
2022
        foreach ($instance->get_feedback_plugins() as $plugin) {
2023
            if ($plugin->is_visible()) {
2024
                $pluginparams = $plugin->get_external_parameters();
2025
                if (!empty($pluginparams)) {
2026
                    $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
2027
                }
2028
            }
2029
        }
2030
 
2031
        $advancedgradingdata = array();
2032
        $methods = array_keys(grading_manager::available_methods(false));
2033
        foreach ($methods as $method) {
2034
            require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
2035
            $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
2036
            if (!empty($details)) {
2037
                $items = array();
2038
                foreach ($details as $key => $value) {
2039
                    $value->required = VALUE_OPTIONAL;
2040
                    unset($value->content->keys['id']);
2041
                    $items[$key] = new external_multiple_structure (new external_single_structure(
2042
                        array(
2043
                            'criterionid' => new external_value(PARAM_INT, 'criterion id'),
2044
                            'fillings' => $value
2045
                        )
2046
                    ));
2047
                }
2048
                $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
2049
            }
2050
        }
2051
 
2052
        return new external_function_parameters(
2053
            array(
2054
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
2055
                'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
2056
                                                               'to all members ' .
2057
                                                               'of the group (for group assignments).'),
2058
                'grades' => new external_multiple_structure(
2059
                    new external_single_structure(
2060
                        array (
2061
                            'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
2062
                            'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '.
2063
                                                                       'Ignored if advanced grading used'),
2064
                            'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
2065
                            'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'),
2066
                            'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
2067
                            'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data',
2068
                                                                          VALUE_DEFAULT, array()),
2069
                            'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
2070
                                                                                   VALUE_DEFAULT, array())
2071
                        )
2072
                    )
2073
                )
2074
            )
2075
        );
2076
    }
2077
 
2078
    /**
2079
     * Save multiple student grades for a single assignment.
2080
     *
2081
     * @param int $assignmentid The id of the assignment
2082
     * @param boolean $applytoall If set to true and this is a team assignment,
2083
     * apply the grade to all members of the group
2084
     * @param array $grades grade data for one or more students that includes
2085
     *                  userid - The id of the student being graded
2086
     *                  grade - The grade (ignored if the assignment uses advanced grading)
2087
     *                  attemptnumber - The attempt number
2088
     *                  addattempt - Allow another attempt
2089
     *                  workflowstate - New workflow state
2090
     *                  plugindata - Custom data used by plugins
2091
     *                  advancedgradingdata - Optional Advanced grading data
2092
     * @throws invalid_parameter_exception if multiple grades are supplied for
2093
     * a team assignment that has $applytoall set to true
2094
     * @return null
2095
     * @since Moodle 2.7
2096
     */
2097
    public static function save_grades($assignmentid, $applytoall, $grades) {
2098
        global $CFG, $USER;
2099
 
2100
        $params = self::validate_parameters(self::save_grades_parameters(),
2101
                                            array('assignmentid' => $assignmentid,
2102
                                                  'applytoall' => $applytoall,
2103
                                                  'grades' => $grades));
2104
 
2105
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
2106
 
2107
        if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
2108
            // Check that only 1 user per submission group is provided.
2109
            $groupids = array();
2110
            foreach ($params['grades'] as $gradeinfo) {
2111
                $group = $assignment->get_submission_group($gradeinfo['userid']);
2112
                if (in_array($group->id, $groupids)) {
2113
                    throw new invalid_parameter_exception('Multiple grades for the same team have been supplied '
2114
                                                          .' this is not permitted when the applytoall flag is set');
2115
                } else {
2116
                    $groupids[] = $group->id;
2117
                }
2118
            }
2119
        }
2120
 
2121
        foreach ($params['grades'] as $gradeinfo) {
2122
            $gradedata = (object)$gradeinfo['plugindata'];
2123
            $gradedata->addattempt = $gradeinfo['addattempt'];
2124
            $gradedata->attemptnumber = $gradeinfo['attemptnumber'];
2125
            $gradedata->workflowstate = $gradeinfo['workflowstate'];
2126
            $gradedata->applytoall = $params['applytoall'];
2127
            $gradedata->grade = $gradeinfo['grade'];
2128
 
2129
            if (!empty($gradeinfo['advancedgradingdata'])) {
2130
                $advancedgrading = array();
2131
                $criteria = reset($gradeinfo['advancedgradingdata']);
2132
                foreach ($criteria as $key => $criterion) {
2133
                    $details = array();
2134
                    foreach ($criterion as $value) {
2135
                        foreach ($value['fillings'] as $filling) {
2136
                            $details[$value['criterionid']] = $filling;
2137
                        }
2138
                    }
2139
                    $advancedgrading[$key] = $details;
2140
                }
2141
                $gradedata->advancedgrading = $advancedgrading;
2142
            }
2143
            $assignment->save_grade($gradeinfo['userid'], $gradedata);
2144
        }
2145
 
2146
        return null;
2147
    }
2148
 
2149
    /**
2150
     * Describes the return value for save_grades
2151
     *
2152
     * @return external_single_structure
2153
     * @since Moodle 2.7
2154
     */
2155
    public static function save_grades_returns() {
2156
        return null;
2157
    }
2158
 
2159
    /**
2160
     * Describes the parameters for copy_previous_attempt
2161
     * @return external_function_parameters
2162
     * @since  Moodle 2.6
2163
     */
2164
    public static function copy_previous_attempt_parameters() {
2165
        return new external_function_parameters(
2166
            array(
2167
                'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
2168
            )
2169
        );
2170
    }
2171
 
2172
    /**
2173
     * Copy a students previous attempt to a new attempt.
2174
     *
2175
     * @param int $assignmentid
2176
     * @return array of warnings to indicate any errors.
2177
     * @since Moodle 2.6
2178
     */
2179
    public static function copy_previous_attempt($assignmentid) {
2180
 
2181
        $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
2182
                                            array('assignmentid' => $assignmentid));
2183
 
2184
        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
2185
 
2186
        $notices = array();
2187
 
2188
        $assignment->copy_previous_attempt($notices);
2189
 
2190
        $warnings = array();
2191
        foreach ($notices as $notice) {
2192
            $warnings[] = self::generate_warning($assignmentid,
2193
                                                 'couldnotcopyprevioussubmission',
2194
                                                 $notice);
2195
        }
2196
 
2197
        return $warnings;
2198
    }
2199
 
2200
    /**
2201
     * Describes the return value for save_submission
2202
     *
2203
     * @return external_single_structure
2204
     * @since Moodle 2.6
2205
     */
2206
    public static function copy_previous_attempt_returns() {
2207
        return new external_warnings();
2208
    }
2209
 
2210
    /**
2211
     * Returns description of method parameters
2212
     *
2213
     * @return external_function_parameters
2214
     * @since Moodle 3.0
2215
     */
2216
    public static function view_grading_table_parameters() {
2217
        return new external_function_parameters(
2218
            array(
2219
                'assignid' => new external_value(PARAM_INT, 'assign instance id')
2220
            )
2221
        );
2222
    }
2223
 
2224
    /**
2225
     * Trigger the grading_table_viewed event.
2226
     *
2227
     * @param int $assignid the assign instance id
2228
     * @return array of warnings and status result
2229
     * @since Moodle 3.0
2230
     * @throws moodle_exception
2231
     */
2232
    public static function view_grading_table($assignid) {
2233
 
2234
        $params = self::validate_parameters(self::view_grading_table_parameters(),
2235
                                            array(
2236
                                                'assignid' => $assignid
2237
                                            ));
2238
        $warnings = array();
2239
 
2240
        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2241
 
2242
        $assign->require_view_grades();
2243
        \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger();
2244
 
2245
        $result = array();
2246
        $result['status'] = true;
2247
        $result['warnings'] = $warnings;
2248
        return $result;
2249
    }
2250
 
2251
    /**
2252
     * Returns description of method result value
2253
     *
2254
     * @return \core_external\external_description
2255
     * @since Moodle 3.0
2256
     */
2257
    public static function view_grading_table_returns() {
2258
        return new external_single_structure([
2259
            'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2260
            'warnings' => new external_warnings()
2261
        ]);
2262
    }
2263
 
2264
    /**
2265
     * Describes the parameters for view_submission_status.
2266
     *
2267
     * @return external_function_parameters
2268
     * @since Moodle 3.1
2269
     */
2270
    public static function view_submission_status_parameters() {
2271
        return new external_function_parameters ([
2272
            'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2273
        ]);
2274
    }
2275
 
2276
    /**
2277
     * Trigger the submission status viewed event.
2278
     *
2279
     * @param int $assignid assign instance id
2280
     * @return array of warnings and status result
2281
     * @since Moodle 3.1
2282
     */
2283
    public static function view_submission_status($assignid) {
2284
 
2285
        $warnings = array();
2286
        $params = array(
2287
            'assignid' => $assignid,
2288
        );
2289
        $params = self::validate_parameters(self::view_submission_status_parameters(), $params);
2290
 
2291
        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2292
 
2293
        \mod_assign\event\submission_status_viewed::create_from_assign($assign)->trigger();
2294
 
2295
        $result = array();
2296
        $result['status'] = true;
2297
        $result['warnings'] = $warnings;
2298
        return $result;
2299
    }
2300
 
2301
    /**
2302
     * Describes the view_submission_status return value.
2303
     *
2304
     * @return external_single_structure
2305
     * @since Moodle 3.1
2306
     */
2307
    public static function view_submission_status_returns() {
2308
        return new external_single_structure(
2309
            array(
2310
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2311
                'warnings' => new external_warnings(),
2312
            )
2313
        );
2314
    }
2315
 
2316
    /**
2317
     * Describes the parameters for get_submission_status.
2318
     *
2319
     * @return external_function_parameters
2320
     * @since Moodle 3.1
2321
     */
2322
    public static function get_submission_status_parameters() {
2323
        return new external_function_parameters (
2324
            array(
2325
                'assignid' => new external_value(PARAM_INT, 'assignment instance id'),
2326
                'userid' => new external_value(PARAM_INT, 'user id (empty for current user)', VALUE_DEFAULT, 0),
2327
                'groupid' => new external_value(PARAM_INT, 'filter by users in group (used for generating the grading summary).
2328
 
2329
            )
2330
        );
2331
    }
2332
 
2333
    /**
2334
     * Returns information about an assignment submission status for a given user.
2335
     *
2336
     * @param int $assignid assignment instance id
2337
     * @param int $userid user id (empty for current user)
2338
     * @param int $groupid filter by users in group id (used for generating the grading summary). Use 0 for all groups information.
2339
     * @return array of warnings and grading, status, feedback and previous attempts information
2340
     * @since Moodle 3.1
2341
     * @throws required_capability_exception
2342
     */
2343
    public static function get_submission_status($assignid, $userid = 0, $groupid = 0) {
2344
        global $USER;
2345
 
2346
        $warnings = array();
2347
 
2348
        $params = array(
2349
            'assignid' => $assignid,
2350
            'userid' => $userid,
2351
            'groupid' => $groupid,
2352
        );
2353
        $params = self::validate_parameters(self::get_submission_status_parameters(), $params);
2354
 
2355
        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2356
 
2357
        // Default value for userid.
2358
        if (empty($params['userid'])) {
2359
            $params['userid'] = $USER->id;
2360
        }
2361
        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2362
        core_user::require_active_user($user);
2363
 
2364
        if (!$assign->can_view_submission($user->id)) {
2365
            throw new required_capability_exception($context, 'mod/assign:viewgrades', 'nopermission', '');
2366
        }
2367
 
2368
        $assign->update_effective_access($user->id);
2369
 
2370
        $gradingsummary = $lastattempt = $feedback = $previousattempts = null;
2371
 
2372
        // Get the renderable since it contais all the info we need.
2373
        if (!empty($params['groupid'])) {
2374
            $groupid = $params['groupid'];
2375
            // Determine is the group is visible to user.
2376
            if (!groups_group_visible($groupid, $course, $cm)) {
2377
                throw new moodle_exception('notingroup');
2378
            }
2379
        } else {
2380
            // A null group means that following functions will calculate the current group.
2381
            // A groupid set to 0 means all groups.
2382
            $groupid = ($params['groupid'] == 0) ? 0 : null;
2383
        }
2384
        if ($assign->can_view_grades($groupid)) {
2385
            $gradingsummary = $assign->get_assign_grading_summary_renderable($groupid);
2386
        }
2387
 
2388
        // Retrieve the rest of the renderable objects.
2389
        if (has_capability('mod/assign:viewownsubmissionsummary', $context, $user, false)) {
2390
            // The user can view the submission summary.
2391
            $lastattempt = $assign->get_assign_submission_status_renderable($user, true);
2392
        }
2393
 
2394
        $feedback = $assign->get_assign_feedback_status_renderable($user);
2395
 
2396
        $previousattempts = $assign->get_assign_attempt_history_renderable($user);
2397
 
2398
        // Now, build the result.
2399
        $result = array();
2400
 
2401
        // First of all, grading summary, this is suitable for teachers/managers.
2402
        if ($gradingsummary) {
2403
            $result['gradingsummary'] = $gradingsummary;
2404
        }
2405
        // Show the grader's identity if 'Hide Grader' is disabled or has the 'Show Hidden Grader' capability.
2406
        $showgradername = (has_capability('mod/assign:showhiddengrader', $context) or
2407
            !$assign->is_hidden_grader());
2408
 
2409
        // Did we submit anything?
2410
        if ($lastattempt) {
2411
            $submissionplugins = $assign->get_submission_plugins();
2412
 
2413
            if (empty($lastattempt->submission)) {
2414
                unset($lastattempt->submission);
2415
            } else {
2416
                $lastattempt->submission->plugins = self::get_plugins_data($assign, $submissionplugins, $lastattempt->submission);
2417
            }
2418
 
2419
            if (empty($lastattempt->teamsubmission)) {
2420
                unset($lastattempt->teamsubmission);
2421
            } else {
2422
                $lastattempt->teamsubmission->plugins = self::get_plugins_data($assign, $submissionplugins,
2423
                                                                                $lastattempt->teamsubmission);
2424
            }
2425
 
2426
            // We need to change the type of some of the structures retrieved from the renderable.
2427
            if (!empty($lastattempt->submissiongroup)) {
2428
                $lastattempt->submissiongroup = $lastattempt->submissiongroup->id;
2429
            } else {
2430
                unset($lastattempt->submissiongroup);
2431
            }
2432
 
2433
            if (!empty($lastattempt->usergroups)) {
2434
                $lastattempt->usergroups = array_keys($lastattempt->usergroups);
2435
            }
2436
            // We cannot use array_keys here.
2437
            if (!empty($lastattempt->submissiongroupmemberswhoneedtosubmit)) {
2438
                $lastattempt->submissiongroupmemberswhoneedtosubmit = array_map(
2439
                                                                            function($e){
2440
                                                                                return $e->id;
2441
                                                                            },
2442
                                                                            $lastattempt->submissiongroupmemberswhoneedtosubmit);
2443
            }
2444
 
2445
            // Can edit its own submission?
2446
            $lastattempt->caneditowner = has_capability('mod/assign:submit', $context, $user, false)
2447
                && $assign->submissions_open($user->id) && $assign->is_any_submission_plugin_enabled();
2448
 
2449
            $result['lastattempt'] = $lastattempt;
2450
        }
2451
 
2452
        // The feedback for our latest submission.
2453
        if ($feedback) {
2454
            if ($feedback->grade) {
2455
                if (!$showgradername) {
2456
                    $feedback->grade->grader = -1;
2457
                }
2458
                $feedbackplugins = $assign->get_feedback_plugins();
2459
                $feedback->plugins = self::get_plugins_data($assign, $feedbackplugins, $feedback->grade);
2460
            } else {
2461
                unset($feedback->plugins);
2462
                unset($feedback->grade);
2463
            }
2464
 
2465
            $result['feedback'] = $feedback;
2466
        }
2467
 
2468
        // Retrieve only previous attempts.
2469
        if ($previousattempts and count($previousattempts->submissions) > 1) {
2470
            // Don't show the last one because it is the current submission.
2471
            array_pop($previousattempts->submissions);
2472
 
2473
            // Show newest to oldest.
2474
            $previousattempts->submissions = array_reverse($previousattempts->submissions);
2475
 
2476
            foreach ($previousattempts->submissions as $i => $submission) {
2477
                $attempt = array();
2478
 
2479
                $grade = null;
2480
                foreach ($previousattempts->grades as $onegrade) {
2481
                    if ($onegrade->attemptnumber == $submission->attemptnumber) {
2482
                        $grade = $onegrade;
2483
                        break;
2484
                    }
2485
                }
2486
 
2487
                $attempt['attemptnumber'] = $submission->attemptnumber;
2488
 
2489
                if ($submission) {
2490
                    $submission->plugins = self::get_plugins_data($assign, $previousattempts->submissionplugins, $submission);
2491
                    $attempt['submission'] = $submission;
2492
                }
2493
 
2494
                if ($grade) {
2495
                    // From object to id.
2496
                    if (!$showgradername) {
2497
                        $grade->grader = -1;
2498
                    } else {
2499
                        $grade->grader = $grade->grader->id;
2500
                    }
2501
 
2502
                    $feedbackplugins = self::get_plugins_data($assign, $previousattempts->feedbackplugins, $grade);
2503
 
2504
                    $attempt['grade'] = $grade;
2505
                    $attempt['feedbackplugins'] = $feedbackplugins;
2506
                }
2507
                $result['previousattempts'][] = $attempt;
2508
            }
2509
        }
2510
 
2511
        // Send back some assignment data as well.
2512
        $instance = $assign->get_instance();
2513
        $assignmentdata = [];
2514
        $attachments = [];
2515
        if ($assign->should_provide_intro_attachments($user->id)) {
2516
            $attachments['intro'] = external_util::get_area_files($context->id, 'mod_assign',
2517
                    ASSIGN_INTROATTACHMENT_FILEAREA, 0);
2518
        }
2519
        if ($instance->activity && ($lastattempt || $assign->submissions_open($user->id, true))) {
2520
            [$assignmentdata['activity'], $assignmentdata['activityformat']] = \core_external\util::format_text(
2521
                $instance->activity,
2522
                $instance->activityformat,
2523
                $context,
2524
                'mod_assign',
2525
                ASSIGN_ACTIVITYATTACHMENT_FILEAREA,
2526
 
2527
            );
2528
            $attachments['activity'] = external_util::get_area_files($context->id, 'mod_assign',
2529
                ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0);
2530
        }
2531
        if (!empty($attachments)) {
2532
            $assignmentdata['attachments'] = $attachments;
2533
        }
2534
        $result['assignmentdata'] = $assignmentdata;
2535
 
2536
        $result['warnings'] = $warnings;
2537
        return $result;
2538
    }
2539
 
2540
    /**
2541
     * Describes the get_submission_status return value.
2542
     *
2543
     * @return external_single_structure
2544
     * @since Moodle 3.1
2545
     */
2546
    public static function get_submission_status_returns() {
2547
        return new external_single_structure(
2548
            array(
2549
                'gradingsummary' => new external_single_structure(
2550
                    array(
2551
                        'participantcount' => new external_value(PARAM_INT, 'Number of users who can submit.'),
2552
                        'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'),
2553
                        'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'),
2554
                        'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'),
2555
                        'submissionssubmittedcount' => new external_value(PARAM_INT, 'Number of submissions in submitted status.'),
2556
                        'submissionsneedgradingcount' => new external_value(PARAM_INT, 'Number of submissions that need grading.'),
2557
                        'warnofungroupedusers' => new external_value(PARAM_ALPHA, 'Whether we need to warn people that there
2558
                                                                        are users without groups (\'warningrequired\'), warn
2559
                                                                        people there are users who will submit in the default
2560
                                                                        group (\'warningoptional\') or no warning (\'\').'),
2561
                    ), 'Grading information.', VALUE_OPTIONAL
2562
                ),
2563
                'lastattempt' => new external_single_structure(
2564
                    array(
2565
                        'submission' => self::get_submission_structure(VALUE_OPTIONAL),
2566
                        'teamsubmission' => self::get_submission_structure(VALUE_OPTIONAL),
2567
                        'submissiongroup' => new external_value(PARAM_INT, 'The submission group id (for group submissions only).',
2568
                                                                VALUE_OPTIONAL),
2569
                        'submissiongroupmemberswhoneedtosubmit' => new external_multiple_structure(
2570
                            new external_value(PARAM_INT, 'USER id.'),
2571
                            'List of users who still need to submit (for group submissions only).',
2572
                            VALUE_OPTIONAL
2573
                        ),
2574
                        'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'),
2575
                        'locked' => new external_value(PARAM_BOOL, 'Whether new submissions are locked.'),
2576
                        'graded' => new external_value(PARAM_BOOL, 'Whether the submission is graded.'),
2577
                        'canedit' => new external_value(PARAM_BOOL, 'Whether the user can edit the current submission.'),
2578
                        'caneditowner' => new external_value(PARAM_BOOL, 'Whether the owner of the submission can edit it.'),
2579
                        'cansubmit' => new external_value(PARAM_BOOL, 'Whether the user can submit.'),
2580
                        'extensionduedate' => new external_value(PARAM_INT, 'Extension due date.'),
2581
                        'timelimit' => new external_value(PARAM_INT, 'Time limit for submission.', VALUE_OPTIONAL),
2582
                        'blindmarking' => new external_value(PARAM_BOOL, 'Whether blind marking is enabled.'),
2583
                        'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.'),
2584
                        'usergroups' => new external_multiple_structure(
2585
                            new external_value(PARAM_INT, 'Group id.'), 'User groups in the course.'
2586
                        ),
2587
                    ), 'Last attempt information.', VALUE_OPTIONAL
2588
                ),
2589
                'feedback' => new external_single_structure(
2590
                    array(
2591
                        'grade' => self::get_grade_structure(VALUE_OPTIONAL),
2592
                        'gradefordisplay' => new external_value(PARAM_RAW, 'Grade rendered into a format suitable for display.'),
2593
                        'gradeddate' => new external_value(PARAM_INT, 'The date the user was graded.'),
2594
                        'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'Plugins info.', VALUE_OPTIONAL),
2595
                    ), 'Feedback for the last attempt.', VALUE_OPTIONAL
2596
                ),
2597
                'previousattempts' => new external_multiple_structure(
2598
                    new external_single_structure(
2599
                        array(
2600
                            'attemptnumber' => new external_value(PARAM_INT, 'Attempt number.'),
2601
                            'submission' => self::get_submission_structure(VALUE_OPTIONAL),
2602
                            'grade' => self::get_grade_structure(VALUE_OPTIONAL),
2603
                            'feedbackplugins' => new external_multiple_structure(self::get_plugin_structure(), 'Feedback info.',
2604
                                                                                    VALUE_OPTIONAL),
2605
                        )
2606
                    ), 'List all the previous attempts did by the user.', VALUE_OPTIONAL
2607
                ),
2608
                'assignmentdata' => new external_single_structure([
2609
                    'attachments' => new external_single_structure([
2610
                        'intro' => new external_files('Intro attachments files', VALUE_OPTIONAL),
2611
                        'activity' => new external_files('Activity attachments files', VALUE_OPTIONAL),
2612
                    ], 'Intro and activity attachments', VALUE_OPTIONAL),
2613
                    'activity' => new external_value(PARAM_RAW, 'Text of activity', VALUE_OPTIONAL),
2614
                    'activityformat' => new external_format_value('activity', VALUE_OPTIONAL),
2615
                ], 'Extra information about assignment', VALUE_OPTIONAL),
2616
                'warnings' => new external_warnings(),
2617
            )
2618
        );
2619
    }
2620
 
2621
    /**
2622
     * Returns description of method parameters
2623
     *
2624
     * @return external_function_parameters
2625
     * @since Moodle 3.1
2626
     */
2627
    public static function list_participants_parameters() {
2628
        return new external_function_parameters(
2629
            array(
2630
                'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2631
                'groupid' => new external_value(PARAM_INT, 'group id'),
2632
                'filter' => new external_value(PARAM_RAW, 'search string to filter the results'),
2633
                'skip' => new external_value(PARAM_INT, 'number of records to skip', VALUE_DEFAULT, 0),
2634
                'limit' => new external_value(PARAM_INT, 'maximum number of records to return', VALUE_DEFAULT, 0),
2635
                'onlyids' => new external_value(PARAM_BOOL, 'Do not return all user fields', VALUE_DEFAULT, false),
2636
                'includeenrolments' => new external_value(PARAM_BOOL, 'Do return courses where the user is enrolled',
2637
                                                          VALUE_DEFAULT, true),
2638
                'tablesort' => new external_value(PARAM_BOOL, 'Apply current user table sorting preferences.',
2639
                                                          VALUE_DEFAULT, false)
2640
            )
2641
        );
2642
    }
2643
 
2644
    /**
2645
     * Retrieves the list of students to be graded for the assignment.
2646
     *
2647
     * @param int $assignid the assign instance id
2648
     * @param int $groupid the current group id
2649
     * @param string $filter search string to filter the results.
2650
     * @param int $skip Number of records to skip
2651
     * @param int $limit Maximum number of records to return
2652
     * @param bool $onlyids Only return user ids.
2653
     * @param bool $includeenrolments Return courses where the user is enrolled.
2654
     * @param bool $tablesort Apply current user table sorting params from the grading table.
2655
     * @return array of warnings and status result
2656
     * @since Moodle 3.1
2657
     * @throws moodle_exception
2658
     */
2659
    public static function list_participants($assignid, $groupid, $filter, $skip,
2660
            $limit, $onlyids, $includeenrolments, $tablesort) {
2661
        global $DB, $CFG;
2662
        require_once($CFG->dirroot . "/mod/assign/locallib.php");
2663
        require_once($CFG->dirroot . "/user/lib.php");
2664
        require_once($CFG->libdir . '/grouplib.php');
2665
 
2666
        $params = self::validate_parameters(self::list_participants_parameters(),
2667
                                            array(
2668
                                                'assignid' => $assignid,
2669
                                                'groupid' => $groupid,
2670
                                                'filter' => $filter,
2671
                                                'skip' => $skip,
2672
                                                'limit' => $limit,
2673
                                                'onlyids' => $onlyids,
2674
                                                'includeenrolments' => $includeenrolments,
2675
                                                'tablesort' => $tablesort
2676
                                            ));
2677
        $warnings = array();
2678
 
2679
        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2680
 
2681
        require_capability('mod/assign:view', $context);
2682
 
2683
        $assign->require_view_grades();
2684
 
2685
        $participants = array();
2686
        $coursegroups = [];
2687
        if (groups_group_visible($params['groupid'], $course, $cm)) {
2688
            $participants = $assign->list_participants_with_filter_status_and_group($params['groupid'], $params['tablesort']);
2689
            $coursegroups = groups_get_all_groups($course->id);
2690
        }
2691
 
2692
        $userfields = user_get_default_fields();
2693
        if (!$params['includeenrolments']) {
2694
            // Remove enrolled courses from users fields to be returned.
2695
            $key = array_search('enrolledcourses', $userfields);
2696
            if ($key !== false) {
2697
                unset($userfields[$key]);
2698
            } else {
2699
                throw new moodle_exception('invaliduserfield', 'error', '', 'enrolledcourses');
2700
            }
2701
        }
2702
 
2703
        $result = array();
2704
        $index = 0;
2705
        foreach ($participants as $record) {
2706
            // Preserve the fullname set by the assignment.
2707
            $fullname = $record->fullname;
2708
            $searchable = $fullname;
2709
            $match = false;
2710
            if (empty($filter)) {
2711
                $match = true;
2712
            } else {
2713
                $filter = core_text::strtolower($filter);
2714
                $value = core_text::strtolower($searchable);
2715
                if (is_string($value) && (core_text::strpos($value, $filter) !== false)) {
2716
                    $match = true;
2717
                }
2718
            }
2719
            if ($match) {
2720
                $index++;
2721
                if ($index <= $params['skip']) {
2722
                    continue;
2723
                }
2724
                if (($params['limit'] > 0) && (($index - $params['skip']) > $params['limit'])) {
2725
                    break;
2726
                }
2727
                // Now we do the expensive lookup of user details because we completed the filtering.
2728
                if (!$assign->is_blind_marking() && !$params['onlyids']) {
2729
                    $userdetails = user_get_user_details($record, $course, $userfields);
2730
                } else {
2731
                    $userdetails = array('id' => $record->id);
2732
                }
2733
                $userdetails['fullname'] = $fullname;
2734
                $userdetails['submitted'] = $record->submitted;
2735
                $userdetails['requiregrading'] = $record->requiregrading;
2736
                $userdetails['grantedextension'] = $record->grantedextension;
2737
                $userdetails['submissionstatus'] = $record->submissionstatus;
2738
                if (!empty($record->groupid)) {
2739
                    $userdetails['groupid'] = $record->groupid;
2740
 
2741
                    if (!empty($coursegroups[$record->groupid])) {
2742
                        // Format properly the group name.
2743
                        $group = $coursegroups[$record->groupid];
2744
                        $userdetails['groupname'] = \core_external\util::format_string($group->name, $context);
2745
                    }
2746
                }
2747
                // Unique id is required for blind marking.
2748
                $userdetails['recordid'] = -1;
2749
                if (!empty($record->recordid)) {
2750
                    $userdetails['recordid'] = $record->recordid;
2751
                }
2752
 
2753
                $result[] = $userdetails;
2754
            }
2755
        }
2756
        return $result;
2757
    }
2758
 
2759
    /**
2760
     * Returns the description of the results of the mod_assign_external::list_participants() method.
2761
     *
2762
     * @return \core_external\external_description
2763
     * @since Moodle 3.1
2764
     */
2765
    public static function list_participants_returns() {
2766
        // Get user description.
2767
        $userdesc = core_user_external::user_description();
2768
        // List unneeded properties.
2769
        $unneededproperties = [
2770
            'auth', 'confirmed', 'lang', 'calendartype', 'theme', 'timezone', 'mailformat'
2771
        ];
2772
        // Remove unneeded properties for consistency with the previous version.
2773
        foreach ($unneededproperties as $prop) {
2774
            unset($userdesc->keys[$prop]);
2775
        }
2776
 
2777
        // Override property attributes for consistency with the previous version.
2778
        $userdesc->keys['fullname']->type = PARAM_NOTAGS;
2779
        $userdesc->keys['profileimageurlsmall']->required = VALUE_OPTIONAL;
2780
        $userdesc->keys['profileimageurl']->required = VALUE_OPTIONAL;
2781
        $userdesc->keys['email']->desc = 'Email address';
2782
        $userdesc->keys['idnumber']->desc = 'The idnumber of the user';
2783
        $userdesc->keys['recordid'] = new external_value(PARAM_INT, 'record id');
2784
 
2785
        // Define other keys.
2786
        $otherkeys = [
2787
            'groups' => new external_multiple_structure(
2788
                new external_single_structure(
2789
                    [
2790
                        'id' => new external_value(PARAM_INT, 'group id'),
2791
                        'name' => new external_value(PARAM_RAW, 'group name'),
2792
                        'description' => new external_value(PARAM_RAW, 'group description'),
2793
                    ]
2794
                ), 'user groups', VALUE_OPTIONAL
2795
            ),
2796
            'roles' => new external_multiple_structure(
2797
                new external_single_structure(
2798
                    [
2799
                        'roleid' => new external_value(PARAM_INT, 'role id'),
2800
                        'name' => new external_value(PARAM_RAW, 'role name'),
2801
                        'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
2802
                        'sortorder' => new external_value(PARAM_INT, 'role sortorder')
2803
                    ]
2804
                ), 'user roles', VALUE_OPTIONAL
2805
            ),
2806
            'enrolledcourses' => new external_multiple_structure(
2807
                new external_single_structure(
2808
                    [
2809
                        'id' => new external_value(PARAM_INT, 'Id of the course'),
2810
                        'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
2811
                        'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
2812
                    ]
2813
                ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL
2814
            ),
2815
            'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'),
2816
            'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'),
2817
            'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'),
2818
            'submissionstatus' => new external_value(PARAM_ALPHA, 'The submission status (new, draft, reopened or submitted).
2819
                Empty when not submitted.', VALUE_OPTIONAL),
2820
            'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL),
2821
            'groupname' => new external_value(PARAM_TEXT, 'for group assignments this is the group name', VALUE_OPTIONAL),
2822
        ];
2823
 
2824
        // Merge keys.
2825
        $userdesc->keys = array_merge($userdesc->keys, $otherkeys);
2826
        return new external_multiple_structure($userdesc);
2827
    }
2828
 
2829
    /**
2830
     * Returns description of method parameters
2831
     *
2832
     * @return external_function_parameters
2833
     * @since Moodle 3.1
2834
     */
2835
    public static function get_participant_parameters() {
2836
        return new external_function_parameters(
2837
            array(
2838
                'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2839
                'userid' => new external_value(PARAM_INT, 'user id'),
2840
                'embeduser' => new external_value(PARAM_BOOL, 'user id', VALUE_DEFAULT, false),
2841
            )
2842
        );
2843
    }
2844
 
2845
    /**
2846
     * Get the user participating in the given assignment. An error with code 'usernotincourse'
2847
     * is thrown is the user isn't a participant of the given assignment.
2848
     *
2849
     * @param int $assignid the assign instance id
2850
     * @param int $userid the user id
2851
     * @param bool $embeduser return user details (only applicable if not blind marking)
2852
     * @return array of warnings and status result
2853
     * @since Moodle 3.1
2854
     * @throws moodle_exception
2855
     */
2856
    public static function get_participant($assignid, $userid, $embeduser) {
2857
        global $DB, $CFG;
2858
        require_once($CFG->dirroot . "/mod/assign/locallib.php");
2859
        require_once($CFG->dirroot . "/user/lib.php");
2860
        require_once($CFG->libdir . '/grouplib.php');
2861
 
2862
        $params = self::validate_parameters(self::get_participant_parameters(), array(
2863
            'assignid' => $assignid,
2864
            'userid' => $userid,
2865
            'embeduser' => $embeduser
2866
        ));
2867
 
2868
        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2869
        $assign->require_view_grades();
2870
 
2871
        $participant = $assign->get_participant($params['userid']);
2872
 
2873
        // Update assign with override information.
2874
        $assign->update_effective_access($params['userid']);
2875
 
2876
        if (!$participant) {
2877
            // No participant found so we can return early.
2878
            throw new moodle_exception('usernotincourse');
2879
        }
2880
 
2881
        $filtered = $assign->is_userid_filtered($userid);
2882
        if (!$filtered) {
2883
            // User is filtered out by user filters or table preferences.
2884
            throw new moodle_exception('userisfilteredout');
2885
        }
2886
 
2887
        $return = array(
2888
            'id' => $participant->id,
2889
            'fullname' => $participant->fullname,
2890
            'submitted' => $participant->submitted,
2891
            'requiregrading' => $participant->requiregrading,
2892
            'grantedextension' => $participant->grantedextension,
2893
            'submissionstatus' => $participant->submissionstatus,
2894
            'blindmarking' => $assign->is_blind_marking(),
2895
            'allowsubmissionsfromdate' => $assign->get_instance($userid)->allowsubmissionsfromdate,
2896
            'duedate' => $assign->get_instance($userid)->duedate,
2897
            'cutoffdate' => $assign->get_instance($userid)->cutoffdate,
2898
            'duedatestr' => userdate($assign->get_instance($userid)->duedate, get_string('strftimedatetime', 'langconfig')),
2899
        );
2900
 
2901
        if (!empty($participant->groupid)) {
2902
            $return['groupid'] = $participant->groupid;
2903
 
2904
            if ($group = groups_get_group($participant->groupid)) {
2905
                // Format properly the group name.
2906
                $return['groupname'] = \core_external\util::format_string($group->name, $context);
2907
            }
2908
        }
2909
 
2910
        // Skip the expensive lookup of user detail if we're blind marking or the caller
2911
        // hasn't asked for user details to be embedded.
2912
        if (!$assign->is_blind_marking() && $embeduser) {
2913
            if ($userdetails = user_get_user_details($participant, $course)) {
2914
                $return['user'] = $userdetails;
2915
            }
2916
        }
2917
 
2918
        return $return;
2919
    }
2920
 
2921
    /**
2922
     * Returns description of method result value
2923
     *
2924
     * @return \core_external\external_description
2925
     * @since Moodle 3.1
2926
     */
2927
    public static function get_participant_returns() {
2928
        $userdescription = core_user_external::user_description();
2929
        $userdescription->default = [];
2930
        $userdescription->required = VALUE_OPTIONAL;
2931
 
2932
        return new external_single_structure(array(
2933
            'id' => new external_value(PARAM_INT, 'ID of the user'),
2934
            'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
2935
            'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'),
2936
            'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'),
2937
            'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'),
2938
            'blindmarking' => new external_value(PARAM_BOOL, 'is blind marking enabled for this assignment'),
2939
            'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allowsubmissionsfromdate for the user'),
2940
            'duedate' => new external_value(PARAM_INT, 'duedate for the user'),
2941
            'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user'),
2942
            'duedatestr' => new external_value(PARAM_TEXT, 'duedate for the user'),
2943
            'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL),
2944
            'groupname' => new external_value(PARAM_TEXT, 'for group assignments this is the group name', VALUE_OPTIONAL),
2945
            'submissionstatus' => new external_value(PARAM_ALPHA, 'The submission status (new, draft, reopened or submitted).
2946
                Empty when not submitted.', VALUE_OPTIONAL),
2947
            'user' => $userdescription,
2948
        ));
2949
    }
2950
 
2951
    /**
2952
     * Describes the parameters for view_assign.
2953
     *
2954
     * @return external_function_parameters
2955
     * @since Moodle 3.2
2956
     */
2957
    public static function view_assign_parameters() {
2958
        return new external_function_parameters (
2959
            array(
2960
                'assignid' => new external_value(PARAM_INT, 'assign instance id'),
2961
            )
2962
        );
2963
    }
2964
 
2965
    /**
2966
     * Update the module completion status.
2967
     *
2968
     * @param int $assignid assign instance id
2969
     * @return array of warnings and status result
2970
     * @since Moodle 3.2
2971
     */
2972
    public static function view_assign($assignid) {
2973
        $warnings = array();
2974
        $params = array(
2975
            'assignid' => $assignid,
2976
        );
2977
        $params = self::validate_parameters(self::view_assign_parameters(), $params);
2978
 
2979
        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
2980
 
2981
        $assign->set_module_viewed();
2982
 
2983
        $result = array();
2984
        $result['status'] = true;
2985
        $result['warnings'] = $warnings;
2986
        return $result;
2987
    }
2988
 
2989
    /**
2990
     * Describes the view_assign return value.
2991
     *
2992
     * @return external_single_structure
2993
     * @since Moodle 3.2
2994
     */
2995
    public static function view_assign_returns() {
2996
        return new external_single_structure(
2997
            array(
2998
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2999
                'warnings' => new external_warnings(),
3000
            )
3001
        );
3002
    }
3003
}