Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.6.x will end 11 November 2019 (12 months).
  • Bug fixes for security issues in 3.6.x will end 11 May 2020 (18 months) - Support has ended.
  • minimum PHP 7.0.0 Note: minimum PHP version has increased since Moodle 3.3. PHP 7.1.x and 7.2.x are supported too. PHP 7.3.x support is being implemented (@ MDL-63420) and not ready for production with this release.
  • Differences Between: [Versions 35 and 36]

       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  }