Merge branch 'MDL-58453-master' of git://github.com/jleyva/moodle
[moodle.git] / mod / feedback / 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  * Feedback external API
19  *
20  * @package    mod_feedback
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.3
25  */
27 defined('MOODLE_INTERNAL') || die;
29 require_once("$CFG->libdir/externallib.php");
31 use mod_feedback\external\feedback_summary_exporter;
32 use mod_feedback\external\feedback_completedtmp_exporter;
33 use mod_feedback\external\feedback_item_exporter;
34 use mod_feedback\external\feedback_valuetmp_exporter;
35 use mod_feedback\external\feedback_value_exporter;
36 use mod_feedback\external\feedback_completed_exporter;
38 /**
39  * Feedback external functions
40  *
41  * @package    mod_feedback
42  * @category   external
43  * @copyright  2017 Juan Leyva <juan@moodle.com>
44  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
45  * @since      Moodle 3.3
46  */
47 class mod_feedback_external extends external_api {
49     /**
50      * Describes the parameters for get_feedbacks_by_courses.
51      *
52      * @return external_function_parameters
53      * @since Moodle 3.3
54      */
55     public static function get_feedbacks_by_courses_parameters() {
56         return new external_function_parameters (
57             array(
58                 'courseids' => new external_multiple_structure(
59                     new external_value(PARAM_INT, 'Course id'), 'Array of course ids', VALUE_DEFAULT, array()
60                 ),
61             )
62         );
63     }
65     /**
66      * Returns a list of feedbacks in a provided list of courses.
67      * If no list is provided all feedbacks that the user can view will be returned.
68      *
69      * @param array $courseids course ids
70      * @return array of warnings and feedbacks
71      * @since Moodle 3.3
72      */
73     public static function get_feedbacks_by_courses($courseids = array()) {
74         global $PAGE;
76         $warnings = array();
77         $returnedfeedbacks = array();
79         $params = array(
80             'courseids' => $courseids,
81         );
82         $params = self::validate_parameters(self::get_feedbacks_by_courses_parameters(), $params);
84         $mycourses = array();
85         if (empty($params['courseids'])) {
86             $mycourses = enrol_get_my_courses();
87             $params['courseids'] = array_keys($mycourses);
88         }
90         // Ensure there are courseids to loop through.
91         if (!empty($params['courseids'])) {
93             list($courses, $warnings) = external_util::validate_courses($params['courseids'], $mycourses);
94             $output = $PAGE->get_renderer('core');
96             // Get the feedbacks in this course, this function checks users visibility permissions.
97             // We can avoid then additional validate_context calls.
98             $feedbacks = get_all_instances_in_courses("feedback", $courses);
99             foreach ($feedbacks as $feedback) {
101                 $context = context_module::instance($feedback->coursemodule);
103                 // Remove fields that are not from the feedback (added by get_all_instances_in_courses).
104                 unset($feedback->coursemodule, $feedback->context, $feedback->visible, $feedback->section, $feedback->groupmode,
105                         $feedback->groupingid);
107                 // Check permissions.
108                 if (!has_capability('mod/feedback:edititems', $context)) {
109                     // Don't return the optional properties.
110                     $properties = feedback_summary_exporter::properties_definition();
111                     foreach ($properties as $property => $config) {
112                         if (!empty($config['optional'])) {
113                             unset($feedback->{$property});
114                         }
115                     }
116                 }
117                 $exporter = new feedback_summary_exporter($feedback, array('context' => $context));
118                 $returnedfeedbacks[] = $exporter->export($output);
119             }
120         }
122         $result = array(
123             'feedbacks' => $returnedfeedbacks,
124             'warnings' => $warnings
125         );
126         return $result;
127     }
129     /**
130      * Describes the get_feedbacks_by_courses return value.
131      *
132      * @return external_single_structure
133      * @since Moodle 3.3
134      */
135     public static function get_feedbacks_by_courses_returns() {
136         return new external_single_structure(
137             array(
138                 'feedbacks' => new external_multiple_structure(
139                     feedback_summary_exporter::get_read_structure()
140                 ),
141                 'warnings' => new external_warnings(),
142             )
143         );
144     }
146     /**
147      * Utility function for validating a feedback.
148      *
149      * @param int $feedbackid feedback instance id
150      * @return array array containing the feedback persistent, course, context and course module objects
151      * @since  Moodle 3.3
152      */
153     protected static function validate_feedback($feedbackid) {
154         global $DB, $USER;
156         // Request and permission validation.
157         $feedback = $DB->get_record('feedback', array('id' => $feedbackid), '*', MUST_EXIST);
158         list($course, $cm) = get_course_and_cm_from_instance($feedback, 'feedback');
160         $context = context_module::instance($cm->id);
161         self::validate_context($context);
163         return array($feedback, $course, $cm, $context);
164     }
166     /**
167      * Utility function for validating access to feedback.
168      *
169      * @param  stdClass   $feedback feedback object
170      * @param  stdClass   $course   course object
171      * @param  stdClass   $cm       course module
172      * @param  stdClass   $context  context object
173      * @throws moodle_exception
174      * @return feedback_completion feedback completion instance
175      * @since  Moodle 3.3
176      */
177     protected static function validate_feedback_access($feedback,  $course, $cm, $context, $checksubmit = false) {
178         $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
180         if (!$feedbackcompletion->can_complete()) {
181             throw new required_capability_exception($context, 'mod/feedback:complete', 'nopermission', '');
182         }
184         if (!$feedbackcompletion->is_open()) {
185             throw new moodle_exception('feedback_is_not_open', 'feedback');
186         }
188         if ($feedbackcompletion->is_empty()) {
189             throw new moodle_exception('no_items_available_yet', 'feedback');
190         }
192         if ($checksubmit && !$feedbackcompletion->can_submit()) {
193             throw new moodle_exception('this_feedback_is_already_submitted', 'feedback');
194         }
195         return $feedbackcompletion;
196     }
198     /**
199      * Describes the parameters for get_feedback_access_information.
200      *
201      * @return external_external_function_parameters
202      * @since Moodle 3.3
203      */
204     public static function get_feedback_access_information_parameters() {
205         return new external_function_parameters (
206             array(
207                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.')
208             )
209         );
210     }
212     /**
213      * Return access information for a given feedback.
214      *
215      * @param int $feedbackid feedback instance id
216      * @return array of warnings and the access information
217      * @since Moodle 3.3
218      * @throws  moodle_exception
219      */
220     public static function get_feedback_access_information($feedbackid) {
221         global $PAGE;
223         $params = array(
224             'feedbackid' => $feedbackid
225         );
226         $params = self::validate_parameters(self::get_feedback_access_information_parameters(), $params);
228         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
229         $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
231         $result = array();
232         // Capabilities first.
233         $result['canviewanalysis'] = $feedbackcompletion->can_view_analysis();
234         $result['cancomplete'] = $feedbackcompletion->can_complete();
235         $result['cansubmit'] = $feedbackcompletion->can_submit();
236         $result['candeletesubmissions'] = has_capability('mod/feedback:deletesubmissions', $context);
237         $result['canviewreports'] = has_capability('mod/feedback:viewreports', $context);
238         $result['canedititems'] = has_capability('mod/feedback:edititems', $context);
240         // Status information.
241         $result['isempty'] = $feedbackcompletion->is_empty();
242         $result['isopen'] = $feedbackcompletion->is_open();
243         $anycourse = ($course->id == SITEID);
244         $result['isalreadysubmitted'] = $feedbackcompletion->is_already_submitted($anycourse);
245         $result['isanonymous'] = $feedbackcompletion->is_anonymous();
247         $result['warnings'] = [];
248         return $result;
249     }
251     /**
252      * Describes the get_feedback_access_information return value.
253      *
254      * @return external_single_structure
255      * @since Moodle 3.3
256      */
257     public static function get_feedback_access_information_returns() {
258         return new external_single_structure(
259             array(
260                 'canviewanalysis' => new external_value(PARAM_BOOL, 'Whether the user can view the analysis or not.'),
261                 'cancomplete' => new external_value(PARAM_BOOL, 'Whether the user can complete the feedback or not.'),
262                 'cansubmit' => new external_value(PARAM_BOOL, 'Whether the user can submit the feedback or not.'),
263                 'candeletesubmissions' => new external_value(PARAM_BOOL, 'Whether the user can delete submissions or not.'),
264                 'canviewreports' => new external_value(PARAM_BOOL, 'Whether the user can view the feedback reports or not.'),
265                 'canedititems' => new external_value(PARAM_BOOL, 'Whether the user can edit feedback items or not.'),
266                 'isempty' => new external_value(PARAM_BOOL, 'Whether the feedback has questions or not.'),
267                 'isopen' => new external_value(PARAM_BOOL, 'Whether the feedback has active access time restrictions or not.'),
268                 'isalreadysubmitted' => new external_value(PARAM_BOOL, 'Whether the feedback is already submitted or not.'),
269                 'isanonymous' => new external_value(PARAM_BOOL, 'Whether the feedback is anonymous or not.'),
270                 'warnings' => new external_warnings(),
271             )
272         );
273     }
275     /**
276      * Describes the parameters for view_feedback.
277      *
278      * @return external_function_parameters
279      * @since Moodle 3.3
280      */
281     public static function view_feedback_parameters() {
282         return new external_function_parameters (
283             array(
284                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
285                 'moduleviewed' => new external_value(PARAM_BOOL, 'If we need to mark the module as viewed for completion',
286                     VALUE_DEFAULT, false),
287             )
288         );
289     }
291     /**
292      * Trigger the course module viewed event and update the module completion status.
293      *
294      * @param int $feedbackid feedback instance id
295      * @param bool $moduleviewed If we need to mark the module as viewed for completion
296      * @return array of warnings and status result
297      * @since Moodle 3.3
298      * @throws moodle_exception
299      */
300     public static function view_feedback($feedbackid, $moduleviewed = false) {
302         $params = array('feedbackid' => $feedbackid, 'moduleviewed' => $moduleviewed);
303         $params = self::validate_parameters(self::view_feedback_parameters(), $params);
304         $warnings = array();
306         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
307         $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
309         // Trigger module viewed event.
310         $feedbackcompletion->trigger_module_viewed($course);
311         if ($params['moduleviewed']) {
312             if (!$feedbackcompletion->is_open()) {
313                 throw new moodle_exception('feedback_is_not_open', 'feedback');
314             }
315             // Mark activity viewed for completion-tracking.
316             $feedbackcompletion->set_module_viewed($course);
317         }
319         $result = array(
320             'status' => true,
321             'warnings' => $warnings,
322         );
323         return $result;
324     }
326     /**
327      * Describes the view_feedback return value.
328      *
329      * @return external_single_structure
330      * @since Moodle 3.3
331      */
332     public static function view_feedback_returns() {
333         return new external_single_structure(
334             array(
335                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
336                 'warnings' => new external_warnings(),
337             )
338         );
339     }
341     /**
342      * Describes the parameters for get_current_completed_tmp.
343      *
344      * @return external_function_parameters
345      * @since Moodle 3.3
346      */
347     public static function get_current_completed_tmp_parameters() {
348         return new external_function_parameters (
349             array(
350                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
351             )
352         );
353     }
355     /**
356      * Returns the temporary completion record for the current user.
357      *
358      * @param int $feedbackid feedback instance id
359      * @return array of warnings and status result
360      * @since Moodle 3.3
361      * @throws moodle_exception
362      */
363     public static function get_current_completed_tmp($feedbackid) {
364         global $PAGE;
366         $params = array('feedbackid' => $feedbackid);
367         $params = self::validate_parameters(self::get_current_completed_tmp_parameters(), $params);
368         $warnings = array();
370         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
371         $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
373         if ($completed = $feedbackcompletion->get_current_completed_tmp()) {
374             $exporter = new feedback_completedtmp_exporter($completed);
375             return array(
376                 'feedback' => $exporter->export($PAGE->get_renderer('core')),
377                 'warnings' => $warnings,
378             );
379         }
380         throw new moodle_exception('not_started', 'feedback');
381     }
383     /**
384      * Describes the get_current_completed_tmp return value.
385      *
386      * @return external_single_structure
387      * @since Moodle 3.3
388      */
389     public static function get_current_completed_tmp_returns() {
390         return new external_single_structure(
391             array(
392                 'feedback' => feedback_completedtmp_exporter::get_read_structure(),
393                 'warnings' => new external_warnings(),
394             )
395         );
396     }
398     /**
399      * Describes the parameters for get_items.
400      *
401      * @return external_function_parameters
402      * @since Moodle 3.3
403      */
404     public static function get_items_parameters() {
405         return new external_function_parameters (
406             array(
407                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
408             )
409         );
410     }
412     /**
413      * Returns the items (questions) in the given feedback.
414      *
415      * @param int $feedbackid feedback instance id
416      * @return array of warnings and feedbacks
417      * @since Moodle 3.3
418      */
419     public static function get_items($feedbackid) {
420         global $PAGE;
422         $params = array('feedbackid' => $feedbackid);
423         $params = self::validate_parameters(self::get_items_parameters(), $params);
424         $warnings = array();
426         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
427         self::validate_feedback_access($feedback,  $course, $cm, $context);
429         $feedbackstructure = new mod_feedback_structure($feedback, $cm, $course->id);
430         $returneditems = array();
431         if ($items = $feedbackstructure->get_items()) {
432             foreach ($items as $item) {
433                 $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
434                 unset($item->itemnr);   // Added by the function, not part of the record.
435                 $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
436                 $returneditems[] = $exporter->export($PAGE->get_renderer('core'));
437             }
438         }
440         $result = array(
441             'items' => $returneditems,
442             'warnings' => $warnings
443         );
444         return $result;
445     }
447     /**
448      * Describes the get_items return value.
449      *
450      * @return external_single_structure
451      * @since Moodle 3.3
452      */
453     public static function get_items_returns() {
454         return new external_single_structure(
455             array(
456                 'items' => new external_multiple_structure(
457                     feedback_item_exporter::get_read_structure()
458                 ),
459                 'warnings' => new external_warnings(),
460             )
461         );
462     }
464     /**
465      * Describes the parameters for launch_feedback.
466      *
467      * @return external_function_parameters
468      * @since Moodle 3.3
469      */
470     public static function launch_feedback_parameters() {
471         return new external_function_parameters (
472             array(
473                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
474             )
475         );
476     }
478     /**
479      * Starts or continues a feedback submission
480      *
481      * @param array $feedbackid feedback instance id
482      * @return array of warnings and launch information
483      * @since Moodle 3.3
484      */
485     public static function launch_feedback($feedbackid) {
486         global $PAGE;
488         $params = array('feedbackid' => $feedbackid);
489         $params = self::validate_parameters(self::launch_feedback_parameters(), $params);
490         $warnings = array();
492         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
493         // Check we can do a new submission (or continue an existing).
494         $feedbackcompletion = self::validate_feedback_access($feedback,  $course, $cm, $context, true);
496         $gopage = $feedbackcompletion->get_resume_page();
497         if ($gopage === null) {
498             $gopage = -1; // Last page.
499         }
501         $result = array(
502             'gopage' => $gopage,
503             'warnings' => $warnings
504         );
505         return $result;
506     }
508     /**
509      * Describes the launch_feedback return value.
510      *
511      * @return external_single_structure
512      * @since Moodle 3.3
513      */
514     public static function launch_feedback_returns() {
515         return new external_single_structure(
516             array(
517                 'gopage' => new external_value(PARAM_INT, 'The next page to go (-1 if we were already in the last page). 0 for first page.'),
518                 'warnings' => new external_warnings(),
519             )
520         );
521     }
523     /**
524      * Describes the parameters for get_page_items.
525      *
526      * @return external_function_parameters
527      * @since Moodle 3.3
528      */
529     public static function get_page_items_parameters() {
530         return new external_function_parameters (
531             array(
532                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
533                 'page' => new external_value(PARAM_INT, 'The page to get starting by 0'),
534             )
535         );
536     }
538     /**
539      * Get a single feedback page items.
540      *
541      * @param int $feedbackid feedback instance id
542      * @param int $page the page to get starting by 0
543      * @return array of warnings and launch information
544      * @since Moodle 3.3
545      */
546     public static function get_page_items($feedbackid, $page) {
547         global $PAGE;
549         $params = array('feedbackid' => $feedbackid, 'page' => $page);
550         $params = self::validate_parameters(self::get_page_items_parameters(), $params);
551         $warnings = array();
553         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
555         $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
557         $page = $params['page'];
558         $pages = $feedbackcompletion->get_pages();
559         $pageitems = $pages[$page];
560         $hasnextpage = $page < count($pages) - 1; // Until we complete this page we can not trust get_next_page().
561         $hasprevpage = $page && ($feedbackcompletion->get_previous_page($page, false) !== null);
563         $returneditems = array();
564         foreach ($pageitems as $item) {
565             $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
566             unset($item->itemnr);   // Added by the function, not part of the record.
567             $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
568             $returneditems[] = $exporter->export($PAGE->get_renderer('core'));
569         }
571         $result = array(
572             'items' => $returneditems,
573             'hasprevpage' => $hasprevpage,
574             'hasnextpage' => $hasnextpage,
575             'warnings' => $warnings
576         );
577         return $result;
578     }
580     /**
581      * Describes the get_page_items return value.
582      *
583      * @return external_single_structure
584      * @since Moodle 3.3
585      */
586     public static function get_page_items_returns() {
587         return new external_single_structure(
588             array(
589                 'items' => new external_multiple_structure(
590                     feedback_item_exporter::get_read_structure()
591                 ),
592                 'hasprevpage' => new external_value(PARAM_BOOL, 'Whether is a previous page.'),
593                 'hasnextpage' => new external_value(PARAM_BOOL, 'Whether there are more pages.'),
594                 'warnings' => new external_warnings(),
595             )
596         );
597     }
599     /**
600      * Describes the parameters for process_page.
601      *
602      * @return external_function_parameters
603      * @since Moodle 3.3
604      */
605     public static function process_page_parameters() {
606         return new external_function_parameters (
607             array(
608                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
609                 'page' => new external_value(PARAM_INT, 'The page being processed.'),
610                 'responses' => new external_multiple_structure(
611                     new external_single_structure(
612                         array(
613                             'name' => new external_value(PARAM_NOTAGS, 'The response name (usually type[index]_id).'),
614                             'value' => new external_value(PARAM_RAW, 'The response value.'),
615                         )
616                     ), 'The data to be processed.'
617                 ),
618                 'goprevious' => new external_value(PARAM_BOOL, 'Whether we want to jump to previous page.', VALUE_DEFAULT, false),
619             )
620         );
621     }
623     /**
624      * Process a jump between pages.
625      *
626      * @param array $feedbackid feedback instance id
627      * @param array $page the page being processed
628      * @param array $responses the responses to be processed
629      * @param bool $goprevious whether we want to jump to previous page
630      * @return array of warnings and launch information
631      * @since Moodle 3.3
632      */
633     public static function process_page($feedbackid, $page, $responses, $goprevious = false) {
634         global $USER, $SESSION;
636         $params = array('feedbackid' => $feedbackid, 'page' => $page, 'responses' => $responses, 'goprevious' => $goprevious);
637         $params = self::validate_parameters(self::process_page_parameters(), $params);
638         $warnings = array();
639         $siteaftersubmit = $completionpagecontents = '';
641         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
642         // Check we can do a new submission (or continue an existing).
643         $feedbackcompletion = self::validate_feedback_access($feedback,  $course, $cm, $context, true);
645         // Create the $_POST object required by the feedback question engine.
646         $_POST = array();
647         foreach ($responses as $response) {
648             $_POST[$response['name']] = $response['value'];
649         }
650         // Force fields.
651         $_POST['id'] = $cm->id;
652         $_POST['courseid'] = $course->id;
653         $_POST['gopage'] = $params['page'];
654         $_POST['_qf__mod_feedback_complete_form'] = 1;
655         if (!$params['goprevious']) {
656             if ($feedbackcompletion->get_next_page($params['page'], false) === null) {
657                 $_POST['savevalues'] = 1;   // If there is no next page, it means we are finishing the feedback.
658             } else {
659                 $_POST['gonextpage'] = 1;   // If we are not going to previous page or finishing we are going forward.
660             }
661         }
663         // Ignore sesskey (deep in some APIs), the request is already validated.
664         $USER->ignoresesskey = true;
665         feedback_init_feedback_session();
666         $SESSION->feedback->is_started = true;
668         $feedbackcompletion->process_page($params['page'], $params['goprevious']);
669         $completed = $feedbackcompletion->just_completed();
670         if ($completed) {
671             $jumpto = 0;
672             if ($feedback->page_after_submit) {
673                 $completionpagecontents = $feedbackcompletion->page_after_submit();
674             }
676             if ($feedback->site_after_submit) {
677                 $siteaftersubmit = feedback_encode_target_url($feedback->site_after_submit);
678             }
679         } else {
680             $jumpto = $feedbackcompletion->get_jumpto();
681         }
683         $result = array(
684             'jumpto' => $jumpto,
685             'completed' => $completed,
686             'completionpagecontents' => $completionpagecontents,
687             'siteaftersubmit' => $siteaftersubmit,
688             'warnings' => $warnings
689         );
690         return $result;
691     }
693     /**
694      * Describes the process_page return value.
695      *
696      * @return external_single_structure
697      * @since Moodle 3.3
698      */
699     public static function process_page_returns() {
700         return new external_single_structure(
701             array(
702                 'jumpto' => new external_value(PARAM_INT, 'The page to jump to.'),
703                 'completed' => new external_value(PARAM_BOOL, 'If the user completed the feedback.'),
704                 'completionpagecontents' => new external_value(PARAM_RAW, 'The completion page contents.'),
705                 'siteaftersubmit' => new external_value(PARAM_RAW, 'The link (could be relative) to show after submit.'),
706                 'warnings' => new external_warnings(),
707             )
708         );
709     }
711     /**
712      * Describes the parameters for get_analysis.
713      *
714      * @return external_function_parameters
715      * @since Moodle 3.3
716      */
717     public static function get_analysis_parameters() {
718         return new external_function_parameters (
719             array(
720                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
721                 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
722                                                 VALUE_DEFAULT, 0),
723             )
724         );
725     }
727     /**
728      * Retrieves the feedback analysis.
729      *
730      * @param array $feedbackid feedback instance id
731      * @return array of warnings and launch information
732      * @since Moodle 3.3
733      */
734     public static function get_analysis($feedbackid, $groupid = 0) {
735         global $PAGE;
737         $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid);
738         $params = self::validate_parameters(self::get_analysis_parameters(), $params);
739         $warnings = $itemsdata = array();
741         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
743         // Check permissions.
744         $feedbackstructure = new mod_feedback_structure($feedback, $cm);
745         if (!$feedbackstructure->can_view_analysis()) {
746             throw new required_capability_exception($context, 'mod/feedback:viewanalysepage', 'nopermission', '');
747         }
749         if (!empty($params['groupid'])) {
750             $groupid = $params['groupid'];
751             // Determine is the group is visible to user.
752             if (!groups_group_visible($groupid, $course, $cm)) {
753                 throw new moodle_exception('notingroup');
754             }
755         } else {
756             // Check to see if groups are being used here.
757             if ($groupmode = groups_get_activity_groupmode($cm)) {
758                 $groupid = groups_get_activity_group($cm);
759                 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
760                 if (!groups_group_visible($groupid, $course, $cm)) {
761                     throw new moodle_exception('notingroup');
762                 }
763             } else {
764                 $groupid = 0;
765             }
766         }
768         // Summary data.
769         $summary = new mod_feedback\output\summary($feedbackstructure, $groupid);
770         $summarydata = $summary->export_for_template($PAGE->get_renderer('core'));
772         $checkanonymously = true;
773         if ($groupid > 0 AND $feedback->anonymous == FEEDBACK_ANONYMOUS_YES) {
774             $completedcount = $feedbackstructure->count_completed_responses($groupid);
775             if ($completedcount < FEEDBACK_MIN_ANONYMOUS_COUNT_IN_GROUP) {
776                 $checkanonymously = false;
777             }
778         }
780         if ($checkanonymously) {
781             // Get the items of the feedback.
782             $items = $feedbackstructure->get_items(true);
783             foreach ($items as $item) {
784                 $itemobj = feedback_get_item_class($item->typ);
785                 $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
786                 unset($item->itemnr);   // Added by the function, not part of the record.
787                 $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
789                 $itemsdata[] = array(
790                     'item' => $exporter->export($PAGE->get_renderer('core')),
791                     'data' => $itemobj->get_analysed_for_external($item, $groupid),
792                 );
793             }
794         } else {
795             $warnings[] = array(
796                 'item' => 'feedback',
797                 'itemid' => $feedback->id,
798                 'warningcode' => 'insufficientresponsesforthisgroup',
799                 'message' => s(get_string('insufficient_responses_for_this_group', 'feedback'))
800             );
801         }
803         $result = array(
804             'completedcount' => $summarydata->completedcount,
805             'itemscount' => $summarydata->itemscount,
806             'itemsdata' => $itemsdata,
807             'warnings' => $warnings
808         );
809         return $result;
810     }
812     /**
813      * Describes the get_analysis return value.
814      *
815      * @return external_single_structure
816      * @since Moodle 3.3
817      */
818     public static function get_analysis_returns() {
819         return new external_single_structure(
820             array(
821             'completedcount' => new external_value(PARAM_INT, 'Number of completed submissions.'),
822             'itemscount' => new external_value(PARAM_INT, 'Number of items (questions).'),
823             'itemsdata' => new external_multiple_structure(
824                 new external_single_structure(
825                     array(
826                         'item' => feedback_item_exporter::get_read_structure(),
827                         'data' => new external_multiple_structure(
828                             new external_value(PARAM_RAW, 'The analysis data (can be json encoded)')
829                         ),
830                     )
831                 )
832             ),
833             'warnings' => new external_warnings(),
834             )
835         );
836     }
838     /**
839      * Describes the parameters for get_unfinished_responses.
840      *
841      * @return external_function_parameters
842      * @since Moodle 3.3
843      */
844     public static function get_unfinished_responses_parameters() {
845         return new external_function_parameters (
846             array(
847                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
848             )
849         );
850     }
852     /**
853      * Retrieves responses from the current unfinished attempt.
854      *
855      * @param array $feedbackid feedback instance id
856      * @return array of warnings and launch information
857      * @since Moodle 3.3
858      */
859     public static function get_unfinished_responses($feedbackid) {
860         global $PAGE;
862         $params = array('feedbackid' => $feedbackid);
863         $params = self::validate_parameters(self::get_unfinished_responses_parameters(), $params);
864         $warnings = $itemsdata = array();
866         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
867         $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
869         $responses = array();
870         $unfinished = $feedbackcompletion->get_unfinished_responses();
871         foreach ($unfinished as $u) {
872             $exporter = new feedback_valuetmp_exporter($u);
873             $responses[] = $exporter->export($PAGE->get_renderer('core'));
874         }
876         $result = array(
877             'responses' => $responses,
878             'warnings' => $warnings
879         );
880         return $result;
881     }
883     /**
884      * Describes the get_unfinished_responses return value.
885      *
886      * @return external_single_structure
887      * @since Moodle 3.3
888      */
889     public static function get_unfinished_responses_returns() {
890         return new external_single_structure(
891             array(
892             'responses' => new external_multiple_structure(
893                 feedback_valuetmp_exporter::get_read_structure()
894             ),
895             'warnings' => new external_warnings(),
896             )
897         );
898     }
900     /**
901      * Describes the parameters for get_finished_responses.
902      *
903      * @return external_function_parameters
904      * @since Moodle 3.3
905      */
906     public static function get_finished_responses_parameters() {
907         return new external_function_parameters (
908             array(
909                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
910             )
911         );
912     }
914     /**
915      * Retrieves responses from the last finished attempt.
916      *
917      * @param array $feedbackid feedback instance id
918      * @return array of warnings and the responses
919      * @since Moodle 3.3
920      */
921     public static function get_finished_responses($feedbackid) {
922         global $PAGE;
924         $params = array('feedbackid' => $feedbackid);
925         $params = self::validate_parameters(self::get_finished_responses_parameters(), $params);
926         $warnings = $itemsdata = array();
928         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
929         $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
931         $responses = array();
932         // Load and get the responses from the last completed feedback.
933         $feedbackcompletion->find_last_completed();
934         $unfinished = $feedbackcompletion->get_finished_responses();
935         foreach ($unfinished as $u) {
936             $exporter = new feedback_value_exporter($u);
937             $responses[] = $exporter->export($PAGE->get_renderer('core'));
938         }
940         $result = array(
941             'responses' => $responses,
942             'warnings' => $warnings
943         );
944         return $result;
945     }
947     /**
948      * Describes the get_finished_responses return value.
949      *
950      * @return external_single_structure
951      * @since Moodle 3.3
952      */
953     public static function get_finished_responses_returns() {
954         return new external_single_structure(
955             array(
956             'responses' => new external_multiple_structure(
957                 feedback_value_exporter::get_read_structure()
958             ),
959             'warnings' => new external_warnings(),
960             )
961         );
962     }
964     /**
965      * Describes the parameters for get_non_respondents.
966      *
967      * @return external_function_parameters
968      * @since Moodle 3.3
969      */
970     public static function get_non_respondents_parameters() {
971         return new external_function_parameters (
972             array(
973                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
974                 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group.',
975                                                 VALUE_DEFAULT, 0),
976                 'sort' => new external_value(PARAM_ALPHA, 'Sort param, must be firstname, lastname or lastaccess (default).',
977                                                 VALUE_DEFAULT, 'lastaccess'),
978                 'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
979                 'perpage' => new external_value(PARAM_INT, 'The number of records to return per page.', VALUE_DEFAULT, 0),
980             )
981         );
982     }
984     /**
985      * Retrieves a list of students who didn't submit the feedback.
986      *
987      * @param int $feedbackid feedback instance id
988      * @param int $groupid Group id, 0 means that the function will determine the user group'
989      * @param str $sort sort param, must be firstname, lastname or lastaccess (default)
990      * @param int $page the page of records to return
991      * @param int $perpage the number of records to return per page
992      * @return array of warnings and users ids
993      * @since Moodle 3.3
994      */
995     public static function get_non_respondents($feedbackid, $groupid = 0, $sort = 'lastaccess', $page = 0, $perpage = 0) {
996         global $CFG;
997         require_once($CFG->dirroot . '/mod/feedback/lib.php');
999         $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'sort' => $sort, 'page' => $page, 'perpage' => $perpage);
1000         $params = self::validate_parameters(self::get_non_respondents_parameters(), $params);
1001         $warnings = $nonrespondents = array();
1003         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
1005         if ($feedback->anonymous != FEEDBACK_ANONYMOUS_NO || $feedback->course == SITEID) {
1006             throw new moodle_exception('anonymous', 'feedback');
1007         }
1009         // Check permissions.
1010         require_capability('mod/feedback:viewreports', $context);
1012         if (!empty($params['groupid'])) {
1013             $groupid = $params['groupid'];
1014             // Determine is the group is visible to user.
1015             if (!groups_group_visible($groupid, $course, $cm)) {
1016                 throw new moodle_exception('notingroup');
1017             }
1018         } else {
1019             // Check to see if groups are being used here.
1020             if ($groupmode = groups_get_activity_groupmode($cm)) {
1021                 $groupid = groups_get_activity_group($cm);
1022                 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
1023                 if (!groups_group_visible($groupid, $course, $cm)) {
1024                     throw new moodle_exception('notingroup');
1025                 }
1026             } else {
1027                 $groupid = 0;
1028             }
1029         }
1031         if ($params['sort'] !== 'firstname' && $params['sort'] !== 'lastname' && $params['sort'] !== 'lastaccess') {
1032             throw new invalid_parameter_exception('Invalid sort param, must be firstname, lastname or lastaccess.');
1033         }
1035         // Check if we are page filtering.
1036         if ($params['perpage'] == 0) {
1037             $page = $params['page'];
1038             $perpage = FEEDBACK_DEFAULT_PAGE_COUNT;
1039         } else {
1040             $perpage = $params['perpage'];
1041             $page = $perpage * $params['page'];
1042         }
1043         $users = feedback_get_incomplete_users($cm, $groupid, $params['sort'], $page, $perpage, true);
1044         foreach ($users as $user) {
1045             $nonrespondents[] = [
1046                 'courseid' => $course->id,
1047                 'userid'   => $user->id,
1048                 'fullname' => fullname($user),
1049                 'started'  => $user->feedbackstarted
1050             ];
1051         }
1053         $result = array(
1054             'users' => $nonrespondents,
1055             'total' => feedback_count_incomplete_users($cm, $groupid),
1056             'warnings' => $warnings
1057         );
1058         return $result;
1059     }
1061     /**
1062      * Describes the get_non_respondents return value.
1063      *
1064      * @return external_single_structure
1065      * @since Moodle 3.3
1066      */
1067     public static function get_non_respondents_returns() {
1068         return new external_single_structure(
1069             array(
1070                 'users' => new external_multiple_structure(
1071                     new external_single_structure(
1072                         array(
1073                             'courseid' => new external_value(PARAM_INT, 'Course id'),
1074                             'userid' => new external_value(PARAM_INT, 'The user id'),
1075                             'fullname' => new external_value(PARAM_TEXT, 'User full name'),
1076                             'started' => new external_value(PARAM_BOOL, 'If the user has started the attempt'),
1077                         )
1078                     )
1079                 ),
1080                 'total' => new external_value(PARAM_INT, 'Total number of non respondents'),
1081                 'warnings' => new external_warnings(),
1082             )
1083         );
1084     }
1086     /**
1087      * Describes the parameters for get_responses_analysis.
1088      *
1089      * @return external_function_parameters
1090      * @since Moodle 3.3
1091      */
1092     public static function get_responses_analysis_parameters() {
1093         return new external_function_parameters (
1094             array(
1095                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
1096                 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
1097                                                 VALUE_DEFAULT, 0),
1098                 'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
1099                 'perpage' => new external_value(PARAM_INT, 'The number of records to return per page', VALUE_DEFAULT, 0),
1100             )
1101         );
1102     }
1104     /**
1105      * Return the feedback user responses.
1106      *
1107      * @param int $feedbackid feedback instance id
1108      * @param int $groupid Group id, 0 means that the function will determine the user group
1109      * @param int $page the page of records to return
1110      * @param int $perpage the number of records to return per page
1111      * @return array of warnings and users attemps and responses
1112      * @throws moodle_exception
1113      * @since Moodle 3.3
1114      */
1115     public static function get_responses_analysis($feedbackid, $groupid = 0, $page = 0, $perpage = 0) {
1117         $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'page' => $page, 'perpage' => $perpage);
1118         $params = self::validate_parameters(self::get_responses_analysis_parameters(), $params);
1119         $warnings = $itemsdata = array();
1121         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
1123         // Check permissions.
1124         require_capability('mod/feedback:viewreports', $context);
1126         if (!empty($params['groupid'])) {
1127             $groupid = $params['groupid'];
1128             // Determine is the group is visible to user.
1129             if (!groups_group_visible($groupid, $course, $cm)) {
1130                 throw new moodle_exception('notingroup');
1131             }
1132         } else {
1133             // Check to see if groups are being used here.
1134             if ($groupmode = groups_get_activity_groupmode($cm)) {
1135                 $groupid = groups_get_activity_group($cm);
1136                 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
1137                 if (!groups_group_visible($groupid, $course, $cm)) {
1138                     throw new moodle_exception('notingroup');
1139                 }
1140             } else {
1141                 $groupid = 0;
1142             }
1143         }
1145         $feedbackstructure = new mod_feedback_structure($feedback, $cm, $course->id);
1146         $responsestable = new mod_feedback_responses_table($feedbackstructure, $groupid);
1147         $anonresponsestable = new mod_feedback_responses_anon_table($feedbackstructure, $groupid);
1149         $result = array(
1150             'attempts'          => $responsestable->export_external_structure($params['page'], $params['perpage']),
1151             'totalattempts'     => $responsestable->get_total_responses_count(),
1152             'anonattempts'      => $anonresponsestable->export_external_structure($params['page'], $params['perpage']),
1153             'totalanonattempts' => $anonresponsestable->get_total_responses_count(),
1154             'warnings'       => $warnings
1155         );
1156         return $result;
1157     }
1159     /**
1160      * Describes the get_responses_analysis return value.
1161      *
1162      * @return external_single_structure
1163      * @since Moodle 3.3
1164      */
1165     public static function get_responses_analysis_returns() {
1166         $responsestructure = new external_multiple_structure(
1167             new external_single_structure(
1168                 array(
1169                     'id' => new external_value(PARAM_INT, 'Response id'),
1170                     'name' => new external_value(PARAM_RAW, 'Response name'),
1171                     'printval' => new external_value(PARAM_RAW, 'Response ready for output'),
1172                     'rawval' => new external_value(PARAM_RAW, 'Response raw value'),
1173                 )
1174             )
1175         );
1177         return new external_single_structure(
1178             array(
1179                 'attempts' => new external_multiple_structure(
1180                     new external_single_structure(
1181                         array(
1182                             'id' => new external_value(PARAM_INT, 'Completed id'),
1183                             'courseid' => new external_value(PARAM_INT, 'Course id'),
1184                             'userid' => new external_value(PARAM_INT, 'User who responded'),
1185                             'timemodified' => new external_value(PARAM_INT, 'Time modified for the response'),
1186                             'fullname' => new external_value(PARAM_TEXT, 'User full name'),
1187                             'responses' => $responsestructure
1188                         )
1189                     )
1190                 ),
1191                 'totalattempts' => new external_value(PARAM_INT, 'Total responses count.'),
1192                 'anonattempts' => new external_multiple_structure(
1193                     new external_single_structure(
1194                         array(
1195                             'id' => new external_value(PARAM_INT, 'Completed id'),
1196                             'courseid' => new external_value(PARAM_INT, 'Course id'),
1197                             'number' => new external_value(PARAM_INT, 'Response number'),
1198                             'responses' => $responsestructure
1199                         )
1200                     )
1201                 ),
1202                 'totalanonattempts' => new external_value(PARAM_INT, 'Total anonymous responses count.'),
1203                 'warnings' => new external_warnings(),
1204             )
1205         );
1206     }
1208     /**
1209      * Describes the parameters for get_last_completed.
1210      *
1211      * @return external_function_parameters
1212      * @since Moodle 3.3
1213      */
1214     public static function get_last_completed_parameters() {
1215         return new external_function_parameters (
1216             array(
1217                 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
1218             )
1219         );
1220     }
1222     /**
1223      * Retrieves the last completion record for the current user.
1224      *
1225      * @param int $feedbackid feedback instance id
1226      * @return array of warnings and the last completed record
1227      * @since Moodle 3.3
1228      * @throws moodle_exception
1229      */
1230     public static function get_last_completed($feedbackid) {
1231         global $PAGE;
1233         $params = array('feedbackid' => $feedbackid);
1234         $params = self::validate_parameters(self::get_last_completed_parameters(), $params);
1235         $warnings = array();
1237         list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']);
1238         $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
1240         if ($feedbackcompletion->is_anonymous()) {
1241              throw new moodle_exception('anonymous', 'feedback');
1242         }
1243         if ($completed = $feedbackcompletion->find_last_completed()) {
1244             $exporter = new feedback_completed_exporter($completed);
1245             return array(
1246                 'completed' => $exporter->export($PAGE->get_renderer('core')),
1247                 'warnings' => $warnings,
1248             );
1249         }
1250         throw new moodle_exception('not_completed_yet', 'feedback');
1251     }
1253     /**
1254      * Describes the get_last_completed return value.
1255      *
1256      * @return external_single_structure
1257      * @since Moodle 3.3
1258      */
1259     public static function get_last_completed_returns() {
1260         return new external_single_structure(
1261             array(
1262                 'completed' => feedback_completed_exporter::get_read_structure(),
1263                 'warnings' => new external_warnings(),
1264             )
1265         );
1266     }