2f0145da9f5cded53c2828c2ed5fa0b947b6b165
[moodle.git] / mod / workshop / classes / external.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Workshop external API
19  *
20  * @package    mod_workshop
21  * @category   external
22  * @copyright  2017 Juan Leyva <juan@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  * @since      Moodle 3.4
25  */
27 defined('MOODLE_INTERNAL') || die;
29 require_once("$CFG->libdir/externallib.php");
30 require_once($CFG->dirroot . '/mod/workshop/locallib.php');
32 use mod_workshop\external\workshop_summary_exporter;
33 use mod_workshop\external\submission_exporter;
34 use mod_workshop\external\assessment_exporter;
36 /**
37  * Workshop external functions
38  *
39  * @package    mod_workshop
40  * @category   external
41  * @copyright  2017 Juan Leyva <juan@moodle.com>
42  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43  * @since      Moodle 3.4
44  */
45 class mod_workshop_external extends external_api {
47     /**
48      * Describes the parameters for get_workshops_by_courses.
49      *
50      * @return external_function_parameters
51      * @since Moodle 3.4
52      */
53     public static function get_workshops_by_courses_parameters() {
54         return new external_function_parameters (
55             array(
56                 'courseids' => new external_multiple_structure(
57                     new external_value(PARAM_INT, 'Course id'), 'Array of course ids', VALUE_DEFAULT, array()
58                 ),
59             )
60         );
61     }
63     /**
64      * Returns a list of workshops in a provided list of courses.
65      * If no list is provided all workshops that the user can view will be returned.
66      *
67      * @param array $courseids course ids
68      * @return array of warnings and workshops
69      * @since Moodle 3.4
70      */
71     public static function get_workshops_by_courses($courseids = array()) {
72         global $PAGE;
74         $warnings = array();
75         $returnedworkshops = array();
77         $params = array(
78             'courseids' => $courseids,
79         );
80         $params = self::validate_parameters(self::get_workshops_by_courses_parameters(), $params);
82         $mycourses = array();
83         if (empty($params['courseids'])) {
84             $mycourses = enrol_get_my_courses();
85             $params['courseids'] = array_keys($mycourses);
86         }
88         // Ensure there are courseids to loop through.
89         if (!empty($params['courseids'])) {
91             list($courses, $warnings) = external_util::validate_courses($params['courseids'], $mycourses);
92             $output = $PAGE->get_renderer('core');
94             // Get the workshops in this course, this function checks users visibility permissions.
95             // We can avoid then additional validate_context calls.
96             $workshops = get_all_instances_in_courses("workshop", $courses);
97             foreach ($workshops as $workshop) {
99                 $context = context_module::instance($workshop->coursemodule);
100                 // Remove fields that are not from the workshop (added by get_all_instances_in_courses).
101                 unset($workshop->coursemodule, $workshop->context, $workshop->visible, $workshop->section, $workshop->groupmode,
102                         $workshop->groupingid);
104                 $exporter = new workshop_summary_exporter($workshop, array('context' => $context));
105                 $returnedworkshops[] = $exporter->export($output);
106             }
107         }
109         $result = array(
110             'workshops' => $returnedworkshops,
111             'warnings' => $warnings
112         );
113         return $result;
114     }
116     /**
117      * Describes the get_workshops_by_courses return value.
118      *
119      * @return external_single_structure
120      * @since Moodle 3.4
121      */
122     public static function get_workshops_by_courses_returns() {
123         return new external_single_structure(
124             array(
125                 'workshops' => new external_multiple_structure(
126                     workshop_summary_exporter::get_read_structure()
127                 ),
128                 'warnings' => new external_warnings(),
129             )
130         );
131     }
133     /**
134      * Utility function for validating a workshop.
135      *
136      * @param int $workshopid workshop instance id
137      * @return array array containing the workshop object, course, context and course module objects
138      * @since  Moodle 3.4
139      */
140     protected static function validate_workshop($workshopid) {
141         global $DB, $USER;
143         // Request and permission validation.
144         $workshop = $DB->get_record('workshop', array('id' => $workshopid), '*', MUST_EXIST);
145         list($course, $cm) = get_course_and_cm_from_instance($workshop, 'workshop');
147         $context = context_module::instance($cm->id);
148         self::validate_context($context);
150         $workshop = new workshop($workshop, $cm, $course);
152         return array($workshop, $course, $cm, $context);
153     }
156     /**
157      * Describes the parameters for get_workshop_access_information.
158      *
159      * @return external_external_function_parameters
160      * @since Moodle 3.4
161      */
162     public static function get_workshop_access_information_parameters() {
163         return new external_function_parameters (
164             array(
165                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.')
166             )
167         );
168     }
170     /**
171      * Return access information for a given workshop.
172      *
173      * @param int $workshopid workshop instance id
174      * @return array of warnings and the access information
175      * @since Moodle 3.4
176      * @throws  moodle_exception
177      */
178     public static function get_workshop_access_information($workshopid) {
179         global $USER;
181         $params = self::validate_parameters(self::get_workshop_access_information_parameters(), array('workshopid' => $workshopid));
183         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
185         $result = array();
186         // Return all the available capabilities.
187         $capabilities = load_capability_def('mod_workshop');
188         foreach ($capabilities as $capname => $capdata) {
189             // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
190             $field = 'can' . str_replace('mod/workshop:', '', $capname);
191             $result[$field] = has_capability($capname, $context);
192         }
194         // Now, specific features access information.
195         $result['creatingsubmissionallowed'] = $workshop->creating_submission_allowed($USER->id);
196         $result['modifyingsubmissionallowed'] = $workshop->modifying_submission_allowed($USER->id);
197         $result['assessingallowed'] = $workshop->assessing_allowed($USER->id);
198         $result['assessingexamplesallowed'] = $workshop->assessing_examples_allowed();
199         if (is_null($result['assessingexamplesallowed'])) {
200             $result['assessingexamplesallowed'] = false;
201         }
202         $result['examplesassessedbeforesubmission'] = $workshop->check_examples_assessed_before_submission($USER->id);
203         list($result['examplesassessedbeforeassessment'], $code) = $workshop->check_examples_assessed_before_assessment($USER->id);
205         $result['warnings'] = array();
206         return $result;
207     }
209     /**
210      * Describes the get_workshop_access_information return value.
211      *
212      * @return external_single_structure
213      * @since Moodle 3.4
214      */
215     public static function get_workshop_access_information_returns() {
217         $structure = array(
218             'creatingsubmissionallowed' => new external_value(PARAM_BOOL,
219                 'Is the given user allowed to create their submission?'),
220             'modifyingsubmissionallowed' => new external_value(PARAM_BOOL,
221                 'Is the user allowed to modify his existing submission?'),
222             'assessingallowed' => new external_value(PARAM_BOOL,
223                 'Is the user allowed to create/edit his assessments?'),
224             'assessingexamplesallowed' => new external_value(PARAM_BOOL,
225                 'Are reviewers allowed to create/edit their assessments of the example submissions?.'),
226             'examplesassessedbeforesubmission' => new external_value(PARAM_BOOL,
227                 'Whether the given user has assessed all his required examples before submission
228                 (always true if there are not examples to assess or not configured to check before submission).'),
229             'examplesassessedbeforeassessment' => new external_value(PARAM_BOOL,
230                 'Whether the given user has assessed all his required examples before assessment
231                 (always true if there are not examples to assessor not configured to check before assessment).'),
232             'warnings' => new external_warnings()
233         );
235         $capabilities = load_capability_def('mod_workshop');
236         foreach ($capabilities as $capname => $capdata) {
237             // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
238             $field = 'can' . str_replace('mod/workshop:', '', $capname);
239             $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.');
240         }
242         return new external_single_structure($structure);
243     }
245     /**
246      * Describes the parameters for get_user_plan.
247      *
248      * @return external_external_function_parameters
249      * @since Moodle 3.4
250      */
251     public static function get_user_plan_parameters() {
252         return new external_function_parameters (
253             array(
254                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
255                 'userid' => new external_value(PARAM_INT, 'User id (empty or 0 for current user).', VALUE_DEFAULT, 0),
256             )
257         );
258     }
260     /**
261      * Return the planner information for the given user.
262      *
263      * @param int $workshopid workshop instance id
264      * @param int $userid user id
265      * @return array of warnings and the user plan
266      * @since Moodle 3.4
267      * @throws  moodle_exception
268      */
269     public static function get_user_plan($workshopid, $userid = 0) {
270         global $USER;
272         $params = array(
273             'workshopid' => $workshopid,
274             'userid' => $userid,
275         );
276         $params = self::validate_parameters(self::get_user_plan_parameters(), $params);
278         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
280         // Extra checks so only users with permissions can view other users plans.
281         if (empty($params['userid']) || $params['userid'] == $USER->id) {
282             $userid = $USER->id;
283         } else {
284             require_capability('moodle/course:manageactivities', $context);
285             $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
286             core_user::require_active_user($user);
287             if (!$workshop->check_group_membership($user->id)) {
288                 throw new moodle_exception('notingroup');
289             }
290             $userid = $user->id;
291         }
293         // Get the user plan information ready for external functions.
294         $userplan = new workshop_user_plan($workshop, $userid);
295         $userplan = array('phases' => $userplan->phases, 'examples' => $userplan->get_examples());
296         foreach ($userplan['phases'] as $phasecode => $phase) {
297             $phase->code = $phasecode;
298             $userplan['phases'][$phasecode] = (array) $phase;
299             foreach ($userplan['phases'][$phasecode]['tasks'] as $taskcode => $task) {
300                 $task->code = $taskcode;
301                 if ($task->link instanceof moodle_url) {
302                     $task->link = $task->link->out(false);
303                 }
304                 $userplan['phases'][$phasecode]['tasks'][$taskcode] = (array) $task;
305             }
306             foreach ($userplan['phases'][$phasecode]['actions'] as $actioncode => $action) {
307                 if ($action->url instanceof moodle_url) {
308                     $action->url = $action->url->out(false);
309                 }
310                 $userplan['phases'][$phasecode]['actions'][$actioncode] = (array) $action;
311             }
312         }
314         $result['userplan'] = $userplan;
315         $result['warnings'] = array();
316         return $result;
317     }
319     /**
320      * Describes the get_user_plan return value.
321      *
322      * @return external_single_structure
323      * @since Moodle 3.4
324      */
325     public static function get_user_plan_returns() {
326         return new external_single_structure(
327             array(
328                 'userplan' => new external_single_structure(
329                     array(
330                         'phases' => new external_multiple_structure(
331                             new external_single_structure(
332                                 array(
333                                     'code' => new external_value(PARAM_INT, 'Phase code.'),
334                                     'title' => new external_value(PARAM_NOTAGS, 'Phase title.'),
335                                     'active' => new external_value(PARAM_BOOL, 'Whether is the active task.'),
336                                     'tasks' => new external_multiple_structure(
337                                         new external_single_structure(
338                                             array(
339                                                 'code' => new external_value(PARAM_ALPHA, 'Task code.'),
340                                                 'title' => new external_value(PARAM_RAW, 'Task title.'),
341                                                 'link' => new external_value(PARAM_URL, 'Link to task.'),
342                                                 'details' => new external_value(PARAM_RAW, 'Task details.', VALUE_OPTIONAL),
343                                                 'completed' => new external_value(PARAM_NOTAGS,
344                                                     'Completion information (maybe empty, maybe a boolean or generic info.'),
345                                             )
346                                         )
347                                     ),
348                                     'actions' => new external_multiple_structure(
349                                         new external_single_structure(
350                                             array(
351                                                 'type' => new external_value(PARAM_ALPHA, 'Action type.', VALUE_OPTIONAL),
352                                                 'label' => new external_value(PARAM_RAW, 'Action label.', VALUE_OPTIONAL),
353                                                 'url' => new external_value(PARAM_URL, 'Link to action.'),
354                                                 'method' => new external_value(PARAM_ALPHA, 'Get or post.', VALUE_OPTIONAL),
355                                             )
356                                         )
357                                     ),
358                                 )
359                             )
360                         ),
361                         'examples' => new external_multiple_structure(
362                             new external_single_structure(
363                                 array(
364                                     'id' => new external_value(PARAM_INT, 'Example submission id.'),
365                                     'title' => new external_value(PARAM_RAW, 'Example submission title.'),
366                                     'assessmentid' => new external_value(PARAM_INT, 'Example submission assessment id.'),
367                                     'grade' => new external_value(PARAM_FLOAT, 'The submission grade.'),
368                                     'gradinggrade' => new external_value(PARAM_FLOAT, 'The assessment grade.'),
369                                 )
370                             )
371                         ),
372                     )
373                 ),
374                 'warnings' => new external_warnings(),
375             )
376         );
377     }
379     /**
380      * Describes the parameters for view_workshop.
381      *
382      * @return external_function_parameters
383      * @since Moodle 3.4
384      */
385     public static function view_workshop_parameters() {
386         return new external_function_parameters (
387             array(
388                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id'),
389             )
390         );
391     }
393     /**
394      * Trigger the course module viewed event and update the module completion status.
395      *
396      * @param int $workshopid workshop instance id
397      * @return array of warnings and status result
398      * @since Moodle 3.4
399      * @throws moodle_exception
400      */
401     public static function view_workshop($workshopid) {
403         $params = array('workshopid' => $workshopid);
404         $params = self::validate_parameters(self::view_workshop_parameters(), $params);
405         $warnings = array();
407         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
409         $workshop->set_module_viewed();
411         $result = array(
412             'status' => true,
413             'warnings' => $warnings,
414         );
415         return $result;
416     }
418     /**
419      * Describes the view_workshop return value.
420      *
421      * @return external_single_structure
422      * @since Moodle 3.4
423      */
424     public static function view_workshop_returns() {
425         return new external_single_structure(
426             array(
427                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
428                 'warnings' => new external_warnings(),
429             )
430         );
431     }
433     /**
434      * Returns the description of the external function parameters.
435      *
436      * @return external_function_parameters
437      * @since Moodle 3.4
438      */
439     public static function add_submission_parameters() {
440         return new external_function_parameters(array(
441             'workshopid' => new external_value(PARAM_INT, 'Workshop id'),
442             'title' => new external_value(PARAM_TEXT, 'Submission title'),
443             'content' => new external_value(PARAM_RAW, 'Submission text content', VALUE_DEFAULT, ''),
444             'contentformat' => new external_value(PARAM_INT, 'The format used for the content', VALUE_DEFAULT, FORMAT_MOODLE),
445             'inlineattachmentsid' => new external_value(PARAM_INT, 'The draft file area id for inline attachments in the content',
446                 VALUE_DEFAULT, 0),
447             'attachmentsid' => new external_value(PARAM_INT, 'The draft file area id for attachments', VALUE_DEFAULT, 0),
448         ));
449     }
451     /**
452      * Add a new submission to a given workshop.
453      *
454      * @param int $workshopid the workshop id
455      * @param string $title             the submission title
456      * @param string  $content          the submission text content
457      * @param int  $contentformat       the format used for the content
458      * @param int $inlineattachmentsid  the draft file area id for inline attachments in the content
459      * @param int $attachmentsid        the draft file area id for attachments
460      * @return array Containing the new created submission id and warnings.
461      * @since Moodle 3.4
462      * @throws moodle_exception
463      */
464     public static function add_submission($workshopid, $title, $content = '', $contentformat = FORMAT_MOODLE,
465             $inlineattachmentsid = 0, $attachmentsid = 0) {
466         global $USER;
468         $params = self::validate_parameters(self::add_submission_parameters(), array(
469             'workshopid' => $workshopid,
470             'title' => $title,
471             'content' => $content,
472             'contentformat' => $contentformat,
473             'inlineattachmentsid' => $inlineattachmentsid,
474             'attachmentsid' => $attachmentsid,
475         ));
476         $warnings = array();
478         // Get and validate the workshop.
479         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
480         require_capability('mod/workshop:submit', $context);
482         // Check if we can submit now.
483         $canaddsubmission = $workshop->creating_submission_allowed($USER->id);
484         $canaddsubmission = $canaddsubmission && $workshop->check_examples_assessed_before_submission($USER->id);
485         if (!$canaddsubmission) {
486             throw new moodle_exception('nopermissions', 'error', '', 'add submission');
487         }
489         // Prepare the submission object.
490         $submission = new stdClass;
491         $submission->id = null;
492         $submission->cmid = $cm->id;
493         $submission->example = 0;
494         $submission->title = trim($params['title']);
495         $submission->content_editor = array(
496             'text' => $params['content'],
497             'format' => $params['contentformat'],
498             'itemid' => $params['inlineattachmentsid'],
499         );
500         $submission->attachment_filemanager = $params['attachmentsid'];
502         if (empty($submission->title)) {
503             throw new moodle_exception('errorinvalidparam', 'webservice', '', 'title');
504         }
506         $errors = $workshop->validate_submission_data((array) $submission);
507         // We can get several errors, return them in warnings.
508         if (!empty($errors)) {
509             $submission->id = 0;
510             foreach ($errors as $itemname => $message) {
511                 $warnings[] = array(
512                     'item' => $itemname,
513                     'itemid' => 0,
514                     'warningcode' => 'fielderror',
515                     'message' => s($message)
516                 );
517             }
518             return array(
519                 'status' => false,
520                 'warnings' => $warnings
521             );
522         } else {
523             $submission->id = $workshop->edit_submission($submission);
524             return array(
525                 'status' => true,
526                 'submissionid' => $submission->id,
527                 'warnings' => $warnings
528             );
529         }
530     }
532     /**
533      * Returns the description of the external function return value.
534      *
535      * @return external_description
536      * @since Moodle 3.4
537      */
538     public static function add_submission_returns() {
539         return new external_single_structure(array(
540             'status' => new external_value(PARAM_BOOL, 'True if the submission was created false otherwise.'),
541             'submissionid' => new external_value(PARAM_INT, 'New workshop submission id.', VALUE_OPTIONAL),
542             'warnings' => new external_warnings()
543         ));
544     }
546     /**
547      * Returns the description of the external function parameters.
548      *
549      * @return external_function_parameters
550      * @since Moodle 3.4
551      */
552     public static function update_submission_parameters() {
553         return new external_function_parameters(array(
554             'submissionid' => new external_value(PARAM_INT, 'Submission id'),
555             'title' => new external_value(PARAM_TEXT, 'Submission title'),
556             'content' => new external_value(PARAM_RAW, 'Submission text content', VALUE_DEFAULT, ''),
557             'contentformat' => new external_value(PARAM_INT, 'The format used for the content', VALUE_DEFAULT, FORMAT_MOODLE),
558             'inlineattachmentsid' => new external_value(PARAM_INT, 'The draft file area id for inline attachments in the content',
559                 VALUE_DEFAULT, 0),
560             'attachmentsid' => new external_value(PARAM_INT, 'The draft file area id for attachments', VALUE_DEFAULT, 0),
561         ));
562     }
565     /**
566      * Updates the given submission.
567      *
568      * @param int $submissionid         the submission id
569      * @param string $title             the submission title
570      * @param string  $content          the submission text content
571      * @param int  $contentformat       the format used for the content
572      * @param int $inlineattachmentsid  the draft file area id for inline attachments in the content
573      * @param int $attachmentsid        the draft file area id for attachments
574      * @return array whether the submission was updated and warnings.
575      * @since Moodle 3.4
576      * @throws moodle_exception
577      */
578     public static function update_submission($submissionid, $title, $content = '', $contentformat = FORMAT_MOODLE,
579             $inlineattachmentsid = 0, $attachmentsid = 0) {
580         global $USER, $DB;
582         $params = self::validate_parameters(self::update_submission_parameters(), array(
583             'submissionid' => $submissionid,
584             'title' => $title,
585             'content' => $content,
586             'contentformat' => $contentformat,
587             'inlineattachmentsid' => $inlineattachmentsid,
588             'attachmentsid' => $attachmentsid,
589         ));
590         $warnings = array();
592         // Get and validate the submission and workshop.
593         $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
594         list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
595         require_capability('mod/workshop:submit', $context);
597         // Check if we can update the submission.
598         $canupdatesubmission = $submission->authorid == $USER->id;
599         $canupdatesubmission = $canupdatesubmission && $workshop->modifying_submission_allowed($USER->id);
600         $canupdatesubmission = $canupdatesubmission && $workshop->check_examples_assessed_before_submission($USER->id);
601         if (!$canupdatesubmission) {
602             throw new moodle_exception('nopermissions', 'error', '', 'update submission');
603         }
605         // Prepare the submission object.
606         $submission->title = trim($params['title']);
607         if (empty($submission->title)) {
608             throw new moodle_exception('errorinvalidparam', 'webservice', '', 'title');
609         }
610         $submission->content_editor = array(
611             'text' => $params['content'],
612             'format' => $params['contentformat'],
613             'itemid' => $params['inlineattachmentsid'],
614         );
615         $submission->attachment_filemanager = $params['attachmentsid'];
617         $errors = $workshop->validate_submission_data((array) $submission);
618         // We can get several errors, return them in warnings.
619         if (!empty($errors)) {
620             $status = false;
621             foreach ($errors as $itemname => $message) {
622                 $warnings[] = array(
623                     'item' => $itemname,
624                     'itemid' => 0,
625                     'warningcode' => 'fielderror',
626                     'message' => s($message)
627                 );
628             }
629         } else {
630             $status = true;
631             $submission->id = $workshop->edit_submission($submission);
632         }
634         return array(
635             'status' => $status,
636             'warnings' => $warnings
637         );
638     }
640     /**
641      * Returns the description of the external function return value.
642      *
643      * @return external_description
644      * @since Moodle 3.4
645      */
646     public static function update_submission_returns() {
647         return new external_single_structure(array(
648             'status' => new external_value(PARAM_BOOL, 'True if the submission was updated false otherwise.'),
649             'warnings' => new external_warnings()
650         ));
651     }
653     /**
654      * Returns the description of the external function parameters.
655      *
656      * @return external_function_parameters
657      * @since Moodle 3.4
658      */
659     public static function delete_submission_parameters() {
660         return new external_function_parameters(
661             array(
662                 'submissionid' => new external_value(PARAM_INT, 'Submission id'),
663             )
664         );
665     }
668     /**
669      * Deletes the given submission.
670      *
671      * @param int $submissionid the submission id.
672      * @return array containing the result status and warnings.
673      * @since Moodle 3.4
674      * @throws moodle_exception
675      */
676     public static function delete_submission($submissionid) {
677         global $USER, $DB;
679         $params = self::validate_parameters(self::delete_submission_parameters(), array('submissionid' => $submissionid));
680         $warnings = array();
682         // Get and validate the submission and workshop.
683         $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
684         list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
686         // Check if we can delete the submission.
687         if (!has_capability('mod/workshop:deletesubmissions', $context)) {
688             require_capability('mod/workshop:submit', $context);
689             // We can delete our own submission, on time and not yet assessed.
690             $candeletesubmission = $submission->authorid == $USER->id;
691             $candeletesubmission = $candeletesubmission && $workshop->modifying_submission_allowed($USER->id);
692             $candeletesubmission = $candeletesubmission && count($workshop->get_assessments_of_submission($submission->id)) == 0;
693             if (!$candeletesubmission) {
694                 throw new moodle_exception('nopermissions', 'error', '', 'delete submission');
695             }
696         }
698         $workshop->delete_submission($submission);
700         return array(
701             'status' => true,
702             'warnings' => $warnings
703         );
704     }
706     /**
707      * Returns the description of the external function return value.
708      *
709      * @return external_description
710      * @since Moodle 3.4
711      */
712     public static function delete_submission_returns() {
713         return new external_single_structure(array(
714             'status' => new external_value(PARAM_BOOL, 'True if the submission was deleted.'),
715             'warnings' => new external_warnings()
716         ));
717     }
719     /**
720      * Helper method for returning the submission data according the current user capabilities and current phase.
721      *
722      * @param  stdClass $submission the submission data
723      * @param  workshop $workshop   the workshop class
724      * @param  bool $canviewauthorpublished whether the user has the capability mod/workshop:viewauthorpublished on
725      * @param  bool $canviewauthornames whether the user has the capability mod/workshop:vviewauthornames on
726      * @param  bool $canviewallsubmissions whether the user has the capability mod/workshop:viewallsubmissions on
727      * @return stdClass object with the submission data filtered
728      * @since Moodle 3.4
729      */
730     protected static function prepare_submission_for_external($submission, workshop $workshop, $canviewauthorpublished = null,
731             $canviewauthornames = null, $canviewallsubmissions = null) {
732         global $USER;
734         if (is_null($canviewauthorpublished)) {
735             $canviewauthorpublished = has_capability('mod/workshop:viewauthorpublished', $workshop->context);
736         }
737         if (is_null($canviewauthornames)) {
738             $canviewauthornames = has_capability('mod/workshop:viewauthornames', $workshop->context);
739         }
740         if (is_null($canviewallsubmissions)) {
741             $canviewallsubmissions = has_capability('mod/workshop:viewallsubmissions', $workshop->context);
742         }
744         $ownsubmission = $submission->authorid == $USER->id;
745         if (!$canviewauthornames && !$ownsubmission) {
746             $submission->authorid = 0;
747         }
749         $isworkshopclosed = $workshop->phase == workshop::PHASE_CLOSED;
750         $canviewsubmissiondetail = $ownsubmission || $canviewallsubmissions;
751         // If the workshop is not closed or the user can't see the submission detail: remove grading or feedback information.
752         if (!$isworkshopclosed || !$canviewsubmissiondetail) {
753             $properties = submission_exporter::properties_definition();
754             foreach ($properties as $attribute => $settings) {
755                 if (!empty($settings['optional'])) {
756                     unset($submission->{$attribute});
757                 }
758             }
759         }
760         return $submission;
761     }
763     /**
764      * Returns description of method parameters
765      *
766      * @return external_function_parameters
767      * @since Moodle 3.4
768      */
769     public static function get_submissions_parameters() {
770         return new external_function_parameters(
771             array(
772                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
773                 'userid' => new external_value(PARAM_INT, 'Get submissions done by this user. Use 0 or empty for the current user',
774                                                 VALUE_DEFAULT, 0),
775                 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group.
776                                                    It will return submissions done by users in the given group.',
777                                                    VALUE_DEFAULT, 0),
778                 'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
779                 'perpage' => new external_value(PARAM_INT, 'The number of records to return per page.', VALUE_DEFAULT, 0),
780             )
781         );
782     }
784     /**
785      * Retrieves all the workshop submissions visible by the current user or the one done by the given user.
786      *
787      * @param int $workshopid       the workshop instance id
788      * @param int $userid           get submissions done by this user
789      * @param int $groupid          (optional) group id, 0 means that the function will determine the user group
790      * @param int $page             page of records to return
791      * @param int $perpage          number of records to return per page
792      * @return array of warnings and the entries
793      * @since Moodle 3.4
794      * @throws moodle_exception
795      */
796     public static function get_submissions($workshopid, $userid = 0, $groupid = 0, $page = 0, $perpage = 0) {
797         global $PAGE, $USER;
799         $params = array('workshopid' => $workshopid, 'userid' => $userid, 'groupid' => $groupid,
800             'page' => $page, 'perpage' => $perpage);
801         $params = self::validate_parameters(self::get_submissions_parameters(), $params);
802         $submissions = $warnings = array();
804         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
806         if (empty($params['groupid'])) {
807             // Check to see if groups are being used here.
808             if ($groupmode = groups_get_activity_groupmode($cm)) {
809                 $groupid = groups_get_activity_group($cm);
810                 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
811                 if (!groups_group_visible($groupid, $course, $cm)) {
812                     throw new moodle_exception('notingroup');
813                 }
814             } else {
815                 $groupid = 0;
816             }
817         }
819         if (!empty($params['userid']) && $params['userid'] != $USER->id) {
820             $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
821             core_user::require_active_user($user);
822             if (!$workshop->check_group_membership($user->id)) {
823                 throw new moodle_exception('notingroup');
824             }
825         }
827         $totalfilesize = 0;
828         list($submissionsrecords, $totalcount) =
829             $workshop->get_visible_submissions($params['userid'], $groupid, $params['page'], $params['perpage']);
831         if ($totalcount) {
833             $canviewauthorpublished = has_capability('mod/workshop:viewauthorpublished', $context);
834             $canviewauthornames = has_capability('mod/workshop:viewauthornames', $context);
835             $canviewallsubmissions = has_capability('mod/workshop:viewallsubmissions', $context);
837             $related = array('context' => $context);
838             foreach ($submissionsrecords as $submission) {
839                 $submission = self::prepare_submission_for_external($submission, $workshop, $canviewauthorpublished,
840                     $canviewauthornames, $canviewallsubmissions);
842                 $exporter = new submission_exporter($submission, $related);
843                 $submissions[] = $exporter->export($PAGE->get_renderer('core'));
844             }
846             // Retrieve total files size for the submissions (so external clients know how many data they'd need to download).
847             $fs = get_file_storage();
848             $files = $fs->get_area_files($context->id, 'mod_workshop', array('submission_content', 'submission_attachment'));
849             foreach ($files as $file) {
850                 if ($file->is_directory() || !isset($submissionsrecords[$file->get_itemid()])) {
851                     continue;
852                 }
853                 $totalfilesize += $file->get_filesize();
854             }
855         }
857         return array(
858             'submissions' => $submissions,
859             'totalcount' => $totalcount,
860             'totalfilesize' => $totalfilesize,
861         );
862     }
864     /**
865      * Returns description of method result value
866      *
867      * @return external_description
868      * @since Moodle 3.4
869      */
870     public static function get_submissions_returns() {
871         return new external_single_structure(
872             array(
873                 'submissions' => new external_multiple_structure(
874                     submission_exporter::get_read_structure()
875                 ),
876                 'totalcount' => new external_value(PARAM_INT, 'Total count of submissions.'),
877                 'totalfilesize' => new external_value(PARAM_INT, 'Total size (bytes) of the files included in the submissions.'),
878                 'warnings' => new external_warnings()
879             )
880         );
881     }
883     /**
884      * Helper method for validating a submission.
885      *
886      * @param  stdClass   $submission submission object
887      * @param  workshop   $workshop     workshop instance
888      * @return void
889      * @since  Moodle 3.4
890      */
891     protected static function validate_submission($submission, workshop $workshop) {
892         global $USER;
894         $workshopclosed = $workshop->phase == workshop::PHASE_CLOSED;
895         $canviewpublished = has_capability('mod/workshop:viewpublishedsubmissions', $workshop->context);
897         $canview = $submission->authorid == $USER->id;  // I did it.
898         $canview = $canview || !empty($workshop->get_assessment_of_submission_by_user($submission->id, $USER->id));  // I reviewed.
899         $canview = $canview || has_capability('mod/workshop:viewallsubmissions', $workshop->context); // I can view all.
900         $canview = $canview || ($submission->published && $workshopclosed && $canviewpublished);    // It has been published.
902         if ($canview) {
903             // Here we should check if the user share group.
904             if ($submission->authorid != $USER->id &&
905                     !groups_user_groups_visible($workshop->course, $submission->authorid, $workshop->cm)) {
906                 throw new moodle_exception('notingroup');
907             }
908         } else {
909             throw new moodle_exception('nopermissions', 'error', '', 'view submission');
910         }
911     }
913     /**
914      * Returns the description of the external function parameters.
915      *
916      * @return external_function_parameters
917      * @since Moodle 3.4
918      */
919     public static function get_submission_parameters() {
920         return new external_function_parameters(
921             array(
922                 'submissionid' => new external_value(PARAM_INT, 'Submission id'),
923             )
924         );
925     }
928     /**
929      * Retrieves the given submission.
930      *
931      * @param int $submissionid the submission id
932      * @return array containing the submission and warnings.
933      * @since Moodle 3.4
934      * @throws moodle_exception
935      */
936     public static function get_submission($submissionid) {
937         global $USER, $DB, $PAGE;
939         $params = self::validate_parameters(self::get_submission_parameters(), array('submissionid' => $submissionid));
940         $warnings = array();
942         // Get and validate the submission and workshop.
943         $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
944         list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
946         self::validate_submission($submission, $workshop);
948         $submission = self::prepare_submission_for_external($submission, $workshop);
950         $related = array('context' => $context);
951         $exporter = new submission_exporter($submission, $related);
952         return array(
953             'submission' => $exporter->export($PAGE->get_renderer('core')),
954             'warnings' => $warnings
955         );
956     }
958     /**
959      * Returns description of method result value
960      *
961      * @return external_description
962      * @since Moodle 3.4
963      */
964     public static function get_submission_returns() {
965         return new external_single_structure(
966             array(
967                 'submission' => submission_exporter::get_read_structure(),
968                 'warnings' => new external_warnings()
969             )
970         );
971     }
973     /**
974      * Helper method for validating if the current user can view the submission assessments.
975      *
976      * @param  stdClass   $submission submission object
977      * @param  workshop   $workshop     workshop instance
978      * @return void
979      * @since  Moodle 3.4
980      */
981     protected static function check_view_submission_assessments($submission, workshop $workshop) {
982         global $USER;
984         $ownsubmission = $submission->authorid == $USER->id;
985         $canview = has_capability('mod/workshop:viewallassessments', $workshop->context) ||
986             ($ownsubmission && $workshop->assessments_available());
988         if ($canview) {
989             // Here we should check if the user share group.
990             if ($submission->authorid != $USER->id &&
991                     !groups_user_groups_visible($workshop->course, $submission->authorid, $workshop->cm)) {
992                 throw new moodle_exception('notingroup');
993             }
994         } else {
995             throw new moodle_exception('nopermissions', 'error', '', 'view assessment');
996         }
997     }
999     /**
1000      * Helper method for returning the assessment data according the current user capabilities and current phase.
1001      *
1002      * @param  stdClass $assessment the assessment data
1003      * @param  workshop $workshop   the workshop class
1004      * @return stdClass object with the assessment data filtered or null if is not viewable yet
1005      * @since Moodle 3.4
1006      */
1007     protected static function prepare_assessment_for_external($assessment, workshop $workshop) {
1008         global $USER;
1009         static $canviewallassessments = null;
1010         static $canviewreviewers = null;
1011         static $canoverridegrades = null;
1013         // Remove all the properties that does not belong to the assessment table.
1014         $properties = assessment_exporter::properties_definition();
1015         foreach ($assessment as $key => $value) {
1016             if (!isset($properties[$key])) {
1017                 unset($assessment->{$key});
1018             }
1019         }
1021         if (is_null($canviewallassessments)) {
1022             $canviewallassessments = has_capability('mod/workshop:viewallassessments', $workshop->context);
1023         }
1024         if (is_null($canviewreviewers)) {
1025             $canviewreviewers = has_capability('mod/workshop:viewreviewernames', $workshop->context);
1026         }
1027         if (is_null($canoverridegrades)) {
1028             $canoverridegrades = has_capability('mod/workshop:overridegrades', $workshop->context);
1029         }
1031         $isreviewer = $assessment->reviewerid == $USER->id;
1033         if (!$isreviewer && is_null($assessment->grade) && !$canviewallassessments) {
1034             // Students do not see peer-assessment that are not graded yet.
1035             return null;
1036         }
1038         // Remove the feedback for the reviewer if the feedback phase is not valid or if we don't have enough permissions to see it.
1039         if ($workshop->phase < workshop::PHASE_EVALUATION || !($isreviewer || $canviewallassessments)) {
1040             // Remove all the feedback information (all the optional fields).
1041             foreach ($properties as $attribute => $settings) {
1042                 if (!empty($settings['optional'])) {
1043                     unset($assessment->{$attribute});
1044                 }
1045             }
1046         }
1048         if (!$isreviewer && !$canviewreviewers) {
1049             $assessment->reviewerid = 0;
1050         }
1052         return $assessment;
1053     }
1055     /**
1056      * Returns the description of the external function parameters.
1057      *
1058      * @return external_function_parameters
1059      * @since Moodle 3.4
1060      */
1061     public static function get_submission_assessments_parameters() {
1062         return new external_function_parameters(
1063             array(
1064                 'submissionid' => new external_value(PARAM_INT, 'Submission id'),
1065             )
1066         );
1067     }
1070     /**
1071      * Retrieves the given submission assessments.
1072      *
1073      * @param int $submissionid the submission id
1074      * @return array containing the assessments and warnings.
1075      * @since Moodle 3.4
1076      * @throws moodle_exception
1077      */
1078     public static function get_submission_assessments($submissionid) {
1079         global $USER, $DB, $PAGE;
1081         $params = self::validate_parameters(self::get_submission_assessments_parameters(), array('submissionid' => $submissionid));
1082         $warnings = $assessments = array();
1084         // Get and validate the submission and workshop.
1085         $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
1086         list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1088         // Check that we can get the assessments and get them.
1089         self::check_view_submission_assessments($submission, $workshop);
1090         $assessmentsrecords = $workshop->get_assessments_of_submission($submission->id);
1092         $related = array('context' => $context);
1093         foreach ($assessmentsrecords as $assessment) {
1094             $assessment = self::prepare_assessment_for_external($assessment, $workshop);
1095             if (empty($assessment)) {
1096                 continue;
1097             }
1098             $exporter = new assessment_exporter($assessment, $related);
1099             $assessments[] = $exporter->export($PAGE->get_renderer('core'));
1100         }
1102         return array(
1103             'assessments' => $assessments,
1104             'warnings' => $warnings
1105         );
1106     }
1108     /**
1109      * Returns description of method result value
1110      *
1111      * @return external_description
1112      * @since Moodle 3.4
1113      */
1114     public static function get_submission_assessments_returns() {
1115         return new external_single_structure(
1116             array(
1117                 'assessments' => new external_multiple_structure(
1118                     assessment_exporter::get_read_structure()
1119                 ),
1120                 'warnings' => new external_warnings()
1121             )
1122         );
1123     }
1125     /**
1126      * Returns the description of the external function parameters.
1127      *
1128      * @return external_function_parameters
1129      * @since Moodle 3.4
1130      */
1131     public static function get_assessment_parameters() {
1132         return new external_function_parameters(
1133             array(
1134                 'assessmentid' => new external_value(PARAM_INT, 'Assessment id'),
1135             )
1136         );
1137     }
1140     /**
1141      * Retrieves the given assessment.
1142      *
1143      * @param int $assessmentid the assessment id
1144      * @return array containing the assessment and warnings.
1145      * @since Moodle 3.4
1146      * @throws moodle_exception
1147      */
1148     public static function get_assessment($assessmentid) {
1149         global $DB, $PAGE;
1151         $params = self::validate_parameters(self::get_assessment_parameters(), array('assessmentid' => $assessmentid));
1152         $warnings = array();
1154         // Get and validate the assessment, submission and workshop.
1155         $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
1156         $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
1157         list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1159         // Check that we can get the assessment.
1160         $workshop->check_view_assessment($assessment, $submission);
1162         $assessment = $workshop->get_assessment_by_id($assessment->id);
1163         $assessment = self::prepare_assessment_for_external($assessment, $workshop);
1164         if (empty($assessment)) {
1165             throw new moodle_exception('nopermissions', 'error', '', 'view assessment');
1166         }
1167         $related = array('context' => $context);
1168         $exporter = new assessment_exporter($assessment, $related);
1170         return array(
1171             'assessment' => $exporter->export($PAGE->get_renderer('core')),
1172             'warnings' => $warnings
1173         );
1174     }
1176     /**
1177      * Returns description of method result value
1178      *
1179      * @return external_description
1180      * @since Moodle 3.4
1181      */
1182     public static function get_assessment_returns() {
1183         return new external_single_structure(
1184             array(
1185                 'assessment' => assessment_exporter::get_read_structure(),
1186                 'warnings' => new external_warnings()
1187             )
1188         );
1189     }
1191     /**
1192      * Returns the description of the external function parameters.
1193      *
1194      * @return external_function_parameters
1195      * @since Moodle 3.4
1196      */
1197     public static function get_assessment_form_definition_parameters() {
1198         return new external_function_parameters(
1199             array(
1200                 'assessmentid' => new external_value(PARAM_INT, 'Assessment id'),
1201                 'mode' => new external_value(PARAM_ALPHA, 'The form mode (assessment or preview)', VALUE_DEFAULT, 'assessment'),
1202             )
1203         );
1204     }
1207     /**
1208      * Retrieves the assessment form definition (data required to be able to display the assessment form).
1209      *
1210      * @param int $assessmentid the assessment id
1211      * @param string $mode the form mode (assessment or preview)
1212      * @return array containing the assessment and warnings.
1213      * @since Moodle 3.4
1214      * @throws moodle_exception
1215      */
1216     public static function get_assessment_form_definition($assessmentid, $mode = 'assessment') {
1217         global $DB, $USER;
1219         $params = self::validate_parameters(
1220             self::get_assessment_form_definition_parameters(), array('assessmentid' => $assessmentid, 'mode' => $mode)
1221         );
1222         $warnings = $pending = array();
1224         if ($params['mode'] != 'assessment' && $params['mode'] != 'preview') {
1225             throw new invalid_parameter_exception('Invalid value for mode parameter (value: ' . $params['mode'] . ')');
1226         }
1228         // Get and validate the assessment, submission and workshop.
1229         $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
1230         $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
1231         list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1233         // Check we can edit the assessment (so we can get the form data).
1234         $workshop->check_edit_assessment($assessment, $submission);
1236         $cansetassessmentweight = has_capability('mod/workshop:allocate', $context);
1237         $pending = $workshop->get_pending_assessments_by_reviewer($assessment->reviewerid, $assessment->id);
1239         // Retrieve the data from the strategy plugin.
1240         $strategy = $workshop->grading_strategy_instance();
1241         $strategyname = str_replace('_strategy', '', get_class($strategy)); // Get strategy name.
1242         $mform = $strategy->get_assessment_form(null, $params['mode'], $assessment, true,
1243             array('editableweight' => $cansetassessmentweight, 'pending' => !empty($pending)));
1244         $formdata = $mform->get_customdata();
1246         $result = array(
1247             'dimenssionscount' => $formdata['nodims'],
1248             'descriptionfiles' => external_util::get_area_files($context->id, $strategyname, 'description'),
1249             'warnings' => $warnings
1250         );
1251         // Include missing dimension fields.
1252         for ($i = 0; $i < $formdata['nodims']; $i++) {
1253             $formdata['fields']->{'gradeid__idx_' . $i} = 0;
1254             $formdata['fields']->{'peercomment__idx_' . $i} = '';
1255         }
1257         // Convert all the form data for external.
1258         foreach (array('options', 'fields', 'current') as $typeofdata) {
1259             $result[$typeofdata] = array();
1261             if (!empty($formdata[$typeofdata])) {
1262                 $alldata = (array) $formdata[$typeofdata];
1263                 foreach ($alldata as $key => $val) {
1264                     if (strpos($key, 'peercomment__idx_') === 0) {
1265                         // Format reviewer comment.
1266                         list($val, $format) = external_format_text($val, FORMAT_MOODLE, $context->id);
1267                     } else if (strpos($key, 'description__idx_')) {
1268                         // Format dimension description.
1269                         $id = str_replace('description__idx_', '', $key);
1270                         list($val, $format) = external_format_text($val, $alldata['dimensionid__idx_' . $id . 'format'],
1271                             $context->id, $strategyname, 'description', $alldata['dimensionid__idx_' . $id]);
1272                     }
1273                     $result[$typeofdata][] = array(
1274                         'name' => $key,
1275                         'value' => $val
1276                     );
1277                 }
1278             }
1279         }
1281         return $result;
1282     }
1284     /**
1285      * Returns description of method result value
1286      *
1287      * @return external_description
1288      * @since Moodle 3.4
1289      */
1290     public static function get_assessment_form_definition_returns() {
1291         return new external_single_structure(
1292             array(
1293                 'dimenssionscount' => new external_value(PARAM_INT, 'The number of dimenssions used by the form.'),
1294                 'descriptionfiles' => new external_files('Files in the description text'),
1295                 'options' => new external_multiple_structure(
1296                     new external_single_structure(
1297                         array(
1298                             'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name.'),
1299                             'value' => new external_value(PARAM_NOTAGS, 'Option value.')
1300                         )
1301                     ), 'The form options.'
1302                 ),
1303                 'fields' => new external_multiple_structure(
1304                     new external_single_structure(
1305                         array(
1306                             'name' => new external_value(PARAM_ALPHANUMEXT, 'Field name.'),
1307                             'value' => new external_value(PARAM_RAW, 'Field default value.')
1308                         )
1309                     ), 'The form fields.'
1310                 ),
1311                 'current' => new external_multiple_structure(
1312                     new external_single_structure(
1313                         array(
1314                             'name' => new external_value(PARAM_ALPHANUMEXT, 'Field name.'),
1315                             'value' => new external_value(PARAM_RAW, 'Current field value.')
1316                         )
1317                     ), 'The current field values.'
1318                 ),
1319                 'warnings' => new external_warnings()
1320             )
1321         );
1322     }
1324     /**
1325      * Returns the description of the external function parameters.
1326      *
1327      * @return external_function_parameters
1328      * @since Moodle 3.4
1329      */
1330     public static function get_reviewer_assessments_parameters() {
1331         return new external_function_parameters(
1332             array(
1333                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
1334                 'userid' => new external_value(PARAM_INT, 'User id who did the assessment review (empty or 0 for current user).',
1335                     VALUE_DEFAULT, 0),
1336             )
1337         );
1338     }
1341     /**
1342      * Retrieves all the assessments reviewed by the given user.
1343      *
1344      * @param int $workshopid   the workshop instance id
1345      * @param int $userid       the reviewer user id
1346      * @return array containing the user assessments and warnings.
1347      * @since Moodle 3.4
1348      * @throws moodle_exception
1349      */
1350     public static function get_reviewer_assessments($workshopid, $userid = 0) {
1351         global $USER, $DB, $PAGE;
1353         $params = self::validate_parameters(
1354             self::get_reviewer_assessments_parameters(), array('workshopid' => $workshopid, 'userid' => $userid)
1355         );
1356         $warnings = $assessments = array();
1358         // Get and validate the submission and workshop.
1359         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
1361         // Extra checks so only users with permissions can view other users assessments.
1362         if (empty($params['userid']) || $params['userid'] == $USER->id) {
1363             $userid = $USER->id;
1364             list($assessed, $notice) = $workshop->check_examples_assessed_before_assessment($userid);
1365             if (!$assessed) {
1366                 throw new moodle_exception($notice, 'mod_workshop');
1367             }
1368             if ($workshop->phase < workshop::PHASE_ASSESSMENT) {    // Can view assessments only in assessment phase onwards.
1369                 throw new moodle_exception('nopermissions', 'error', '', 'view assessments');
1370             }
1371         } else {
1372             require_capability('mod/workshop:viewallassessments', $context);
1373             $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1374             core_user::require_active_user($user);
1375             if (!$workshop->check_group_membership($user->id)) {
1376                 throw new moodle_exception('notingroup');
1377             }
1378             $userid = $user->id;
1379         }
1380         // Now get all my assessments (includes those pending review).
1381         $assessmentsrecords = $workshop->get_assessments_by_reviewer($userid);
1383         $related = array('context' => $context);
1384         foreach ($assessmentsrecords as $assessment) {
1385             $assessment = self::prepare_assessment_for_external($assessment, $workshop);
1386             if (empty($assessment)) {
1387                 continue;
1388             }
1389             $exporter = new assessment_exporter($assessment, $related);
1390             $assessments[] = $exporter->export($PAGE->get_renderer('core'));
1391         }
1393         return array(
1394             'assessments' => $assessments,
1395             'warnings' => $warnings
1396         );
1397     }
1399     /**
1400      * Returns description of method result value
1401      *
1402      * @return external_description
1403      * @since Moodle 3.4
1404      */
1405     public static function get_reviewer_assessments_returns() {
1406         return new external_single_structure(
1407             array(
1408                 'assessments' => new external_multiple_structure(
1409                     assessment_exporter::get_read_structure()
1410                 ),
1411                 'warnings' => new external_warnings()
1412             )
1413         );
1414     }
1416     /**
1417      * Returns the description of the external function parameters.
1418      *
1419      * @return external_function_parameters
1420      * @since Moodle 3.4
1421      */
1422     public static function update_assessment_parameters() {
1423         return new external_function_parameters(
1424             array(
1425                 'assessmentid' => new external_value(PARAM_INT, 'Assessment id.'),
1426                 'data' => new external_multiple_structure (
1427                     new external_single_structure(
1428                         array(
1429                             'name' => new external_value(PARAM_ALPHANUMEXT,
1430                                 'The assessment data (use WS get_assessment_form_definition for obtaining the data to sent).
1431                                 Apart from that data, you can optionally send:
1432                                 feedbackauthor (str); the feedback for the submission author
1433                                 feedbackauthorformat (int); the format of the feedbackauthor
1434                                 feedbackauthorinlineattachmentsid (int); the draft file area for the editor attachments
1435                                 feedbackauthorattachmentsid (int); the draft file area id for the feedback attachments'
1436                             ),
1437                             'value' => new external_value(PARAM_RAW, 'The value of the option.')
1438                         )
1439                     ), 'Assessment data'
1440                 )
1441             )
1442         );
1443     }
1446     /**
1447      * Updates an assessment.
1448      *
1449      * @param int $assessmentid the assessment id
1450      * @param array $data the assessment data
1451      * @return array indicates if the assessment was updated, the new raw grade and possible warnings.
1452      * @since Moodle 3.4
1453      * @throws moodle_exception
1454      */
1455     public static function update_assessment($assessmentid, $data) {
1456         global $DB, $USER;
1458         $params = self::validate_parameters(
1459             self::update_assessment_parameters(), array('assessmentid' => $assessmentid, 'data' => $data)
1460         );
1461         $warnings = array();
1463         // Get and validate the assessment, submission and workshop.
1464         $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
1465         $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
1466         list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1468         // Check we can edit the assessment.
1469         $workshop->check_edit_assessment($assessment, $submission);
1471         // Process data.
1472         $data = new stdClass;
1473         $data->feedbackauthor_editor = array();
1475         foreach ($params['data'] as $wsdata) {
1476             $name = trim($wsdata['name']);
1477             switch ($name) {
1478                 case 'feedbackauthor':
1479                     $data->feedbackauthor_editor['text'] = $wsdata['value'];
1480                     break;
1481                 case 'feedbackauthorformat':
1482                     $data->feedbackauthor_editor['format'] = clean_param($wsdata['value'], PARAM_FORMAT);
1483                     break;
1484                 case 'feedbackauthorinlineattachmentsid':
1485                     $data->feedbackauthor_editor['itemid'] = clean_param($wsdata['value'], PARAM_INT);
1486                     break;
1487                 case 'feedbackauthorattachmentsid':
1488                     $data->feedbackauthorattachment_filemanager = clean_param($wsdata['value'], PARAM_INT);
1489                     break;
1490                 default:
1491                     $data->{$wsdata['name']} = $wsdata['value'];    // Validation will be done in the form->validation.
1492             }
1493         }
1495         $cansetassessmentweight = has_capability('mod/workshop:allocate', $context);
1496         $pending = $workshop->get_pending_assessments_by_reviewer($assessment->reviewerid, $assessment->id);
1497         // Retrieve the data from the strategy plugin.
1498         $strategy = $workshop->grading_strategy_instance();
1499         $mform = $strategy->get_assessment_form(null, 'assessment', $assessment, true,
1500             array('editableweight' => $cansetassessmentweight, 'pending' => !empty($pending)));
1502         $errors = $mform->validation((array) $data, array());
1503         // We can get several errors, return them in warnings.
1504         if (!empty($errors)) {
1505             $status = false;
1506             $rawgrade = null;
1507             foreach ($errors as $itemname => $message) {
1508                 $warnings[] = array(
1509                     'item' => $itemname,
1510                     'itemid' => 0,
1511                     'warningcode' => 'fielderror',
1512                     'message' => s($message)
1513                 );
1514             }
1515         } else {
1516             $rawgrade = $workshop->edit_assessment($assessment, $submission, $data, $strategy);
1517             $status = true;
1518         }
1520         return array(
1521             'status' => $status,
1522             'rawgrade' => $rawgrade,
1523             'warnings' => $warnings,
1524         );
1525     }
1527     /**
1528      * Returns description of method result value
1529      *
1530      * @return external_description
1531      * @since Moodle 3.4
1532      */
1533     public static function update_assessment_returns() {
1534         return new external_single_structure(
1535             array(
1536                 'status' => new external_value(PARAM_BOOL, 'status: true if the assessment was added or updated false otherwise.'),
1537                 'rawgrade' => new external_value(PARAM_FLOAT, 'Raw percentual grade (0.00000 to 100.00000) for submission.',
1538                     VALUE_OPTIONAL),
1539                 'warnings' => new external_warnings()
1540             )
1541         );
1542     }
1544     /**
1545      * Returns the description of the external function parameters.
1546      *
1547      * @return external_external_function_parameters
1548      * @since Moodle 3.4
1549      */
1550     public static function get_grades_parameters() {
1551         return new external_function_parameters (
1552             array(
1553                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
1554                 'userid' => new external_value(PARAM_INT, 'User id (empty or 0 for current user).', VALUE_DEFAULT, 0),
1555             )
1556         );
1557     }
1559     /**
1560      * Returns the grades information for the given workshop and user.
1561      *
1562      * @param int $workshopid workshop instance id
1563      * @param int $userid user id
1564      * @return array of warnings and the user plan
1565      * @since Moodle 3.4
1566      * @throws  moodle_exception
1567      */
1568     public static function get_grades($workshopid, $userid = 0) {
1569         global $USER;
1571         $params = array(
1572             'workshopid' => $workshopid,
1573             'userid' => $userid,
1574         );
1575         $params = self::validate_parameters(self::get_grades_parameters(), $params);
1576         $warnings = array();
1578         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
1580         // Extra checks so only users with permissions can view other users plans.
1581         if (empty($params['userid']) || $params['userid'] == $USER->id) {
1582             $userid = $USER->id;
1583         } else {
1584             require_capability('mod/workshop:viewallassessments', $context);
1585             $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1586             core_user::require_active_user($user);
1587             if (!$workshop->check_group_membership($user->id)) {
1588                 throw new moodle_exception('notingroup');
1589             }
1590             $userid = $user->id;
1591         }
1593         $finalgrades = $workshop->get_gradebook_grades($userid);
1595         $result = array('warnings' => $warnings);
1596         if ($finalgrades !== false) {
1597             if (!empty($finalgrades->submissiongrade)) {
1598                 if (is_numeric($finalgrades->submissiongrade->grade)) {
1599                     $result['submissionrawgrade'] = $finalgrades->submissiongrade->grade;
1600                 }
1601                 $result['submissionlongstrgrade'] = $finalgrades->submissiongrade->str_long_grade;
1602                 $result['submissiongradehidden'] = $finalgrades->submissiongrade->hidden;
1603             }
1604             if (!empty($finalgrades->assessmentgrade)) {
1605                 if (is_numeric($finalgrades->assessmentgrade->grade)) {
1606                     $result['assessmentrawgrade'] = $finalgrades->assessmentgrade->grade;
1607                 }
1608                 $result['assessmentlongstrgrade'] = $finalgrades->assessmentgrade->str_long_grade;
1609                 $result['assessmentgradehidden'] = $finalgrades->assessmentgrade->hidden;
1610             }
1611         }
1613         return $result;
1614     }
1616     /**
1617      * Returns description of method result value.
1618      *
1619      * @return external_single_structure
1620      * @since Moodle 3.4
1621      */
1622     public static function get_grades_returns() {
1623         return new external_single_structure(
1624             array(
1625                 'assessmentrawgrade' => new external_value(PARAM_FLOAT, 'The assessment raw (numeric) grade.', VALUE_OPTIONAL),
1626                 'assessmentlongstrgrade' => new external_value(PARAM_NOTAGS, 'The assessment string grade.', VALUE_OPTIONAL),
1627                 'assessmentgradehidden' => new external_value(PARAM_BOOL, 'Whether the grade is hidden or not.', VALUE_OPTIONAL),
1628                 'submissionrawgrade' => new external_value(PARAM_FLOAT, 'The submission raw (numeric) grade.', VALUE_OPTIONAL),
1629                 'submissionlongstrgrade' => new external_value(PARAM_NOTAGS, 'The submission string grade.', VALUE_OPTIONAL),
1630                 'submissiongradehidden' => new external_value(PARAM_BOOL, 'Whether the grade is hidden or not.', VALUE_OPTIONAL),
1631                 'warnings' => new external_warnings(),
1632             )
1633         );
1634     }
1636     /**
1637      * Returns the description of the external function parameters.
1638      *
1639      * @return external_function_parameters
1640      * @since Moodle 3.4
1641      */
1642     public static function evaluate_assessment_parameters() {
1643         return new external_function_parameters(
1644             array(
1645                 'assessmentid' => new external_value(PARAM_INT, 'Assessment id.'),
1646                 'feedbacktext' => new external_value(PARAM_RAW, 'The feedback for the reviewer.', VALUE_DEFAULT, ''),
1647                 'feedbackformat' => new external_value(PARAM_INT, 'The feedback format for text.', VALUE_DEFAULT, FORMAT_MOODLE),
1648                 'weight' => new external_value(PARAM_INT, 'The new weight for the assessment.', VALUE_DEFAULT, 1),
1649                 'gradinggradeover' => new external_value(PARAM_ALPHANUMEXT, 'The new grading grade.', VALUE_DEFAULT, ''),
1650             )
1651         );
1652     }
1655     /**
1656      * Evaluates an assessment (used by teachers for provide feedback to the reviewer).
1657      *
1658      * @param int $assessmentid the assessment id
1659      * @param str $feedbacktext the feedback for the reviewer
1660      * @param int $feedbackformat the feedback format for the reviewer text
1661      * @param int $weight the new weight for the assessment
1662      * @param mixed $gradinggradeover the new grading grade (empty for no overriding the grade)
1663      * @return array containing the status and warnings.
1664      * @since Moodle 3.4
1665      * @throws moodle_exception
1666      */
1667     public static function evaluate_assessment($assessmentid, $feedbacktext = '', $feedbackformat = FORMAT_MOODLE, $weight = 1,
1668             $gradinggradeover = '') {
1669         global $DB;
1671         $params = self::validate_parameters(
1672             self::evaluate_assessment_parameters(),
1673             array(
1674                 'assessmentid' => $assessmentid,
1675                 'feedbacktext' => $feedbacktext,
1676                 'feedbackformat' => $feedbackformat,
1677                 'weight' => $weight,
1678                 'gradinggradeover' => $gradinggradeover,
1679             )
1680         );
1681         $warnings = array();
1683         // Get and validate the assessment, submission and workshop.
1684         $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
1685         $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
1686         list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1688         // Check we can evaluate the assessment.
1689         $workshop->check_view_assessment($assessment, $submission);
1690         $cansetassessmentweight = has_capability('mod/workshop:allocate', $context);
1691         $canoverridegrades      = has_capability('mod/workshop:overridegrades', $context);
1692         if (!$canoverridegrades && !$cansetassessmentweight) {
1693             throw new moodle_exception('nopermissions', 'error', '', 'evaluate assessments');
1694         }
1696         // Process data.
1697         $data = new stdClass;
1698         $data->asid = $assessment->id;
1699         $data->feedbackreviewer_editor = array(
1700             'text' => $params['feedbacktext'],
1701             'format' => $params['feedbackformat'],
1702         );
1703         $data->weight = $params['weight'];
1704         $data->gradinggradeover = $params['gradinggradeover'];
1706         $options = array(
1707             'editable' => true,
1708             'editableweight' => $cansetassessmentweight,
1709             'overridablegradinggrade' => $canoverridegrades
1710         );
1711         $feedbackform = $workshop->get_feedbackreviewer_form(null, $assessment, $options);
1713         $errors = $feedbackform->validation((array) $data, array());
1714         // We can get several errors, return them in warnings.
1715         if (!empty($errors)) {
1716             $status = false;
1717             foreach ($errors as $itemname => $message) {
1718                 $warnings[] = array(
1719                     'item' => $itemname,
1720                     'itemid' => 0,
1721                     'warningcode' => 'fielderror',
1722                     'message' => s($message)
1723                 );
1724             }
1725         } else {
1726             $workshop->evaluate_assessment($assessment, $data, $cansetassessmentweight, $canoverridegrades);
1727             $status = true;
1728         }
1730         return array(
1731             'status' => $status,
1732             'warnings' => $warnings,
1733         );
1734     }
1736     /**
1737      * Returns description of method result value
1738      *
1739      * @return external_description
1740      * @since Moodle 3.4
1741      */
1742     public static function evaluate_assessment_returns() {
1743         return new external_single_structure(
1744             array(
1745                 'status' => new external_value(PARAM_BOOL, 'status: true if the assessment was evaluated, false otherwise.'),
1746                 'warnings' => new external_warnings()
1747             )
1748         );
1749     }
1751     /**
1752      * Returns description of method parameters
1753      *
1754      * @return external_function_parameters
1755      * @since Moodle 3.4
1756      */
1757     public static function get_grades_report_parameters() {
1758         return new external_function_parameters(
1759             array(
1760                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
1761                 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group.',
1762                                                    VALUE_DEFAULT, 0),
1763                 'sortby' => new external_value(PARAM_ALPHA, 'sort by this element: lastname, firstname, submissiontitle,
1764                     submissionmodified, submissiongrade, gradinggrade.', VALUE_DEFAULT, 'lastname'),
1765                 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'ASC'),
1766                 'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
1767                 'perpage' => new external_value(PARAM_INT, 'The number of records to return per page.', VALUE_DEFAULT, 0),
1768             )
1769         );
1770     }
1772     /**
1773      * Retrieves the assessment grades report.
1774      *
1775      * @param int $workshopid       the workshop instance id
1776      * @param int $groupid          (optional) group id, 0 means that the function will determine the user group
1777      * @param string $sortby        sort by this element
1778      * @param string $sortdirection sort direction: ASC or DESC
1779      * @param int $page             page of records to return
1780      * @param int $perpage          number of records to return per page
1781      * @return array of warnings and the report data
1782      * @since Moodle 3.4
1783      * @throws moodle_exception
1784      */
1785     public static function get_grades_report($workshopid, $groupid = 0, $sortby = 'lastname', $sortdirection = 'ASC',
1786             $page = 0, $perpage = 0) {
1787         global $USER;
1789         $params = array('workshopid' => $workshopid, 'groupid' => $groupid, 'sortby' => $sortby, 'sortdirection' => $sortdirection,
1790             'page' => $page, 'perpage' => $perpage);
1791         $params = self::validate_parameters(self::get_grades_report_parameters(), $params);
1792         $submissions = $warnings = array();
1794         $sortallowedvalues = array('lastname', 'firstname', 'submissiontitle', 'submissionmodified', 'submissiongrade',
1795             'gradinggrade');
1796         if (!in_array($params['sortby'], $sortallowedvalues)) {
1797             throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
1798                 'allowed values are: ' . implode(',', $sortallowedvalues));
1799         }
1801         $sortdirection = strtoupper($params['sortdirection']);
1802         $directionallowedvalues = array('ASC', 'DESC');
1803         if (!in_array($sortdirection, $directionallowedvalues)) {
1804             throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
1805                 'allowed values are: ' . implode(',', $directionallowedvalues));
1806         }
1808         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
1809         require_capability('mod/workshop:viewallassessments', $context);
1811         if (!empty($params['groupid'])) {
1812             $groupid = $params['groupid'];
1813             // Determine is the group is visible to user.
1814             if (!groups_group_visible($groupid, $course, $cm)) {
1815                 throw new moodle_exception('notingroup');
1816             }
1817         } else {
1818             // Check to see if groups are being used here.
1819             if ($groupmode = groups_get_activity_groupmode($cm)) {
1820                 $groupid = groups_get_activity_group($cm);
1821                 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
1822                 if (!groups_group_visible($groupid, $course, $cm)) {
1823                     throw new moodle_exception('notingroup');
1824                 }
1825             } else {
1826                 $groupid = 0;
1827             }
1828         }
1830         if ($workshop->phase >= workshop::PHASE_SUBMISSION) {
1831             $showauthornames = has_capability('mod/workshop:viewauthornames', $context);
1832             $showreviewernames = has_capability('mod/workshop:viewreviewernames', $context);
1834             if ($workshop->phase >= workshop::PHASE_EVALUATION) {
1835                 $showsubmissiongrade = true;
1836                 $showgradinggrade = true;
1837             } else {
1838                 $showsubmissiongrade = false;
1839                 $showgradinggrade = false;
1840             }
1842             $data = $workshop->prepare_grading_report_data($USER->id, $groupid, $params['page'], $params['perpage'],
1843                 $params['sortby'], $sortdirection);
1845             if (!empty($data)) {
1846                 // Populate the display options for the submissions report.
1847                 $reportopts                      = new stdclass();
1848                 $reportopts->showauthornames     = $showauthornames;
1849                 $reportopts->showreviewernames   = $showreviewernames;
1850                 $reportopts->sortby              = $params['sortby'];
1851                 $reportopts->sorthow             = $sortdirection;
1852                 $reportopts->showsubmissiongrade = $showsubmissiongrade;
1853                 $reportopts->showgradinggrade    = $showgradinggrade;
1854                 $reportopts->workshopphase       = $workshop->phase;
1856                 $report = new workshop_grading_report($data, $reportopts);
1857                 return array(
1858                     'report' => $report->export_data_for_external(),
1859                     'warnings' => array(),
1860                 );
1861             }
1862         }
1863         throw new moodle_exception('nothingfound', 'workshop');
1864     }
1866     /**
1867      * Returns description of method result value
1868      *
1869      * @return external_description
1870      * @since Moodle 3.4
1871      */
1872     public static function get_grades_report_returns() {
1874         $reviewstructure = new external_single_structure(
1875             array(
1876                 'userid' => new external_value(PARAM_INT, 'The id of the user (0 when is configured to do not display names).'),
1877                 'assessmentid' => new external_value(PARAM_INT, 'The id of the assessment.'),
1878                 'submissionid' => new external_value(PARAM_INT, 'The id of the submission assessed.'),
1879                 'grade' => new external_value(PARAM_FLOAT, 'The grade for submission.'),
1880                 'gradinggrade' => new external_value(PARAM_FLOAT, 'The grade for assessment.'),
1881                 'gradinggradeover' => new external_value(PARAM_FLOAT, 'The aggregated grade overrided.'),
1882                 'weight' => new external_value(PARAM_INT, 'The weight of the assessment for aggregation.'),
1883             )
1884         );
1886         return new external_single_structure(
1887             array(
1888                 'report' => new external_single_structure(
1889                     array(
1890                         'grades' => new external_multiple_structure(
1891                             new external_single_structure(
1892                                 array(
1893                                     'userid' => new external_value(PARAM_INT, 'The id of the user being displayed in the report.'),
1894                                     'submissionid' => new external_value(PARAM_INT, 'Submission id.'),
1895                                     'submissiontitle' => new external_value(PARAM_RAW, 'Submission title.'),
1896                                     'submissionmodified' => new external_value(PARAM_INT, 'Timestamp submission was updated.'),
1897                                     'submissiongrade' => new external_value(PARAM_FLOAT, 'Aggregated grade for the submission.',
1898                                         VALUE_OPTIONAL),
1899                                     'gradinggrade' => new external_value(PARAM_FLOAT, 'Computed grade for the assessment.',
1900                                         VALUE_OPTIONAL),
1901                                     'submissiongradeover' => new external_value(PARAM_FLOAT, 'Grade for the assessment overrided
1902                                         by the teacher.', VALUE_OPTIONAL),
1903                                     'submissiongradeoverby' => new external_value(PARAM_INT, 'The id of the user who overrided
1904                                         the grade.', VALUE_OPTIONAL),
1905                                     'submissionpublished' => new external_value(PARAM_INT, 'Whether is a submission published.',
1906                                         VALUE_OPTIONAL),
1907                                     'reviewedby' => new external_multiple_structure($reviewstructure, 'The users who reviewed the
1908                                         user submission.', VALUE_OPTIONAL),
1909                                     'reviewerof' => new external_multiple_structure($reviewstructure, 'The assessments the user
1910                                         reviewed.', VALUE_OPTIONAL),
1911                                 )
1912                             )
1913                         ),
1914                         'totalcount' => new external_value(PARAM_INT, 'Number of total submissions.'),
1915                     )
1916                 ),
1917                 'warnings' => new external_warnings()
1918             )
1919         );
1920     }