Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

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