Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   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/>.
  16  
  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;
  30  
  31  /**
  32   * Feedback external functions
  33   *
  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
  39   */
  40  class mod_feedback_external extends external_api {
  41  
  42      /**
  43       * Describes the parameters for get_feedbacks_by_courses.
  44       *
  45       * @return external_function_parameters
  46       * @since Moodle 3.3
  47       */
  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()
  53                  ),
  54              )
  55          );
  56      }
  57  
  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.
  61       *
  62       * @param array $courseids course ids
  63       * @return array of warnings and feedbacks
  64       * @since Moodle 3.3
  65       */
  66      public static function get_feedbacks_by_courses($courseids = array()) {
  67          global $PAGE;
  68  
  69          $warnings = array();
  70          $returnedfeedbacks = array();
  71  
  72          $params = array(
  73              'courseids' => $courseids,
  74          );
  75          $params = self::validate_parameters(self::get_feedbacks_by_courses_parameters(), $params);
  76  
  77          $mycourses = array();
  78          if (empty($params['courseids'])) {
  79              $mycourses = enrol_get_my_courses();
  80              $params['courseids'] = array_keys($mycourses);
  81          }
  82  
  83          // Ensure there are courseids to loop through.
  84          if (!empty($params['courseids'])) {
  85  
  86              list($courses, $warnings) = util::validate_courses($params['courseids'], $mycourses);
  87              $output = $PAGE->get_renderer('core');
  88  
  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) {
  93  
  94                  $context = context_module::instance($feedback->coursemodule);
  95  
  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);
  99  
 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});
 107                          }
 108                      }
 109                  }
 110                  $exporter = new feedback_summary_exporter($feedback, array('context' => $context));
 111                  $returnedfeedbacks[] = $exporter->export($output);
 112              }
 113          }
 114  
 115          $result = array(
 116              'feedbacks' => $returnedfeedbacks,
 117              'warnings' => $warnings
 118          );
 119          return $result;
 120      }
 121  
 122      /**
 123       * Describes the get_feedbacks_by_courses return value.
 124       *
 125       * @return external_single_structure
 126       * @since Moodle 3.3
 127       */
 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()
 133                  ),
 134                  'warnings' => new external_warnings(),
 135              )
 136          );
 137      }
 138  
 139      /**
 140       * Utility function for validating a feedback.
 141       *
 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
 147       */
 148      protected static function validate_feedback($feedbackid, $courseid = 0) {
 149          global $DB, $USER;
 150  
 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');
 154  
 155          $context = context_module::instance($cm->id);
 156          self::validate_context($context);
 157  
 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));
 163  
 164              $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $courseid);
 165              if (!$feedbackcompletion->check_course_is_mapped()) {
 166                  throw new moodle_exception('cannotaccess', 'mod_feedback');
 167              }
 168          }
 169  
 170          return array($feedback, $feedbackcourse, $cm, $context, $completioncourse);
 171      }
 172  
 173      /**
 174       * Utility function for validating access to feedback.
 175       *
 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
 183       */
 184      protected static function validate_feedback_access($feedback, $course, $cm, $context, $checksubmit = false) {
 185          $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $course->id);
 186  
 187          if (!$feedbackcompletion->can_complete()) {
 188              throw new required_capability_exception($context, 'mod/feedback:complete', 'nopermission', '');
 189          }
 190  
 191          if (!$feedbackcompletion->is_open()) {
 192              throw new moodle_exception('feedback_is_not_open', 'feedback');
 193          }
 194  
 195          if ($feedbackcompletion->is_empty()) {
 196              throw new moodle_exception('no_items_available_yet', 'feedback');
 197          }
 198  
 199          if ($checksubmit && !$feedbackcompletion->can_submit()) {
 200              throw new moodle_exception('this_feedback_is_already_submitted', 'feedback');
 201          }
 202          return $feedbackcompletion;
 203      }
 204  
 205      /**
 206       * Describes the parameters for get_feedback_access_information.
 207       *
 208       * @return external_external_function_parameters
 209       * @since Moodle 3.3
 210       */
 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),
 217              )
 218          );
 219      }
 220  
 221      /**
 222       * Return access information for a given feedback.
 223       *
 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
 229       */
 230      public static function get_feedback_access_information($feedbackid, $courseid = 0) {
 231          global $PAGE;
 232  
 233          $params = array(
 234              'feedbackid' => $feedbackid,
 235              'courseid' => $courseid,
 236          );
 237          $params = self::validate_parameters(self::get_feedback_access_information_parameters(), $params);
 238  
 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);
 242  
 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);
 251  
 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();
 258  
 259          $result['warnings'] = [];
 260          return $result;
 261      }
 262  
 263      /**
 264       * Describes the get_feedback_access_information return value.
 265       *
 266       * @return external_single_structure
 267       * @since Moodle 3.3
 268       */
 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(),
 283              )
 284          );
 285      }
 286  
 287      /**
 288       * Describes the parameters for view_feedback.
 289       *
 290       * @return external_function_parameters
 291       * @since Moodle 3.3
 292       */
 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),
 301              )
 302          );
 303      }
 304  
 305      /**
 306       * Trigger the course module viewed event and update the module completion status.
 307       *
 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
 314       */
 315      public static function view_feedback($feedbackid, $moduleviewed = false, $courseid = 0) {
 316  
 317          $params = array('feedbackid' => $feedbackid, 'moduleviewed' => $moduleviewed, 'courseid' => $courseid);
 318          $params = self::validate_parameters(self::view_feedback_parameters(), $params);
 319          $warnings = array();
 320  
 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);
 324  
 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');
 330              }
 331              // Mark activity viewed for completion-tracking.
 332              $feedbackcompletion->set_module_viewed();
 333          }
 334  
 335          $result = array(
 336              'status' => true,
 337              'warnings' => $warnings,
 338          );
 339          return $result;
 340      }
 341  
 342      /**
 343       * Describes the view_feedback return value.
 344       *
 345       * @return external_single_structure
 346       * @since Moodle 3.3
 347       */
 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(),
 353              )
 354          );
 355      }
 356  
 357      /**
 358       * Describes the parameters for get_current_completed_tmp.
 359       *
 360       * @return external_function_parameters
 361       * @since Moodle 3.3
 362       */
 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),
 369              )
 370          );
 371      }
 372  
 373      /**
 374       * Returns the temporary completion record for the current user.
 375       *
 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
 381       */
 382      public static function get_current_completed_tmp($feedbackid, $courseid = 0) {
 383          global $PAGE;
 384  
 385          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
 386          $params = self::validate_parameters(self::get_current_completed_tmp_parameters(), $params);
 387          $warnings = array();
 388  
 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);
 392  
 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,
 398              );
 399          }
 400          throw new moodle_exception('not_started', 'feedback');
 401      }
 402  
 403      /**
 404       * Describes the get_current_completed_tmp return value.
 405       *
 406       * @return external_single_structure
 407       * @since Moodle 3.3
 408       */
 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(),
 414              )
 415          );
 416      }
 417  
 418      /**
 419       * Describes the parameters for get_items.
 420       *
 421       * @return external_function_parameters
 422       * @since Moodle 3.3
 423       */
 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),
 430              )
 431          );
 432      }
 433  
 434      /**
 435       * Returns the items (questions) in the given feedback.
 436       *
 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
 441       */
 442      public static function get_items($feedbackid, $courseid = 0) {
 443          global $PAGE;
 444  
 445          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
 446          $params = self::validate_parameters(self::get_items_parameters(), $params);
 447          $warnings = array();
 448          $returneditems = array();
 449  
 450          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 451              $params['courseid']);
 452  
 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(),
 463              ];
 464          }
 465  
 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',
 471          ];
 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'));
 482                  }
 483              }
 484          } else if ($userhasaccess) {
 485              $warnings[] = [
 486                  'item' => $feedback->id,
 487                  'warningcode' => 'nopermission',
 488                  'message' => 'nopermission',
 489              ];
 490          }
 491  
 492          $result = array(
 493              'items' => $returneditems,
 494              'warnings' => $warnings
 495          );
 496          return $result;
 497      }
 498  
 499      /**
 500       * Describes the get_items return value.
 501       *
 502       * @return external_single_structure
 503       * @since Moodle 3.3
 504       */
 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()
 510                  ),
 511                  'warnings' => new external_warnings(),
 512              )
 513          );
 514      }
 515  
 516      /**
 517       * Describes the parameters for launch_feedback.
 518       *
 519       * @return external_function_parameters
 520       * @since Moodle 3.3
 521       */
 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),
 528              )
 529          );
 530      }
 531  
 532      /**
 533       * Starts or continues a feedback submission
 534       *
 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
 539       */
 540      public static function launch_feedback($feedbackid, $courseid = 0) {
 541          global $PAGE;
 542  
 543          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
 544          $params = self::validate_parameters(self::launch_feedback_parameters(), $params);
 545          $warnings = array();
 546  
 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);
 551  
 552          $gopage = $feedbackcompletion->get_resume_page();
 553          if ($gopage === null) {
 554              $gopage = -1; // Last page.
 555          }
 556  
 557          $result = array(
 558              'gopage' => $gopage,
 559              'warnings' => $warnings
 560          );
 561          return $result;
 562      }
 563  
 564      /**
 565       * Describes the launch_feedback return value.
 566       *
 567       * @return external_single_structure
 568       * @since Moodle 3.3
 569       */
 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(),
 575              )
 576          );
 577      }
 578  
 579      /**
 580       * Describes the parameters for get_page_items.
 581       *
 582       * @return external_function_parameters
 583       * @since Moodle 3.3
 584       */
 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),
 592              )
 593          );
 594      }
 595  
 596      /**
 597       * Get a single feedback page items.
 598       *
 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
 604       */
 605      public static function get_page_items($feedbackid, $page, $courseid = 0) {
 606          global $PAGE;
 607  
 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;
 614  
 615          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 616              $params['courseid']);
 617  
 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(),
 629              ];
 630          }
 631  
 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',
 637          ];
 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 = [];
 641  
 642              if ($feedbackcompletion == null) {
 643                  $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
 644              }
 645  
 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);
 651  
 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'));
 657              }
 658          } else if ($userhasaccess) {
 659              $warnings[] = [
 660                  'item' => $feedback->id,
 661                  'warningcode' => 'nopermission',
 662                  'message' => get_string('nopermission', 'mod_feedback'),
 663              ];
 664          }
 665  
 666          $result = array(
 667              'items' => $returneditems,
 668              'hasprevpage' => $hasprevpage,
 669              'hasnextpage' => $hasnextpage,
 670              'warnings' => $warnings
 671          );
 672          return $result;
 673      }
 674  
 675      /**
 676       * Describes the get_page_items return value.
 677       *
 678       * @return external_single_structure
 679       * @since Moodle 3.3
 680       */
 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()
 686                  ),
 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(),
 690              )
 691          );
 692      }
 693  
 694      /**
 695       * Describes the parameters for process_page.
 696       *
 697       * @return external_function_parameters
 698       * @since Moodle 3.3
 699       */
 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.'),
 710                          )
 711                      ), 'The data to be processed.', VALUE_DEFAULT, array()
 712                  ),
 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),
 716              )
 717          );
 718      }
 719  
 720      /**
 721       * Process a jump between pages.
 722       *
 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
 730       */
 731      public static function process_page($feedbackid, $page, $responses = [], $goprevious = false, $courseid = 0) {
 732          global $USER, $SESSION;
 733  
 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 = '';
 739  
 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);
 744  
 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'];
 753              }
 754          }
 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;
 760  
 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.
 766              }
 767          }
 768  
 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;
 773  
 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();
 780              }
 781  
 782              if ($feedback->site_after_submit) {
 783                  $siteaftersubmit = feedback_encode_target_url($feedback->site_after_submit);
 784              }
 785          } else {
 786              $jumpto = $feedbackcompletion->get_jumpto();
 787          }
 788  
 789          $result = array(
 790              'jumpto' => $jumpto,
 791              'completed' => $completed,
 792              'completionpagecontents' => $completionpagecontents,
 793              'siteaftersubmit' => $siteaftersubmit,
 794              'warnings' => $warnings
 795          );
 796          return $result;
 797      }
 798  
 799      /**
 800       * Describes the process_page return value.
 801       *
 802       * @return external_single_structure
 803       * @since Moodle 3.3
 804       */
 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(),
 813              )
 814          );
 815      }
 816  
 817      /**
 818       * Describes the parameters for get_analysis.
 819       *
 820       * @return external_function_parameters
 821       * @since Moodle 3.3
 822       */
 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),
 831              )
 832          );
 833      }
 834  
 835      /**
 836       * Retrieves the feedback analysis.
 837       *
 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
 843       */
 844      public static function get_analysis($feedbackid, $groupid = 0, $courseid = 0) {
 845          global $PAGE;
 846  
 847          $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'courseid' => $courseid);
 848          $params = self::validate_parameters(self::get_analysis_parameters(), $params);
 849          $warnings = $itemsdata = array();
 850  
 851          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 852              $params['courseid']);
 853  
 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', '');
 858          }
 859  
 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');
 865              }
 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');
 873                  }
 874              } else {
 875                  $groupid = 0;
 876              }
 877          }
 878  
 879          // Summary data.
 880          $summary = new mod_feedback\output\summary($feedbackstructure, $groupid);
 881          $summarydata = $summary->export_for_template($PAGE->get_renderer('core'));
 882  
 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;
 888              }
 889          }
 890  
 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));
 899  
 900                  $itemsdata[] = array(
 901                      'item' => $exporter->export($PAGE->get_renderer('core')),
 902                      'data' => $itemobj->get_analysed_for_external($item, $groupid),
 903                  );
 904              }
 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'))
 911              );
 912          }
 913  
 914          $result = array(
 915              'completedcount' => $summarydata->completedcount,
 916              'itemscount' => $summarydata->itemscount,
 917              'itemsdata' => $itemsdata,
 918              'warnings' => $warnings
 919          );
 920          return $result;
 921      }
 922  
 923      /**
 924       * Describes the get_analysis return value.
 925       *
 926       * @return external_single_structure
 927       * @since Moodle 3.3
 928       */
 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)')
 940                          ),
 941                      )
 942                  )
 943              ),
 944              'warnings' => new external_warnings(),
 945              )
 946          );
 947      }
 948  
 949      /**
 950       * Describes the parameters for get_unfinished_responses.
 951       *
 952       * @return external_function_parameters
 953       * @since Moodle 3.3
 954       */
 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),
 961              )
 962          );
 963      }
 964  
 965      /**
 966       * Retrieves responses from the current unfinished attempt.
 967       *
 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
 972       */
 973      public static function get_unfinished_responses($feedbackid, $courseid = 0) {
 974          global $PAGE;
 975  
 976          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
 977          $params = self::validate_parameters(self::get_unfinished_responses_parameters(), $params);
 978          $warnings = $itemsdata = array();
 979  
 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);
 983  
 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'));
 989          }
 990  
 991          $result = array(
 992              'responses' => $responses,
 993              'warnings' => $warnings
 994          );
 995          return $result;
 996      }
 997  
 998      /**
 999       * Describes the get_unfinished_responses return value.
1000       *
1001       * @return external_single_structure
1002       * @since Moodle 3.3
1003       */
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()
1009              ),
1010              'warnings' => new external_warnings(),
1011              )
1012          );
1013      }
1014  
1015      /**
1016       * Describes the parameters for get_finished_responses.
1017       *
1018       * @return external_function_parameters
1019       * @since Moodle 3.3
1020       */
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),
1027              )
1028          );
1029      }
1030  
1031      /**
1032       * Retrieves responses from the last finished attempt.
1033       *
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
1038       */
1039      public static function get_finished_responses($feedbackid, $courseid = 0) {
1040          global $PAGE;
1041  
1042          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
1043          $params = self::validate_parameters(self::get_finished_responses_parameters(), $params);
1044          $warnings = $itemsdata = array();
1045  
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);
1049  
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'));
1057          }
1058  
1059          $result = array(
1060              'responses' => $responses,
1061              'warnings' => $warnings
1062          );
1063          return $result;
1064      }
1065  
1066      /**
1067       * Describes the get_finished_responses return value.
1068       *
1069       * @return external_single_structure
1070       * @since Moodle 3.3
1071       */
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()
1077              ),
1078              'warnings' => new external_warnings(),
1079              )
1080          );
1081      }
1082  
1083      /**
1084       * Describes the parameters for get_non_respondents.
1085       *
1086       * @return external_function_parameters
1087       * @since Moodle 3.3
1088       */
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),
1101              )
1102          );
1103      }
1104  
1105      /**
1106       * Retrieves a list of students who didn't submit the feedback.
1107       *
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
1116       */
1117      public static function get_non_respondents($feedbackid, $groupid = 0, $sort = 'lastaccess', $page = 0, $perpage = 0,
1118              $courseid = 0) {
1119  
1120          global $CFG;
1121          require_once($CFG->dirroot . '/mod/feedback/lib.php');
1122  
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();
1127  
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();
1132  
1133          if ($feedback->anonymous != FEEDBACK_ANONYMOUS_NO || $feedback->course == SITEID) {
1134              throw new moodle_exception('anonymous', 'feedback');
1135          }
1136  
1137          // Check permissions.
1138          require_capability('mod/feedback:viewreports', $context);
1139  
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');
1145              }
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');
1153                  }
1154              } else {
1155                  $groupid = 0;
1156              }
1157          }
1158  
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.');
1161          }
1162  
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'];
1170          }
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
1178              ];
1179          }
1180  
1181          $result = array(
1182              'users' => $nonrespondents,
1183              'total' => feedback_count_incomplete_users($cm, $groupid),
1184              'warnings' => $warnings
1185          );
1186          return $result;
1187      }
1188  
1189      /**
1190       * Describes the get_non_respondents return value.
1191       *
1192       * @return external_single_structure
1193       * @since Moodle 3.3
1194       */
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'),
1205                          )
1206                      )
1207                  ),
1208                  'total' => new external_value(PARAM_INT, 'Total number of non respondents'),
1209                  'warnings' => new external_warnings(),
1210              )
1211          );
1212      }
1213  
1214      /**
1215       * Describes the parameters for get_responses_analysis.
1216       *
1217       * @return external_function_parameters
1218       * @since Moodle 3.3
1219       */
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),
1230              )
1231          );
1232      }
1233  
1234      /**
1235       * Return the feedback user responses.
1236       *
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
1245       */
1246      public static function get_responses_analysis($feedbackid, $groupid = 0, $page = 0, $perpage = 0, $courseid = 0) {
1247  
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();
1252  
1253          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
1254              $params['courseid']);
1255  
1256          // Check permissions.
1257          require_capability('mod/feedback:viewreports', $context);
1258  
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');
1264              }
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');
1272                  }
1273              } else {
1274                  $groupid = 0;
1275              }
1276          }
1277  
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);
1283  
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
1290          );
1291          return $result;
1292      }
1293  
1294      /**
1295       * Describes the get_responses_analysis return value.
1296       *
1297       * @return external_single_structure
1298       * @since Moodle 3.3
1299       */
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'),
1308                  )
1309              )
1310          );
1311  
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
1323                          )
1324                      )
1325                  ),
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
1334                          )
1335                      )
1336                  ),
1337                  'totalanonattempts' => new external_value(PARAM_INT, 'Total anonymous responses count.'),
1338                  'warnings' => new external_warnings(),
1339              )
1340          );
1341      }
1342  
1343      /**
1344       * Describes the parameters for get_last_completed.
1345       *
1346       * @return external_function_parameters
1347       * @since Moodle 3.3
1348       */
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),
1355              )
1356          );
1357      }
1358  
1359      /**
1360       * Retrieves the last completion record for the current user.
1361       *
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
1366       */
1367      public static function get_last_completed($feedbackid, $courseid = 0) {
1368          global $PAGE;
1369  
1370          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
1371          $params = self::validate_parameters(self::get_last_completed_parameters(), $params);
1372          $warnings = array();
1373  
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);
1377  
1378          if ($feedbackcompletion->is_anonymous()) {
1379               throw new moodle_exception('anonymous', 'feedback');
1380          }
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,
1386              );
1387          }
1388          throw new moodle_exception('not_completed_yet', 'feedback');
1389      }
1390  
1391      /**
1392       * Describes the get_last_completed return value.
1393       *
1394       * @return external_single_structure
1395       * @since Moodle 3.3
1396       */
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(),
1402              )
1403          );
1404      }
1405  }