weekly release 4.5dev+
[moodle.git] / mod / feedback / classes / external.php
blob8dc80d853687343b29a6046630d7e5f11a84b323
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.
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 use mod_feedback\external\feedback_summary_exporter;
18 use mod_feedback\external\feedback_completedtmp_exporter;
19 use mod_feedback\external\feedback_item_exporter;
20 use mod_feedback\external\feedback_valuetmp_exporter;
21 use mod_feedback\external\feedback_value_exporter;
22 use mod_feedback\external\feedback_completed_exporter;
23 use core_external\external_api;
24 use core_external\external_function_parameters;
25 use core_external\external_multiple_structure;
26 use core_external\external_single_structure;
27 use core_external\external_value;
28 use core_external\external_warnings;
29 use core_external\util;
31 /**
32 * Feedback external functions
34 * @package mod_feedback
35 * @category external
36 * @copyright 2017 Juan Leyva <juan@moodle.com>
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 * @since Moodle 3.3
40 class mod_feedback_external extends external_api {
42 /**
43 * Describes the parameters for get_feedbacks_by_courses.
45 * @return external_function_parameters
46 * @since Moodle 3.3
48 public static function get_feedbacks_by_courses_parameters() {
49 return new external_function_parameters (
50 array(
51 'courseids' => new external_multiple_structure(
52 new external_value(PARAM_INT, 'Course id'), 'Array of course ids', VALUE_DEFAULT, array()
58 /**
59 * Returns a list of feedbacks in a provided list of courses.
60 * If no list is provided all feedbacks that the user can view will be returned.
62 * @param array $courseids course ids
63 * @return array of warnings and feedbacks
64 * @since Moodle 3.3
66 public static function get_feedbacks_by_courses($courseids = array()) {
67 global $PAGE;
69 $warnings = array();
70 $returnedfeedbacks = array();
72 $params = array(
73 'courseids' => $courseids,
75 $params = self::validate_parameters(self::get_feedbacks_by_courses_parameters(), $params);
77 $mycourses = array();
78 if (empty($params['courseids'])) {
79 $mycourses = enrol_get_my_courses();
80 $params['courseids'] = array_keys($mycourses);
83 // Ensure there are courseids to loop through.
84 if (!empty($params['courseids'])) {
86 list($courses, $warnings) = util::validate_courses($params['courseids'], $mycourses);
87 $output = $PAGE->get_renderer('core');
89 // Get the feedbacks in this course, this function checks users visibility permissions.
90 // We can avoid then additional validate_context calls.
91 $feedbacks = get_all_instances_in_courses("feedback", $courses);
92 foreach ($feedbacks as $feedback) {
94 $context = context_module::instance($feedback->coursemodule);
96 // Remove fields that are not from the feedback (added by get_all_instances_in_courses).
97 unset($feedback->coursemodule, $feedback->context, $feedback->visible, $feedback->section, $feedback->groupmode,
98 $feedback->groupingid);
100 // Check permissions.
101 if (!has_capability('mod/feedback:edititems', $context)) {
102 // Don't return the optional properties.
103 $properties = feedback_summary_exporter::properties_definition();
104 foreach ($properties as $property => $config) {
105 if (!empty($config['optional'])) {
106 unset($feedback->{$property});
110 $exporter = new feedback_summary_exporter($feedback, array('context' => $context));
111 $returnedfeedbacks[] = $exporter->export($output);
115 $result = array(
116 'feedbacks' => $returnedfeedbacks,
117 'warnings' => $warnings
119 return $result;
123 * Describes the get_feedbacks_by_courses return value.
125 * @return external_single_structure
126 * @since Moodle 3.3
128 public static function get_feedbacks_by_courses_returns() {
129 return new external_single_structure(
130 array(
131 'feedbacks' => new external_multiple_structure(
132 feedback_summary_exporter::get_read_structure()
134 'warnings' => new external_warnings(),
140 * Utility function for validating a feedback.
142 * @param int $feedbackid feedback instance id
143 * @param int $courseid courseid course where user completes the feedback (for site feedbacks only)
144 * @return array containing the feedback, feedback course, context, course module and the course where is being completed.
145 * @throws moodle_exception
146 * @since Moodle 3.3
148 protected static function validate_feedback($feedbackid, $courseid = 0) {
149 global $DB, $USER;
151 // Request and permission validation.
152 $feedback = $DB->get_record('feedback', array('id' => $feedbackid), '*', MUST_EXIST);
153 list($feedbackcourse, $cm) = get_course_and_cm_from_instance($feedback, 'feedback');
155 $context = context_module::instance($cm->id);
156 self::validate_context($context);
158 // Set default completion course.
159 $completioncourse = (object) array('id' => 0);
160 if ($feedbackcourse->id == SITEID && $courseid) {
161 $completioncourse = get_course($courseid);
162 self::validate_context(context_course::instance($courseid));
164 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $courseid);
165 if (!$feedbackcompletion->check_course_is_mapped()) {
166 throw new moodle_exception('cannotaccess', 'mod_feedback');
170 return array($feedback, $feedbackcourse, $cm, $context, $completioncourse);
174 * Utility function for validating access to feedback.
176 * @param stdClass $feedback feedback object
177 * @param stdClass $course course where user completes the feedback (for site feedbacks only)
178 * @param stdClass $cm course module
179 * @param stdClass $context context object
180 * @throws moodle_exception
181 * @return mod_feedback_completion feedback completion instance
182 * @since Moodle 3.3
184 protected static function validate_feedback_access($feedback, $course, $cm, $context, $checksubmit = false) {
185 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
187 if (!$feedbackcompletion->can_complete()) {
188 throw new required_capability_exception($context, 'mod/feedback:complete', 'nopermission', '');
191 if (!$feedbackcompletion->is_open()) {
192 throw new moodle_exception('feedback_is_not_open', 'feedback');
195 if ($feedbackcompletion->is_empty()) {
196 throw new moodle_exception('no_items_available_yet', 'feedback');
199 if ($checksubmit && !$feedbackcompletion->can_submit()) {
200 throw new moodle_exception('this_feedback_is_already_submitted', 'feedback');
202 return $feedbackcompletion;
206 * Describes the parameters for get_feedback_access_information.
208 * @return external_external_function_parameters
209 * @since Moodle 3.3
211 public static function get_feedback_access_information_parameters() {
212 return new external_function_parameters (
213 array(
214 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
215 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
216 VALUE_DEFAULT, 0),
222 * Return access information for a given feedback.
224 * @param int $feedbackid feedback instance id
225 * @param int $courseid course where user completes the feedback (for site feedbacks only)
226 * @return array of warnings and the access information
227 * @since Moodle 3.3
228 * @throws moodle_exception
230 public static function get_feedback_access_information($feedbackid, $courseid = 0) {
231 global $PAGE;
233 $params = array(
234 'feedbackid' => $feedbackid,
235 'courseid' => $courseid,
237 $params = self::validate_parameters(self::get_feedback_access_information_parameters(), $params);
239 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
240 $params['courseid']);
241 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
243 $result = array();
244 // Capabilities first.
245 $result['canviewanalysis'] = $feedbackcompletion->can_view_analysis();
246 $result['cancomplete'] = $feedbackcompletion->can_complete();
247 $result['cansubmit'] = $feedbackcompletion->can_submit();
248 $result['candeletesubmissions'] = has_capability('mod/feedback:deletesubmissions', $context);
249 $result['canviewreports'] = has_capability('mod/feedback:viewreports', $context);
250 $result['canedititems'] = has_capability('mod/feedback:edititems', $context);
252 // Status information.
253 $result['isempty'] = $feedbackcompletion->is_empty();
254 $result['isopen'] = $feedbackcompletion->is_open();
255 $anycourse = ($course->id == SITEID);
256 $result['isalreadysubmitted'] = $feedbackcompletion->is_already_submitted($anycourse);
257 $result['isanonymous'] = $feedbackcompletion->is_anonymous();
259 $result['warnings'] = [];
260 return $result;
264 * Describes the get_feedback_access_information return value.
266 * @return external_single_structure
267 * @since Moodle 3.3
269 public static function get_feedback_access_information_returns() {
270 return new external_single_structure(
271 array(
272 'canviewanalysis' => new external_value(PARAM_BOOL, 'Whether the user can view the analysis or not.'),
273 'cancomplete' => new external_value(PARAM_BOOL, 'Whether the user can complete the feedback or not.'),
274 'cansubmit' => new external_value(PARAM_BOOL, 'Whether the user can submit the feedback or not.'),
275 'candeletesubmissions' => new external_value(PARAM_BOOL, 'Whether the user can delete submissions or not.'),
276 'canviewreports' => new external_value(PARAM_BOOL, 'Whether the user can view the feedback reports or not.'),
277 'canedititems' => new external_value(PARAM_BOOL, 'Whether the user can edit feedback items or not.'),
278 'isempty' => new external_value(PARAM_BOOL, 'Whether the feedback has questions or not.'),
279 'isopen' => new external_value(PARAM_BOOL, 'Whether the feedback has active access time restrictions or not.'),
280 'isalreadysubmitted' => new external_value(PARAM_BOOL, 'Whether the feedback is already submitted or not.'),
281 'isanonymous' => new external_value(PARAM_BOOL, 'Whether the feedback is anonymous or not.'),
282 'warnings' => new external_warnings(),
288 * Describes the parameters for view_feedback.
290 * @return external_function_parameters
291 * @since Moodle 3.3
293 public static function view_feedback_parameters() {
294 return new external_function_parameters (
295 array(
296 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
297 'moduleviewed' => new external_value(PARAM_BOOL, 'If we need to mark the module as viewed for completion',
298 VALUE_DEFAULT, false),
299 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
300 VALUE_DEFAULT, 0),
306 * Trigger the course module viewed event and update the module completion status.
308 * @param int $feedbackid feedback instance id
309 * @param bool $moduleviewed If we need to mark the module as viewed for completion
310 * @param int $courseid course where user completes the feedback (for site feedbacks only)
311 * @return array of warnings and status result
312 * @since Moodle 3.3
313 * @throws moodle_exception
315 public static function view_feedback($feedbackid, $moduleviewed = false, $courseid = 0) {
317 $params = array('feedbackid' => $feedbackid, 'moduleviewed' => $moduleviewed, 'courseid' => $courseid);
318 $params = self::validate_parameters(self::view_feedback_parameters(), $params);
319 $warnings = array();
321 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
322 $params['courseid']);
323 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
325 // Trigger module viewed event.
326 $feedbackcompletion->trigger_module_viewed();
327 if ($params['moduleviewed']) {
328 if (!$feedbackcompletion->is_open()) {
329 throw new moodle_exception('feedback_is_not_open', 'feedback');
331 // Mark activity viewed for completion-tracking.
332 $feedbackcompletion->set_module_viewed();
335 $result = array(
336 'status' => true,
337 'warnings' => $warnings,
339 return $result;
343 * Describes the view_feedback return value.
345 * @return external_single_structure
346 * @since Moodle 3.3
348 public static function view_feedback_returns() {
349 return new external_single_structure(
350 array(
351 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
352 'warnings' => new external_warnings(),
358 * Describes the parameters for get_current_completed_tmp.
360 * @return external_function_parameters
361 * @since Moodle 3.3
363 public static function get_current_completed_tmp_parameters() {
364 return new external_function_parameters (
365 array(
366 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
367 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
368 VALUE_DEFAULT, 0),
374 * Returns the temporary completion record for the current user.
376 * @param int $feedbackid feedback instance id
377 * @param int $courseid course where user completes the feedback (for site feedbacks only)
378 * @return array of warnings and status result
379 * @since Moodle 3.3
380 * @throws moodle_exception
382 public static function get_current_completed_tmp($feedbackid, $courseid = 0) {
383 global $PAGE;
385 $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
386 $params = self::validate_parameters(self::get_current_completed_tmp_parameters(), $params);
387 $warnings = array();
389 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
390 $params['courseid']);
391 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
393 if ($completed = $feedbackcompletion->get_current_completed_tmp()) {
394 $exporter = new feedback_completedtmp_exporter($completed);
395 return array(
396 'feedback' => $exporter->export($PAGE->get_renderer('core')),
397 'warnings' => $warnings,
400 throw new moodle_exception('not_started', 'feedback');
404 * Describes the get_current_completed_tmp return value.
406 * @return external_single_structure
407 * @since Moodle 3.3
409 public static function get_current_completed_tmp_returns() {
410 return new external_single_structure(
411 array(
412 'feedback' => feedback_completedtmp_exporter::get_read_structure(),
413 'warnings' => new external_warnings(),
419 * Describes the parameters for get_items.
421 * @return external_function_parameters
422 * @since Moodle 3.3
424 public static function get_items_parameters() {
425 return new external_function_parameters (
426 array(
427 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
428 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
429 VALUE_DEFAULT, 0),
435 * Returns the items (questions) in the given feedback.
437 * @param int $feedbackid feedback instance id
438 * @param int $courseid course where user completes the feedback (for site feedbacks only)
439 * @return array of warnings and feedbacks
440 * @since Moodle 3.3
442 public static function get_items($feedbackid, $courseid = 0) {
443 global $PAGE;
445 $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
446 $params = self::validate_parameters(self::get_items_parameters(), $params);
447 $warnings = array();
448 $returneditems = array();
450 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
451 $params['courseid']);
453 $userhasaccess = true;
454 try {
455 // Check the user has access to the feedback.
456 self::validate_feedback_access($feedback, $completioncourse, $cm, $context, true);
457 } catch (moodle_exception $e) {
458 $userhasaccess = false;
459 $warnings[] = [
460 'item' => $feedback->id,
461 'warningcode' => clean_param($e->errorcode, PARAM_ALPHANUM),
462 'message' => $e->getMessage(),
466 // For consistency with the web behaviour, the items should be returned only when the user can edit or view reports (to
467 // include non-editing teachers too).
468 $capabilities = [
469 'mod/feedback:edititems',
470 'mod/feedback:viewreports',
472 if ($userhasaccess || has_any_capability($capabilities, $context)) {
473 // Remove previous warnings because, although the user might not have access, they have the proper capability.
474 $warnings = [];
475 $feedbackstructure = new mod_feedback_structure($feedback, $cm, $completioncourse->id);
476 if ($items = $feedbackstructure->get_items()) {
477 foreach ($items as $item) {
478 $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
479 unset($item->itemnr); // Added by the function, not part of the record.
480 $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
481 $returneditems[] = $exporter->export($PAGE->get_renderer('core'));
484 } else if ($userhasaccess) {
485 $warnings[] = [
486 'item' => $feedback->id,
487 'warningcode' => 'nopermission',
488 'message' => 'nopermission',
492 $result = array(
493 'items' => $returneditems,
494 'warnings' => $warnings
496 return $result;
500 * Describes the get_items return value.
502 * @return external_single_structure
503 * @since Moodle 3.3
505 public static function get_items_returns() {
506 return new external_single_structure(
507 array(
508 'items' => new external_multiple_structure(
509 feedback_item_exporter::get_read_structure()
511 'warnings' => new external_warnings(),
517 * Describes the parameters for launch_feedback.
519 * @return external_function_parameters
520 * @since Moodle 3.3
522 public static function launch_feedback_parameters() {
523 return new external_function_parameters (
524 array(
525 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
526 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
527 VALUE_DEFAULT, 0),
533 * Starts or continues a feedback submission
535 * @param array $feedbackid feedback instance id
536 * @param int $courseid course where user completes a feedback (for site feedbacks only).
537 * @return array of warnings and launch information
538 * @since Moodle 3.3
540 public static function launch_feedback($feedbackid, $courseid = 0) {
541 global $PAGE;
543 $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
544 $params = self::validate_parameters(self::launch_feedback_parameters(), $params);
545 $warnings = array();
547 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
548 $params['courseid']);
549 // Check we can do a new submission (or continue an existing).
550 $feedbackcompletion = self::validate_feedback_access($feedback, $completioncourse, $cm, $context, true);
552 $gopage = $feedbackcompletion->get_resume_page();
553 if ($gopage === null) {
554 $gopage = -1; // Last page.
557 $result = array(
558 'gopage' => $gopage,
559 'warnings' => $warnings
561 return $result;
565 * Describes the launch_feedback return value.
567 * @return external_single_structure
568 * @since Moodle 3.3
570 public static function launch_feedback_returns() {
571 return new external_single_structure(
572 array(
573 'gopage' => new external_value(PARAM_INT, 'The next page to go (-1 if we were already in the last page). 0 for first page.'),
574 'warnings' => new external_warnings(),
580 * Describes the parameters for get_page_items.
582 * @return external_function_parameters
583 * @since Moodle 3.3
585 public static function get_page_items_parameters() {
586 return new external_function_parameters (
587 array(
588 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
589 'page' => new external_value(PARAM_INT, 'The page to get starting by 0'),
590 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
591 VALUE_DEFAULT, 0),
597 * Get a single feedback page items.
599 * @param int $feedbackid feedback instance id
600 * @param int $page the page to get starting by 0
601 * @param int $courseid course where user completes the feedback (for site feedbacks only)
602 * @return array of warnings and launch information
603 * @since Moodle 3.3
605 public static function get_page_items($feedbackid, $page, $courseid = 0) {
606 global $PAGE;
608 $params = array('feedbackid' => $feedbackid, 'page' => $page, 'courseid' => $courseid);
609 $params = self::validate_parameters(self::get_page_items_parameters(), $params);
610 $warnings = array();
611 $returneditems = array();
612 $hasprevpage = false;
613 $hasnextpage = false;
615 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
616 $params['courseid']);
618 $userhasaccess = true;
619 $feedbackcompletion = null;
620 try {
621 // Check the user has access to the feedback.
622 $feedbackcompletion = self::validate_feedback_access($feedback, $completioncourse, $cm, $context, true);
623 } catch (moodle_exception $e) {
624 $userhasaccess = false;
625 $warnings[] = [
626 'item' => $feedback->id,
627 'warningcode' => str_replace('_', '', $e->errorcode),
628 'message' => $e->getMessage(),
632 // For consistency with the web behaviour, the items should be returned only when the user can edit or view reports (to
633 // include non-editing teachers too).
634 $capabilities = [
635 'mod/feedback:edititems',
636 'mod/feedback:viewreports',
638 if ($userhasaccess || has_any_capability($capabilities, $context)) {
639 // Remove previous warnings because, although the user might not have access, they have the proper capability.
640 $warnings = [];
642 if ($feedbackcompletion == null) {
643 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
646 $page = $params['page'];
647 $pages = $feedbackcompletion->get_pages();
648 $pageitems = $pages[$page];
649 $hasnextpage = $page < count($pages) - 1; // Until we complete this page we can not trust get_next_page().
650 $hasprevpage = $page && ($feedbackcompletion->get_previous_page($page, false) !== null);
652 foreach ($pageitems as $item) {
653 $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
654 unset($item->itemnr); // Added by the function, not part of the record.
655 $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
656 $returneditems[] = $exporter->export($PAGE->get_renderer('core'));
658 } else if ($userhasaccess) {
659 $warnings[] = [
660 'item' => $feedback->id,
661 'warningcode' => 'nopermission',
662 'message' => get_string('nopermission', 'mod_feedback'),
666 $result = array(
667 'items' => $returneditems,
668 'hasprevpage' => $hasprevpage,
669 'hasnextpage' => $hasnextpage,
670 'warnings' => $warnings
672 return $result;
676 * Describes the get_page_items return value.
678 * @return external_single_structure
679 * @since Moodle 3.3
681 public static function get_page_items_returns() {
682 return new external_single_structure(
683 array(
684 'items' => new external_multiple_structure(
685 feedback_item_exporter::get_read_structure()
687 'hasprevpage' => new external_value(PARAM_BOOL, 'Whether is a previous page.'),
688 'hasnextpage' => new external_value(PARAM_BOOL, 'Whether there are more pages.'),
689 'warnings' => new external_warnings(),
695 * Describes the parameters for process_page.
697 * @return external_function_parameters
698 * @since Moodle 3.3
700 public static function process_page_parameters() {
701 return new external_function_parameters (
702 array(
703 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
704 'page' => new external_value(PARAM_INT, 'The page being processed.'),
705 'responses' => new external_multiple_structure(
706 new external_single_structure(
707 array(
708 'name' => new external_value(PARAM_NOTAGS, 'The response name (usually type[index]_id).'),
709 'value' => new external_value(PARAM_RAW, 'The response value.'),
711 ), 'The data to be processed.', VALUE_DEFAULT, array()
713 'goprevious' => new external_value(PARAM_BOOL, 'Whether we want to jump to previous page.', VALUE_DEFAULT, false),
714 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
715 VALUE_DEFAULT, 0),
721 * Process a jump between pages.
723 * @param array $feedbackid feedback instance id
724 * @param array $page the page being processed
725 * @param array $responses the responses to be processed
726 * @param bool $goprevious whether we want to jump to previous page
727 * @param int $courseid course where user completes the feedback (for site feedbacks only)
728 * @return array of warnings and launch information
729 * @since Moodle 3.3
731 public static function process_page($feedbackid, $page, $responses = [], $goprevious = false, $courseid = 0) {
732 global $USER, $SESSION;
734 $params = array('feedbackid' => $feedbackid, 'page' => $page, 'responses' => $responses, 'goprevious' => $goprevious,
735 'courseid' => $courseid);
736 $params = self::validate_parameters(self::process_page_parameters(), $params);
737 $warnings = array();
738 $siteaftersubmit = $completionpagecontents = '';
740 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
741 $params['courseid']);
742 // Check we can do a new submission (or continue an existing).
743 $feedbackcompletion = self::validate_feedback_access($feedback, $completioncourse, $cm, $context, true);
745 // Create the $_POST object required by the feedback question engine.
746 $_POST = array();
747 foreach ($responses as $response) {
748 // First check if we are handling array parameters.
749 if (preg_match('/(.+)\[(.+)\]$/', $response['name'], $matches)) {
750 $_POST[$matches[1]][$matches[2]] = $response['value'];
751 } else {
752 $_POST[$response['name']] = $response['value'];
755 // Force fields.
756 $_POST['id'] = $cm->id;
757 $_POST['courseid'] = $courseid;
758 $_POST['gopage'] = $params['page'];
759 $_POST['_qf__mod_feedback_complete_form'] = 1;
761 // Determine where to go, backwards or forward.
762 if (!$params['goprevious']) {
763 $_POST['gonextpage'] = 1; // Even if we are saving values we need this set.
764 if ($feedbackcompletion->get_next_page($params['page'], false) === null) {
765 $_POST['savevalues'] = 1; // If there is no next page, it means we are finishing the feedback.
769 // Ignore sesskey (deep in some APIs), the request is already validated.
770 $USER->ignoresesskey = true;
771 feedback_init_feedback_session();
772 $SESSION->feedback->is_started = true;
774 $feedbackcompletion->process_page($params['page'], $params['goprevious']);
775 $completed = $feedbackcompletion->just_completed();
776 if ($completed) {
777 $jumpto = 0;
778 if ($feedback->page_after_submit) {
779 $completionpagecontents = $feedbackcompletion->page_after_submit();
782 if ($feedback->site_after_submit) {
783 $siteaftersubmit = feedback_encode_target_url($feedback->site_after_submit);
785 } else {
786 $jumpto = $feedbackcompletion->get_jumpto();
789 $result = array(
790 'jumpto' => $jumpto,
791 'completed' => $completed,
792 'completionpagecontents' => $completionpagecontents,
793 'siteaftersubmit' => $siteaftersubmit,
794 'warnings' => $warnings
796 return $result;
800 * Describes the process_page return value.
802 * @return external_single_structure
803 * @since Moodle 3.3
805 public static function process_page_returns() {
806 return new external_single_structure(
807 array(
808 'jumpto' => new external_value(PARAM_INT, 'The page to jump to.'),
809 'completed' => new external_value(PARAM_BOOL, 'If the user completed the feedback.'),
810 'completionpagecontents' => new external_value(PARAM_RAW, 'The completion page contents.'),
811 'siteaftersubmit' => new external_value(PARAM_RAW, 'The link (could be relative) to show after submit.'),
812 'warnings' => new external_warnings(),
818 * Describes the parameters for get_analysis.
820 * @return external_function_parameters
821 * @since Moodle 3.3
823 public static function get_analysis_parameters() {
824 return new external_function_parameters (
825 array(
826 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
827 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
828 VALUE_DEFAULT, 0),
829 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
830 VALUE_DEFAULT, 0),
836 * Retrieves the feedback analysis.
838 * @param array $feedbackid feedback instance id
839 * @param int $groupid group id, 0 means that the function will determine the user group
840 * @param int $courseid course where user completes the feedback (for site feedbacks only)
841 * @return array of warnings and launch information
842 * @since Moodle 3.3
844 public static function get_analysis($feedbackid, $groupid = 0, $courseid = 0) {
845 global $PAGE;
847 $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'courseid' => $courseid);
848 $params = self::validate_parameters(self::get_analysis_parameters(), $params);
849 $warnings = $itemsdata = array();
851 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
852 $params['courseid']);
854 // Check permissions.
855 $feedbackstructure = new mod_feedback_structure($feedback, $cm, $completioncourse->id);
856 if (!$feedbackstructure->can_view_analysis()) {
857 throw new required_capability_exception($context, 'mod/feedback:viewanalysepage', 'nopermission', '');
860 if (!empty($params['groupid'])) {
861 $groupid = $params['groupid'];
862 // Determine is the group is visible to user.
863 if (!groups_group_visible($groupid, $course, $cm)) {
864 throw new moodle_exception('notingroup');
866 } else {
867 // Check to see if groups are being used here.
868 if ($groupmode = groups_get_activity_groupmode($cm)) {
869 $groupid = groups_get_activity_group($cm);
870 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
871 if (!groups_group_visible($groupid, $course, $cm)) {
872 throw new moodle_exception('notingroup');
874 } else {
875 $groupid = 0;
879 // Summary data.
880 $summary = new mod_feedback\output\summary($feedbackstructure, $groupid);
881 $summarydata = $summary->export_for_template($PAGE->get_renderer('core'));
883 $checkanonymously = true;
884 if ($groupid > 0 AND $feedback->anonymous == FEEDBACK_ANONYMOUS_YES) {
885 $completedcount = $feedbackstructure->count_completed_responses($groupid);
886 if ($completedcount < FEEDBACK_MIN_ANONYMOUS_COUNT_IN_GROUP) {
887 $checkanonymously = false;
891 if ($checkanonymously) {
892 // Get the items of the feedback.
893 $items = $feedbackstructure->get_items(true);
894 foreach ($items as $item) {
895 $itemobj = feedback_get_item_class($item->typ);
896 $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
897 unset($item->itemnr); // Added by the function, not part of the record.
898 $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
900 $itemsdata[] = array(
901 'item' => $exporter->export($PAGE->get_renderer('core')),
902 'data' => $itemobj->get_analysed_for_external($item, $groupid),
905 } else {
906 $warnings[] = array(
907 'item' => 'feedback',
908 'itemid' => $feedback->id,
909 'warningcode' => 'insufficientresponsesforthisgroup',
910 'message' => s(get_string('insufficient_responses_for_this_group', 'feedback'))
914 $result = array(
915 'completedcount' => $summarydata->completedcount,
916 'itemscount' => $summarydata->itemscount,
917 'itemsdata' => $itemsdata,
918 'warnings' => $warnings
920 return $result;
924 * Describes the get_analysis return value.
926 * @return external_single_structure
927 * @since Moodle 3.3
929 public static function get_analysis_returns() {
930 return new external_single_structure(
931 array(
932 'completedcount' => new external_value(PARAM_INT, 'Number of completed submissions.'),
933 'itemscount' => new external_value(PARAM_INT, 'Number of items (questions).'),
934 'itemsdata' => new external_multiple_structure(
935 new external_single_structure(
936 array(
937 'item' => feedback_item_exporter::get_read_structure(),
938 'data' => new external_multiple_structure(
939 new external_value(PARAM_RAW, 'The analysis data (can be json encoded)')
944 'warnings' => new external_warnings(),
950 * Describes the parameters for get_unfinished_responses.
952 * @return external_function_parameters
953 * @since Moodle 3.3
955 public static function get_unfinished_responses_parameters() {
956 return new external_function_parameters (
957 array(
958 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
959 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
960 VALUE_DEFAULT, 0),
966 * Retrieves responses from the current unfinished attempt.
968 * @param array $feedbackid feedback instance id
969 * @param int $courseid course where user completes the feedback (for site feedbacks only)
970 * @return array of warnings and launch information
971 * @since Moodle 3.3
973 public static function get_unfinished_responses($feedbackid, $courseid = 0) {
974 global $PAGE;
976 $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
977 $params = self::validate_parameters(self::get_unfinished_responses_parameters(), $params);
978 $warnings = $itemsdata = array();
980 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
981 $params['courseid']);
982 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
984 $responses = array();
985 $unfinished = $feedbackcompletion->get_unfinished_responses();
986 foreach ($unfinished as $u) {
987 $exporter = new feedback_valuetmp_exporter($u);
988 $responses[] = $exporter->export($PAGE->get_renderer('core'));
991 $result = array(
992 'responses' => $responses,
993 'warnings' => $warnings
995 return $result;
999 * Describes the get_unfinished_responses return value.
1001 * @return external_single_structure
1002 * @since Moodle 3.3
1004 public static function get_unfinished_responses_returns() {
1005 return new external_single_structure(
1006 array(
1007 'responses' => new external_multiple_structure(
1008 feedback_valuetmp_exporter::get_read_structure()
1010 'warnings' => new external_warnings(),
1016 * Describes the parameters for get_finished_responses.
1018 * @return external_function_parameters
1019 * @since Moodle 3.3
1021 public static function get_finished_responses_parameters() {
1022 return new external_function_parameters (
1023 array(
1024 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
1025 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
1026 VALUE_DEFAULT, 0),
1032 * Retrieves responses from the last finished attempt.
1034 * @param array $feedbackid feedback instance id
1035 * @param int $courseid course where user completes the feedback (for site feedbacks only)
1036 * @return array of warnings and the responses
1037 * @since Moodle 3.3
1039 public static function get_finished_responses($feedbackid, $courseid = 0) {
1040 global $PAGE;
1042 $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
1043 $params = self::validate_parameters(self::get_finished_responses_parameters(), $params);
1044 $warnings = $itemsdata = array();
1046 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
1047 $params['courseid']);
1048 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
1050 $responses = array();
1051 // Load and get the responses from the last completed feedback.
1052 $feedbackcompletion->find_last_completed();
1053 $unfinished = $feedbackcompletion->get_finished_responses();
1054 foreach ($unfinished as $u) {
1055 $exporter = new feedback_value_exporter($u);
1056 $responses[] = $exporter->export($PAGE->get_renderer('core'));
1059 $result = array(
1060 'responses' => $responses,
1061 'warnings' => $warnings
1063 return $result;
1067 * Describes the get_finished_responses return value.
1069 * @return external_single_structure
1070 * @since Moodle 3.3
1072 public static function get_finished_responses_returns() {
1073 return new external_single_structure(
1074 array(
1075 'responses' => new external_multiple_structure(
1076 feedback_value_exporter::get_read_structure()
1078 'warnings' => new external_warnings(),
1084 * Describes the parameters for get_non_respondents.
1086 * @return external_function_parameters
1087 * @since Moodle 3.3
1089 public static function get_non_respondents_parameters() {
1090 return new external_function_parameters (
1091 array(
1092 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
1093 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group.',
1094 VALUE_DEFAULT, 0),
1095 'sort' => new external_value(PARAM_ALPHA, 'Sort param, must be firstname, lastname or lastaccess (default).',
1096 VALUE_DEFAULT, 'lastaccess'),
1097 'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
1098 'perpage' => new external_value(PARAM_INT, 'The number of records to return per page.', VALUE_DEFAULT, 0),
1099 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
1100 VALUE_DEFAULT, 0),
1106 * Retrieves a list of students who didn't submit the feedback.
1108 * @param int $feedbackid feedback instance id
1109 * @param int $groupid Group id, 0 means that the function will determine the user group'
1110 * @param str $sort sort param, must be firstname, lastname or lastaccess (default)
1111 * @param int $page the page of records to return
1112 * @param int $perpage the number of records to return per page
1113 * @param int $courseid course where user completes the feedback (for site feedbacks only)
1114 * @return array of warnings and users ids
1115 * @since Moodle 3.3
1117 public static function get_non_respondents($feedbackid, $groupid = 0, $sort = 'lastaccess', $page = 0, $perpage = 0,
1118 $courseid = 0) {
1120 global $CFG;
1121 require_once($CFG->dirroot . '/mod/feedback/lib.php');
1123 $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'sort' => $sort, 'page' => $page,
1124 'perpage' => $perpage, 'courseid' => $courseid);
1125 $params = self::validate_parameters(self::get_non_respondents_parameters(), $params);
1126 $warnings = $nonrespondents = array();
1128 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
1129 $params['courseid']);
1130 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
1131 $completioncourseid = $feedbackcompletion->get_courseid();
1133 if ($feedback->anonymous != FEEDBACK_ANONYMOUS_NO || $feedback->course == SITEID) {
1134 throw new moodle_exception('anonymous', 'feedback');
1137 // Check permissions.
1138 require_capability('mod/feedback:viewreports', $context);
1140 if (!empty($params['groupid'])) {
1141 $groupid = $params['groupid'];
1142 // Determine is the group is visible to user.
1143 if (!groups_group_visible($groupid, $course, $cm)) {
1144 throw new moodle_exception('notingroup');
1146 } else {
1147 // Check to see if groups are being used here.
1148 if ($groupmode = groups_get_activity_groupmode($cm)) {
1149 $groupid = groups_get_activity_group($cm);
1150 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
1151 if (!groups_group_visible($groupid, $course, $cm)) {
1152 throw new moodle_exception('notingroup');
1154 } else {
1155 $groupid = 0;
1159 if ($params['sort'] !== 'firstname' && $params['sort'] !== 'lastname' && $params['sort'] !== 'lastaccess') {
1160 throw new invalid_parameter_exception('Invalid sort param, must be firstname, lastname or lastaccess.');
1163 // Check if we are page filtering.
1164 if ($params['perpage'] == 0) {
1165 $page = $params['page'];
1166 $perpage = FEEDBACK_DEFAULT_PAGE_COUNT;
1167 } else {
1168 $perpage = $params['perpage'];
1169 $page = $perpage * $params['page'];
1171 $users = feedback_get_incomplete_users($cm, $groupid, $params['sort'], $page, $perpage, true);
1172 foreach ($users as $user) {
1173 $nonrespondents[] = [
1174 'courseid' => $completioncourseid,
1175 'userid' => $user->id,
1176 'fullname' => fullname($user),
1177 'started' => $user->feedbackstarted
1181 $result = array(
1182 'users' => $nonrespondents,
1183 'total' => feedback_count_incomplete_users($cm, $groupid),
1184 'warnings' => $warnings
1186 return $result;
1190 * Describes the get_non_respondents return value.
1192 * @return external_single_structure
1193 * @since Moodle 3.3
1195 public static function get_non_respondents_returns() {
1196 return new external_single_structure(
1197 array(
1198 'users' => new external_multiple_structure(
1199 new external_single_structure(
1200 array(
1201 'courseid' => new external_value(PARAM_INT, 'Course id'),
1202 'userid' => new external_value(PARAM_INT, 'The user id'),
1203 'fullname' => new external_value(PARAM_TEXT, 'User full name'),
1204 'started' => new external_value(PARAM_BOOL, 'If the user has started the attempt'),
1208 'total' => new external_value(PARAM_INT, 'Total number of non respondents'),
1209 'warnings' => new external_warnings(),
1215 * Describes the parameters for get_responses_analysis.
1217 * @return external_function_parameters
1218 * @since Moodle 3.3
1220 public static function get_responses_analysis_parameters() {
1221 return new external_function_parameters (
1222 array(
1223 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
1224 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
1225 VALUE_DEFAULT, 0),
1226 'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
1227 'perpage' => new external_value(PARAM_INT, 'The number of records to return per page', VALUE_DEFAULT, 0),
1228 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
1229 VALUE_DEFAULT, 0),
1235 * Return the feedback user responses.
1237 * @param int $feedbackid feedback instance id
1238 * @param int $groupid Group id, 0 means that the function will determine the user group
1239 * @param int $page the page of records to return
1240 * @param int $perpage the number of records to return per page
1241 * @param int $courseid course where user completes the feedback (for site feedbacks only)
1242 * @return array of warnings and users attemps and responses
1243 * @throws moodle_exception
1244 * @since Moodle 3.3
1246 public static function get_responses_analysis($feedbackid, $groupid = 0, $page = 0, $perpage = 0, $courseid = 0) {
1248 $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'page' => $page, 'perpage' => $perpage,
1249 'courseid' => $courseid);
1250 $params = self::validate_parameters(self::get_responses_analysis_parameters(), $params);
1251 $warnings = $itemsdata = array();
1253 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
1254 $params['courseid']);
1256 // Check permissions.
1257 require_capability('mod/feedback:viewreports', $context);
1259 if (!empty($params['groupid'])) {
1260 $groupid = $params['groupid'];
1261 // Determine is the group is visible to user.
1262 if (!groups_group_visible($groupid, $course, $cm)) {
1263 throw new moodle_exception('notingroup');
1265 } else {
1266 // Check to see if groups are being used here.
1267 if ($groupmode = groups_get_activity_groupmode($cm)) {
1268 $groupid = groups_get_activity_group($cm);
1269 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
1270 if (!groups_group_visible($groupid, $course, $cm)) {
1271 throw new moodle_exception('notingroup');
1273 } else {
1274 $groupid = 0;
1278 $feedbackstructure = new mod_feedback_structure($feedback, $cm, $completioncourse->id);
1279 $responsestable = new mod_feedback_responses_table($feedbackstructure, $groupid);
1280 // Ensure responses number is correct prior returning them.
1281 $feedbackstructure->shuffle_anonym_responses();
1282 $anonresponsestable = new mod_feedback_responses_anon_table($feedbackstructure, $groupid);
1284 $result = array(
1285 'attempts' => $responsestable->export_external_structure($params['page'], $params['perpage']),
1286 'totalattempts' => $responsestable->get_total_responses_count(),
1287 'anonattempts' => $anonresponsestable->export_external_structure($params['page'], $params['perpage']),
1288 'totalanonattempts' => $anonresponsestable->get_total_responses_count(),
1289 'warnings' => $warnings
1291 return $result;
1295 * Describes the get_responses_analysis return value.
1297 * @return external_single_structure
1298 * @since Moodle 3.3
1300 public static function get_responses_analysis_returns() {
1301 $responsestructure = new external_multiple_structure(
1302 new external_single_structure(
1303 array(
1304 'id' => new external_value(PARAM_INT, 'Response id'),
1305 'name' => new external_value(PARAM_RAW, 'Response name'),
1306 'printval' => new external_value(PARAM_RAW, 'Response ready for output'),
1307 'rawval' => new external_value(PARAM_RAW, 'Response raw value'),
1312 return new external_single_structure(
1313 array(
1314 'attempts' => new external_multiple_structure(
1315 new external_single_structure(
1316 array(
1317 'id' => new external_value(PARAM_INT, 'Completed id'),
1318 'courseid' => new external_value(PARAM_INT, 'Course id'),
1319 'userid' => new external_value(PARAM_INT, 'User who responded'),
1320 'timemodified' => new external_value(PARAM_INT, 'Time modified for the response'),
1321 'fullname' => new external_value(PARAM_TEXT, 'User full name'),
1322 'responses' => $responsestructure
1326 'totalattempts' => new external_value(PARAM_INT, 'Total responses count.'),
1327 'anonattempts' => new external_multiple_structure(
1328 new external_single_structure(
1329 array(
1330 'id' => new external_value(PARAM_INT, 'Completed id'),
1331 'courseid' => new external_value(PARAM_INT, 'Course id'),
1332 'number' => new external_value(PARAM_INT, 'Response number'),
1333 'responses' => $responsestructure
1337 'totalanonattempts' => new external_value(PARAM_INT, 'Total anonymous responses count.'),
1338 'warnings' => new external_warnings(),
1344 * Describes the parameters for get_last_completed.
1346 * @return external_function_parameters
1347 * @since Moodle 3.3
1349 public static function get_last_completed_parameters() {
1350 return new external_function_parameters (
1351 array(
1352 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
1353 'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
1354 VALUE_DEFAULT, 0),
1360 * Retrieves the last completion record for the current user.
1362 * @param int $feedbackid feedback instance id
1363 * @return array of warnings and the last completed record
1364 * @since Moodle 3.3
1365 * @throws moodle_exception
1367 public static function get_last_completed($feedbackid, $courseid = 0) {
1368 global $PAGE;
1370 $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
1371 $params = self::validate_parameters(self::get_last_completed_parameters(), $params);
1372 $warnings = array();
1374 list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
1375 $params['courseid']);
1376 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
1378 if ($feedbackcompletion->is_anonymous()) {
1379 throw new moodle_exception('anonymous', 'feedback');
1381 if ($completed = $feedbackcompletion->find_last_completed()) {
1382 $exporter = new feedback_completed_exporter($completed);
1383 return array(
1384 'completed' => $exporter->export($PAGE->get_renderer('core')),
1385 'warnings' => $warnings,
1388 throw new moodle_exception('not_completed_yet', 'feedback');
1392 * Describes the get_last_completed return value.
1394 * @return external_single_structure
1395 * @since Moodle 3.3
1397 public static function get_last_completed_returns() {
1398 return new external_single_structure(
1399 array(
1400 'completed' => feedback_completed_exporter::get_read_structure(),
1401 'warnings' => new external_warnings(),