Proyectos de Subversion Moodle

Rev

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