Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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  
 456          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 457              $params['courseid']);
 458  
 459          $feedbackstructure = new mod_feedback_structure($feedback, $cm, $completioncourse->id);
 460          $returneditems = array();
 461          if ($items = $feedbackstructure->get_items()) {
 462              foreach ($items as $item) {
 463                  $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
 464                  unset($item->itemnr);   // Added by the function, not part of the record.
 465                  $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
 466                  $returneditems[] = $exporter->export($PAGE->get_renderer('core'));
 467              }
 468          }
 469  
 470          $result = array(
 471              'items' => $returneditems,
 472              'warnings' => $warnings
 473          );
 474          return $result;
 475      }
 476  
 477      /**
 478       * Describes the get_items return value.
 479       *
 480       * @return external_single_structure
 481       * @since Moodle 3.3
 482       */
 483      public static function get_items_returns() {
 484          return new external_single_structure(
 485              array(
 486                  'items' => new external_multiple_structure(
 487                      feedback_item_exporter::get_read_structure()
 488                  ),
 489                  'warnings' => new external_warnings(),
 490              )
 491          );
 492      }
 493  
 494      /**
 495       * Describes the parameters for launch_feedback.
 496       *
 497       * @return external_function_parameters
 498       * @since Moodle 3.3
 499       */
 500      public static function launch_feedback_parameters() {
 501          return new external_function_parameters (
 502              array(
 503                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
 504                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
 505                      VALUE_DEFAULT, 0),
 506              )
 507          );
 508      }
 509  
 510      /**
 511       * Starts or continues a feedback submission
 512       *
 513       * @param array $feedbackid feedback instance id
 514       * @param int $courseid course where user completes a feedback (for site feedbacks only).
 515       * @return array of warnings and launch information
 516       * @since Moodle 3.3
 517       */
 518      public static function launch_feedback($feedbackid, $courseid = 0) {
 519          global $PAGE;
 520  
 521          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
 522          $params = self::validate_parameters(self::launch_feedback_parameters(), $params);
 523          $warnings = array();
 524  
 525          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 526              $params['courseid']);
 527          // Check we can do a new submission (or continue an existing).
 528          $feedbackcompletion = self::validate_feedback_access($feedback, $completioncourse, $cm, $context, true);
 529  
 530          $gopage = $feedbackcompletion->get_resume_page();
 531          if ($gopage === null) {
 532              $gopage = -1; // Last page.
 533          }
 534  
 535          $result = array(
 536              'gopage' => $gopage,
 537              'warnings' => $warnings
 538          );
 539          return $result;
 540      }
 541  
 542      /**
 543       * Describes the launch_feedback return value.
 544       *
 545       * @return external_single_structure
 546       * @since Moodle 3.3
 547       */
 548      public static function launch_feedback_returns() {
 549          return new external_single_structure(
 550              array(
 551                  'gopage' => new external_value(PARAM_INT, 'The next page to go (-1 if we were already in the last page). 0 for first page.'),
 552                  'warnings' => new external_warnings(),
 553              )
 554          );
 555      }
 556  
 557      /**
 558       * Describes the parameters for get_page_items.
 559       *
 560       * @return external_function_parameters
 561       * @since Moodle 3.3
 562       */
 563      public static function get_page_items_parameters() {
 564          return new external_function_parameters (
 565              array(
 566                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
 567                  'page' => new external_value(PARAM_INT, 'The page to get starting by 0'),
 568                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
 569                      VALUE_DEFAULT, 0),
 570              )
 571          );
 572      }
 573  
 574      /**
 575       * Get a single feedback page items.
 576       *
 577       * @param int $feedbackid feedback instance id
 578       * @param int $page the page to get starting by 0
 579       * @param int $courseid course where user completes the feedback (for site feedbacks only)
 580       * @return array of warnings and launch information
 581       * @since Moodle 3.3
 582       */
 583      public static function get_page_items($feedbackid, $page, $courseid = 0) {
 584          global $PAGE;
 585  
 586          $params = array('feedbackid' => $feedbackid, 'page' => $page, 'courseid' => $courseid);
 587          $params = self::validate_parameters(self::get_page_items_parameters(), $params);
 588          $warnings = array();
 589  
 590          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 591              $params['courseid']);
 592  
 593          $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
 594  
 595          $page = $params['page'];
 596          $pages = $feedbackcompletion->get_pages();
 597          $pageitems = $pages[$page];
 598          $hasnextpage = $page < count($pages) - 1; // Until we complete this page we can not trust get_next_page().
 599          $hasprevpage = $page && ($feedbackcompletion->get_previous_page($page, false) !== null);
 600  
 601          $returneditems = array();
 602          foreach ($pageitems as $item) {
 603              $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
 604              unset($item->itemnr);   // Added by the function, not part of the record.
 605              $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
 606              $returneditems[] = $exporter->export($PAGE->get_renderer('core'));
 607          }
 608  
 609          $result = array(
 610              'items' => $returneditems,
 611              'hasprevpage' => $hasprevpage,
 612              'hasnextpage' => $hasnextpage,
 613              'warnings' => $warnings
 614          );
 615          return $result;
 616      }
 617  
 618      /**
 619       * Describes the get_page_items return value.
 620       *
 621       * @return external_single_structure
 622       * @since Moodle 3.3
 623       */
 624      public static function get_page_items_returns() {
 625          return new external_single_structure(
 626              array(
 627                  'items' => new external_multiple_structure(
 628                      feedback_item_exporter::get_read_structure()
 629                  ),
 630                  'hasprevpage' => new external_value(PARAM_BOOL, 'Whether is a previous page.'),
 631                  'hasnextpage' => new external_value(PARAM_BOOL, 'Whether there are more pages.'),
 632                  'warnings' => new external_warnings(),
 633              )
 634          );
 635      }
 636  
 637      /**
 638       * Describes the parameters for process_page.
 639       *
 640       * @return external_function_parameters
 641       * @since Moodle 3.3
 642       */
 643      public static function process_page_parameters() {
 644          return new external_function_parameters (
 645              array(
 646                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
 647                  'page' => new external_value(PARAM_INT, 'The page being processed.'),
 648                  'responses' => new external_multiple_structure(
 649                      new external_single_structure(
 650                          array(
 651                              'name' => new external_value(PARAM_NOTAGS, 'The response name (usually type[index]_id).'),
 652                              'value' => new external_value(PARAM_RAW, 'The response value.'),
 653                          )
 654                      ), 'The data to be processed.', VALUE_DEFAULT, array()
 655                  ),
 656                  'goprevious' => new external_value(PARAM_BOOL, 'Whether we want to jump to previous page.', VALUE_DEFAULT, false),
 657                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
 658                      VALUE_DEFAULT, 0),
 659              )
 660          );
 661      }
 662  
 663      /**
 664       * Process a jump between pages.
 665       *
 666       * @param array $feedbackid feedback instance id
 667       * @param array $page the page being processed
 668       * @param array $responses the responses to be processed
 669       * @param bool $goprevious whether we want to jump to previous page
 670       * @param int $courseid course where user completes the feedback (for site feedbacks only)
 671       * @return array of warnings and launch information
 672       * @since Moodle 3.3
 673       */
 674      public static function process_page($feedbackid, $page, $responses = [], $goprevious = false, $courseid = 0) {
 675          global $USER, $SESSION;
 676  
 677          $params = array('feedbackid' => $feedbackid, 'page' => $page, 'responses' => $responses, 'goprevious' => $goprevious,
 678              'courseid' => $courseid);
 679          $params = self::validate_parameters(self::process_page_parameters(), $params);
 680          $warnings = array();
 681          $siteaftersubmit = $completionpagecontents = '';
 682  
 683          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 684              $params['courseid']);
 685          // Check we can do a new submission (or continue an existing).
 686          $feedbackcompletion = self::validate_feedback_access($feedback, $completioncourse, $cm, $context, true);
 687  
 688          // Create the $_POST object required by the feedback question engine.
 689          $_POST = array();
 690          foreach ($responses as $response) {
 691              // First check if we are handling array parameters.
 692              if (preg_match('/(.+)\[(.+)\]$/', $response['name'], $matches)) {
 693                  $_POST[$matches[1]][$matches[2]] = $response['value'];
 694              } else {
 695                  $_POST[$response['name']] = $response['value'];
 696              }
 697          }
 698          // Force fields.
 699          $_POST['id'] = $cm->id;
 700          $_POST['courseid'] = $courseid;
 701          $_POST['gopage'] = $params['page'];
 702          $_POST['_qf__mod_feedback_complete_form'] = 1;
 703  
 704          // Determine where to go, backwards or forward.
 705          if (!$params['goprevious']) {
 706              $_POST['gonextpage'] = 1;   // Even if we are saving values we need this set.
 707              if ($feedbackcompletion->get_next_page($params['page'], false) === null) {
 708                  $_POST['savevalues'] = 1;   // If there is no next page, it means we are finishing the feedback.
 709              }
 710          }
 711  
 712          // Ignore sesskey (deep in some APIs), the request is already validated.
 713          $USER->ignoresesskey = true;
 714          feedback_init_feedback_session();
 715          $SESSION->feedback->is_started = true;
 716  
 717          $feedbackcompletion->process_page($params['page'], $params['goprevious']);
 718          $completed = $feedbackcompletion->just_completed();
 719          if ($completed) {
 720              $jumpto = 0;
 721              if ($feedback->page_after_submit) {
 722                  $completionpagecontents = $feedbackcompletion->page_after_submit();
 723              }
 724  
 725              if ($feedback->site_after_submit) {
 726                  $siteaftersubmit = feedback_encode_target_url($feedback->site_after_submit);
 727              }
 728          } else {
 729              $jumpto = $feedbackcompletion->get_jumpto();
 730          }
 731  
 732          $result = array(
 733              'jumpto' => $jumpto,
 734              'completed' => $completed,
 735              'completionpagecontents' => $completionpagecontents,
 736              'siteaftersubmit' => $siteaftersubmit,
 737              'warnings' => $warnings
 738          );
 739          return $result;
 740      }
 741  
 742      /**
 743       * Describes the process_page return value.
 744       *
 745       * @return external_single_structure
 746       * @since Moodle 3.3
 747       */
 748      public static function process_page_returns() {
 749          return new external_single_structure(
 750              array(
 751                  'jumpto' => new external_value(PARAM_INT, 'The page to jump to.'),
 752                  'completed' => new external_value(PARAM_BOOL, 'If the user completed the feedback.'),
 753                  'completionpagecontents' => new external_value(PARAM_RAW, 'The completion page contents.'),
 754                  'siteaftersubmit' => new external_value(PARAM_RAW, 'The link (could be relative) to show after submit.'),
 755                  'warnings' => new external_warnings(),
 756              )
 757          );
 758      }
 759  
 760      /**
 761       * Describes the parameters for get_analysis.
 762       *
 763       * @return external_function_parameters
 764       * @since Moodle 3.3
 765       */
 766      public static function get_analysis_parameters() {
 767          return new external_function_parameters (
 768              array(
 769                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
 770                  'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
 771                                                  VALUE_DEFAULT, 0),
 772                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
 773                      VALUE_DEFAULT, 0),
 774              )
 775          );
 776      }
 777  
 778      /**
 779       * Retrieves the feedback analysis.
 780       *
 781       * @param array $feedbackid feedback instance id
 782       * @param int $groupid group id, 0 means that the function will determine the user group
 783       * @param int $courseid course where user completes the feedback (for site feedbacks only)
 784       * @return array of warnings and launch information
 785       * @since Moodle 3.3
 786       */
 787      public static function get_analysis($feedbackid, $groupid = 0, $courseid = 0) {
 788          global $PAGE;
 789  
 790          $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'courseid' => $courseid);
 791          $params = self::validate_parameters(self::get_analysis_parameters(), $params);
 792          $warnings = $itemsdata = array();
 793  
 794          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 795              $params['courseid']);
 796  
 797          // Check permissions.
 798          $feedbackstructure = new mod_feedback_structure($feedback, $cm, $completioncourse->id);
 799          if (!$feedbackstructure->can_view_analysis()) {
 800              throw new required_capability_exception($context, 'mod/feedback:viewanalysepage', 'nopermission', '');
 801          }
 802  
 803          if (!empty($params['groupid'])) {
 804              $groupid = $params['groupid'];
 805              // Determine is the group is visible to user.
 806              if (!groups_group_visible($groupid, $course, $cm)) {
 807                  throw new moodle_exception('notingroup');
 808              }
 809          } else {
 810              // Check to see if groups are being used here.
 811              if ($groupmode = groups_get_activity_groupmode($cm)) {
 812                  $groupid = groups_get_activity_group($cm);
 813                  // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
 814                  if (!groups_group_visible($groupid, $course, $cm)) {
 815                      throw new moodle_exception('notingroup');
 816                  }
 817              } else {
 818                  $groupid = 0;
 819              }
 820          }
 821  
 822          // Summary data.
 823          $summary = new mod_feedback\output\summary($feedbackstructure, $groupid);
 824          $summarydata = $summary->export_for_template($PAGE->get_renderer('core'));
 825  
 826          $checkanonymously = true;
 827          if ($groupid > 0 AND $feedback->anonymous == FEEDBACK_ANONYMOUS_YES) {
 828              $completedcount = $feedbackstructure->count_completed_responses($groupid);
 829              if ($completedcount < FEEDBACK_MIN_ANONYMOUS_COUNT_IN_GROUP) {
 830                  $checkanonymously = false;
 831              }
 832          }
 833  
 834          if ($checkanonymously) {
 835              // Get the items of the feedback.
 836              $items = $feedbackstructure->get_items(true);
 837              foreach ($items as $item) {
 838                  $itemobj = feedback_get_item_class($item->typ);
 839                  $itemnumber = empty($item->itemnr) ? null : $item->itemnr;
 840                  unset($item->itemnr);   // Added by the function, not part of the record.
 841                  $exporter = new feedback_item_exporter($item, array('context' => $context, 'itemnumber' => $itemnumber));
 842  
 843                  $itemsdata[] = array(
 844                      'item' => $exporter->export($PAGE->get_renderer('core')),
 845                      'data' => $itemobj->get_analysed_for_external($item, $groupid),
 846                  );
 847              }
 848          } else {
 849              $warnings[] = array(
 850                  'item' => 'feedback',
 851                  'itemid' => $feedback->id,
 852                  'warningcode' => 'insufficientresponsesforthisgroup',
 853                  'message' => s(get_string('insufficient_responses_for_this_group', 'feedback'))
 854              );
 855          }
 856  
 857          $result = array(
 858              'completedcount' => $summarydata->completedcount,
 859              'itemscount' => $summarydata->itemscount,
 860              'itemsdata' => $itemsdata,
 861              'warnings' => $warnings
 862          );
 863          return $result;
 864      }
 865  
 866      /**
 867       * Describes the get_analysis return value.
 868       *
 869       * @return external_single_structure
 870       * @since Moodle 3.3
 871       */
 872      public static function get_analysis_returns() {
 873          return new external_single_structure(
 874              array(
 875              'completedcount' => new external_value(PARAM_INT, 'Number of completed submissions.'),
 876              'itemscount' => new external_value(PARAM_INT, 'Number of items (questions).'),
 877              'itemsdata' => new external_multiple_structure(
 878                  new external_single_structure(
 879                      array(
 880                          'item' => feedback_item_exporter::get_read_structure(),
 881                          'data' => new external_multiple_structure(
 882                              new external_value(PARAM_RAW, 'The analysis data (can be json encoded)')
 883                          ),
 884                      )
 885                  )
 886              ),
 887              'warnings' => new external_warnings(),
 888              )
 889          );
 890      }
 891  
 892      /**
 893       * Describes the parameters for get_unfinished_responses.
 894       *
 895       * @return external_function_parameters
 896       * @since Moodle 3.3
 897       */
 898      public static function get_unfinished_responses_parameters() {
 899          return new external_function_parameters (
 900              array(
 901                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
 902                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
 903                      VALUE_DEFAULT, 0),
 904              )
 905          );
 906      }
 907  
 908      /**
 909       * Retrieves responses from the current unfinished attempt.
 910       *
 911       * @param array $feedbackid feedback instance id
 912       * @param int $courseid course where user completes the feedback (for site feedbacks only)
 913       * @return array of warnings and launch information
 914       * @since Moodle 3.3
 915       */
 916      public static function get_unfinished_responses($feedbackid, $courseid = 0) {
 917          global $PAGE;
 918  
 919          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
 920          $params = self::validate_parameters(self::get_unfinished_responses_parameters(), $params);
 921          $warnings = $itemsdata = array();
 922  
 923          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 924              $params['courseid']);
 925          $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
 926  
 927          $responses = array();
 928          $unfinished = $feedbackcompletion->get_unfinished_responses();
 929          foreach ($unfinished as $u) {
 930              $exporter = new feedback_valuetmp_exporter($u);
 931              $responses[] = $exporter->export($PAGE->get_renderer('core'));
 932          }
 933  
 934          $result = array(
 935              'responses' => $responses,
 936              'warnings' => $warnings
 937          );
 938          return $result;
 939      }
 940  
 941      /**
 942       * Describes the get_unfinished_responses return value.
 943       *
 944       * @return external_single_structure
 945       * @since Moodle 3.3
 946       */
 947      public static function get_unfinished_responses_returns() {
 948          return new external_single_structure(
 949              array(
 950              'responses' => new external_multiple_structure(
 951                  feedback_valuetmp_exporter::get_read_structure()
 952              ),
 953              'warnings' => new external_warnings(),
 954              )
 955          );
 956      }
 957  
 958      /**
 959       * Describes the parameters for get_finished_responses.
 960       *
 961       * @return external_function_parameters
 962       * @since Moodle 3.3
 963       */
 964      public static function get_finished_responses_parameters() {
 965          return new external_function_parameters (
 966              array(
 967                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'),
 968                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
 969                      VALUE_DEFAULT, 0),
 970              )
 971          );
 972      }
 973  
 974      /**
 975       * Retrieves responses from the last finished attempt.
 976       *
 977       * @param array $feedbackid feedback instance id
 978       * @param int $courseid course where user completes the feedback (for site feedbacks only)
 979       * @return array of warnings and the responses
 980       * @since Moodle 3.3
 981       */
 982      public static function get_finished_responses($feedbackid, $courseid = 0) {
 983          global $PAGE;
 984  
 985          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
 986          $params = self::validate_parameters(self::get_finished_responses_parameters(), $params);
 987          $warnings = $itemsdata = array();
 988  
 989          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
 990              $params['courseid']);
 991          $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
 992  
 993          $responses = array();
 994          // Load and get the responses from the last completed feedback.
 995          $feedbackcompletion->find_last_completed();
 996          $unfinished = $feedbackcompletion->get_finished_responses();
 997          foreach ($unfinished as $u) {
 998              $exporter = new feedback_value_exporter($u);
 999              $responses[] = $exporter->export($PAGE->get_renderer('core'));
1000          }
1001  
1002          $result = array(
1003              'responses' => $responses,
1004              'warnings' => $warnings
1005          );
1006          return $result;
1007      }
1008  
1009      /**
1010       * Describes the get_finished_responses return value.
1011       *
1012       * @return external_single_structure
1013       * @since Moodle 3.3
1014       */
1015      public static function get_finished_responses_returns() {
1016          return new external_single_structure(
1017              array(
1018              'responses' => new external_multiple_structure(
1019                  feedback_value_exporter::get_read_structure()
1020              ),
1021              'warnings' => new external_warnings(),
1022              )
1023          );
1024      }
1025  
1026      /**
1027       * Describes the parameters for get_non_respondents.
1028       *
1029       * @return external_function_parameters
1030       * @since Moodle 3.3
1031       */
1032      public static function get_non_respondents_parameters() {
1033          return new external_function_parameters (
1034              array(
1035                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
1036                  'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group.',
1037                                                  VALUE_DEFAULT, 0),
1038                  'sort' => new external_value(PARAM_ALPHA, 'Sort param, must be firstname, lastname or lastaccess (default).',
1039                                                  VALUE_DEFAULT, 'lastaccess'),
1040                  'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
1041                  'perpage' => new external_value(PARAM_INT, 'The number of records to return per page.', VALUE_DEFAULT, 0),
1042                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
1043                      VALUE_DEFAULT, 0),
1044              )
1045          );
1046      }
1047  
1048      /**
1049       * Retrieves a list of students who didn't submit the feedback.
1050       *
1051       * @param int $feedbackid feedback instance id
1052       * @param int $groupid Group id, 0 means that the function will determine the user group'
1053       * @param str $sort sort param, must be firstname, lastname or lastaccess (default)
1054       * @param int $page the page of records to return
1055       * @param int $perpage the number of records to return per page
1056       * @param int $courseid course where user completes the feedback (for site feedbacks only)
1057       * @return array of warnings and users ids
1058       * @since Moodle 3.3
1059       */
1060      public static function get_non_respondents($feedbackid, $groupid = 0, $sort = 'lastaccess', $page = 0, $perpage = 0,
1061              $courseid = 0) {
1062  
1063          global $CFG;
1064          require_once($CFG->dirroot . '/mod/feedback/lib.php');
1065  
1066          $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'sort' => $sort, 'page' => $page,
1067              'perpage' => $perpage, 'courseid' => $courseid);
1068          $params = self::validate_parameters(self::get_non_respondents_parameters(), $params);
1069          $warnings = $nonrespondents = array();
1070  
1071          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
1072              $params['courseid']);
1073          $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
1074          $completioncourseid = $feedbackcompletion->get_courseid();
1075  
1076          if ($feedback->anonymous != FEEDBACK_ANONYMOUS_NO || $feedback->course == SITEID) {
1077              throw new moodle_exception('anonymous', 'feedback');
1078          }
1079  
1080          // Check permissions.
1081          require_capability('mod/feedback:viewreports', $context);
1082  
1083          if (!empty($params['groupid'])) {
1084              $groupid = $params['groupid'];
1085              // Determine is the group is visible to user.
1086              if (!groups_group_visible($groupid, $course, $cm)) {
1087                  throw new moodle_exception('notingroup');
1088              }
1089          } else {
1090              // Check to see if groups are being used here.
1091              if ($groupmode = groups_get_activity_groupmode($cm)) {
1092                  $groupid = groups_get_activity_group($cm);
1093                  // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
1094                  if (!groups_group_visible($groupid, $course, $cm)) {
1095                      throw new moodle_exception('notingroup');
1096                  }
1097              } else {
1098                  $groupid = 0;
1099              }
1100          }
1101  
1102          if ($params['sort'] !== 'firstname' && $params['sort'] !== 'lastname' && $params['sort'] !== 'lastaccess') {
1103              throw new invalid_parameter_exception('Invalid sort param, must be firstname, lastname or lastaccess.');
1104          }
1105  
1106          // Check if we are page filtering.
1107          if ($params['perpage'] == 0) {
1108              $page = $params['page'];
1109              $perpage = FEEDBACK_DEFAULT_PAGE_COUNT;
1110          } else {
1111              $perpage = $params['perpage'];
1112              $page = $perpage * $params['page'];
1113          }
1114          $users = feedback_get_incomplete_users($cm, $groupid, $params['sort'], $page, $perpage, true);
1115          foreach ($users as $user) {
1116              $nonrespondents[] = [
1117                  'courseid' => $completioncourseid,
1118                  'userid'   => $user->id,
1119                  'fullname' => fullname($user),
1120                  'started'  => $user->feedbackstarted
1121              ];
1122          }
1123  
1124          $result = array(
1125              'users' => $nonrespondents,
1126              'total' => feedback_count_incomplete_users($cm, $groupid),
1127              'warnings' => $warnings
1128          );
1129          return $result;
1130      }
1131  
1132      /**
1133       * Describes the get_non_respondents return value.
1134       *
1135       * @return external_single_structure
1136       * @since Moodle 3.3
1137       */
1138      public static function get_non_respondents_returns() {
1139          return new external_single_structure(
1140              array(
1141                  'users' => new external_multiple_structure(
1142                      new external_single_structure(
1143                          array(
1144                              'courseid' => new external_value(PARAM_INT, 'Course id'),
1145                              'userid' => new external_value(PARAM_INT, 'The user id'),
1146                              'fullname' => new external_value(PARAM_TEXT, 'User full name'),
1147                              'started' => new external_value(PARAM_BOOL, 'If the user has started the attempt'),
1148                          )
1149                      )
1150                  ),
1151                  'total' => new external_value(PARAM_INT, 'Total number of non respondents'),
1152                  'warnings' => new external_warnings(),
1153              )
1154          );
1155      }
1156  
1157      /**
1158       * Describes the parameters for get_responses_analysis.
1159       *
1160       * @return external_function_parameters
1161       * @since Moodle 3.3
1162       */
1163      public static function get_responses_analysis_parameters() {
1164          return new external_function_parameters (
1165              array(
1166                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
1167                  'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
1168                                                  VALUE_DEFAULT, 0),
1169                  'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
1170                  'perpage' => new external_value(PARAM_INT, 'The number of records to return per page', VALUE_DEFAULT, 0),
1171                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
1172                      VALUE_DEFAULT, 0),
1173              )
1174          );
1175      }
1176  
1177      /**
1178       * Return the feedback user responses.
1179       *
1180       * @param int $feedbackid feedback instance id
1181       * @param int $groupid Group id, 0 means that the function will determine the user group
1182       * @param int $page the page of records to return
1183       * @param int $perpage the number of records to return per page
1184       * @param int $courseid course where user completes the feedback (for site feedbacks only)
1185       * @return array of warnings and users attemps and responses
1186       * @throws moodle_exception
1187       * @since Moodle 3.3
1188       */
1189      public static function get_responses_analysis($feedbackid, $groupid = 0, $page = 0, $perpage = 0, $courseid = 0) {
1190  
1191          $params = array('feedbackid' => $feedbackid, 'groupid' => $groupid, 'page' => $page, 'perpage' => $perpage,
1192              'courseid' => $courseid);
1193          $params = self::validate_parameters(self::get_responses_analysis_parameters(), $params);
1194          $warnings = $itemsdata = array();
1195  
1196          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
1197              $params['courseid']);
1198  
1199          // Check permissions.
1200          require_capability('mod/feedback:viewreports', $context);
1201  
1202          if (!empty($params['groupid'])) {
1203              $groupid = $params['groupid'];
1204              // Determine is the group is visible to user.
1205              if (!groups_group_visible($groupid, $course, $cm)) {
1206                  throw new moodle_exception('notingroup');
1207              }
1208          } else {
1209              // Check to see if groups are being used here.
1210              if ($groupmode = groups_get_activity_groupmode($cm)) {
1211                  $groupid = groups_get_activity_group($cm);
1212                  // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
1213                  if (!groups_group_visible($groupid, $course, $cm)) {
1214                      throw new moodle_exception('notingroup');
1215                  }
1216              } else {
1217                  $groupid = 0;
1218              }
1219          }
1220  
1221          $feedbackstructure = new mod_feedback_structure($feedback, $cm, $completioncourse->id);
1222          $responsestable = new mod_feedback_responses_table($feedbackstructure, $groupid);
1223          // Ensure responses number is correct prior returning them.
1224          $feedbackstructure->shuffle_anonym_responses();
1225          $anonresponsestable = new mod_feedback_responses_anon_table($feedbackstructure, $groupid);
1226  
1227          $result = array(
1228              'attempts'          => $responsestable->export_external_structure($params['page'], $params['perpage']),
1229              'totalattempts'     => $responsestable->get_total_responses_count(),
1230              'anonattempts'      => $anonresponsestable->export_external_structure($params['page'], $params['perpage']),
1231              'totalanonattempts' => $anonresponsestable->get_total_responses_count(),
1232              'warnings'       => $warnings
1233          );
1234          return $result;
1235      }
1236  
1237      /**
1238       * Describes the get_responses_analysis return value.
1239       *
1240       * @return external_single_structure
1241       * @since Moodle 3.3
1242       */
1243      public static function get_responses_analysis_returns() {
1244          $responsestructure = new external_multiple_structure(
1245              new external_single_structure(
1246                  array(
1247                      'id' => new external_value(PARAM_INT, 'Response id'),
1248                      'name' => new external_value(PARAM_RAW, 'Response name'),
1249                      'printval' => new external_value(PARAM_RAW, 'Response ready for output'),
1250                      'rawval' => new external_value(PARAM_RAW, 'Response raw value'),
1251                  )
1252              )
1253          );
1254  
1255          return new external_single_structure(
1256              array(
1257                  'attempts' => new external_multiple_structure(
1258                      new external_single_structure(
1259                          array(
1260                              'id' => new external_value(PARAM_INT, 'Completed id'),
1261                              'courseid' => new external_value(PARAM_INT, 'Course id'),
1262                              'userid' => new external_value(PARAM_INT, 'User who responded'),
1263                              'timemodified' => new external_value(PARAM_INT, 'Time modified for the response'),
1264                              'fullname' => new external_value(PARAM_TEXT, 'User full name'),
1265                              'responses' => $responsestructure
1266                          )
1267                      )
1268                  ),
1269                  'totalattempts' => new external_value(PARAM_INT, 'Total responses count.'),
1270                  'anonattempts' => new external_multiple_structure(
1271                      new external_single_structure(
1272                          array(
1273                              'id' => new external_value(PARAM_INT, 'Completed id'),
1274                              'courseid' => new external_value(PARAM_INT, 'Course id'),
1275                              'number' => new external_value(PARAM_INT, 'Response number'),
1276                              'responses' => $responsestructure
1277                          )
1278                      )
1279                  ),
1280                  'totalanonattempts' => new external_value(PARAM_INT, 'Total anonymous responses count.'),
1281                  'warnings' => new external_warnings(),
1282              )
1283          );
1284      }
1285  
1286      /**
1287       * Describes the parameters for get_last_completed.
1288       *
1289       * @return external_function_parameters
1290       * @since Moodle 3.3
1291       */
1292      public static function get_last_completed_parameters() {
1293          return new external_function_parameters (
1294              array(
1295                  'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id'),
1296                  'courseid' => new external_value(PARAM_INT, 'Course where user completes the feedback (for site feedbacks only).',
1297                      VALUE_DEFAULT, 0),
1298              )
1299          );
1300      }
1301  
1302      /**
1303       * Retrieves the last completion record for the current user.
1304       *
1305       * @param int $feedbackid feedback instance id
1306       * @return array of warnings and the last completed record
1307       * @since Moodle 3.3
1308       * @throws moodle_exception
1309       */
1310      public static function get_last_completed($feedbackid, $courseid = 0) {
1311          global $PAGE;
1312  
1313          $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
1314          $params = self::validate_parameters(self::get_last_completed_parameters(), $params);
1315          $warnings = array();
1316  
1317          list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
1318              $params['courseid']);
1319          $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);
1320  
1321          if ($feedbackcompletion->is_anonymous()) {
1322               throw new moodle_exception('anonymous', 'feedback');
1323          }
1324          if ($completed = $feedbackcompletion->find_last_completed()) {
1325              $exporter = new feedback_completed_exporter($completed);
1326              return array(
1327                  'completed' => $exporter->export($PAGE->get_renderer('core')),
1328                  'warnings' => $warnings,
1329              );
1330          }
1331          throw new moodle_exception('not_completed_yet', 'feedback');
1332      }
1333  
1334      /**
1335       * Describes the get_last_completed return value.
1336       *
1337       * @return external_single_structure
1338       * @since Moodle 3.3
1339       */
1340      public static function get_last_completed_returns() {
1341          return new external_single_structure(
1342              array(
1343                  'completed' => feedback_completed_exporter::get_read_structure(),
1344                  'warnings' => new external_warnings(),
1345              )
1346          );
1347      }
1348  }