2 // This file is part of Moodle - http://moodle.org/
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.
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/>.
18 * Feedback external API
20 * @package mod_feedback
22 * @copyright 2017 Juan Leyva <juan@moodle.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
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
;
36 * Feedback external functions
38 * @package mod_feedback
40 * @copyright 2017 Juan Leyva <juan@moodle.com>
41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44 class mod_feedback_external
extends external_api
{
47 * Describes the parameters for get_feedbacks_by_courses.
49 * @return external_function_parameters
52 public static function get_feedbacks_by_courses_parameters() {
53 return new external_function_parameters (
55 'courseids' => new external_multiple_structure(
56 new external_value(PARAM_INT
, 'Course id'), 'Array of course ids', VALUE_DEFAULT
, array()
63 * Returns a list of feedbacks in a provided list of courses.
64 * If no list is provided all feedbacks that the user can view will be returned.
66 * @param array $courseids course ids
67 * @return array of warnings and feedbacks
70 public static function get_feedbacks_by_courses($courseids = array()) {
74 $returnedfeedbacks = array();
77 'courseids' => $courseids,
79 $params = self
::validate_parameters(self
::get_feedbacks_by_courses_parameters(), $params);
82 if (empty($params['courseids'])) {
83 $mycourses = enrol_get_my_courses();
84 $params['courseids'] = array_keys($mycourses);
87 // Ensure there are courseids to loop through.
88 if (!empty($params['courseids'])) {
90 list($courses, $warnings) = external_util
::validate_courses($params['courseids'], $mycourses);
91 $output = $PAGE->get_renderer('core');
93 // Get the feedbacks in this course, this function checks users visibility permissions.
94 // We can avoid then additional validate_context calls.
95 $feedbacks = get_all_instances_in_courses("feedback", $courses);
96 foreach ($feedbacks as $feedback) {
98 $context = context_module
::instance($feedback->coursemodule
);
100 // Remove fields that are not from the feedback (added by get_all_instances_in_courses).
101 unset($feedback->coursemodule
, $feedback->context
, $feedback->visible
, $feedback->section
, $feedback->groupmode
,
102 $feedback->groupingid
);
104 // Check permissions.
105 if (!has_capability('mod/feedback:edititems', $context)) {
106 // Don't return the optional properties.
107 $properties = feedback_summary_exporter
::properties_definition();
108 foreach ($properties as $property => $config) {
109 if (!empty($config['optional'])) {
110 unset($feedback->{$property});
114 $exporter = new feedback_summary_exporter($feedback, array('context' => $context));
115 $returnedfeedbacks[] = $exporter->export($output);
120 'feedbacks' => $returnedfeedbacks,
121 'warnings' => $warnings
127 * Describes the get_feedbacks_by_courses return value.
129 * @return external_single_structure
132 public static function get_feedbacks_by_courses_returns() {
133 return new external_single_structure(
135 'feedbacks' => new external_multiple_structure(
136 feedback_summary_exporter
::get_read_structure()
138 'warnings' => new external_warnings(),
144 * Utility function for validating a feedback.
146 * @param int $feedbackid feedback instance id
147 * @return array array containing the feedback persistent, course, context and course module objects
150 protected static function validate_feedback($feedbackid) {
153 // Request and permission validation.
154 $feedback = $DB->get_record('feedback', array('id' => $feedbackid), '*', MUST_EXIST
);
155 list($course, $cm) = get_course_and_cm_from_instance($feedback, 'feedback');
157 $context = context_module
::instance($cm->id
);
158 self
::validate_context($context);
160 return array($feedback, $course, $cm, $context);
164 * Utility function for validating access to feedback.
166 * @param stdClass $feedback feedback object
167 * @param stdClass $course course object
168 * @param stdClass $cm course module
169 * @param stdClass $context context object
170 * @throws moodle_exception
171 * @return feedback_completion feedback completion instance
174 protected static function validate_feedback_access($feedback, $course, $cm, $context, $checksubmit = false) {
175 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id
);
177 if (!$feedbackcompletion->can_complete()) {
178 throw new required_capability_exception($context, 'mod/feedback:complete', 'nopermission', '');
181 if (!$feedbackcompletion->is_open()) {
182 throw new moodle_exception('feedback_is_not_open', 'feedback');
185 if ($feedbackcompletion->is_empty()) {
186 throw new moodle_exception('no_items_available_yet', 'feedback');
189 if ($checksubmit && !$feedbackcompletion->can_submit()) {
190 throw new moodle_exception('this_feedback_is_already_submitted', 'feedback');
192 return $feedbackcompletion;
196 * Describes the parameters for get_feedback_access_information.
198 * @return external_external_function_parameters
201 public static function get_feedback_access_information_parameters() {
202 return new external_function_parameters (
204 'feedbackid' => new external_value(PARAM_INT
, 'Feedback instance id.')
210 * Return access information for a given feedback.
212 * @param int $feedbackid feedback instance id
213 * @return array of warnings and the access information
215 * @throws moodle_exception
217 public static function get_feedback_access_information($feedbackid) {
221 'feedbackid' => $feedbackid
223 $params = self
::validate_parameters(self
::get_feedback_access_information_parameters(), $params);
225 list($feedback, $course, $cm, $context) = self
::validate_feedback($params['feedbackid']);
226 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id
);
229 // Capabilities first.
230 $result['canviewanalysis'] = $feedbackcompletion->can_view_analysis();
231 $result['cancomplete'] = $feedbackcompletion->can_complete();
232 $result['cansubmit'] = $feedbackcompletion->can_submit();
233 $result['candeletesubmissions'] = has_capability('mod/feedback:deletesubmissions', $context);
234 $result['canviewreports'] = has_capability('mod/feedback:viewreports', $context);
235 $result['canedititems'] = has_capability('mod/feedback:edititems', $context);
237 // Status information.
238 $result['isempty'] = $feedbackcompletion->is_empty();
239 $result['isopen'] = $feedbackcompletion->is_open();
240 $anycourse = ($course->id
== SITEID
);
241 $result['isalreadysubmitted'] = $feedbackcompletion->is_already_submitted($anycourse);
242 $result['isanonymous'] = $feedbackcompletion->is_anonymous();
244 $result['warnings'] = [];
249 * Describes the get_feedback_access_information return value.
251 * @return external_single_structure
254 public static function get_feedback_access_information_returns() {
255 return new external_single_structure(
257 'canviewanalysis' => new external_value(PARAM_BOOL
, 'Whether the user can view the analysis or not.'),
258 'cancomplete' => new external_value(PARAM_BOOL
, 'Whether the user can complete the feedback or not.'),
259 'cansubmit' => new external_value(PARAM_BOOL
, 'Whether the user can submit the feedback or not.'),
260 'candeletesubmissions' => new external_value(PARAM_BOOL
, 'Whether the user can delete submissions or not.'),
261 'canviewreports' => new external_value(PARAM_BOOL
, 'Whether the user can view the feedback reports or not.'),
262 'canedititems' => new external_value(PARAM_BOOL
, 'Whether the user can edit feedback items or not.'),
263 'isempty' => new external_value(PARAM_BOOL
, 'Whether the feedback has questions or not.'),
264 'isopen' => new external_value(PARAM_BOOL
, 'Whether the feedback has active access time restrictions or not.'),
265 'isalreadysubmitted' => new external_value(PARAM_BOOL
, 'Whether the feedback is already submitted or not.'),
266 'isanonymous' => new external_value(PARAM_BOOL
, 'Whether the feedback is anonymous or not.'),
267 'warnings' => new external_warnings(),
273 * Describes the parameters for view_feedback.
275 * @return external_function_parameters
278 public static function view_feedback_parameters() {
279 return new external_function_parameters (
281 'feedbackid' => new external_value(PARAM_INT
, 'Feedback instance id'),
282 'moduleviewed' => new external_value(PARAM_BOOL
, 'If we need to mark the module as viewed for completion',
283 VALUE_DEFAULT
, false),
289 * Trigger the course module viewed event and update the module completion status.
291 * @param int $feedbackid feedback instance id
292 * @param bool $moduleviewed If we need to mark the module as viewed for completion
293 * @return array of warnings and status result
295 * @throws moodle_exception
297 public static function view_feedback($feedbackid, $moduleviewed = false) {
299 $params = array('feedbackid' => $feedbackid, 'moduleviewed' => $moduleviewed);
300 $params = self
::validate_parameters(self
::view_feedback_parameters(), $params);
303 list($feedback, $course, $cm, $context) = self
::validate_feedback($params['feedbackid']);
304 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id
);
306 // Trigger module viewed event.
307 $feedbackcompletion->trigger_module_viewed($course);
308 if ($params['moduleviewed']) {
309 if (!$feedbackcompletion->is_open()) {
310 throw new moodle_exception('feedback_is_not_open', 'feedback');
312 // Mark activity viewed for completion-tracking.
313 $feedbackcompletion->set_module_viewed($course);
318 'warnings' => $warnings,
324 * Describes the view_feedback return value.
326 * @return external_single_structure
329 public static function view_feedback_returns() {
330 return new external_single_structure(
332 'status' => new external_value(PARAM_BOOL
, 'status: true if success'),
333 'warnings' => new external_warnings(),
339 * Describes the parameters for get_current_completed_tmp.
341 * @return external_function_parameters
344 public static function get_current_completed_tmp_parameters() {
345 return new external_function_parameters (
347 'feedbackid' => new external_value(PARAM_INT
, 'Feedback instance id'),
353 * Returns the temporary completion record for the current user.
355 * @param int $feedbackid feedback instance id
356 * @return array of warnings and status result
358 * @throws moodle_exception
360 public static function get_current_completed_tmp($feedbackid) {
363 $params = array('feedbackid' => $feedbackid);
364 $params = self
::validate_parameters(self
::get_current_completed_tmp_parameters(), $params);
367 list($feedback, $course, $cm, $context) = self
::validate_feedback($params['feedbackid']);
368 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id
);
370 if ($completed = $feedbackcompletion->get_current_completed_tmp()) {
371 $exporter = new feedback_completedtmp_exporter($completed);
373 'feedback' => $exporter->export($PAGE->get_renderer('core')),
374 'warnings' => $warnings,
377 throw new moodle_exception('not_started', 'feedback');
381 * Describes the get_current_completed_tmp return value.
383 * @return external_single_structure
386 public static function get_current_completed_tmp_returns() {
387 return new external_single_structure(
389 'feedback' => feedback_completedtmp_exporter
::get_read_structure(),
390 'warnings' => new external_warnings(),
396 * Describes the parameters for get_items.
398 * @return external_function_parameters
401 public static function get_items_parameters() {
402 return new external_function_parameters (
404 'feedbackid' => new external_value(PARAM_INT
, 'Feedback instance id'),
410 * Returns the items (questions) in the given feedback.
412 * @param int $feedbackid feedback instance id
413 * @return array of warnings and feedbacks
416 public static function get_items($feedbackid) {
419 $params = array('feedbackid' => $feedbackid);
420 $params = self
::validate_parameters(self
::get_items_parameters(), $params);
423 list($feedback, $course, $cm, $context) = self
::validate_feedback($params['feedbackid']);
424 self
::validate_feedback_access($feedback, $course, $cm, $context);
426 $feedbackstructure = new mod_feedback_structure($feedback, $cm, $course->id
);
427 $returneditems = array();
428 if ($items = $feedbackstructure->get_items()) {
429 foreach ($items as $item) {
430 $itemnumber = empty($item->itemnr
) ?
null : $item->itemnr
;
431 unset($item->itemnr
); // Added by the function, not part of the record.
432 $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
433 $returneditems[] = $exporter->export($PAGE->get_renderer('core'));
438 'items' => $returneditems,
439 'warnings' => $warnings
445 * Describes the get_items return value.
447 * @return external_single_structure
450 public static function get_items_returns() {
451 return new external_single_structure(
453 'items' => new external_multiple_structure(
454 feedback_item_exporter
::get_read_structure()
456 'warnings' => new external_warnings(),
462 * Describes the parameters for launch_feedback.
464 * @return external_function_parameters
467 public static function launch_feedback_parameters() {
468 return new external_function_parameters (
470 'feedbackid' => new external_value(PARAM_INT
, 'Feedback instance id'),
476 * Starts or continues a feedback submission
478 * @param array $feedbackid feedback instance id
479 * @return array of warnings and launch information
482 public static function launch_feedback($feedbackid) {
485 $params = array('feedbackid' => $feedbackid);
486 $params = self
::validate_parameters(self
::launch_feedback_parameters(), $params);
489 list($feedback, $course, $cm, $context) = self
::validate_feedback($params['feedbackid']);
490 // Check we can do a new submission (or continue an existing).
491 $feedbackcompletion = self
::validate_feedback_access($feedback, $course, $cm, $context, true);
493 $gopage = $feedbackcompletion->get_resume_page();
494 if ($gopage === null) {
495 $gopage = -1; // Last page.
500 'warnings' => $warnings
506 * Describes the launch_feedback return value.
508 * @return external_single_structure
511 public static function launch_feedback_returns() {
512 return new external_single_structure(
514 'gopage' => new external_value(PARAM_INT
, 'The next page to go (-1 if we were already in the last page). 0 for first page.'),
515 'warnings' => new external_warnings(),
521 * Describes the parameters for get_page_items.
523 * @return external_function_parameters
526 public static function get_page_items_parameters() {
527 return new external_function_parameters (
529 'feedbackid' => new external_value(PARAM_INT
, 'Feedback instance id'),
530 'page' => new external_value(PARAM_INT
, 'The page to get starting by 0'),
536 * Get a single feedback page items.
538 * @param int $feedbackid feedback instance id
539 * @param int $page the page to get starting by 0
540 * @return array of warnings and launch information
543 public static function get_page_items($feedbackid, $page) {
546 $params = array('feedbackid' => $feedbackid, 'page' => $page);
547 $params = self
::validate_parameters(self
::get_page_items_parameters(), $params);
550 list($feedback, $course, $cm, $context) = self
::validate_feedback($params['feedbackid']);
552 $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id
);
554 $page = $params['page'];
555 $pages = $feedbackcompletion->get_pages();
556 $pageitems = $pages[$page];
557 $hasnextpage = $page < count($pages) - 1; // Until we complete this page we can not trust get_next_page().
558 $hasprevpage = $page && ($feedbackcompletion->get_previous_page($page, false) !== null);
560 $returneditems = array();
561 foreach ($pageitems as $item) {
562 $itemnumber = empty($item->itemnr
) ?
null : $item->itemnr
;
563 unset($item->itemnr
); // Added by the function, not part of the record.
564 $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
565 $returneditems[] = $exporter->export($PAGE->get_renderer('core'));
569 'items' => $returneditems,
570 'hasprevpage' => $hasprevpage,
571 'hasnextpage' => $hasnextpage,
572 'warnings' => $warnings
578 * Describes the get_page_items return value.
580 * @return external_single_structure
583 public static function get_page_items_returns() {
584 return new external_single_structure(
586 'items' => new external_multiple_structure(
587 feedback_item_exporter
::get_read_structure()
589 'hasprevpage' => new external_value(PARAM_BOOL
, 'Whether is a previous page.'),
590 'hasnextpage' => new external_value(PARAM_BOOL
, 'Whether there are more pages.'),
591 'warnings' => new external_warnings(),
597 * Describes the parameters for process_page.
599 * @return external_function_parameters
602 public static function process_page_parameters() {
603 return new external_function_parameters (
605 'feedbackid' => new external_value(PARAM_INT
, 'Feedback instance id.'),
606 'page' => new external_value(PARAM_INT
, 'The page being processed.'),
607 'responses' => new external_multiple_structure(
608 new external_single_structure(
610 'name' => new external_value(PARAM_NOTAGS
, 'The response name (usually type[index]_id).'),
611 'value' => new external_value(PARAM_RAW
, 'The response value.'),
613 ), 'The data to be processed.'
615 'goprevious' => new external_value(PARAM_BOOL
, 'Whether we want to jump to previous page.', VALUE_DEFAULT
, false),
621 * Process a jump between pages.
623 * @param array $feedbackid feedback instance id
624 * @param array $page the page being processed
625 * @param array $responses the responses to be processed
626 * @param bool $goprevious whether we want to jump to previous page
627 * @return array of warnings and launch information
630 public static function process_page($feedbackid, $page, $responses, $goprevious = false) {
631 global $USER, $SESSION;
633 $params = array('feedbackid' => $feedbackid, 'page' => $page, 'responses' => $responses, 'goprevious' => $goprevious);
634 $params = self
::validate_parameters(self
::process_page_parameters(), $params);
636 $siteaftersubmit = $completionpagecontents = '';
638 list($feedback, $course, $cm, $context) = self
::validate_feedback($params['feedbackid']);
639 // Check we can do a new submission (or continue an existing).
640 $feedbackcompletion = self
::validate_feedback_access($feedback, $course, $cm, $context, true);
642 // Create the $_POST object required by the feedback question engine.
644 foreach ($responses as $response) {
645 $_POST[$response['name']] = $response['value'];
648 $_POST['id'] = $cm->id
;
649 $_POST['courseid'] = $course->id
;
650 $_POST['gopage'] = $params['page'];
651 $_POST['_qf__mod_feedback_complete_form'] = 1;
652 if (!$params['goprevious']) {
653 if ($feedbackcompletion->get_next_page($params['page'], false) === null) {
654 $_POST['savevalues'] = 1; // If there is no next page, it means we are finishing the feedback.
656 $_POST['gonextpage'] = 1; // If we are not going to previous page or finishing we are going forward.
660 // Ignore sesskey (deep in some APIs), the request is already validated.
661 $USER->ignoresesskey
= true;
662 feedback_init_feedback_session();
663 $SESSION->feedback
->is_started
= true;
665 $feedbackcompletion->process_page($params['page'], $params['goprevious']);
666 $completed = $feedbackcompletion->just_completed();
669 if ($feedback->page_after_submit
) {
670 $completionpagecontents = $feedbackcompletion->page_after_submit();
673 if ($feedback->site_after_submit
) {
674 $siteaftersubmit = feedback_encode_target_url($feedback->site_after_submit
);
677 $jumpto = $feedbackcompletion->get_jumpto();
682 'completed' => $completed,
683 'completionpagecontents' => $completionpagecontents,
684 'siteaftersubmit' => $siteaftersubmit,
685 'warnings' => $warnings
691 * Describes the process_page return value.
693 * @return external_single_structure
696 public static function process_page_returns() {
697 return new external_single_structure(
699 'jumpto' => new external_value(PARAM_INT
, 'The page to jump to.'),
700 'completed' => new external_value(PARAM_BOOL
, 'If the user completed the feedback.'),
701 'completionpagecontents' => new external_value(PARAM_RAW
, 'The completion page contents.'),
702 'siteaftersubmit' => new external_value(PARAM_RAW
, 'The link (could be relative) to show after submit.'),
703 'warnings' => new external_warnings(),
709 * Describes the parameters for get_analysis.
711 * @return external_function_parameters
714 public static function get_analysis_parameters() {
715 return new external_function_parameters (
717 'feedbackid' => new external_value(PARAM_INT
, 'Feedback instance id'),
718 'groupid' => new external_value(PARAM_INT
, 'Group id, 0 means that the function will determine the user group',
725 * Retrieves the feedback analysis.
727 * @param array $feedbackid feedback instance id
728 * @return array of warnings and launch information
731 public static function get_analysis($feedbackid, $groupid = 0) {
734 $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid);
735 $params = self
::validate_parameters(self
::get_analysis_parameters(), $params);
736 $warnings = $itemsdata = array();
738 list($feedback, $course, $cm, $context) = self
::validate_feedback($params['feedbackid']);
740 // Check permissions.
741 $feedbackstructure = new mod_feedback_structure($feedback, $cm);
742 if (!$feedbackstructure->can_view_analysis()) {
743 throw new required_capability_exception($context, 'mod/feedback:viewanalysepage', 'nopermission', '');
746 if (!empty($params['groupid'])) {
747 $groupid = $params['groupid'];
748 // Determine is the group is visible to user.
749 if (!groups_group_visible($groupid, $course, $cm)) {
750 throw new moodle_exception('notingroup');
753 // Check to see if groups are being used here.
754 if ($groupmode = groups_get_activity_groupmode($cm)) {
755 $groupid = groups_get_activity_group($cm);
756 // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
757 if (!groups_group_visible($groupid, $course, $cm)) {
758 throw new moodle_exception('notingroup');
766 $summary = new mod_feedback\output\
summary($feedbackstructure, $groupid);
767 $summarydata = $summary->export_for_template($PAGE->get_renderer('core'));
769 $checkanonymously = true;
770 if ($groupid > 0 AND $feedback->anonymous
== FEEDBACK_ANONYMOUS_YES
) {
771 $completedcount = $feedbackstructure->count_completed_responses($groupid);
772 if ($completedcount < FEEDBACK_MIN_ANONYMOUS_COUNT_IN_GROUP
) {
773 $checkanonymously = false;
777 if ($checkanonymously) {
778 // Get the items of the feedback.
779 $items = $feedbackstructure->get_items(true);
780 foreach ($items as $item) {
781 $itemobj = feedback_get_item_class($item->typ
);
782 $itemnumber = empty($item->itemnr
) ?
null : $item->itemnr
;
783 unset($item->itemnr
); // Added by the function, not part of the record.
784 $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
786 $itemsdata[] = array(
787 'item' => $exporter->export($PAGE->get_renderer('core')),
788 'data' => $itemobj->get_analysed_for_external($item, $groupid),
793 'item' => 'feedback',
794 'itemid' => $feedback->id
,
795 'warningcode' => 'insufficientresponsesforthisgroup',
796 'message' => s(get_string('insufficient_responses_for_this_group', 'feedback'))
801 'completedcount' => $summarydata->completedcount
,
802 'itemscount' => $summarydata->itemscount
,
803 'itemsdata' => $itemsdata,
804 'warnings' => $warnings
810 * Describes the get_analysis return value.
812 * @return external_single_structure
815 public static function get_analysis_returns() {
816 return new external_single_structure(
818 'completedcount' => new external_value(PARAM_INT
, 'Number of completed submissions.'),
819 'itemscount' => new external_value(PARAM_INT
, 'Number of items (questions).'),
820 'itemsdata' => new external_multiple_structure(
821 new external_single_structure(
823 'item' => feedback_item_exporter
::get_read_structure(),
824 'data' => new external_multiple_structure(
825 new external_value(PARAM_RAW
, 'The analysis data (can be json encoded)')
830 'warnings' => new external_warnings(),