Search moodle.org's
Developer Documentation

See Release Notes

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

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Workshop external API
  19   *
  20   * @package    mod_workshop
  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.4
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die;
  28  
  29  require_once($CFG->dirroot . '/mod/workshop/locallib.php');
  30  
  31  use core_external\external_api;
  32  use core_external\external_files;
  33  use core_external\external_function_parameters;
  34  use core_external\external_multiple_structure;
  35  use core_external\external_single_structure;
  36  use core_external\external_value;
  37  use core_external\external_warnings;
  38  use core_external\util;
  39  use mod_workshop\external\workshop_summary_exporter;
  40  use mod_workshop\external\submission_exporter;
  41  use mod_workshop\external\assessment_exporter;
  42  
  43  /**
  44   * Workshop external functions
  45   *
  46   * @package    mod_workshop
  47   * @category   external
  48   * @copyright  2017 Juan Leyva <juan@moodle.com>
  49   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  50   * @since      Moodle 3.4
  51   */
  52  class mod_workshop_external extends external_api {
  53  
  54      /**
  55       * Describes the parameters for get_workshops_by_courses.
  56       *
  57       * @return external_function_parameters
  58       * @since Moodle 3.4
  59       */
  60      public static function get_workshops_by_courses_parameters() {
  61          return new external_function_parameters (
  62              array(
  63                  'courseids' => new external_multiple_structure(
  64                      new external_value(PARAM_INT, 'Course id'), 'Array of course ids', VALUE_DEFAULT, array()
  65                  ),
  66              )
  67          );
  68      }
  69  
  70      /**
  71       * Returns a list of workshops in a provided list of courses.
  72       * If no list is provided all workshops that the user can view will be returned.
  73       *
  74       * @param array $courseids course ids
  75       * @return array of warnings and workshops
  76       * @since Moodle 3.4
  77       */
  78      public static function get_workshops_by_courses($courseids = array()) {
  79          global $PAGE;
  80  
  81          $warnings = array();
  82          $returnedworkshops = array();
  83  
  84          $params = array(
  85              'courseids' => $courseids,
  86          );
  87          $params = self::validate_parameters(self::get_workshops_by_courses_parameters(), $params);
  88  
  89          $mycourses = array();
  90          if (empty($params['courseids'])) {
  91              $mycourses = enrol_get_my_courses();
  92              $params['courseids'] = array_keys($mycourses);
  93          }
  94  
  95          // Ensure there are courseids to loop through.
  96          if (!empty($params['courseids'])) {
  97  
  98              list($courses, $warnings) = util::validate_courses($params['courseids'], $mycourses);
  99              $output = $PAGE->get_renderer('core');
 100  
 101              // Get the workshops in this course, this function checks users visibility permissions.
 102              // We can avoid then additional validate_context calls.
 103              $workshops = get_all_instances_in_courses("workshop", $courses);
 104              foreach ($workshops as $workshop) {
 105  
 106                  $context = context_module::instance($workshop->coursemodule);
 107                  // Remove fields that are not from the workshop (added by get_all_instances_in_courses).
 108                  unset($workshop->coursemodule, $workshop->context, $workshop->visible, $workshop->section, $workshop->groupmode,
 109                          $workshop->groupingid);
 110  
 111                  $exporter = new workshop_summary_exporter($workshop, array('context' => $context));
 112                  $returnedworkshops[] = $exporter->export($output);
 113              }
 114          }
 115  
 116          $result = array(
 117              'workshops' => $returnedworkshops,
 118              'warnings' => $warnings
 119          );
 120          return $result;
 121      }
 122  
 123      /**
 124       * Describes the get_workshops_by_courses return value.
 125       *
 126       * @return external_single_structure
 127       * @since Moodle 3.4
 128       */
 129      public static function get_workshops_by_courses_returns() {
 130          return new external_single_structure(
 131              array(
 132                  'workshops' => new external_multiple_structure(
 133                      workshop_summary_exporter::get_read_structure()
 134                  ),
 135                  'warnings' => new external_warnings(),
 136              )
 137          );
 138      }
 139  
 140      /**
 141       * Utility function for validating a workshop.
 142       *
 143       * @param int $workshopid workshop instance id
 144       * @return array array containing the workshop object, course, context and course module objects
 145       * @since  Moodle 3.4
 146       */
 147      protected static function validate_workshop($workshopid) {
 148          global $DB, $USER;
 149  
 150          // Request and permission validation.
 151          $workshop = $DB->get_record('workshop', array('id' => $workshopid), '*', MUST_EXIST);
 152          list($course, $cm) = get_course_and_cm_from_instance($workshop, 'workshop');
 153  
 154          $context = context_module::instance($cm->id);
 155          self::validate_context($context);
 156  
 157          $workshop = new workshop($workshop, $cm, $course);
 158  
 159          return array($workshop, $course, $cm, $context);
 160      }
 161  
 162  
 163      /**
 164       * Describes the parameters for get_workshop_access_information.
 165       *
 166       * @return external_external_function_parameters
 167       * @since Moodle 3.4
 168       */
 169      public static function get_workshop_access_information_parameters() {
 170          return new external_function_parameters (
 171              array(
 172                  'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.')
 173              )
 174          );
 175      }
 176  
 177      /**
 178       * Return access information for a given workshop.
 179       *
 180       * @param int $workshopid workshop instance id
 181       * @return array of warnings and the access information
 182       * @since Moodle 3.4
 183       * @throws  moodle_exception
 184       */
 185      public static function get_workshop_access_information($workshopid) {
 186          global $USER;
 187  
 188          $params = self::validate_parameters(self::get_workshop_access_information_parameters(), array('workshopid' => $workshopid));
 189  
 190          list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
 191  
 192          $result = array();
 193          // Return all the available capabilities.
 194          $capabilities = load_capability_def('mod_workshop');
 195          foreach ($capabilities as $capname => $capdata) {
 196              // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
 197              $field = 'can' . str_replace('mod/workshop:', '', $capname);
 198              $result[$field] = has_capability($capname, $context);
 199          }
 200  
 201          // Now, specific features access information.
 202          $result['creatingsubmissionallowed'] = $workshop->creating_submission_allowed($USER->id);
 203          $result['modifyingsubmissionallowed'] = $workshop->modifying_submission_allowed($USER->id);
 204          $result['assessingallowed'] = $workshop->assessing_allowed($USER->id);
 205          $result['assessingexamplesallowed'] = $workshop->assessing_examples_allowed();
 206          if (is_null($result['assessingexamplesallowed'])) {
 207              $result['assessingexamplesallowed'] = false;
 208          }
 209          $result['examplesassessedbeforesubmission'] = $workshop->check_examples_assessed_before_submission($USER->id);
 210          list($result['examplesassessedbeforeassessment'], $code) = $workshop->check_examples_assessed_before_assessment($USER->id);
 211  
 212          $result['warnings'] = array();
 213          return $result;
 214      }
 215  
 216      /**
 217       * Describes the get_workshop_access_information return value.
 218       *
 219       * @return external_single_structure
 220       * @since Moodle 3.4
 221       */
 222      public static function get_workshop_access_information_returns() {
 223  
 224          $structure = array(
 225              'creatingsubmissionallowed' => new external_value(PARAM_BOOL,
 226                  'Is the given user allowed to create their submission?'),
 227              'modifyingsubmissionallowed' => new external_value(PARAM_BOOL,
 228                  'Is the user allowed to modify his existing submission?'),
 229              'assessingallowed' => new external_value(PARAM_BOOL,
 230                  'Is the user allowed to create/edit his assessments?'),
 231              'assessingexamplesallowed' => new external_value(PARAM_BOOL,
 232                  'Are reviewers allowed to create/edit their assessments of the example submissions?.'),
 233              'examplesassessedbeforesubmission' => new external_value(PARAM_BOOL,
 234                  'Whether the given user has assessed all his required examples before submission
 235                  (always true if there are not examples to assess or not configured to check before submission).'),
 236              'examplesassessedbeforeassessment' => new external_value(PARAM_BOOL,
 237                  'Whether the given user has assessed all his required examples before assessment
 238                  (always true if there are not examples to assessor not configured to check before assessment).'),
 239              'warnings' => new external_warnings()
 240          );
 241  
 242          $capabilities = load_capability_def('mod_workshop');
 243          foreach ($capabilities as $capname => $capdata) {
 244              // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
 245              $field = 'can' . str_replace('mod/workshop:', '', $capname);
 246              $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.');
 247          }
 248  
 249          return new external_single_structure($structure);
 250      }
 251  
 252      /**
 253       * Describes the parameters for get_user_plan.
 254       *
 255       * @return external_external_function_parameters
 256       * @since Moodle 3.4
 257       */
 258      public static function get_user_plan_parameters() {
 259          return new external_function_parameters (
 260              array(
 261                  'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
 262                  'userid' => new external_value(PARAM_INT, 'User id (empty or 0 for current user).', VALUE_DEFAULT, 0),
 263              )
 264          );
 265      }
 266  
 267      /**
 268       * Return the planner information for the given user.
 269       *
 270       * @param int $workshopid workshop instance id
 271       * @param int $userid user id
 272       * @return array of warnings and the user plan
 273       * @since Moodle 3.4
 274       * @throws  moodle_exception
 275       */
 276      public static function get_user_plan($workshopid, $userid = 0) {
 277          global $USER;
 278  
 279          $params = array(
 280              'workshopid' => $workshopid,
 281              'userid' => $userid,
 282          );
 283          $params = self::validate_parameters(self::get_user_plan_parameters(), $params);
 284  
 285          list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
 286  
 287          // Extra checks so only users with permissions can view other users plans.
 288          if (empty($params['userid']) || $params['userid'] == $USER->id) {
 289              $userid = $USER->id;
 290          } else {
 291              require_capability('moodle/course:manageactivities', $context);
 292              $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
 293              core_user::require_active_user($user);
 294              if (!$workshop->check_group_membership($user->id)) {
 295                  throw new moodle_exception('notingroup');
 296              }
 297              $userid = $user->id;
 298          }
 299  
 300          // Get the user plan information ready for external functions.
 301          $userplan = new workshop_user_plan($workshop, $userid);
 302          $userplan = array('phases' => $userplan->phases, 'examples' => $userplan->get_examples());
 303          foreach ($userplan['phases'] as $phasecode => $phase) {
 304              $phase->code = $phasecode;
 305              $userplan['phases'][$phasecode] = (array) $phase;
 306              foreach ($userplan['phases'][$phasecode]['tasks'] as $taskcode => $task) {
 307                  $task->code = $taskcode;
 308                  if ($task->link instanceof moodle_url) {
 309                      $task->link = $task->link->out(false);
 310                  }
 311                  $userplan['phases'][$phasecode]['tasks'][$taskcode] = (array) $task;
 312              }
 313              foreach ($userplan['phases'][$phasecode]['actions'] as $actioncode => $action) {
 314                  if ($action->url instanceof moodle_url) {
 315                      $action->url = $action->url->out(false);
 316                  }
 317                  $userplan['phases'][$phasecode]['actions'][$actioncode] = (array) $action;
 318              }
 319          }
 320  
 321          $result['userplan'] = $userplan;
 322          $result['warnings'] = array();
 323          return $result;
 324      }
 325  
 326      /**
 327       * Describes the get_user_plan return value.
 328       *
 329       * @return external_single_structure
 330       * @since Moodle 3.4
 331       */
 332      public static function get_user_plan_returns() {
 333          return new external_single_structure(
 334              array(
 335                  'userplan' => new external_single_structure(
 336                      array(
 337                          'phases' => new external_multiple_structure(
 338                              new external_single_structure(
 339                                  array(
 340                                      'code' => new external_value(PARAM_INT, 'Phase code.'),
 341                                      'title' => new external_value(PARAM_NOTAGS, 'Phase title.'),
 342                                      'active' => new external_value(PARAM_BOOL, 'Whether is the active task.'),
 343                                      'tasks' => new external_multiple_structure(
 344                                          new external_single_structure(
 345                                              array(
 346                                                  'code' => new external_value(PARAM_ALPHA, 'Task code.'),
 347                                                  'title' => new external_value(PARAM_RAW, 'Task title.'),
 348                                                  'link' => new external_value(PARAM_URL, 'Link to task.'),
 349                                                  'details' => new external_value(PARAM_RAW, 'Task details.', VALUE_OPTIONAL),
 350                                                  'completed' => new external_value(PARAM_NOTAGS,
 351                                                      'Completion information (maybe empty, maybe a boolean or generic info.'),
 352                                              )
 353                                          )
 354                                      ),
 355                                      'actions' => new external_multiple_structure(
 356                                          new external_single_structure(
 357                                              array(
 358                                                  'type' => new external_value(PARAM_ALPHA, 'Action type.', VALUE_OPTIONAL),
 359                                                  'label' => new external_value(PARAM_RAW, 'Action label.', VALUE_OPTIONAL),
 360                                                  'url' => new external_value(PARAM_URL, 'Link to action.'),
 361                                                  'method' => new external_value(PARAM_ALPHA, 'Get or post.', VALUE_OPTIONAL),
 362                                              )
 363                                          )
 364                                      ),
 365                                  )
 366                              )
 367                          ),
 368                          'examples' => new external_multiple_structure(
 369                              new external_single_structure(
 370                                  array(
 371                                      'id' => new external_value(PARAM_INT, 'Example submission id.'),
 372                                      'title' => new external_value(PARAM_RAW, 'Example submission title.'),
 373                                      'assessmentid' => new external_value(PARAM_INT, 'Example submission assessment id.'),
 374                                      'grade' => new external_value(PARAM_FLOAT, 'The submission grade.'),
 375                                      'gradinggrade' => new external_value(PARAM_FLOAT, 'The assessment grade.'),
 376                                  )
 377                              )
 378                          ),
 379                      )
 380                  ),
 381                  'warnings' => new external_warnings(),
 382              )
 383          );
 384      }
 385  
 386      /**
 387       * Describes the parameters for view_workshop.
 388       *
 389       * @return external_function_parameters
 390       * @since Moodle 3.4
 391       */
 392      public static function view_workshop_parameters() {
 393          return new external_function_parameters (
 394              array(
 395                  'workshopid' => new external_value(PARAM_INT, 'Workshop instance id'),
 396              )
 397          );
 398      }
 399  
 400      /**
 401       * Trigger the course module viewed event and update the module completion status.
 402       *
 403       * @param int $workshopid workshop instance id
 404       * @return array of warnings and status result
 405       * @since Moodle 3.4
 406       * @throws moodle_exception
 407       */
 408      public static function view_workshop($workshopid) {
 409  
 410          $params = array('workshopid' => $workshopid);
 411          $params = self::validate_parameters(self::view_workshop_parameters(), $params);
 412          $warnings = array();
 413  
 414          list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
 415  
 416          $workshop->set_module_viewed();
 417  
 418          $result = array(
 419              'status' => true,
 420              'warnings' => $warnings,
 421          );
 422          return $result;
 423      }
 424  
 425      /**
 426       * Describes the view_workshop return value.
 427       *
 428       * @return external_single_structure
 429       * @since Moodle 3.4
 430       */
 431      public static function view_workshop_returns() {
 432          return new external_single_structure(
 433              array(
 434                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
 435                  'warnings' => new external_warnings(),
 436              )
 437          );
 438      }
 439  
 440      /**
 441       * Returns the description of the external function parameters.
 442       *
 443       * @return external_function_parameters
 444       * @since Moodle 3.4
 445       */
 446      public static function add_submission_parameters() {
 447          return new external_function_parameters(array(
 448              'workshopid' => new external_value(PARAM_INT, 'Workshop id'),
 449              'title' => new external_value(PARAM_TEXT, 'Submission title'),
 450              'content' => new external_value(PARAM_RAW, 'Submission text content', VALUE_DEFAULT, ''),
 451              'contentformat' => new external_value(PARAM_INT, 'The format used for the content', VALUE_DEFAULT, FORMAT_MOODLE),
 452              'inlineattachmentsid' => new external_value(PARAM_INT, 'The draft file area id for inline attachments in the content',
 453                  VALUE_DEFAULT, 0),
 454              'attachmentsid' => new external_value(PARAM_INT, 'The draft file area id for attachments', VALUE_DEFAULT, 0),
 455          ));
 456      }
 457  
 458      /**
 459       * Add a new submission to a given workshop.
 460       *
 461       * @param int $workshopid the workshop id
 462       * @param string $title             the submission title
 463       * @param string  $content          the submission text content
 464       * @param int  $contentformat       the format used for the content
 465       * @param int $inlineattachmentsid  the draft file area id for inline attachments in the content
 466       * @param int $attachmentsid        the draft file area id for attachments
 467       * @return array Containing the new created submission id and warnings.
 468       * @since Moodle 3.4
 469       * @throws moodle_exception
 470       */
 471      public static function add_submission($workshopid, $title, $content = '', $contentformat = FORMAT_MOODLE,
 472              $inlineattachmentsid = 0, $attachmentsid = 0) {
 473          global $USER;
 474  
 475          $params = self::validate_parameters(self::add_submission_parameters(), array(
 476              'workshopid' => $workshopid,
 477              'title' => $title,
 478              'content' => $content,
 479              'contentformat' => $contentformat,
 480              'inlineattachmentsid' => $inlineattachmentsid,
 481              'attachmentsid' => $attachmentsid,
 482          ));
 483          $warnings = array();
 484  
 485          // Get and validate the workshop.
 486          list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
 487          require_capability('mod/workshop:submit', $context);
 488  
 489          // Check if we can submit now.
 490          $canaddsubmission = $workshop->creating_submission_allowed($USER->id);
 491          $canaddsubmission = $canaddsubmission && $workshop->check_examples_assessed_before_submission($USER->id);
 492          if (!$canaddsubmission) {
 493              throw new moodle_exception('nopermissions', 'error', '', 'add submission');
 494          }
 495  
 496          // Prepare the submission object.
 497          $submission = new stdClass;
 498          $submission->id = null;
 499          $submission->cmid = $cm->id;
 500          $submission->example = 0;
 501          $submission->title = trim($params['title']);
 502          $submission->content_editor = array(
 503              'text' => $params['content'],
 504              'format' => $params['contentformat'],
 505              'itemid' => $params['inlineattachmentsid'],
 506          );
 507          $submission->attachment_filemanager = $params['attachmentsid'];
 508  
 509          if (empty($submission->title)) {
 510              throw new moodle_exception('errorinvalidparam', 'webservice', '', 'title');
 511          }
 512  
 513          $errors = $workshop->validate_submission_data((array) $submission);
 514          // We can get several errors, return them in warnings.
 515          if (!empty($errors)) {
 516              $submission->id = 0;
 517              foreach ($errors as $itemname => $message) {
 518                  $warnings[] = array(
 519                      'item' => $itemname,
 520                      'itemid' => 0,
 521                      'warningcode' => 'fielderror',
 522                      'message' => s($message)
 523                  );
 524              }
 525              return array(
 526                  'status' => false,
 527                  'warnings' => $warnings
 528              );
 529          } else {
 530              $submission->id = $workshop->edit_submission($submission);
 531              return array(
 532                  'status' => true,
 533                  'submissionid' => $submission->id,
 534                  'warnings' => $warnings
 535              );
 536          }
 537      }
 538  
 539      /**
 540       * Returns the description of the external function return value.
 541       *
 542       * @return \core_external\external_description
 543       * @since Moodle 3.4
 544       */
 545      public static function add_submission_returns() {
 546          return new external_single_structure(array(
 547              'status' => new external_value(PARAM_BOOL, 'True if the submission was created false otherwise.'),
 548              'submissionid' => new external_value(PARAM_INT, 'New workshop submission id.', VALUE_OPTIONAL),
 549              'warnings' => new external_warnings()
 550          ));
 551      }
 552  
 553      /**
 554       * Returns the description of the external function parameters.
 555       *
 556       * @return external_function_parameters
 557       * @since Moodle 3.4
 558       */
 559      public static function update_submission_parameters() {
 560          return new external_function_parameters(array(
 561              'submissionid' => new external_value(PARAM_INT, 'Submission id'),
 562              'title' => new external_value(PARAM_TEXT, 'Submission title'),
 563              'content' => new external_value(PARAM_RAW, 'Submission text content', VALUE_DEFAULT, ''),
 564              'contentformat' => new external_value(PARAM_INT, 'The format used for the content', VALUE_DEFAULT, FORMAT_MOODLE),
 565              'inlineattachmentsid' => new external_value(PARAM_INT, 'The draft file area id for inline attachments in the content',
 566                  VALUE_DEFAULT, 0),
 567              'attachmentsid' => new external_value(PARAM_INT, 'The draft file area id for attachments', VALUE_DEFAULT, 0),
 568          ));
 569      }
 570  
 571  
 572      /**
 573       * Updates the given submission.
 574       *
 575       * @param int $submissionid         the submission id
 576       * @param string $title             the submission title
 577       * @param string  $content          the submission text content
 578       * @param int  $contentformat       the format used for the content
 579       * @param int $inlineattachmentsid  the draft file area id for inline attachments in the content
 580       * @param int $attachmentsid        the draft file area id for attachments
 581       * @return array whether the submission was updated and warnings.
 582       * @since Moodle 3.4
 583       * @throws moodle_exception
 584       */
 585      public static function update_submission($submissionid, $title, $content = '', $contentformat = FORMAT_MOODLE,
 586              $inlineattachmentsid = 0, $attachmentsid = 0) {
 587          global $USER, $DB;
 588  
 589          $params = self::validate_parameters(self::update_submission_parameters(), array(
 590              'submissionid' => $submissionid,
 591              'title' => $title,
 592              'content' => $content,
 593              'contentformat' => $contentformat,
 594              'inlineattachmentsid' => $inlineattachmentsid,
 595              'attachmentsid' => $attachmentsid,
 596          ));
 597          $warnings = array();
 598  
 599          // Get and validate the submission and workshop.
 600          $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
 601          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
 602          require_capability('mod/workshop:submit', $context);
 603  
 604          // Check if we can update the submission.
 605          $canupdatesubmission = $submission->authorid == $USER->id;
 606          $canupdatesubmission = $canupdatesubmission && $workshop->modifying_submission_allowed($USER->id);
 607          $canupdatesubmission = $canupdatesubmission && $workshop->check_examples_assessed_before_submission($USER->id);
 608          if (!$canupdatesubmission) {
 609              throw new moodle_exception('nopermissions', 'error', '', 'update submission');
 610          }
 611  
 612          // Prepare the submission object.
 613          $submission->title = trim($params['title']);
 614          if (empty($submission->title)) {
 615              throw new moodle_exception('errorinvalidparam', 'webservice', '', 'title');
 616          }
 617          $submission->content_editor = array(
 618              'text' => $params['content'],
 619              'format' => $params['contentformat'],
 620              'itemid' => $params['inlineattachmentsid'],
 621          );
 622          $submission->attachment_filemanager = $params['attachmentsid'];
 623  
 624          $errors = $workshop->validate_submission_data((array) $submission);
 625          // We can get several errors, return them in warnings.
 626          if (!empty($errors)) {
 627              $status = false;
 628              foreach ($errors as $itemname => $message) {
 629                  $warnings[] = array(
 630                      'item' => $itemname,
 631                      'itemid' => 0,
 632                      'warningcode' => 'fielderror',
 633                      'message' => s($message)
 634                  );
 635              }
 636          } else {
 637              $status = true;
 638              $submission->id = $workshop->edit_submission($submission);
 639          }
 640  
 641          return array(
 642              'status' => $status,
 643              'warnings' => $warnings
 644          );
 645      }
 646  
 647      /**
 648       * Returns the description of the external function return value.
 649       *
 650       * @return \core_external\external_description
 651       * @since Moodle 3.4
 652       */
 653      public static function update_submission_returns() {
 654          return new external_single_structure(array(
 655              'status' => new external_value(PARAM_BOOL, 'True if the submission was updated false otherwise.'),
 656              'warnings' => new external_warnings()
 657          ));
 658      }
 659  
 660      /**
 661       * Returns the description of the external function parameters.
 662       *
 663       * @return external_function_parameters
 664       * @since Moodle 3.4
 665       */
 666      public static function delete_submission_parameters() {
 667          return new external_function_parameters(
 668              array(
 669                  'submissionid' => new external_value(PARAM_INT, 'Submission id'),
 670              )
 671          );
 672      }
 673  
 674  
 675      /**
 676       * Deletes the given submission.
 677       *
 678       * @param int $submissionid the submission id.
 679       * @return array containing the result status and warnings.
 680       * @since Moodle 3.4
 681       * @throws moodle_exception
 682       */
 683      public static function delete_submission($submissionid) {
 684          global $USER, $DB;
 685  
 686          $params = self::validate_parameters(self::delete_submission_parameters(), array('submissionid' => $submissionid));
 687          $warnings = array();
 688  
 689          // Get and validate the submission and workshop.
 690          $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
 691          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
 692  
 693          // Check if we can delete the submission.
 694          if (!has_capability('mod/workshop:deletesubmissions', $context)) {
 695              require_capability('mod/workshop:submit', $context);
 696              // We can delete our own submission, on time and not yet assessed.
 697              $candeletesubmission = $submission->authorid == $USER->id;
 698              $candeletesubmission = $candeletesubmission && $workshop->modifying_submission_allowed($USER->id);
 699              $candeletesubmission = $candeletesubmission && count($workshop->get_assessments_of_submission($submission->id)) == 0;
 700              if (!$candeletesubmission) {
 701                  throw new moodle_exception('nopermissions', 'error', '', 'delete submission');
 702              }
 703          }
 704  
 705          $workshop->delete_submission($submission);
 706  
 707          return array(
 708              'status' => true,
 709              'warnings' => $warnings
 710          );
 711      }
 712  
 713      /**
 714       * Returns the description of the external function return value.
 715       *
 716       * @return \core_external\external_description
 717       * @since Moodle 3.4
 718       */
 719      public static function delete_submission_returns() {
 720          return new external_single_structure(array(
 721              'status' => new external_value(PARAM_BOOL, 'True if the submission was deleted.'),
 722              'warnings' => new external_warnings()
 723          ));
 724      }
 725  
 726      /**
 727       * Helper method for returning the submission data according the current user capabilities and current phase.
 728       *
 729       * @param  stdClass $submission the submission data
 730       * @param  workshop $workshop   the workshop class
 731       * @param  bool $canviewauthorpublished whether the user has the capability mod/workshop:viewauthorpublished on
 732       * @param  bool $canviewauthornames whether the user has the capability mod/workshop:vviewauthornames on
 733       * @param  bool $canviewallsubmissions whether the user has the capability mod/workshop:viewallsubmissions on
 734       * @return stdClass object with the submission data filtered
 735       * @since Moodle 3.4
 736       */
 737      protected static function prepare_submission_for_external($submission, workshop $workshop, $canviewauthorpublished = null,
 738              $canviewauthornames = null, $canviewallsubmissions = null) {
 739          global $USER;
 740  
 741          if (is_null($canviewauthorpublished)) {
 742              $canviewauthorpublished = has_capability('mod/workshop:viewauthorpublished', $workshop->context);
 743          }
 744          if (is_null($canviewauthornames)) {
 745              $canviewauthornames = has_capability('mod/workshop:viewauthornames', $workshop->context);
 746          }
 747          if (is_null($canviewallsubmissions)) {
 748              $canviewallsubmissions = has_capability('mod/workshop:viewallsubmissions', $workshop->context);
 749          }
 750  
 751          $ownsubmission = $submission->authorid == $USER->id;
 752          if (!$canviewauthornames && !$ownsubmission) {
 753              $submission->authorid = 0;
 754          }
 755  
 756          // Remove grade, gradeover, gradeoverby, feedbackauthor and timegraded for non-teachers or invalid phase.
 757          // WS mod_workshop_external::get_grades should be used for retrieving grades by students.
 758          if ($workshop->phase < workshop::PHASE_EVALUATION || !$canviewallsubmissions) {
 759              $properties = submission_exporter::properties_definition();
 760              foreach ($properties as $attribute => $settings) {
 761                  // Special case, the feedbackauthor (and who did it) should be returned if the workshop is closed and
 762                  // the user can view it.
 763                  if (($attribute == 'feedbackauthor' || $attribute == 'gradeoverby') &&
 764                          $workshop->phase == workshop::PHASE_CLOSED && $ownsubmission) {
 765                      continue;
 766                  }
 767                  if (!empty($settings['optional'])) {
 768                      unset($submission->{$attribute});
 769                  }
 770              }
 771          }
 772          return $submission;
 773      }
 774  
 775      /**
 776       * Returns description of method parameters
 777       *
 778       * @return external_function_parameters
 779       * @since Moodle 3.4
 780       */
 781      public static function get_submissions_parameters() {
 782          return new external_function_parameters(
 783              array(
 784                  'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
 785                  'userid' => new external_value(PARAM_INT, 'Get submissions done by this user. Use 0 or empty for the current user',
 786                                                  VALUE_DEFAULT, 0),
 787                  'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group.
 788                                                     It will return submissions done by users in the given group.',
 789                                                     VALUE_DEFAULT, 0),
 790                  'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
 791                  'perpage' => new external_value(PARAM_INT, 'The number of records to return per page.', VALUE_DEFAULT, 0),
 792              )
 793          );
 794      }
 795  
 796      /**
 797       * Retrieves all the workshop submissions visible by the current user or the one done by the given user
 798       * (except example submissions).
 799       *
 800       * @param int $workshopid       the workshop instance id
 801       * @param int $userid           get submissions done by this user
 802       * @param int $groupid          (optional) group id, 0 means that the function will determine the user group
 803       * @param int $page             page of records to return
 804       * @param int $perpage          number of records to return per page
 805       * @return array of warnings and the entries
 806       * @since Moodle 3.4
 807       * @throws moodle_exception
 808       */
 809      public static function get_submissions($workshopid, $userid = 0, $groupid = 0, $page = 0, $perpage = 0) {
 810          global $PAGE, $USER;
 811  
 812          $params = array('workshopid' => $workshopid, 'userid' => $userid, 'groupid' => $groupid,
 813              'page' => $page, 'perpage' => $perpage);
 814          $params = self::validate_parameters(self::get_submissions_parameters(), $params);
 815          $submissions = $warnings = array();
 816  
 817          list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
 818  
 819          if (empty($params['groupid'])) {
 820              // Check to see if groups are being used here.
 821              if ($groupmode = groups_get_activity_groupmode($cm)) {
 822                  $groupid = groups_get_activity_group($cm);
 823                  // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
 824                  if (!groups_group_visible($groupid, $course, $cm)) {
 825                      throw new moodle_exception('notingroup');
 826                  }
 827              } else {
 828                  $groupid = 0;
 829              }
 830          }
 831  
 832          if (!empty($params['userid']) && $params['userid'] != $USER->id) {
 833              $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
 834              core_user::require_active_user($user);
 835              if (!$workshop->check_group_membership($user->id)) {
 836                  throw new moodle_exception('notingroup');
 837              }
 838          }
 839  
 840          $totalfilesize = 0;
 841          list($submissionsrecords, $totalcount) =
 842              $workshop->get_visible_submissions($params['userid'], $groupid, $params['page'], $params['perpage']);
 843  
 844          if ($totalcount) {
 845  
 846              $canviewauthorpublished = has_capability('mod/workshop:viewauthorpublished', $context);
 847              $canviewauthornames = has_capability('mod/workshop:viewauthornames', $context);
 848              $canviewallsubmissions = has_capability('mod/workshop:viewallsubmissions', $context);
 849  
 850              $related = array('context' => $context);
 851              foreach ($submissionsrecords as $submission) {
 852                  $submission = self::prepare_submission_for_external($submission, $workshop, $canviewauthorpublished,
 853                      $canviewauthornames, $canviewallsubmissions);
 854  
 855                  $exporter = new submission_exporter($submission, $related);
 856                  $submissions[] = $exporter->export($PAGE->get_renderer('core'));
 857              }
 858  
 859              // Retrieve total files size for the submissions (so external clients know how many data they'd need to download).
 860              $fs = get_file_storage();
 861              $files = $fs->get_area_files($context->id, 'mod_workshop', array('submission_content', 'submission_attachment'));
 862              foreach ($files as $file) {
 863                  if ($file->is_directory()) {
 864                      continue;
 865                  }
 866                  $totalfilesize += $file->get_filesize();
 867              }
 868          }
 869  
 870          return array(
 871              'submissions' => $submissions,
 872              'totalcount' => $totalcount,
 873              'totalfilesize' => $totalfilesize,
 874          );
 875      }
 876  
 877      /**
 878       * Returns description of method result value
 879       *
 880       * @return \core_external\external_description
 881       * @since Moodle 3.4
 882       */
 883      public static function get_submissions_returns() {
 884          return new external_single_structure(
 885              array(
 886                  'submissions' => new external_multiple_structure(
 887                      submission_exporter::get_read_structure()
 888                  ),
 889                  'totalcount' => new external_value(PARAM_INT, 'Total count of submissions.'),
 890                  'totalfilesize' => new external_value(PARAM_INT, 'Total size (bytes) of the files attached to all the
 891                      submissions (even the ones not returned due to pagination).'),
 892                  'warnings' => new external_warnings()
 893              )
 894          );
 895      }
 896  
 897      /**
 898       * Helper method for validating a submission.
 899       *
 900       * @param  stdClass   $submission submission object
 901       * @param  workshop   $workshop     workshop instance
 902       * @return void
 903       * @since  Moodle 3.4
 904       */
 905      protected static function validate_submission($submission, workshop $workshop) {
 906          global $USER;
 907  
 908          $workshopclosed = $workshop->phase == workshop::PHASE_CLOSED;
 909          $canviewpublished = has_capability('mod/workshop:viewpublishedsubmissions', $workshop->context);
 910  
 911          $canview = $submission->authorid == $USER->id;  // I did it.
 912          $canview = $canview || !empty($workshop->get_assessment_of_submission_by_user($submission->id, $USER->id));  // I reviewed.
 913          $canview = $canview || has_capability('mod/workshop:viewallsubmissions', $workshop->context); // I can view all.
 914          $canview = $canview || ($submission->published && $workshopclosed && $canviewpublished);    // It has been published.
 915  
 916          if ($canview) {
 917              // Here we should check if the user share group.
 918              if ($submission->authorid != $USER->id &&
 919                      !groups_user_groups_visible($workshop->course, $submission->authorid, $workshop->cm)) {
 920                  throw new moodle_exception('notingroup');
 921              }
 922          } else {
 923              throw new moodle_exception('nopermissions', 'error', '', 'view submission');
 924          }
 925      }
 926  
 927      /**
 928       * Returns the description of the external function parameters.
 929       *
 930       * @return external_function_parameters
 931       * @since Moodle 3.4
 932       */
 933      public static function get_submission_parameters() {
 934          return new external_function_parameters(
 935              array(
 936                  'submissionid' => new external_value(PARAM_INT, 'Submission id'),
 937              )
 938          );
 939      }
 940  
 941  
 942      /**
 943       * Retrieves the given submission.
 944       *
 945       * @param int $submissionid the submission id
 946       * @return array containing the submission and warnings.
 947       * @since Moodle 3.4
 948       * @throws moodle_exception
 949       */
 950      public static function get_submission($submissionid) {
 951          global $USER, $DB, $PAGE;
 952  
 953          $params = self::validate_parameters(self::get_submission_parameters(), array('submissionid' => $submissionid));
 954          $warnings = array();
 955  
 956          // Get and validate the submission and workshop.
 957          $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
 958          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
 959  
 960          self::validate_submission($submission, $workshop);
 961  
 962          $submission = self::prepare_submission_for_external($submission, $workshop);
 963  
 964          $related = array('context' => $context);
 965          $exporter = new submission_exporter($submission, $related);
 966          return array(
 967              'submission' => $exporter->export($PAGE->get_renderer('core')),
 968              'warnings' => $warnings
 969          );
 970      }
 971  
 972      /**
 973       * Returns description of method result value
 974       *
 975       * @return \core_external\external_description
 976       * @since Moodle 3.4
 977       */
 978      public static function get_submission_returns() {
 979          return new external_single_structure(
 980              array(
 981                  'submission' => submission_exporter::get_read_structure(),
 982                  'warnings' => new external_warnings()
 983              )
 984          );
 985      }
 986  
 987      /**
 988       * Helper method for validating if the current user can view the submission assessments.
 989       *
 990       * @param  stdClass   $submission submission object
 991       * @param  workshop   $workshop     workshop instance
 992       * @return void
 993       * @since  Moodle 3.4
 994       */
 995      protected static function check_view_submission_assessments($submission, workshop $workshop) {
 996          global $USER;
 997  
 998          $ownsubmission = $submission->authorid == $USER->id;
 999          $canview = has_capability('mod/workshop:viewallassessments', $workshop->context) ||
1000              ($ownsubmission && $workshop->assessments_available());
1001  
1002          if ($canview) {
1003              // Here we should check if the user share group.
1004              if ($submission->authorid != $USER->id &&
1005                      !groups_user_groups_visible($workshop->course, $submission->authorid, $workshop->cm)) {
1006                  throw new moodle_exception('notingroup');
1007              }
1008          } else {
1009              throw new moodle_exception('nopermissions', 'error', '', 'view assessment');
1010          }
1011      }
1012  
1013      /**
1014       * Helper method for returning the assessment data according the current user capabilities and current phase.
1015       *
1016       * @param  stdClass $assessment the assessment data
1017       * @param  workshop $workshop   the workshop class
1018       * @return stdClass object with the assessment data filtered or null if is not viewable yet
1019       * @since Moodle 3.4
1020       */
1021      protected static function prepare_assessment_for_external($assessment, workshop $workshop) {
1022          global $USER;
1023          static $canviewallassessments = null;
1024          static $canviewreviewers = null;
1025          static $canoverridegrades = null;
1026  
1027          // Remove all the properties that does not belong to the assessment table.
1028          $properties = assessment_exporter::properties_definition();
1029          foreach ($assessment as $key => $value) {
1030              if (!isset($properties[$key])) {
1031                  unset($assessment->{$key});
1032              }
1033          }
1034  
1035          if (is_null($canviewallassessments)) {
1036              $canviewallassessments = has_capability('mod/workshop:viewallassessments', $workshop->context);
1037          }
1038          if (is_null($canviewreviewers)) {
1039              $canviewreviewers = has_capability('mod/workshop:viewreviewernames', $workshop->context);
1040          }
1041          if (is_null($canoverridegrades)) {
1042              $canoverridegrades = has_capability('mod/workshop:overridegrades', $workshop->context);
1043          }
1044  
1045          $isreviewer = $assessment->reviewerid == $USER->id;
1046  
1047          if (!$isreviewer && is_null($assessment->grade) && !$canviewallassessments) {
1048              // Students do not see peer-assessment that are not graded yet.
1049              return null;
1050          }
1051  
1052          // Remove the feedback for the reviewer if:
1053          // I can't see it in the evaluation phase because I'm not a teacher or the reviewer AND
1054          // I can't see it in the assessment phase because I'm not a teacher.
1055          if (($workshop->phase < workshop::PHASE_EVALUATION || !($isreviewer || $canviewallassessments)) &&
1056                  ($workshop->phase < workshop::PHASE_ASSESSMENT || !$canviewallassessments) ) {
1057              // Remove all the feedback information (all the optional fields).
1058              foreach ($properties as $attribute => $settings) {
1059                  if (!empty($settings['optional'])) {
1060                      unset($assessment->{$attribute});
1061                  }
1062              }
1063          }
1064  
1065          if (!$isreviewer && !$canviewreviewers) {
1066              $assessment->reviewerid = 0;
1067          }
1068  
1069          return $assessment;
1070      }
1071  
1072      /**
1073       * Returns the description of the external function parameters.
1074       *
1075       * @return external_function_parameters
1076       * @since Moodle 3.4
1077       */
1078      public static function get_submission_assessments_parameters() {
1079          return new external_function_parameters(
1080              array(
1081                  'submissionid' => new external_value(PARAM_INT, 'Submission id'),
1082              )
1083          );
1084      }
1085  
1086  
1087      /**
1088       * Retrieves the given submission assessments.
1089       *
1090       * @param int $submissionid the submission id
1091       * @return array containing the assessments and warnings.
1092       * @since Moodle 3.4
1093       * @throws moodle_exception
1094       */
1095      public static function get_submission_assessments($submissionid) {
1096          global $USER, $DB, $PAGE;
1097  
1098          $params = self::validate_parameters(self::get_submission_assessments_parameters(), array('submissionid' => $submissionid));
1099          $warnings = $assessments = array();
1100  
1101          // Get and validate the submission and workshop.
1102          $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
1103          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1104  
1105          // Check that we can get the assessments and get them.
1106          self::check_view_submission_assessments($submission, $workshop);
1107          $assessmentsrecords = $workshop->get_assessments_of_submission($submission->id);
1108  
1109          $related = array('context' => $context);
1110          foreach ($assessmentsrecords as $assessment) {
1111              $assessment = self::prepare_assessment_for_external($assessment, $workshop);
1112              if (empty($assessment)) {
1113                  continue;
1114              }
1115              $exporter = new assessment_exporter($assessment, $related);
1116              $assessments[] = $exporter->export($PAGE->get_renderer('core'));
1117          }
1118  
1119          return array(
1120              'assessments' => $assessments,
1121              'warnings' => $warnings
1122          );
1123      }
1124  
1125      /**
1126       * Returns description of method result value
1127       *
1128       * @return \core_external\external_description
1129       * @since Moodle 3.4
1130       */
1131      public static function get_submission_assessments_returns() {
1132          return new external_single_structure(
1133              array(
1134                  'assessments' => new external_multiple_structure(
1135                      assessment_exporter::get_read_structure()
1136                  ),
1137                  'warnings' => new external_warnings()
1138              )
1139          );
1140      }
1141  
1142      /**
1143       * Returns the description of the external function parameters.
1144       *
1145       * @return external_function_parameters
1146       * @since Moodle 3.4
1147       */
1148      public static function get_assessment_parameters() {
1149          return new external_function_parameters(
1150              array(
1151                  'assessmentid' => new external_value(PARAM_INT, 'Assessment id'),
1152              )
1153          );
1154      }
1155  
1156  
1157      /**
1158       * Retrieves the given assessment.
1159       *
1160       * @param int $assessmentid the assessment id
1161       * @return array containing the assessment and warnings.
1162       * @since Moodle 3.4
1163       * @throws moodle_exception
1164       */
1165      public static function get_assessment($assessmentid) {
1166          global $DB, $PAGE;
1167  
1168          $params = self::validate_parameters(self::get_assessment_parameters(), array('assessmentid' => $assessmentid));
1169          $warnings = array();
1170  
1171          // Get and validate the assessment, submission and workshop.
1172          $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
1173          $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
1174          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1175  
1176          // Check that we can get the assessment.
1177          $workshop->check_view_assessment($assessment, $submission);
1178  
1179          $assessment = $workshop->get_assessment_by_id($assessment->id);
1180          $assessment = self::prepare_assessment_for_external($assessment, $workshop);
1181          if (empty($assessment)) {
1182              throw new moodle_exception('nopermissions', 'error', '', 'view assessment');
1183          }
1184          $related = array('context' => $context);
1185          $exporter = new assessment_exporter($assessment, $related);
1186  
1187          return array(
1188              'assessment' => $exporter->export($PAGE->get_renderer('core')),
1189              'warnings' => $warnings
1190          );
1191      }
1192  
1193      /**
1194       * Returns description of method result value
1195       *
1196       * @return \core_external\external_description
1197       * @since Moodle 3.4
1198       */
1199      public static function get_assessment_returns() {
1200          return new external_single_structure(
1201              array(
1202                  'assessment' => assessment_exporter::get_read_structure(),
1203                  'warnings' => new external_warnings()
1204              )
1205          );
1206      }
1207  
1208      /**
1209       * Returns the description of the external function parameters.
1210       *
1211       * @return external_function_parameters
1212       * @since Moodle 3.4
1213       */
1214      public static function get_assessment_form_definition_parameters() {
1215          return new external_function_parameters(
1216              array(
1217                  'assessmentid' => new external_value(PARAM_INT, 'Assessment id'),
1218                  'mode' => new external_value(PARAM_ALPHA, 'The form mode (assessment or preview)', VALUE_DEFAULT, 'assessment'),
1219              )
1220          );
1221      }
1222  
1223  
1224      /**
1225       * Retrieves the assessment form definition (data required to be able to display the assessment form).
1226       *
1227       * @param int $assessmentid the assessment id
1228       * @param string $mode the form mode (assessment or preview)
1229       * @return array containing the assessment and warnings.
1230       * @since Moodle 3.4
1231       * @throws moodle_exception
1232       */
1233      public static function get_assessment_form_definition($assessmentid, $mode = 'assessment') {
1234          global $DB, $USER;
1235  
1236          $params = self::validate_parameters(
1237              self::get_assessment_form_definition_parameters(), array('assessmentid' => $assessmentid, 'mode' => $mode)
1238          );
1239          $warnings = $pending = array();
1240  
1241          if ($params['mode'] != 'assessment' && $params['mode'] != 'preview') {
1242              throw new invalid_parameter_exception('Invalid value for mode parameter (value: ' . $params['mode'] . ')');
1243          }
1244  
1245          // Get and validate the assessment, submission and workshop.
1246          $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
1247          $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
1248          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1249  
1250          // Check we can view the assessment (so we can get the form data).
1251          $workshop->check_view_assessment($assessment, $submission);
1252  
1253          $cansetassessmentweight = has_capability('mod/workshop:allocate', $context);
1254          $pending = $workshop->get_pending_assessments_by_reviewer($assessment->reviewerid, $assessment->id);
1255  
1256          // Retrieve the data from the strategy plugin.
1257          $strategy = $workshop->grading_strategy_instance();
1258          $strategyname = str_replace('_strategy', '', get_class($strategy)); // Get strategy name.
1259          $mform = $strategy->get_assessment_form(null, $params['mode'], $assessment, true,
1260              array('editableweight' => $cansetassessmentweight, 'pending' => !empty($pending)));
1261          $formdata = $mform->get_customdata();
1262  
1263          $result = array(
1264              'dimenssionscount' => $formdata['nodims'],
1265              'descriptionfiles' => util::get_area_files($context->id, $strategyname, 'description'),
1266              'warnings' => $warnings
1267          );
1268          // Include missing dimension fields.
1269          for ($i = 0; $i < $formdata['nodims']; $i++) {
1270              $formdata['fields']->{'gradeid__idx_' . $i} = 0;
1271              $formdata['fields']->{'peercomment__idx_' . $i} = '';
1272          }
1273  
1274          // Convert all the form data for external.
1275          foreach (array('options', 'fields', 'current') as $typeofdata) {
1276              $result[$typeofdata] = array();
1277  
1278              if (!empty($formdata[$typeofdata])) {
1279                  $alldata = (array) $formdata[$typeofdata];
1280                  foreach ($alldata as $key => $val) {
1281                      if (strpos($key, 'description__idx_')) {
1282                          // Format dimension description.
1283                          $id = str_replace('description__idx_', '', $key);
1284                          list($val, $format) = \core_external\util::format_text($val, $alldata['dimensionid__idx_' . $id . 'format'],
1285                              $context->id, $strategyname, 'description', $alldata['dimensionid__idx_' . $id]);
1286                      }
1287                      $result[$typeofdata][] = array(
1288                          'name' => $key,
1289                          'value' => $val
1290                      );
1291                  }
1292              }
1293          }
1294  
1295          // Get dimensions info.
1296          $grader = $workshop->grading_strategy_instance();
1297          $result['dimensionsinfo'] = $grader->get_dimensions_info();
1298  
1299          return $result;
1300      }
1301  
1302      /**
1303       * Returns description of method result value
1304       *
1305       * @return \core_external\external_description
1306       * @since Moodle 3.4
1307       */
1308      public static function get_assessment_form_definition_returns() {
1309          return new external_single_structure(
1310              array(
1311                  'dimenssionscount' => new external_value(PARAM_INT, 'The number of dimenssions used by the form.'),
1312                  'descriptionfiles' => new external_files('Files in the description text'),
1313                  'options' => new external_multiple_structure(
1314                      new external_single_structure(
1315                          array(
1316                              'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name.'),
1317                              'value' => new external_value(PARAM_NOTAGS, 'Option value.')
1318                          )
1319                      ), 'The form options.'
1320                  ),
1321                  'fields' => new external_multiple_structure(
1322                      new external_single_structure(
1323                          array(
1324                              'name' => new external_value(PARAM_ALPHANUMEXT, 'Field name.'),
1325                              'value' => new external_value(PARAM_RAW, 'Field default value.')
1326                          )
1327                      ), 'The form fields.'
1328                  ),
1329                  'current' => new external_multiple_structure(
1330                      new external_single_structure(
1331                          array(
1332                              'name' => new external_value(PARAM_ALPHANUMEXT, 'Field name.'),
1333                              'value' => new external_value(PARAM_RAW, 'Current field value.')
1334                          )
1335                      ), 'The current field values.'
1336                  ),
1337                  'dimensionsinfo' => new external_multiple_structure(
1338                      new external_single_structure(
1339                          array(
1340                              'id' => new external_value(PARAM_INT, 'Dimension id.'),
1341                              'min' => new external_value(PARAM_FLOAT, 'Minimum grade for the dimension.'),
1342                              'max' => new external_value(PARAM_FLOAT, 'Maximum grade for the dimension.'),
1343                              'weight' => new external_value(PARAM_TEXT, 'The weight of the dimension.'),
1344                              'scale' => new external_value(PARAM_TEXT, 'Scale items (if used).', VALUE_OPTIONAL),
1345                          )
1346                      ), 'The dimensions general information.'
1347                  ),
1348                  'warnings' => new external_warnings()
1349              )
1350          );
1351      }
1352  
1353      /**
1354       * Returns the description of the external function parameters.
1355       *
1356       * @return external_function_parameters
1357       * @since Moodle 3.4
1358       */
1359      public static function get_reviewer_assessments_parameters() {
1360          return new external_function_parameters(
1361              array(
1362                  'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
1363                  'userid' => new external_value(PARAM_INT, 'User id who did the assessment review (empty or 0 for current user).',
1364                      VALUE_DEFAULT, 0),
1365              )
1366          );
1367      }
1368  
1369  
1370      /**
1371       * Retrieves all the assessments reviewed by the given user.
1372       *
1373       * @param int $workshopid   the workshop instance id
1374       * @param int $userid       the reviewer user id
1375       * @return array containing the user assessments and warnings.
1376       * @since Moodle 3.4
1377       * @throws moodle_exception
1378       */
1379      public static function get_reviewer_assessments($workshopid, $userid = 0) {
1380          global $USER, $DB, $PAGE;
1381  
1382          $params = self::validate_parameters(
1383              self::get_reviewer_assessments_parameters(), array('workshopid' => $workshopid, 'userid' => $userid)
1384          );
1385          $warnings = $assessments = array();
1386  
1387          // Get and validate the submission and workshop.
1388          list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
1389  
1390          // Extra checks so only users with permissions can view other users assessments.
1391          if (empty($params['userid']) || $params['userid'] == $USER->id) {
1392              $userid = $USER->id;
1393              list($assessed, $notice) = $workshop->check_examples_assessed_before_assessment($userid);
1394              if (!$assessed) {
1395                  throw new moodle_exception($notice, 'mod_workshop');
1396              }
1397              if ($workshop->phase < workshop::PHASE_ASSESSMENT) {    // Can view assessments only in assessment phase onwards.
1398                  throw new moodle_exception('nopermissions', 'error', '', 'view assessments');
1399              }
1400          } else {
1401              require_capability('mod/workshop:viewallassessments', $context);
1402              $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1403              core_user::require_active_user($user);
1404              if (!$workshop->check_group_membership($user->id)) {
1405                  throw new moodle_exception('notingroup');
1406              }
1407              $userid = $user->id;
1408          }
1409          // Now get all my assessments (includes those pending review).
1410          $assessmentsrecords = $workshop->get_assessments_by_reviewer($userid);
1411  
1412          $related = array('context' => $context);
1413          foreach ($assessmentsrecords as $assessment) {
1414              $assessment = self::prepare_assessment_for_external($assessment, $workshop);
1415              if (empty($assessment)) {
1416                  continue;
1417              }
1418              $exporter = new assessment_exporter($assessment, $related);
1419              $assessments[] = $exporter->export($PAGE->get_renderer('core'));
1420          }
1421  
1422          return array(
1423              'assessments' => $assessments,
1424              'warnings' => $warnings
1425          );
1426      }
1427  
1428      /**
1429       * Returns description of method result value
1430       *
1431       * @return \core_external\external_description
1432       * @since Moodle 3.4
1433       */
1434      public static function get_reviewer_assessments_returns() {
1435          return new external_single_structure(
1436              array(
1437                  'assessments' => new external_multiple_structure(
1438                      assessment_exporter::get_read_structure()
1439                  ),
1440                  'warnings' => new external_warnings()
1441              )
1442          );
1443      }
1444  
1445      /**
1446       * Returns the description of the external function parameters.
1447       *
1448       * @return external_function_parameters
1449       * @since Moodle 3.4
1450       */
1451      public static function update_assessment_parameters() {
1452          return new external_function_parameters(
1453              array(
1454                  'assessmentid' => new external_value(PARAM_INT, 'Assessment id.'),
1455                  'data' => new external_multiple_structure (
1456                      new external_single_structure(
1457                          array(
1458                              'name' => new external_value(PARAM_ALPHANUMEXT,
1459                                  'The assessment data (use WS get_assessment_form_definition for obtaining the data to sent).
1460                                  Apart from that data, you can optionally send:
1461                                  feedbackauthor (str); the feedback for the submission author
1462                                  feedbackauthorformat (int); the format of the feedbackauthor
1463                                  feedbackauthorinlineattachmentsid (int); the draft file area for the editor attachments
1464                                  feedbackauthorattachmentsid (int); the draft file area id for the feedback attachments'
1465                              ),
1466                              'value' => new external_value(PARAM_RAW, 'The value of the option.')
1467                          )
1468                      ), 'Assessment data'
1469                  )
1470              )
1471          );
1472      }
1473  
1474  
1475      /**
1476       * Updates an assessment.
1477       *
1478       * @param int $assessmentid the assessment id
1479       * @param array $data the assessment data
1480       * @return array indicates if the assessment was updated, the new raw grade and possible warnings.
1481       * @since Moodle 3.4
1482       * @throws moodle_exception
1483       */
1484      public static function update_assessment($assessmentid, $data) {
1485          global $DB, $USER;
1486  
1487          $params = self::validate_parameters(
1488              self::update_assessment_parameters(), array('assessmentid' => $assessmentid, 'data' => $data)
1489          );
1490          $warnings = array();
1491  
1492          // Get and validate the assessment, submission and workshop.
1493          $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
1494          $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
1495          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1496  
1497          // Check we can edit the assessment.
1498          $workshop->check_edit_assessment($assessment, $submission);
1499  
1500          // Process data.
1501          $data = new stdClass;
1502          $data->feedbackauthor_editor = array();
1503  
1504          foreach ($params['data'] as $wsdata) {
1505              $name = trim($wsdata['name']);
1506              switch ($name) {
1507                  case 'feedbackauthor':
1508                      $data->feedbackauthor_editor['text'] = $wsdata['value'];
1509                      break;
1510                  case 'feedbackauthorformat':
1511                      $data->feedbackauthor_editor['format'] = clean_param($wsdata['value'], PARAM_FORMAT);
1512                      break;
1513                  case 'feedbackauthorinlineattachmentsid':
1514                      $data->feedbackauthor_editor['itemid'] = clean_param($wsdata['value'], PARAM_INT);
1515                      break;
1516                  case 'feedbackauthorattachmentsid':
1517                      $data->feedbackauthorattachment_filemanager = clean_param($wsdata['value'], PARAM_INT);
1518                      break;
1519                  default:
1520                      $data->{$wsdata['name']} = $wsdata['value'];    // Validation will be done in the form->validation.
1521              }
1522          }
1523  
1524          $cansetassessmentweight = has_capability('mod/workshop:allocate', $context);
1525          $pending = $workshop->get_pending_assessments_by_reviewer($assessment->reviewerid, $assessment->id);
1526          // Retrieve the data from the strategy plugin.
1527          $strategy = $workshop->grading_strategy_instance();
1528          $mform = $strategy->get_assessment_form(null, 'assessment', $assessment, true,
1529              array('editableweight' => $cansetassessmentweight, 'pending' => !empty($pending)));
1530  
1531          $errors = $mform->validation((array) $data, array());
1532          // We can get several errors, return them in warnings.
1533          if (!empty($errors)) {
1534              $status = false;
1535              $rawgrade = null;
1536              foreach ($errors as $itemname => $message) {
1537                  $warnings[] = array(
1538                      'item' => $itemname,
1539                      'itemid' => 0,
1540                      'warningcode' => 'fielderror',
1541                      'message' => s($message)
1542                  );
1543              }
1544          } else {
1545              $rawgrade = $workshop->edit_assessment($assessment, $submission, $data, $strategy);
1546              $status = true;
1547          }
1548  
1549          return array(
1550              'status' => $status,
1551              'rawgrade' => $rawgrade,
1552              'warnings' => $warnings,
1553          );
1554      }
1555  
1556      /**
1557       * Returns description of method result value
1558       *
1559       * @return \core_external\external_description
1560       * @since Moodle 3.4
1561       */
1562      public static function update_assessment_returns() {
1563          return new external_single_structure(
1564              array(
1565                  'status' => new external_value(PARAM_BOOL, 'status: true if the assessment was added or updated false otherwise.'),
1566                  'rawgrade' => new external_value(PARAM_FLOAT, 'Raw percentual grade (0.00000 to 100.00000) for submission.',
1567                      VALUE_OPTIONAL),
1568                  'warnings' => new external_warnings()
1569              )
1570          );
1571      }
1572  
1573      /**
1574       * Returns the description of the external function parameters.
1575       *
1576       * @return external_external_function_parameters
1577       * @since Moodle 3.4
1578       */
1579      public static function get_grades_parameters() {
1580          return new external_function_parameters (
1581              array(
1582                  'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
1583                  'userid' => new external_value(PARAM_INT, 'User id (empty or 0 for current user).', VALUE_DEFAULT, 0),
1584              )
1585          );
1586      }
1587  
1588      /**
1589       * Returns the grades information for the given workshop and user.
1590       *
1591       * @param int $workshopid workshop instance id
1592       * @param int $userid user id
1593       * @return array of warnings and the user plan
1594       * @since Moodle 3.4
1595       * @throws  moodle_exception
1596       */
1597      public static function get_grades($workshopid, $userid = 0) {
1598          global $USER;
1599  
1600          $params = array(
1601              'workshopid' => $workshopid,
1602              'userid' => $userid,
1603          );
1604          $params = self::validate_parameters(self::get_grades_parameters(), $params);
1605          $warnings = array();
1606  
1607          list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
1608  
1609          // Extra checks so only users with permissions can view other users plans.
1610          if (empty($params['userid']) || $params['userid'] == $USER->id) {
1611              $userid = $USER->id;
1612          } else {
1613              require_capability('mod/workshop:viewallassessments', $context);
1614              $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1615              core_user::require_active_user($user);
1616              if (!$workshop->check_group_membership($user->id)) {
1617                  throw new moodle_exception('notingroup');
1618              }
1619              $userid = $user->id;
1620          }
1621  
1622          $finalgrades = $workshop->get_gradebook_grades($userid);
1623  
1624          $result = array('warnings' => $warnings);
1625          if ($finalgrades !== false) {
1626              if (!empty($finalgrades->submissiongrade)) {
1627                  if (is_numeric($finalgrades->submissiongrade->grade)) {
1628                      $result['submissionrawgrade'] = $finalgrades->submissiongrade->grade;
1629                  }
1630                  $result['submissionlongstrgrade'] = $finalgrades->submissiongrade->str_long_grade;
1631                  $result['submissiongradehidden'] = $finalgrades->submissiongrade->hidden;
1632              }
1633              if (!empty($finalgrades->assessmentgrade)) {
1634                  if (is_numeric($finalgrades->assessmentgrade->grade)) {
1635                      $result['assessmentrawgrade'] = $finalgrades->assessmentgrade->grade;
1636                  }
1637                  $result['assessmentlongstrgrade'] = $finalgrades->assessmentgrade->str_long_grade;
1638                  $result['assessmentgradehidden'] = $finalgrades->assessmentgrade->hidden;
1639              }
1640          }
1641  
1642          return $result;
1643      }
1644  
1645      /**
1646       * Returns description of method result value.
1647       *
1648       * @return external_single_structure
1649       * @since Moodle 3.4
1650       */
1651      public static function get_grades_returns() {
1652          return new external_single_structure(
1653              array(
1654                  'assessmentrawgrade' => new external_value(PARAM_FLOAT, 'The assessment raw (numeric) grade.', VALUE_OPTIONAL),
1655                  'assessmentlongstrgrade' => new external_value(PARAM_NOTAGS, 'The assessment string grade.', VALUE_OPTIONAL),
1656                  'assessmentgradehidden' => new external_value(PARAM_BOOL, 'Whether the grade is hidden or not.', VALUE_OPTIONAL),
1657                  'submissionrawgrade' => new external_value(PARAM_FLOAT, 'The submission raw (numeric) grade.', VALUE_OPTIONAL),
1658                  'submissionlongstrgrade' => new external_value(PARAM_NOTAGS, 'The submission string grade.', VALUE_OPTIONAL),
1659                  'submissiongradehidden' => new external_value(PARAM_BOOL, 'Whether the grade is hidden or not.', VALUE_OPTIONAL),
1660                  'warnings' => new external_warnings(),
1661              )
1662          );
1663      }
1664  
1665      /**
1666       * Returns the description of the external function parameters.
1667       *
1668       * @return external_function_parameters
1669       * @since Moodle 3.4
1670       */
1671      public static function evaluate_assessment_parameters() {
1672          return new external_function_parameters(
1673              array(
1674                  'assessmentid' => new external_value(PARAM_INT, 'Assessment id.'),
1675                  'feedbacktext' => new external_value(PARAM_RAW, 'The feedback for the reviewer.', VALUE_DEFAULT, ''),
1676                  'feedbackformat' => new external_value(PARAM_INT, 'The feedback format for text.', VALUE_DEFAULT, FORMAT_MOODLE),
1677                  'weight' => new external_value(PARAM_INT, 'The new weight for the assessment.', VALUE_DEFAULT, 1),
1678                  'gradinggradeover' => new external_value(PARAM_ALPHANUMEXT, 'The new grading grade.', VALUE_DEFAULT, ''),
1679              )
1680          );
1681      }
1682  
1683  
1684      /**
1685       * Evaluates an assessment (used by teachers for provide feedback to the reviewer).
1686       *
1687       * @param int $assessmentid the assessment id
1688       * @param str $feedbacktext the feedback for the reviewer
1689       * @param int $feedbackformat the feedback format for the reviewer text
1690       * @param int $weight the new weight for the assessment
1691       * @param mixed $gradinggradeover the new grading grade (empty for no overriding the grade)
1692       * @return array containing the status and warnings.
1693       * @since Moodle 3.4
1694       * @throws moodle_exception
1695       */
1696      public static function evaluate_assessment($assessmentid, $feedbacktext = '', $feedbackformat = FORMAT_MOODLE, $weight = 1,
1697              $gradinggradeover = '') {
1698          global $DB;
1699  
1700          $params = self::validate_parameters(
1701              self::evaluate_assessment_parameters(),
1702              array(
1703                  'assessmentid' => $assessmentid,
1704                  'feedbacktext' => $feedbacktext,
1705                  'feedbackformat' => $feedbackformat,
1706                  'weight' => $weight,
1707                  'gradinggradeover' => $gradinggradeover,
1708              )
1709          );
1710          $warnings = array();
1711  
1712          // Get and validate the assessment, submission and workshop.
1713          $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
1714          $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
1715          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1716  
1717          // Check we can evaluate the assessment.
1718          $workshop->check_view_assessment($assessment, $submission);
1719          $cansetassessmentweight = has_capability('mod/workshop:allocate', $context);
1720          $canoverridegrades      = has_capability('mod/workshop:overridegrades', $context);
1721          if (!$canoverridegrades && !$cansetassessmentweight) {
1722              throw new moodle_exception('nopermissions', 'error', '', 'evaluate assessments');
1723          }
1724  
1725          // Process data.
1726          $data = new stdClass;
1727          $data->asid = $assessment->id;
1728          $data->feedbackreviewer_editor = array(
1729              'text' => $params['feedbacktext'],
1730              'format' => $params['feedbackformat'],
1731          );
1732          $data->weight = $params['weight'];
1733          $data->gradinggradeover = $params['gradinggradeover'];
1734  
1735          $options = array(
1736              'editable' => true,
1737              'editableweight' => $cansetassessmentweight,
1738              'overridablegradinggrade' => $canoverridegrades
1739          );
1740          $feedbackform = $workshop->get_feedbackreviewer_form(null, $assessment, $options);
1741  
1742          $errors = $feedbackform->validation((array) $data, array());
1743          // Extra checks for the new grade and weight.
1744          $possibleweights = workshop::available_assessment_weights_list();
1745          if ($data->weight < 0 || $data->weight > max(array_keys($possibleweights))) {
1746              $errors['weight'] = 'The new weight must be higher or equal to 0 and cannot be higher than the maximum weight for
1747                  assessment.';
1748          }
1749          if (is_numeric($data->gradinggradeover) &&
1750                  ($data->gradinggradeover < 0 || $data->gradinggradeover > $workshop->gradinggrade)) {
1751              $errors['gradinggradeover'] = 'The new grade must be higher or equal to 0 and cannot be higher than the maximum grade
1752                  for assessment.';
1753          }
1754  
1755          // We can get several errors, return them in warnings.
1756          if (!empty($errors)) {
1757              $status = false;
1758              foreach ($errors as $itemname => $message) {
1759                  $warnings[] = array(
1760                      'item' => $itemname,
1761                      'itemid' => 0,
1762                      'warningcode' => 'fielderror',
1763                      'message' => s($message)
1764                  );
1765              }
1766          } else {
1767              $workshop->evaluate_assessment($assessment, $data, $cansetassessmentweight, $canoverridegrades);
1768              $status = true;
1769          }
1770  
1771          return array(
1772              'status' => $status,
1773              'warnings' => $warnings,
1774          );
1775      }
1776  
1777      /**
1778       * Returns description of method result value
1779       *
1780       * @return \core_external\external_description
1781       * @since Moodle 3.4
1782       */
1783      public static function evaluate_assessment_returns() {
1784          return new external_single_structure(
1785              array(
1786                  'status' => new external_value(PARAM_BOOL, 'status: true if the assessment was evaluated, false otherwise.'),
1787                  'warnings' => new external_warnings()
1788              )
1789          );
1790      }
1791  
1792      /**
1793       * Returns description of method parameters
1794       *
1795       * @return external_function_parameters
1796       * @since Moodle 3.4
1797       */
1798      public static function get_grades_report_parameters() {
1799          return new external_function_parameters(
1800              array(
1801                  'workshopid' => new external_value(PARAM_INT, 'Workshop instance id.'),
1802                  'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group.',
1803                                                     VALUE_DEFAULT, 0),
1804                  'sortby' => new external_value(PARAM_ALPHA, 'sort by this element: lastname, firstname, submissiontitle,
1805                      submissionmodified, submissiongrade, gradinggrade.', VALUE_DEFAULT, 'lastname'),
1806                  'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'ASC'),
1807                  'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
1808                  'perpage' => new external_value(PARAM_INT, 'The number of records to return per page.', VALUE_DEFAULT, 0),
1809              )
1810          );
1811      }
1812  
1813      /**
1814       * Retrieves the assessment grades report.
1815       *
1816       * @param int $workshopid       the workshop instance id
1817       * @param int $groupid          (optional) group id, 0 means that the function will determine the user group
1818       * @param string $sortby        sort by this element
1819       * @param string $sortdirection sort direction: ASC or DESC
1820       * @param int $page             page of records to return
1821       * @param int $perpage          number of records to return per page
1822       * @return array of warnings and the report data
1823       * @since Moodle 3.4
1824       * @throws moodle_exception
1825       */
1826      public static function get_grades_report($workshopid, $groupid = 0, $sortby = 'lastname', $sortdirection = 'ASC',
1827              $page = 0, $perpage = 0) {
1828          global $USER;
1829  
1830          $params = array('workshopid' => $workshopid, 'groupid' => $groupid, 'sortby' => $sortby, 'sortdirection' => $sortdirection,
1831              'page' => $page, 'perpage' => $perpage);
1832          $params = self::validate_parameters(self::get_grades_report_parameters(), $params);
1833          $submissions = $warnings = array();
1834  
1835          $sortallowedvalues = array('lastname', 'firstname', 'submissiontitle', 'submissionmodified', 'submissiongrade',
1836              'gradinggrade');
1837          if (!in_array($params['sortby'], $sortallowedvalues)) {
1838              throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
1839                  'allowed values are: ' . implode(',', $sortallowedvalues));
1840          }
1841  
1842          $sortdirection = strtoupper($params['sortdirection']);
1843          $directionallowedvalues = array('ASC', 'DESC');
1844          if (!in_array($sortdirection, $directionallowedvalues)) {
1845              throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
1846                  'allowed values are: ' . implode(',', $directionallowedvalues));
1847          }
1848  
1849          list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']);
1850          require_capability('mod/workshop:viewallassessments', $context);
1851  
1852          if (!empty($params['groupid'])) {
1853              $groupid = $params['groupid'];
1854              // Determine is the group is visible to user.
1855              if (!groups_group_visible($groupid, $course, $cm)) {
1856                  throw new moodle_exception('notingroup');
1857              }
1858          } else {
1859              // Check to see if groups are being used here.
1860              if ($groupmode = groups_get_activity_groupmode($cm)) {
1861                  $groupid = groups_get_activity_group($cm);
1862                  // Determine is the group is visible to user (this is particullary for the group 0 -> all groups).
1863                  if (!groups_group_visible($groupid, $course, $cm)) {
1864                      throw new moodle_exception('notingroup');
1865                  }
1866              } else {
1867                  $groupid = 0;
1868              }
1869          }
1870  
1871          if ($workshop->phase >= workshop::PHASE_SUBMISSION) {
1872              $showauthornames = has_capability('mod/workshop:viewauthornames', $context);
1873              $showreviewernames = has_capability('mod/workshop:viewreviewernames', $context);
1874  
1875              if ($workshop->phase >= workshop::PHASE_EVALUATION) {
1876                  $showsubmissiongrade = true;
1877                  $showgradinggrade = true;
1878              } else {
1879                  $showsubmissiongrade = false;
1880                  $showgradinggrade = false;
1881              }
1882  
1883              $data = $workshop->prepare_grading_report_data($USER->id, $groupid, $params['page'], $params['perpage'],
1884                  $params['sortby'], $sortdirection);
1885  
1886              if (!empty($data)) {
1887                  // Populate the display options for the submissions report.
1888                  $reportopts                      = new stdclass();
1889                  $reportopts->showauthornames     = $showauthornames;
1890                  $reportopts->showreviewernames   = $showreviewernames;
1891                  $reportopts->sortby              = $params['sortby'];
1892                  $reportopts->sorthow             = $sortdirection;
1893                  $reportopts->showsubmissiongrade = $showsubmissiongrade;
1894                  $reportopts->showgradinggrade    = $showgradinggrade;
1895                  $reportopts->workshopphase       = $workshop->phase;
1896  
1897                  $report = new workshop_grading_report($data, $reportopts);
1898                  return array(
1899                      'report' => $report->export_data_for_external(),
1900                      'warnings' => array(),
1901                  );
1902              }
1903          }
1904          throw new moodle_exception('nothingfound', 'workshop');
1905      }
1906  
1907      /**
1908       * Returns description of method result value
1909       *
1910       * @return \core_external\external_description
1911       * @since Moodle 3.4
1912       */
1913      public static function get_grades_report_returns() {
1914  
1915          $reviewstructure = new external_single_structure(
1916              array(
1917                  'userid' => new external_value(PARAM_INT, 'The id of the user (0 when is configured to do not display names).'),
1918                  'assessmentid' => new external_value(PARAM_INT, 'The id of the assessment.'),
1919                  'submissionid' => new external_value(PARAM_INT, 'The id of the submission assessed.'),
1920                  'grade' => new external_value(PARAM_FLOAT, 'The grade for submission.'),
1921                  'gradinggrade' => new external_value(PARAM_FLOAT, 'The grade for assessment.'),
1922                  'gradinggradeover' => new external_value(PARAM_FLOAT, 'The aggregated grade overrided.'),
1923                  'weight' => new external_value(PARAM_INT, 'The weight of the assessment for aggregation.'),
1924              )
1925          );
1926  
1927          return new external_single_structure(
1928              array(
1929                  'report' => new external_single_structure(
1930                      array(
1931                          'grades' => new external_multiple_structure(
1932                              new external_single_structure(
1933                                  array(
1934                                      'userid' => new external_value(PARAM_INT, 'The id of the user being displayed in the report.'),
1935                                      'submissionid' => new external_value(PARAM_INT, 'Submission id.'),
1936                                      'submissiontitle' => new external_value(PARAM_RAW, 'Submission title.'),
1937                                      'submissionmodified' => new external_value(PARAM_INT, 'Timestamp submission was updated.'),
1938                                      'submissiongrade' => new external_value(PARAM_FLOAT, 'Aggregated grade for the submission.',
1939                                          VALUE_OPTIONAL),
1940                                      'gradinggrade' => new external_value(PARAM_FLOAT, 'Computed grade for the assessment.',
1941                                          VALUE_OPTIONAL),
1942                                      'submissiongradeover' => new external_value(PARAM_FLOAT, 'Grade for the assessment overrided
1943                                          by the teacher.', VALUE_OPTIONAL),
1944                                      'submissiongradeoverby' => new external_value(PARAM_INT, 'The id of the user who overrided
1945                                          the grade.', VALUE_OPTIONAL),
1946                                      'submissionpublished' => new external_value(PARAM_INT, 'Whether is a submission published.',
1947                                          VALUE_OPTIONAL),
1948                                      'reviewedby' => new external_multiple_structure($reviewstructure, 'The users who reviewed the
1949                                          user submission.', VALUE_OPTIONAL),
1950                                      'reviewerof' => new external_multiple_structure($reviewstructure, 'The assessments the user
1951                                          reviewed.', VALUE_OPTIONAL),
1952                                  )
1953                              )
1954                          ),
1955                          'totalcount' => new external_value(PARAM_INT, 'Number of total submissions.'),
1956                      )
1957                  ),
1958                  'warnings' => new external_warnings()
1959              )
1960          );
1961      }
1962  
1963      /**
1964       * Describes the parameters for view_submission.
1965       *
1966       * @return external_function_parameters
1967       * @since Moodle 3.4
1968       */
1969      public static function view_submission_parameters() {
1970          return new external_function_parameters (
1971              array(
1972                  'submissionid' => new external_value(PARAM_INT, 'Submission id'),
1973              )
1974          );
1975      }
1976  
1977      /**
1978       * Trigger the submission viewed event.
1979       *
1980       * @param int $submissionid submission id
1981       * @return array of warnings and status result
1982       * @since Moodle 3.4
1983       * @throws moodle_exception
1984       */
1985      public static function view_submission($submissionid) {
1986          global $DB;
1987  
1988          $params = self::validate_parameters(self::view_submission_parameters(), array('submissionid' => $submissionid));
1989          $warnings = array();
1990  
1991          // Get and validate the submission and workshop.
1992          $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
1993          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
1994  
1995          self::validate_submission($submission, $workshop);
1996  
1997          $workshop->set_submission_viewed($submission);
1998  
1999          $result = array(
2000              'status' => true,
2001              'warnings' => $warnings,
2002          );
2003          return $result;
2004      }
2005  
2006      /**
2007       * Describes the view_submission return value.
2008       *
2009       * @return external_single_structure
2010       * @since Moodle 3.4
2011       */
2012      public static function view_submission_returns() {
2013          return new external_single_structure(
2014              array(
2015                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2016                  'warnings' => new external_warnings(),
2017              )
2018          );
2019      }
2020  
2021      /**
2022       * Returns the description of the external function parameters.
2023       *
2024       * @return external_function_parameters
2025       * @since Moodle 3.4
2026       */
2027      public static function evaluate_submission_parameters() {
2028          return new external_function_parameters(
2029              array(
2030                  'submissionid' => new external_value(PARAM_INT, 'submission id.'),
2031                  'feedbacktext' => new external_value(PARAM_RAW, 'The feedback for the author.', VALUE_DEFAULT, ''),
2032                  'feedbackformat' => new external_value(PARAM_INT, 'The feedback format for text.', VALUE_DEFAULT, FORMAT_MOODLE),
2033                  'published' => new external_value(PARAM_BOOL, 'Publish the submission for others?.', VALUE_DEFAULT, false),
2034                  'gradeover' => new external_value(PARAM_ALPHANUMEXT, 'The new submission grade.', VALUE_DEFAULT, ''),
2035              )
2036          );
2037      }
2038  
2039  
2040      /**
2041       * Evaluates a submission (used by teachers for provide feedback or override the submission grade).
2042       *
2043       * @param int $submissionid the submission id
2044       * @param str $feedbacktext the feedback for the author
2045       * @param int $feedbackformat the feedback format for the reviewer text
2046       * @param bool $published whether to publish the submission for other users
2047       * @param mixed $gradeover the new submission grade (empty for no overriding the grade)
2048       * @return array containing the status and warnings.
2049       * @since Moodle 3.4
2050       * @throws moodle_exception
2051       */
2052      public static function evaluate_submission($submissionid, $feedbacktext = '', $feedbackformat = FORMAT_MOODLE, $published = 1,
2053              $gradeover = '') {
2054          global $DB;
2055  
2056          $params = self::validate_parameters(
2057              self::evaluate_submission_parameters(),
2058              array(
2059                  'submissionid' => $submissionid,
2060                  'feedbacktext' => $feedbacktext,
2061                  'feedbackformat' => $feedbackformat,
2062                  'published' => $published,
2063                  'gradeover' => $gradeover,
2064              )
2065          );
2066          $warnings = array();
2067  
2068          // Get and validate the submission, submission and workshop.
2069          $submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
2070          list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
2071  
2072          // Check we can evaluate the submission.
2073          self::validate_submission($submission, $workshop);
2074          $canpublish  = has_capability('mod/workshop:publishsubmissions', $context);
2075          $canoverride = ($workshop->phase == workshop::PHASE_EVALUATION &&
2076              has_capability('mod/workshop:overridegrades', $context));
2077  
2078          if (!$canpublish && !$canoverride) {
2079              throw new moodle_exception('nopermissions', 'error', '', 'evaluate submission');
2080          }
2081  
2082          // Process data.
2083          $data = new stdClass;
2084          $data->id = $submission->id;
2085          $data->feedbackauthor_editor = array(
2086              'text' => $params['feedbacktext'],
2087              'format' => $params['feedbackformat'],
2088          );
2089          $data->published = $params['published'];
2090          $data->gradeover = $params['gradeover'];
2091  
2092          $options = array(
2093              'editable' => true,
2094              'editablepublished' => $canpublish,
2095              'overridablegrade' => $canoverride
2096          );
2097          $feedbackform = $workshop->get_feedbackauthor_form(null, $submission, $options);
2098  
2099          $errors = $feedbackform->validation((array) $data, array());
2100          // Extra checks for the new grade (if set).
2101          if (is_numeric($data->gradeover) && $data->gradeover > $workshop->grade) {
2102              $errors['gradeover'] = 'The new grade cannot be higher than the maximum grade for submission.';
2103          }
2104  
2105          // We can get several errors, return them in warnings.
2106          if (!empty($errors)) {
2107              $status = false;
2108              foreach ($errors as $itemname => $message) {
2109                  $warnings[] = array(
2110                      'item' => $itemname,
2111                      'itemid' => 0,
2112                      'warningcode' => 'fielderror',
2113                      'message' => s($message)
2114                  );
2115              }
2116          } else {
2117              $workshop->evaluate_submission($submission, $data, $canpublish, $canoverride);
2118              $status = true;
2119          }
2120  
2121          return array(
2122              'status' => $status,
2123              'warnings' => $warnings,
2124          );
2125      }
2126  
2127      /**
2128       * Returns description of method result value
2129       *
2130       * @return \core_external\external_description
2131       * @since Moodle 3.4
2132       */
2133      public static function evaluate_submission_returns() {
2134          return new external_single_structure(
2135              array(
2136                  'status' => new external_value(PARAM_BOOL, 'status: true if the submission was evaluated, false otherwise.'),
2137                  'warnings' => new external_warnings()
2138              )
2139          );
2140      }
2141  }