MDL-59243 mod_workshop: Return creation status in create_submission WS
[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;
34 /**
35  * Workshop external functions
36  *
37  * @package    mod_workshop
38  * @category   external
39  * @copyright  2017 Juan Leyva <juan@moodle.com>
40  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  * @since      Moodle 3.4
42  */
43 class mod_workshop_external extends external_api {
45     /**
46      * Describes the parameters for get_workshops_by_courses.
47      *
48      * @return external_function_parameters
49      * @since Moodle 3.4
50      */
51     public static function get_workshops_by_courses_parameters() {
52         return new external_function_parameters (
53             array(
54                 'courseids' => new external_multiple_structure(
55                     new external_value(PARAM_INT, 'Course id'), 'Array of course ids', VALUE_DEFAULT, array()
56                 ),
57             )
58         );
59     }
61     /**
62      * Returns a list of workshops in a provided list of courses.
63      * If no list is provided all workshops that the user can view will be returned.
64      *
65      * @param array $courseids course ids
66      * @return array of warnings and workshops
67      * @since Moodle 3.4
68      */
69     public static function get_workshops_by_courses($courseids = array()) {
70         global $PAGE;
72         $warnings = array();
73         $returnedworkshops = array();
75         $params = array(
76             'courseids' => $courseids,
77         );
78         $params = self::validate_parameters(self::get_workshops_by_courses_parameters(), $params);
80         $mycourses = array();
81         if (empty($params['courseids'])) {
82             $mycourses = enrol_get_my_courses();
83             $params['courseids'] = array_keys($mycourses);
84         }
86         // Ensure there are courseids to loop through.
87         if (!empty($params['courseids'])) {
89             list($courses, $warnings) = external_util::validate_courses($params['courseids'], $mycourses);
90             $output = $PAGE->get_renderer('core');
92             // Get the workshops in this course, this function checks users visibility permissions.
93             // We can avoid then additional validate_context calls.
94             $workshops = get_all_instances_in_courses("workshop", $courses);
95             foreach ($workshops as $workshop) {
97                 $context = context_module::instance($workshop->coursemodule);
98                 // Remove fields that are not from the workshop (added by get_all_instances_in_courses).
99                 unset($workshop->coursemodule, $workshop->context, $workshop->visible, $workshop->section, $workshop->groupmode,
100                         $workshop->groupingid);
102                 $exporter = new workshop_summary_exporter($workshop, array('context' => $context));
103                 $returnedworkshops[] = $exporter->export($output);
104             }
105         }
107         $result = array(
108             'workshops' => $returnedworkshops,
109             'warnings' => $warnings
110         );
111         return $result;
112     }
114     /**
115      * Describes the get_workshops_by_courses return value.
116      *
117      * @return external_single_structure
118      * @since Moodle 3.4
119      */
120     public static function get_workshops_by_courses_returns() {
121         return new external_single_structure(
122             array(
123                 'workshops' => new external_multiple_structure(
124                     workshop_summary_exporter::get_read_structure()
125                 ),
126                 'warnings' => new external_warnings(),
127             )
128         );
129     }
131     /**
132      * Utility function for validating a workshop.
133      *
134      * @param int $workshopid workshop instance id
135      * @return array array containing the workshop object, course, context and course module objects
136      * @since  Moodle 3.4
137      */
138     protected static function validate_workshop($workshopid) {
139         global $DB, $USER;
141         // Request and permission validation.
142         $workshop = $DB->get_record('workshop', array('id' => $workshopid), '*', MUST_EXIST);
143         list($course, $cm) = get_course_and_cm_from_instance($workshop, 'workshop');
145         $context = context_module::instance($cm->id);
146         self::validate_context($context);
148         $workshop = new workshop($workshop, $cm, $course);
150         return array($workshop, $course, $cm, $context);
151     }
154     /**
155      * Describes the parameters for get_workshop_access_information.
156      *
157      * @return external_external_function_parameters
158      * @since Moodle 3.4
159      */
160     public static function get_workshop_access_information_parameters() {
161         return new external_function_parameters (
162             array(
163                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.')
164             )
165         );
166     }
168     /**
169      * Return access information for a given workshop.
170      *
171      * @param int $workshopid workshop instance id
172      * @return array of warnings and the access information
173      * @since Moodle 3.4
174      * @throws  moodle_exception
175      */
176     public static function get_workshop_access_information($workshopid) {
177         global $USER;
179         $params = self::validate_parameters(self::get_workshop_access_information_parameters(), array('workshopid' => $workshopid));
181         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
183         $result = array();
184         // Return all the available capabilities.
185         $capabilities = load_capability_def('mod_workshop');
186         foreach ($capabilities as $capname => $capdata) {
187             // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
188             $field = 'can' . str_replace('mod/workshop:', '', $capname);
189             $result[$field] = has_capability($capname, $context);
190         }
192         // Now, specific features access information.
193         $result['creatingsubmissionallowed'] = $workshop->creating_submission_allowed($USER->id);
194         $result['modifyingsubmissionallowed'] = $workshop->modifying_submission_allowed($USER->id);
195         $result['assessingallowed'] = $workshop->assessing_allowed($USER->id);
196         $result['assessingexamplesallowed'] = $workshop->assessing_examples_allowed();
197         if (is_null($result['assessingexamplesallowed'])) {
198             $result['assessingexamplesallowed'] = false;
199         }
200         $result['examplesassessed'] = $workshop->check_examples_assessed($USER->id);
202         $result['warnings'] = array();
203         return $result;
204     }
206     /**
207      * Describes the get_workshop_access_information return value.
208      *
209      * @return external_single_structure
210      * @since Moodle 3.4
211      */
212     public static function get_workshop_access_information_returns() {
214         $structure = array(
215             'creatingsubmissionallowed' => new external_value(PARAM_BOOL,
216                 'Is the given user allowed to create their submission?'),
217             'modifyingsubmissionallowed' => new external_value(PARAM_BOOL,
218                 'Is the user allowed to modify his existing submission?'),
219             'assessingallowed' => new external_value(PARAM_BOOL,
220                 'Is the user allowed to create/edit his assessments?'),
221             'assessingexamplesallowed' => new external_value(PARAM_BOOL,
222                 'Are reviewers allowed to create/edit their assessments of the example submissions?.'),
223             'examplesassessed' => new external_value(PARAM_BOOL,
224                 'Whether the given user has assessed all his required examples (always true if there are no examples to assess).'),
225             'warnings' => new external_warnings()
226         );
228         $capabilities = load_capability_def('mod_workshop');
229         foreach ($capabilities as $capname => $capdata) {
230             // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
231             $field = 'can' . str_replace('mod/workshop:', '', $capname);
232             $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.');
233         }
235         return new external_single_structure($structure);
236     }
238     /**
239      * Describes the parameters for get_user_plan.
240      *
241      * @return external_external_function_parameters
242      * @since Moodle 3.4
243      */
244     public static function get_user_plan_parameters() {
245         return new external_function_parameters (
246             array(
247                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
248                 'userid' => new external_value(PARAM_INT, 'User id (empty or 0 for current user).', VALUE_DEFAULT, 0),
249             )
250         );
251     }
253     /**
254      * Return the planner information for the given user.
255      *
256      * @param int $workshopid workshop instance id
257      * @param int $userid user id
258      * @return array of warnings and the user plan
259      * @since Moodle 3.4
260      * @throws  moodle_exception
261      */
262     public static function get_user_plan($workshopid, $userid = 0) {
263         global $USER;
265         $params = array(
266             'workshopid' => $workshopid,
267             'userid' => $userid,
268         );
269         $params = self::validate_parameters(self::get_user_plan_parameters(), $params);
271         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
273         // Extra checks so only users with permissions can view other users plans.
274         if (empty($params['userid']) || $params['userid'] == $USER->id) {
275             $userid = $USER->id;
276         } else {
277             require_capability('moodle/course:manageactivities', $context);
278             $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
279             core_user::require_active_user($user);
280             if (!$workshop->check_group_membership($user->id)) {
281                 throw new moodle_exception('notingroup');
282             }
283             $userid = $user->id;
284         }
286         // Get the user plan information ready for external functions.
287         $userplan = new workshop_user_plan($workshop, $userid);
288         $userplan = array('phases' => $userplan->phases, 'examples' => $userplan->get_examples());
289         foreach ($userplan['phases'] as $phasecode => $phase) {
290             $phase->code = $phasecode;
291             $userplan['phases'][$phasecode] = (array) $phase;
292             foreach ($userplan['phases'][$phasecode]['tasks'] as $taskcode => $task) {
293                 $task->code = $taskcode;
294                 if ($task->link instanceof moodle_url) {
295                     $task->link = $task->link->out(false);
296                 }
297                 $userplan['phases'][$phasecode]['tasks'][$taskcode] = (array) $task;
298             }
299             foreach ($userplan['phases'][$phasecode]['actions'] as $actioncode => $action) {
300                 if ($action->url instanceof moodle_url) {
301                     $action->url = $action->url->out(false);
302                 }
303                 $userplan['phases'][$phasecode]['actions'][$actioncode] = (array) $action;
304             }
305         }
307         $result['userplan'] = $userplan;
308         $result['warnings'] = array();
309         return $result;
310     }
312     /**
313      * Describes the get_user_plan return value.
314      *
315      * @return external_single_structure
316      * @since Moodle 3.4
317      */
318     public static function get_user_plan_returns() {
319         return new external_single_structure(
320             array(
321                 'userplan' => new external_single_structure(
322                     array(
323                         'phases' => new external_multiple_structure(
324                             new external_single_structure(
325                                 array(
326                                     'code' => new external_value(PARAM_INT, 'Phase code.'),
327                                     'title' => new external_value(PARAM_NOTAGS, 'Phase title.'),
328                                     'active' => new external_value(PARAM_BOOL, 'Whether is the active task.'),
329                                     'tasks' => new external_multiple_structure(
330                                         new external_single_structure(
331                                             array(
332                                                 'code' => new external_value(PARAM_ALPHA, 'Task code.'),
333                                                 'title' => new external_value(PARAM_RAW, 'Task title.'),
334                                                 'link' => new external_value(PARAM_URL, 'Link to task.'),
335                                                 'details' => new external_value(PARAM_RAW, 'Task details.', VALUE_OPTIONAL),
336                                                 'completed' => new external_value(PARAM_NOTAGS,
337                                                     'Completion information (maybe empty, maybe a boolean or generic info.'),
338                                             )
339                                         )
340                                     ),
341                                     'actions' => new external_multiple_structure(
342                                         new external_single_structure(
343                                             array(
344                                                 'type' => new external_value(PARAM_ALPHA, 'Action type.', VALUE_OPTIONAL),
345                                                 'label' => new external_value(PARAM_RAW, 'Action label.', VALUE_OPTIONAL),
346                                                 'url' => new external_value(PARAM_URL, 'Link to action.'),
347                                                 'method' => new external_value(PARAM_ALPHA, 'Get or post.', VALUE_OPTIONAL),
348                                             )
349                                         )
350                                     ),
351                                 )
352                             )
353                         ),
354                         'examples' => new external_multiple_structure(
355                             new external_single_structure(
356                                 array(
357                                     'id' => new external_value(PARAM_INT, 'Example submission id.'),
358                                     'title' => new external_value(PARAM_RAW, 'Example submission title.'),
359                                     'assessmentid' => new external_value(PARAM_INT, 'Example submission assessment id.'),
360                                     'grade' => new external_value(PARAM_FLOAT, 'The submission grade.'),
361                                     'gradinggrade' => new external_value(PARAM_FLOAT, 'The assessment grade.'),
362                                 )
363                             )
364                         ),
365                     )
366                 ),
367                 'warnings' => new external_warnings(),
368             )
369         );
370     }
372     /**
373      * Describes the parameters for view_workshop.
374      *
375      * @return external_function_parameters
376      * @since Moodle 3.4
377      */
378     public static function view_workshop_parameters() {
379         return new external_function_parameters (
380             array(
381                 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id'),
382             )
383         );
384     }
386     /**
387      * Trigger the course module viewed event and update the module completion status.
388      *
389      * @param int $workshopid workshop instance id
390      * @return array of warnings and status result
391      * @since Moodle 3.4
392      * @throws moodle_exception
393      */
394     public static function view_workshop($workshopid) {
396         $params = array('workshopid' => $workshopid);
397         $params = self::validate_parameters(self::view_workshop_parameters(), $params);
398         $warnings = array();
400         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
402         $workshop->set_module_viewed();
404         $result = array(
405             'status' => true,
406             'warnings' => $warnings,
407         );
408         return $result;
409     }
411     /**
412      * Describes the view_workshop return value.
413      *
414      * @return external_single_structure
415      * @since Moodle 3.4
416      */
417     public static function view_workshop_returns() {
418         return new external_single_structure(
419             array(
420                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
421                 'warnings' => new external_warnings(),
422             )
423         );
424     }
426     /**
427      * Returns the description of the external function parameters.
428      *
429      * @return external_function_parameters
430      * @since Moodle 3.4
431      */
432     public static function add_submission_parameters() {
433         return new external_function_parameters(array(
434             'workshopid' => new external_value(PARAM_INT, 'Workshop id'),
435             'title' => new external_value(PARAM_TEXT, 'Submission title'),
436             'content' => new external_value(PARAM_RAW, 'Submission text content', VALUE_DEFAULT, ''),
437             'contentformat' => new external_value(PARAM_INT, 'The format used for the content', VALUE_DEFAULT, FORMAT_MOODLE),
438             'inlineattachmentsid' => new external_value(PARAM_INT, 'The draft file area id for inline attachments in the content',
439                 VALUE_DEFAULT, 0),
440             'attachmentsid' => new external_value(PARAM_INT, 'The draft file area id for attachments', VALUE_DEFAULT, 0),
441         ));
442     }
444     /**
445      * Add a new submission to a given workshop.
446      *
447      * @param int $workshopid the workshop id
448      * @param string $title             the submission title
449      * @param string  $content          the submission text content
450      * @param int  $contentformat       the format used for the content
451      * @param int $inlineattachmentsid  the draft file area id for inline attachments in the content
452      * @param int $attachmentsid        the draft file area id for attachments
453      * @return array Containing the new created submission id and warnings.
454      * @since Moodle 3.4
455      * @throws moodle_exception
456      */
457     public static function add_submission($workshopid, $title, $content = '', $contentformat = FORMAT_MOODLE,
458             $inlineattachmentsid = 0, $attachmentsid = 0) {
459         global $USER;
461         $params = self::validate_parameters(self::add_submission_parameters(), array(
462             'workshopid' => $workshopid,
463             'title' => $title,
464             'content' => $content,
465             'contentformat' => $contentformat,
466             'inlineattachmentsid' => $inlineattachmentsid,
467             'attachmentsid' => $attachmentsid,
468         ));
469         $warnings = array();
471         // Get and validate the workshop.
472         list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
473         require_capability('mod/workshop:submit', $context);
475         // Check if we can submit now.
476         $canaddsubmission = $workshop->creating_submission_allowed($USER->id);
477         $canaddsubmission = $canaddsubmission && $workshop->check_examples_assessed($USER->id);
478         if (!$canaddsubmission) {
479             throw new moodle_exception('nopermissions', 'error', '', 'add submission');
480         }
482         // Prepare the submission object.
483         $submission = new stdClass;
484         $submission->id = null;
485         $submission->cmid = $cm->id;
486         $submission->example = 0;
487         $submission->title = trim($params['title']);
488         $submission->content_editor = array(
489             'text' => $params['content'],
490             'format' => $params['contentformat'],
491             'itemid' => $params['inlineattachmentsid'],
492         );
493         $submission->attachment_filemanager = $params['attachmentsid'];
495         if (empty($submission->title)) {
496             throw new moodle_exception('errorinvalidparam', 'webservice', '', 'title');
497         }
499         $errors = $workshop->validate_submission_data((array) $submission);
500         // We can get several errors, return them in warnings.
501         if (!empty($errors)) {
502             $submission->id = 0;
503             foreach ($errors as $itemname => $message) {
504                 $warnings[] = array(
505                     'item' => $itemname,
506                     'itemid' => 0,
507                     'warningcode' => 'fielderror',
508                     'message' => s($message)
509                 );
510             }
511             return array(
512                 'status' => false,
513                 'warnings' => $warnings
514             );
515         } else {
516             $submission->id = $workshop->edit_submission($submission);
517             return array(
518                 'status' => true,
519                 'submissionid' => $submission->id,
520                 'warnings' => $warnings
521             );
522         }
523     }
525     /**
526      * Returns the description of the external function return value.
527      *
528      * @return external_description
529      * @since Moodle 3.4
530      */
531     public static function add_submission_returns() {
532         return new external_single_structure(array(
533             'status' => new external_value(PARAM_BOOL, 'True if the submission was created false otherwise.'),
534             'submissionid' => new external_value(PARAM_INT, 'New workshop submission id.', VALUE_OPTIONAL),
535             'warnings' => new external_warnings()
536         ));
537     }