Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
  • Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 37 and 311] [Versions 38 and 311] [Versions 39 and 311]

       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   * External assign API
      19   *
      20   * @package    mod_assign
      21   * @since      Moodle 2.4
      22   * @copyright  2012 Paul Charsley
      23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      24   */
      25  
      26  defined('MOODLE_INTERNAL') || die;
      27  
      28  require_once("$CFG->libdir/externallib.php");
      29  require_once("$CFG->dirroot/user/externallib.php");
      30  require_once("$CFG->dirroot/mod/assign/locallib.php");
      31  
      32  /**
      33   * Assign functions
      34   * @copyright 2012 Paul Charsley
      35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      36   */
      37  class mod_assign_external extends external_api {
      38  
      39      /**
      40       * Generate a warning in a standard structure for a known failure.
      41       *
      42       * @param int $assignmentid - The assignment
      43       * @param string $warningcode - The key for the warning message
      44       * @param string $detail - A description of the error
      45       * @return array - Warning structure containing item, itemid, warningcode, message
      46       */
      47      private static function generate_warning($assignmentid, $warningcode, $detail) {
      48          $warningmessages = array(
      49              'couldnotlock'=>'Could not lock the submission for this user.',
      50              'couldnotunlock'=>'Could not unlock the submission for this user.',
      51              'couldnotsubmitforgrading'=>'Could not submit assignment for grading.',
      52              'couldnotrevealidentities'=>'Could not reveal identities.',
      53              'couldnotgrantextensions'=>'Could not grant submission date extensions.',
      54              'couldnotrevert'=>'Could not revert submission to draft.',
      55              'invalidparameters'=>'Invalid parameters.',
      56              'couldnotsavesubmission'=>'Could not save submission.',
      57              'couldnotsavegrade'=>'Could not save grade.'
      58          );
      59  
      60          $message = $warningmessages[$warningcode];
      61          if (empty($message)) {
      62              $message = 'Unknown warning type.';
      63          }
      64  
      65          return array('item' => s($detail),
      66                       'itemid'=>$assignmentid,
      67                       'warningcode'=>$warningcode,
      68                       'message'=>$message);
      69      }
      70  
      71      /**
      72       * Describes the parameters for get_grades
      73       * @return external_function_parameters
      74       * @since  Moodle 2.4
      75       */
      76      public static function get_grades_parameters() {
      77          return new external_function_parameters(
      78              array(
      79                  'assignmentids' => new external_multiple_structure(
      80                      new external_value(PARAM_INT, 'assignment id'),
      81                      '1 or more assignment ids',
      82                      VALUE_REQUIRED),
      83                  'since' => new external_value(PARAM_INT,
      84                            'timestamp, only return records where timemodified >= since',
      85                            VALUE_DEFAULT, 0)
      86              )
      87          );
      88      }
      89  
      90      /**
      91       * Returns grade information from assign_grades for the requested assignment ids
      92       * @param int[] $assignmentids
      93       * @param int $since only return records with timemodified >= since
      94       * @return array of grade records for each requested assignment
      95       * @since  Moodle 2.4
      96       */
      97      public static function get_grades($assignmentids, $since = 0) {
      98          global $DB;
      99          $params = self::validate_parameters(self::get_grades_parameters(),
     100                          array('assignmentids' => $assignmentids,
     101                                'since' => $since));
     102  
     103          $assignments = array();
     104          $warnings = array();
     105          $requestedassignmentids = $params['assignmentids'];
     106  
     107          // Check the user is allowed to get the grades for the assignments requested.
     108          $placeholders = array();
     109          list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
     110          $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
     111                 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
     112          $placeholders['modname'] = 'assign';
     113          $cms = $DB->get_records_sql($sql, $placeholders);
     114          foreach ($cms as $cm) {
     115              try {
     116                  $context = context_module::instance($cm->id);
     117                  self::validate_context($context);
     118                  $assign = new assign($context, null, null);
     119                  $assign->require_view_grades();
     120              } catch (Exception $e) {
     121                  $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
     122                  $warning = array();
     123                  $warning['item'] = 'assignment';
     124                  $warning['itemid'] = $cm->instance;
     125                  $warning['warningcode'] = '1';
     126                  $warning['message'] = 'No access rights in module context';
     127                  $warnings[] = $warning;
     128              }
     129          }
     130  
     131          // Create the query and populate an array of grade records from the recordset results.
     132          if (count ($requestedassignmentids) > 0) {
     133              $placeholders = array();
     134              list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
     135  
     136              $sql = "SELECT ag.id,
     137                             ag.assignment,
     138                             ag.userid,
     139                             ag.timecreated,
     140                             ag.timemodified,
     141                             ag.grader,
     142                             ag.grade,
     143                             ag.attemptnumber
     144                        FROM {assign_grades} ag, {assign_submission} s
     145                       WHERE s.assignment $inorequalsql
     146                         AND s.userid = ag.userid
     147                         AND s.latest = 1
     148                         AND s.attemptnumber = ag.attemptnumber
     149                         AND ag.timemodified  >= :since
     150                         AND ag.assignment = s.assignment
     151                    ORDER BY ag.assignment, ag.id";
     152  
     153              $placeholders['since'] = $params['since'];
     154              $rs = $DB->get_recordset_sql($sql, $placeholders);
     155              $currentassignmentid = null;
     156              $assignment = null;
     157              foreach ($rs as $rd) {
     158                  $grade = array();
     159                  $grade['id'] = $rd->id;
     160                  $grade['userid'] = $rd->userid;
     161                  $grade['timecreated'] = $rd->timecreated;
     162                  $grade['timemodified'] = $rd->timemodified;
     163                  $grade['grader'] = $rd->grader;
     164                  $grade['attemptnumber'] = $rd->attemptnumber;
     165                  $grade['grade'] = (string)$rd->grade;
     166  
     167                  if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
     168                      if (!is_null($assignment)) {
     169                          $assignments[] = $assignment;
     170                      }
     171                      $assignment = array();
     172                      $assignment['assignmentid'] = $rd->assignment;
     173                      $assignment['grades'] = array();
     174                      $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
     175                  }
     176                  $assignment['grades'][] = $grade;
     177  
     178                  $currentassignmentid = $rd->assignment;
     179              }
     180              if (!is_null($assignment)) {
     181                  $assignments[] = $assignment;
     182              }
     183              $rs->close();
     184          }
     185          foreach ($requestedassignmentids as $assignmentid) {
     186              $warning = array();
     187              $warning['item'] = 'assignment';
     188              $warning['itemid'] = $assignmentid;
     189              $warning['warningcode'] = '3';
     190              $warning['message'] = 'No grades found';
     191              $warnings[] = $warning;
     192          }
     193  
     194          $result = array();
     195          $result['assignments'] = $assignments;
     196          $result['warnings'] = $warnings;
     197          return $result;
     198      }
     199  
     200      /**
     201       * Creates a grade single structure.
     202       *
     203       * @return external_single_structure a grade single structure.
     204       * @since  Moodle 3.1
     205       */
     206      private static function get_grade_structure($required = VALUE_REQUIRED) {
     207          return new external_single_structure(
     208              array(
     209                  'id'                => new external_value(PARAM_INT, 'grade id'),
     210                  'assignment'        => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
     211                  'userid'            => new external_value(PARAM_INT, 'student id'),
     212                  'attemptnumber'     => new external_value(PARAM_INT, 'attempt number'),
     213                  'timecreated'       => new external_value(PARAM_INT, 'grade creation time'),
     214                  'timemodified'      => new external_value(PARAM_INT, 'grade last modified time'),
     215                  'grader'            => new external_value(PARAM_INT, 'grader, -1 if grader is hidden'),
     216                  'grade'             => new external_value(PARAM_TEXT, 'grade'),
     217                  'gradefordisplay'   => new external_value(PARAM_RAW, 'grade rendered into a format suitable for display',
     218                                                              VALUE_OPTIONAL),
     219              ), 'grade information', $required
     220          );
     221      }
     222  
     223      /**
     224       * Creates an assign_grades external_single_structure
     225       * @return external_single_structure
     226       * @since  Moodle 2.4
     227       */
     228      private static function assign_grades() {
     229          return new external_single_structure(
     230              array (
     231                  'assignmentid'  => new external_value(PARAM_INT, 'assignment id'),
     232                  'grades'        => new external_multiple_structure(self::get_grade_structure())
     233              )
     234          );
     235      }
     236  
     237      /**
     238       * Describes the get_grades return value
     239       * @return external_single_structure
     240       * @since  Moodle 2.4
     241       */
     242      public static function get_grades_returns() {
     243          return new external_single_structure(
     244              array(
     245                  'assignments' => new external_multiple_structure(self::assign_grades(), 'list of assignment grade information'),
     246                  'warnings'      => new external_warnings('item is always \'assignment\'',
     247                      'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
     248                      'errorcode can be 3 (no grades found) or 1 (no permission to get grades)')
     249              )
     250          );
     251      }
     252  
     253      /**
     254       * Returns description of method parameters
     255       *
     256       * @return external_function_parameters
     257       * @since  Moodle 2.4
     258       */
     259      public static function get_assignments_parameters() {
     260          return new external_function_parameters(
     261              array(
     262                  'courseids' => new external_multiple_structure(
     263                      new external_value(PARAM_INT, 'course id, empty for retrieving all the courses where the user is enroled in'),
     264                      '0 or more course ids',
     265                      VALUE_DEFAULT, array()
     266                  ),
     267                  'capabilities'  => new external_multiple_structure(
     268                      new external_value(PARAM_CAPABILITY, 'capability'),
     269                      'list of capabilities used to filter courses',
     270                      VALUE_DEFAULT, array()
     271                  ),
     272                  'includenotenrolledcourses' => new external_value(PARAM_BOOL, 'whether to return courses that the user can see
     273                                                                      even if is not enroled in. This requires the parameter courseids
     274                                                                      to not be empty.', VALUE_DEFAULT, false)
     275              )
     276          );
     277      }
     278  
     279      /**
     280       * Returns an array of courses the user is enrolled, and for each course all of the assignments that the user can
     281       * view within that course.
     282       *
     283       * @param array $courseids An optional array of course ids. If provided only assignments within the given course
     284       * will be returned. If the user is not enrolled in or can't view a given course a warning will be generated and returned.
     285       * @param array $capabilities An array of additional capability checks you wish to be made on the course context.
     286       * @param bool $includenotenrolledcourses Wheter to return courses that the user can see even if is not enroled in.
     287       * This requires the parameter $courseids to not be empty.
     288       * @return An array of courses and warnings.
     289       * @since  Moodle 2.4
     290       */
     291      public static function get_assignments($courseids = array(), $capabilities = array(), $includenotenrolledcourses = false) {
     292          global $USER, $DB, $CFG;
     293  
     294          $params = self::validate_parameters(
     295              self::get_assignments_parameters(),
     296              array(
     297                  'courseids' => $courseids,
     298                  'capabilities' => $capabilities,
     299                  'includenotenrolledcourses' => $includenotenrolledcourses
     300              )
     301          );
     302  
     303          $warnings = array();
     304          $courses = array();
     305          $fields = 'sortorder,shortname,fullname,timemodified';
     306  
     307          // If the courseids list is empty, we return only the courses where the user is enrolled in.
     308          if (empty($params['courseids'])) {
     309              $courses = enrol_get_users_courses($USER->id, true, $fields);
     310              $courseids = array_keys($courses);
     311          } else if ($includenotenrolledcourses) {
     312              // In this case, we don't have to check here for enrolmnents. Maybe the user can see the course even if is not enrolled.
     313              $courseids = $params['courseids'];
     314          } else {
     315              // We need to check for enrolments.
     316              $mycourses = enrol_get_users_courses($USER->id, true, $fields);
     317              $mycourseids = array_keys($mycourses);
     318  
     319              foreach ($params['courseids'] as $courseid) {
     320                  if (!in_array($courseid, $mycourseids)) {
     321                      unset($courses[$courseid]);
     322                      $warnings[] = array(
     323                          'item' => 'course',
     324                          'itemid' => $courseid,
     325                          'warningcode' => '2',
     326                          'message' => 'User is not enrolled or does not have requested capability'
     327                      );
     328                  } else {
     329                      $courses[$courseid] = $mycourses[$courseid];
     330                  }
     331              }
     332              $courseids = array_keys($courses);
     333          }
     334  
     335          foreach ($courseids as $cid) {
     336  
     337              try {
     338                  $context = context_course::instance($cid);
     339                  self::validate_context($context);
     340  
     341                  // Check if this course was already loaded (by enrol_get_users_courses).
     342                  if (!isset($courses[$cid])) {
     343                      $courses[$cid] = get_course($cid);
     344                  }
     345                  $courses[$cid]->contextid = $context->id;
     346              } catch (Exception $e) {
     347                  unset($courses[$cid]);
     348                  $warnings[] = array(
     349                      'item' => 'course',
     350                      'itemid' => $cid,
     351                      'warningcode' => '1',
     352                      'message' => 'No access rights in course context '.$e->getMessage()
     353                  );
     354                  continue;
     355              }
     356              if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) {
     357                  unset($courses[$cid]);
     358              }
     359          }
     360          $extrafields='m.id as assignmentid, ' .
     361                       'm.course, ' .
     362                       'm.nosubmissions, ' .
     363                       'm.submissiondrafts, ' .
     364                       'm.sendnotifications, '.
     365                       'm.sendlatenotifications, ' .
     366                       'm.sendstudentnotifications, ' .
     367                       'm.duedate, ' .
     368                       'm.allowsubmissionsfromdate, '.
     369                       'm.grade, ' .
     370                       'm.timemodified, '.
     371                       'm.completionsubmit, ' .
     372                       'm.cutoffdate, ' .
     373                       'm.gradingduedate, ' .
     374                       'm.teamsubmission, ' .
     375                       'm.requireallteammemberssubmit, '.
     376                       'm.teamsubmissiongroupingid, ' .
     377                       'm.blindmarking, ' .
     378                       'm.hidegrader, ' .
     379                       'm.revealidentities, ' .
     380                       'm.attemptreopenmethod, '.
     381                       'm.maxattempts, ' .
     382                       'm.markingworkflow, ' .
     383                       'm.markingallocation, ' .
     384                       'm.requiresubmissionstatement, '.
     385                       'm.preventsubmissionnotingroup, '.
     386                       'm.intro, '.
     387                       'm.introformat';
     388          $coursearray = array();
     389          foreach ($courses as $id => $course) {
     390              $assignmentarray = array();
     391              // Get a list of assignments for the course.
     392              if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) {
     393                  foreach ($modules as $module) {
     394                      $context = context_module::instance($module->id);
     395                      try {
     396                          self::validate_context($context);
     397                          require_capability('mod/assign:view', $context);
     398                      } catch (Exception $e) {
     399                          $warnings[] = array(
     400                              'item' => 'module',
     401                              'itemid' => $module->id,
     402                              'warningcode' => '1',
     403                              'message' => 'No access rights in module context'
     404                          );
     405                          continue;
     406                      }
     407  
     408                      $assign = new assign($context, null, null);
     409                      // Update assign with override information.
     410                      $assign->update_effective_access($USER->id);
     411  
     412                      // Get configurations for only enabled plugins.
     413                      $plugins = $assign->get_submission_plugins();
     414                      $plugins = array_merge($plugins, $assign->get_feedback_plugins());
     415  
     416                      $configarray = array();
     417                      foreach ($plugins as $plugin) {
     418                          if ($plugin->is_enabled() && $plugin->is_visible()) {
     419                              $configrecords = $plugin->get_config_for_external();
     420                              foreach ($configrecords as $name => $value) {
     421                                  $configarray[] = array(
     422                                      'plugin' => $plugin->get_type(),
     423                                      'subtype' => $plugin->get_subtype(),
     424                                      'name' => $name,
     425                                      'value' => $value
     426                                  );
     427                              }
     428                          }
     429                      }
     430  
     431                      $assignment = array(
     432                          'id' => $module->assignmentid,
     433                          'cmid' => $module->id,
     434                          'course' => $module->course,
     435                          'name' => external_format_string($module->name, $context),
     436                          'nosubmissions' => $module->nosubmissions,
     437                          'submissiondrafts' => $module->submissiondrafts,
     438                          'sendnotifications' => $module->sendnotifications,
     439                          'sendlatenotifications' => $module->sendlatenotifications,
     440                          'sendstudentnotifications' => $module->sendstudentnotifications,
     441                          'duedate' => $assign->get_instance()->duedate,
     442                          'allowsubmissionsfromdate' => $assign->get_instance()->allowsubmissionsfromdate,
     443                          'grade' => $module->grade,
     444                          'timemodified' => $module->timemodified,
     445                          'completionsubmit' => $module->completionsubmit,
     446                          'cutoffdate' => $assign->get_instance()->cutoffdate,
     447                          'gradingduedate' => $assign->get_instance()->gradingduedate,
     448                          'teamsubmission' => $module->teamsubmission,
     449                          'requireallteammemberssubmit' => $module->requireallteammemberssubmit,
     450                          'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid,
     451                          'blindmarking' => $module->blindmarking,
     452                          'hidegrader' => $module->hidegrader,
     453                          'revealidentities' => $module->revealidentities,
     454                          'attemptreopenmethod' => $module->attemptreopenmethod,
     455                          'maxattempts' => $module->maxattempts,
     456                          'markingworkflow' => $module->markingworkflow,
     457                          'markingallocation' => $module->markingallocation,
     458                          'requiresubmissionstatement' => $module->requiresubmissionstatement,
     459                          'preventsubmissionnotingroup' => $module->preventsubmissionnotingroup,
     460                          'configs' => $configarray
     461                      );
     462  
     463                      // Return or not intro and file attachments depending on the plugin settings.
     464                      if ($assign->show_intro()) {
     465                          $options = array('noclean' => true);
     466                          list($assignment['intro'], $assignment['introformat']) =
     467                              external_format_text($module->intro, $module->introformat, $context->id, 'mod_assign', 'intro', null,
     468                                  $options);
     469                          $assignment['introfiles'] = external_util::get_area_files($context->id, 'mod_assign', 'intro', false,
     470                                                                                      false);
     471  
     472                          $assignment['introattachments'] = external_util::get_area_files($context->id, 'mod_assign',
     473                                                              ASSIGN_INTROATTACHMENT_FILEAREA, 0);
     474                      }
     475  
     476                      if ($module->requiresubmissionstatement) {
     477                          // Submission statement is required, return the submission statement value.
     478                          $adminconfig = get_config('assign');
     479                          // Single submission.
     480                          if (!$module->teamsubmission) {
     481                              list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
     482                                  external_format_text($adminconfig->submissionstatement, FORMAT_MOODLE, $context->id,
     483                                      'mod_assign', '', 0);
     484                          } else { // Team submission.
     485                              // One user can submit for the whole team.
     486                              if (!empty($adminconfig->submissionstatementteamsubmission) && !$module->requireallteammemberssubmit) {
     487                                  list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
     488                                      external_format_text($adminconfig->submissionstatementteamsubmission,
     489                                          FORMAT_MOODLE, $context->id, 'mod_assign', '', 0);
     490                              } else if (!empty($adminconfig->submissionstatementteamsubmissionallsubmit) &&
     491                                  $module->requireallteammemberssubmit) {
     492                                  // All team members must submit.
     493                                  list($assignment['submissionstatement'], $assignment['submissionstatementformat']) =
     494                                      external_format_text($adminconfig->submissionstatementteamsubmissionallsubmit,
     495                                          FORMAT_MOODLE, $context->id, 'mod_assign', '', 0);
     496                              }
     497                          }
     498                      }
     499  
     500                      $assignmentarray[] = $assignment;
     501                  }
     502              }
     503              $coursearray[]= array(
     504                  'id' => $courses[$id]->id,
     505                  'fullname' => external_format_string($courses[$id]->fullname, $course->contextid),
     506                  'shortname' => external_format_string($courses[$id]->shortname, $course->contextid),
     507                  'timemodified' => $courses[$id]->timemodified,
     508                  'assignments' => $assignmentarray
     509              );
     510          }
     511  
     512          $result = array(
     513              'courses' => $coursearray,
     514              'warnings' => $warnings
     515          );
     516          return $result;
     517      }
     518  
     519      /**
     520       * Creates an assignment external_single_structure
     521       *
     522       * @return external_single_structure
     523       * @since Moodle 2.4
     524       */
     525      private static function get_assignments_assignment_structure() {
     526          return new external_single_structure(
     527              array(
     528                  'id' => new external_value(PARAM_INT, 'assignment id'),
     529                  'cmid' => new external_value(PARAM_INT, 'course module id'),
     530                  'course' => new external_value(PARAM_INT, 'course id'),
     531                  'name' => new external_value(PARAM_RAW, 'assignment name'),
     532                  'nosubmissions' => new external_value(PARAM_INT, 'no submissions'),
     533                  'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'),
     534                  'sendnotifications' => new external_value(PARAM_INT, 'send notifications'),
     535                  'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'),
     536                  'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'),
     537                  'duedate' => new external_value(PARAM_INT, 'assignment due date'),
     538                  'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'),
     539                  'grade' => new external_value(PARAM_INT, 'grade type'),
     540                  'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'),
     541                  'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'),
     542                  'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'),
     543                  'gradingduedate' => new external_value(PARAM_INT, 'the expected date for marking the submissions'),
     544                  'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'),
     545                  'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'),
     546                  'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'),
     547                  'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'),
     548                  'hidegrader' => new external_value(PARAM_INT, 'If enabled, hide grader to student'),
     549                  'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'),
     550                  'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'),
     551                  'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'),
     552                  'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'),
     553                  'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'),
     554                  'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'),
     555                  'preventsubmissionnotingroup' => new external_value(PARAM_INT, 'Prevent submission not in group', VALUE_OPTIONAL),
     556                  'submissionstatement' => new external_value(PARAM_RAW, 'Submission statement formatted.', VALUE_OPTIONAL),
     557                  'submissionstatementformat' => new external_format_value('submissionstatement', VALUE_OPTIONAL),
     558                  'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings'),
     559                  'intro' => new external_value(PARAM_RAW,
     560                      'assignment intro, not allways returned because it deppends on the activity configuration', VALUE_OPTIONAL),
     561                  'introformat' => new external_format_value('intro', VALUE_OPTIONAL),
     562                  'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
     563                  'introattachments' => new external_files('intro attachments files', VALUE_OPTIONAL),
     564              ), 'assignment information object');
     565      }
     566  
     567      /**
     568       * Creates an assign_plugin_config external_single_structure
     569       *
     570       * @return external_single_structure
     571       * @since Moodle 2.4
     572       */
     573      private static function get_assignments_config_structure() {
     574          return new external_single_structure(
     575              array(
     576                  'id' => new external_value(PARAM_INT, 'assign_plugin_config id', VALUE_OPTIONAL),
     577                  'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
     578                  'plugin' => new external_value(PARAM_TEXT, 'plugin'),
     579                  'subtype' => new external_value(PARAM_TEXT, 'subtype'),
     580                  'name' => new external_value(PARAM_TEXT, 'name'),
     581                  'value' => new external_value(PARAM_TEXT, 'value')
     582              ), 'assignment configuration object'
     583          );
     584      }
     585  
     586      /**
     587       * Creates a course external_single_structure
     588       *
     589       * @return external_single_structure
     590       * @since Moodle 2.4
     591       */
     592      private static function get_assignments_course_structure() {
     593          return new external_single_structure(
     594              array(
     595                  'id' => new external_value(PARAM_INT, 'course id'),
     596                  'fullname' => new external_value(PARAM_RAW, 'course full name'),
     597                  'shortname' => new external_value(PARAM_RAW, 'course short name'),
     598                  'timemodified' => new external_value(PARAM_INT, 'last time modified'),
     599                  'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info')
     600                ), 'course information object'
     601          );
     602      }
     603  
     604      /**
     605       * Describes the return value for get_assignments
     606       *
     607       * @return external_single_structure
     608       * @since Moodle 2.4
     609       */
     610      public static function get_assignments_returns() {
     611          return new external_single_structure(
     612              array(
     613                  'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'),
     614                  'warnings'  => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)',
     615                      'When item is a course then itemid is a course id. When the item is a module then itemid is a module id',
     616                      'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)')
     617              )
     618          );
     619      }
     620  
     621      /**
     622       * Return information (files and text fields) for the given plugins in the assignment.
     623       *
     624       * @param  assign $assign the assignment object
     625       * @param  array $assignplugins array of assignment plugins (submission or feedback)
     626       * @param  stdClass $item the item object (submission or grade)
     627       * @return array an array containing the plugins returned information
     628       */
     629      private static function get_plugins_data($assign, $assignplugins, $item) {
     630          global $CFG;
     631  
     632          $plugins = array();
     633          $fs = get_file_storage();
     634  
     635          foreach ($assignplugins as $assignplugin) {
     636  
     637              if (!$assignplugin->is_enabled() or !$assignplugin->is_visible()) {
     638                  continue;
     639              }
     640  
     641              $plugin = array(
     642                  'name' => $assignplugin->get_name(),
     643                  'type' => $assignplugin->get_type()
     644              );
     645              // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
     646              $component = $assignplugin->get_subtype().'_'.$assignplugin->get_type();
     647  
     648              $fileareas = $assignplugin->get_file_areas();
     649              foreach ($fileareas as $filearea => $name) {
     650                  $fileareainfo = array('area' => $filearea);
     651  
     652                  $fileareainfo['files'] = external_util::get_area_files(
     653                      $assign->get_context()->id,
     654                      $component,
     655                      $filearea,
     656                      $item->id
     657                  );
     658  
     659                  $plugin['fileareas'][] = $fileareainfo;
     660              }
     661  
     662              $editorfields = $assignplugin->get_editor_fields();
     663              foreach ($editorfields as $name => $description) {
     664                  $editorfieldinfo = array(
     665                      'name' => $name,
     666                      'description' => $description,
     667                      'text' => $assignplugin->get_editor_text($name, $item->id),
     668                      'format' => $assignplugin->get_editor_format($name, $item->id)
     669                  );
     670  
     671                  // Now format the text.
     672                  foreach ($fileareas as $filearea => $name) {
     673                      list($editorfieldinfo['text'], $editorfieldinfo['format']) = external_format_text(
     674                          $editorfieldinfo['text'], $editorfieldinfo['format'], $assign->get_context()->id,
     675                          $component, $filearea, $item->id);
     676                  }
     677  
     678                  $plugin['editorfields'][] = $editorfieldinfo;
     679              }
     680              $plugins[] = $plugin;
     681          }
     682          return $plugins;
     683      }
     684  
     685      /**
     686       * Describes the parameters for get_submissions
     687       *
     688       * @return external_function_parameters
     689       * @since Moodle 2.5
     690       */
     691      public static function get_submissions_parameters() {
     692          return new external_function_parameters(
     693              array(
     694                  'assignmentids' => new external_multiple_structure(
     695                      new external_value(PARAM_INT, 'assignment id'),
     696                      '1 or more assignment ids',
     697                      VALUE_REQUIRED),
     698                  'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''),
     699                  'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0),
     700                  'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0)
     701              )
     702          );
     703      }
     704  
     705      /**
     706       * Returns submissions for the requested assignment ids
     707       *
     708       * @param int[] $assignmentids
     709       * @param string $status only return submissions with this status
     710       * @param int $since only return submissions with timemodified >= since
     711       * @param int $before only return submissions with timemodified <= before
     712       * @return array of submissions for each requested assignment
     713       * @since Moodle 2.5
     714       */
     715      public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
     716          global $DB, $CFG;
     717  
     718          $params = self::validate_parameters(self::get_submissions_parameters(),
     719                          array('assignmentids' => $assignmentids,
     720                                'status' => $status,
     721                                'since' => $since,
     722                                'before' => $before));
     723  
     724          $warnings = array();
     725          $assignments = array();
     726  
     727          // Check the user is allowed to get the submissions for the assignments requested.
     728          $placeholders = array();
     729          list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED);
     730          $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
     731                 "WHERE md.name = :modname AND cm.instance ".$inorequalsql;
     732          $placeholders['modname'] = 'assign';
     733          $cms = $DB->get_records_sql($sql, $placeholders);
     734          $assigns = array();
     735          foreach ($cms as $cm) {
     736              try {
     737                  $context = context_module::instance($cm->id);
     738                  self::validate_context($context);
     739                  $assign = new assign($context, null, null);
     740                  $assign->require_view_grades();
     741                  $assigns[] = $assign;
     742              } catch (Exception $e) {
     743                  $warnings[] = array(
     744                      'item' => 'assignment',
     745                      'itemid' => $cm->instance,
     746                      'warningcode' => '1',
     747                      'message' => 'No access rights in module context'
     748                  );
     749              }
     750          }
     751  
     752          foreach ($assigns as $assign) {
     753              $submissions = array();
     754              $placeholders = array('assignid1' => $assign->get_instance()->id,
     755                                    'assignid2' => $assign->get_instance()->id);
     756  
     757              $submissionmaxattempt = 'SELECT mxs.userid, mxs.groupid, MAX(mxs.attemptnumber) AS maxattempt
     758                                       FROM {assign_submission} mxs
     759                                       WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid, mxs.groupid';
     760  
     761              $sql = "SELECT mas.id, mas.assignment,mas.userid,".
     762                     "mas.timecreated,mas.timemodified,mas.status,mas.groupid,mas.attemptnumber ".
     763                     "FROM {assign_submission} mas ".
     764                     "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
     765                     "AND mas.groupid = smx.groupid ".
     766                     "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
     767  
     768              if (!empty($params['status'])) {
     769                  $placeholders['status'] = $params['status'];
     770                  $sql = $sql." AND mas.status = :status";
     771              }
     772              if (!empty($params['before'])) {
     773                  $placeholders['since'] = $params['since'];
     774                  $placeholders['before'] = $params['before'];
     775                  $sql = $sql." AND mas.timemodified BETWEEN :since AND :before";
     776              } else {
     777                  $placeholders['since'] = $params['since'];
     778                  $sql = $sql." AND mas.timemodified >= :since";
     779              }
     780  
     781              $submissionrecords = $DB->get_records_sql($sql, $placeholders);
     782  
     783              if (!empty($submissionrecords)) {
     784                  $submissionplugins = $assign->get_submission_plugins();
     785                  foreach ($submissionrecords as $submissionrecord) {
     786                      $submission = array(
     787                          'id' => $submissionrecord->id,
     788                          'userid' => $submissionrecord->userid,
     789                          'timecreated' => $submissionrecord->timecreated,
     790                          'timemodified' => $submissionrecord->timemodified,
     791                          'status' => $submissionrecord->status,
     792                          'attemptnumber' => $submissionrecord->attemptnumber,
     793                          'groupid' => $submissionrecord->groupid,
     794                          'plugins' => self::get_plugins_data($assign, $submissionplugins, $submissionrecord),
     795                          'gradingstatus' => $assign->get_grading_status($submissionrecord->userid)
     796                      );
     797  
     798                      if (($assign->get_instance()->teamsubmission
     799                          && $assign->can_view_group_submission($submissionrecord->groupid))
     800                          || (!$assign->get_instance()->teamsubmission
     801                          && $assign->can_view_submission($submissionrecord->userid))
     802                      ) {
     803                          $submissions[] = $submission;
     804                      }
     805                  }
     806              } else {
     807                  $warnings[] = array(
     808                      'item' => 'module',
     809                      'itemid' => $assign->get_instance()->id,
     810                      'warningcode' => '3',
     811                      'message' => 'No submissions found'
     812                  );
     813              }
     814  
     815              $assignments[] = array(
     816                  'assignmentid' => $assign->get_instance()->id,
     817                  'submissions' => $submissions
     818              );
     819  
     820          }
     821  
     822          $result = array(
     823              'assignments' => $assignments,
     824              'warnings' => $warnings
     825          );
     826          return $result;
     827      }
     828  
     829      /**
     830       * Creates an assignment plugin structure.
     831       *
     832       * @return external_single_structure the plugin structure
     833       */
     834      private static function get_plugin_structure() {
     835          return new external_single_structure(
     836              array(
     837                  'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
     838                  'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
     839                  'fileareas' => new external_multiple_structure(
     840                      new external_single_structure(
     841                          array (
     842                              'area' => new external_value (PARAM_TEXT, 'file area'),
     843                              'files' => new external_files('files', VALUE_OPTIONAL),
     844                          )
     845                      ), 'fileareas', VALUE_OPTIONAL
     846                  ),
     847                  'editorfields' => new external_multiple_structure(
     848                      new external_single_structure(
     849                          array(
     850                              'name' => new external_value(PARAM_TEXT, 'field name'),
     851                              'description' => new external_value(PARAM_RAW, 'field description'),
     852                              'text' => new external_value (PARAM_RAW, 'field value'),
     853                              'format' => new external_format_value ('text')
     854                          )
     855                      )
     856                      , 'editorfields', VALUE_OPTIONAL
     857                  )
     858              )
     859          );
     860      }
     861  
     862      /**
     863       * Creates a submission structure.
     864       *
     865       * @return external_single_structure the submission structure
     866       */
     867      private static function get_submission_structure($required = VALUE_REQUIRED) {
     868          return new external_single_structure(
     869              array(
     870                  'id' => new external_value(PARAM_INT, 'submission id'),
     871                  'userid' => new external_value(PARAM_INT, 'student id'),
     872                  'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
     873                  'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
     874                  'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
     875                  'status' => new external_value(PARAM_TEXT, 'submission status'),
     876                  'groupid' => new external_value(PARAM_INT, 'group id'),
     877                  'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
     878                  'latest' => new external_value(PARAM_INT, 'latest attempt', VALUE_OPTIONAL),
     879                  'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'plugins', VALUE_OPTIONAL),
     880                  'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.', VALUE_OPTIONAL),
     881              ), 'submission info', $required
     882          );
     883      }
     884  
     885      /**
     886       * Creates an assign_submissions external_single_structure
     887       *
     888       * @return external_single_structure
     889       * @since Moodle 2.5
     890       */
     891      private static function get_submissions_structure() {
     892          return new external_single_structure(
     893              array (
     894                  'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
     895                  'submissions' => new external_multiple_structure(self::get_submission_structure())
     896              )
     897          );
     898      }
     899  
     900      /**
     901       * Describes the get_submissions return value
     902       *
     903       * @return external_single_structure
     904       * @since Moodle 2.5
     905       */
     906      public static function get_submissions_returns() {
     907          return new external_single_structure(
     908              array(
     909                  'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'),
     910                  'warnings' => new external_warnings()
     911              )
     912          );
     913      }
     914  
     915      /**
     916       * Describes the parameters for set_user_flags
     917       * @return external_function_parameters
     918       * @since  Moodle 2.6
     919       */
     920      public static function set_user_flags_parameters() {
     921          return new external_function_parameters(
     922              array(
     923                  'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
     924                  'userflags' => new external_multiple_structure(
     925                      new external_single_structure(
     926                          array(
     927                              'userid'           => new external_value(PARAM_INT, 'student id'),
     928                              'locked'           => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL),
     929                              'mailed'           => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL),
     930                              'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL),
     931                              'workflowstate'    => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL),
     932                              'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL)
     933                          )
     934                      )
     935                  )
     936              )
     937          );
     938      }
     939  
     940      /**
     941       * Create or update user_flags records
     942       *
     943       * @param int $assignmentid the assignment for which the userflags are created or updated
     944       * @param array $userflags  An array of userflags to create or update
     945       * @return array containing success or failure information for each record
     946       * @since Moodle 2.6
     947       */
     948      public static function set_user_flags($assignmentid, $userflags = array()) {
     949          global $CFG, $DB;
     950  
     951          $params = self::validate_parameters(self::set_user_flags_parameters(),
     952                                              array('assignmentid' => $assignmentid,
     953                                                    'userflags' => $userflags));
     954  
     955          // Load assignment if it exists and if the user has the capability.
     956          list($assign, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
     957          require_capability('mod/assign:grade', $context);
     958  
     959          $results = array();
     960          foreach ($params['userflags'] as $userflag) {
     961              $success = true;
     962              $result = array();
     963  
     964              $record = $assign->get_user_flags($userflag['userid'], false);
     965              if ($record) {
     966                  if (isset($userflag['locked'])) {
     967                      $record->locked = $userflag['locked'];
     968                  }
     969                  if (isset($userflag['mailed'])) {
     970                      $record->mailed = $userflag['mailed'];
     971                  }
     972                  if (isset($userflag['extensionduedate'])) {
     973                      $record->extensionduedate = $userflag['extensionduedate'];
     974                  }
     975                  if (isset($userflag['workflowstate'])) {
     976                      $record->workflowstate = $userflag['workflowstate'];
     977                  }
     978                  if (isset($userflag['allocatedmarker'])) {
     979                      $record->allocatedmarker = $userflag['allocatedmarker'];
     980                  }
     981                  if ($assign->update_user_flags($record)) {
     982                      $result['id'] = $record->id;
     983                      $result['userid'] = $userflag['userid'];
     984                  } else {
     985                      $result['id'] = $record->id;
     986                      $result['userid'] = $userflag['userid'];
     987                      $result['errormessage'] = 'Record created but values could not be set';
     988                  }
     989              } else {
     990                  $record = $assign->get_user_flags($userflag['userid'], true);
     991                  $setfields = isset($userflag['locked'])
     992                               || isset($userflag['mailed'])
     993                               || isset($userflag['extensionduedate'])
     994                               || isset($userflag['workflowstate'])
     995                               || isset($userflag['allocatedmarker']);
     996                  if ($record) {
     997                      if ($setfields) {
     998                          if (isset($userflag['locked'])) {
     999                              $record->locked = $userflag['locked'];
    1000                          }
    1001                          if (isset($userflag['mailed'])) {
    1002                              $record->mailed = $userflag['mailed'];
    1003                          }
    1004                          if (isset($userflag['extensionduedate'])) {
    1005                              $record->extensionduedate = $userflag['extensionduedate'];
    1006                          }
    1007                          if (isset($userflag['workflowstate'])) {
    1008                              $record->workflowstate = $userflag['workflowstate'];
    1009                          }
    1010                          if (isset($userflag['allocatedmarker'])) {
    1011                              $record->allocatedmarker = $userflag['allocatedmarker'];
    1012                          }
    1013                          if ($assign->update_user_flags($record)) {
    1014                              $result['id'] = $record->id;
    1015                              $result['userid'] = $userflag['userid'];
    1016                          } else {
    1017                              $result['id'] = $record->id;
    1018                              $result['userid'] = $userflag['userid'];
    1019                              $result['errormessage'] = 'Record created but values could not be set';
    1020                          }
    1021                      } else {
    1022                          $result['id'] = $record->id;
    1023                          $result['userid'] = $userflag['userid'];
    1024                      }
    1025                  } else {
    1026                      $result['id'] = -1;
    1027                      $result['userid'] = $userflag['userid'];
    1028                      $result['errormessage'] = 'Record could not be created';
    1029                  }
    1030              }
    1031  
    1032              $results[] = $result;
    1033          }
    1034          return $results;
    1035      }
    1036  
    1037      /**
    1038       * Describes the set_user_flags return value
    1039       * @return external_multiple_structure
    1040       * @since  Moodle 2.6
    1041       */
    1042      public static function set_user_flags_returns() {
    1043          return new external_multiple_structure(
    1044              new external_single_structure(
    1045                  array(
    1046                      'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'),
    1047                      'userid' => new external_value(PARAM_INT, 'userid of record'),
    1048                      'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL)
    1049                  )
    1050              )
    1051          );
    1052      }
    1053  
    1054      /**
    1055       * Describes the parameters for get_user_flags
    1056       * @return external_function_parameters
    1057       * @since  Moodle 2.6
    1058       */
    1059      public static function get_user_flags_parameters() {
    1060          return new external_function_parameters(
    1061              array(
    1062                  'assignmentids' => new external_multiple_structure(
    1063                      new external_value(PARAM_INT, 'assignment id'),
    1064                      '1 or more assignment ids',
    1065                      VALUE_REQUIRED)
    1066              )
    1067          );
    1068      }
    1069  
    1070      /**
    1071       * Returns user flag information from assign_user_flags for the requested assignment ids
    1072       * @param int[] $assignmentids
    1073       * @return array of user flag records for each requested assignment
    1074       * @since  Moodle 2.6
    1075       */
    1076      public static function get_user_flags($assignmentids) {
    1077          global $DB;
    1078          $params = self::validate_parameters(self::get_user_flags_parameters(),
    1079                          array('assignmentids' => $assignmentids));
    1080  
    1081          $assignments = array();
    1082          $warnings = array();
    1083          $requestedassignmentids = $params['assignmentids'];
    1084  
    1085          // Check the user is allowed to get the user flags for the assignments requested.
    1086          $placeholders = array();
    1087          list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
    1088          $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
    1089                 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
    1090          $placeholders['modname'] = 'assign';
    1091          $cms = $DB->get_records_sql($sql, $placeholders);
    1092          foreach ($cms as $cm) {
    1093              try {
    1094                  $context = context_module::instance($cm->id);
    1095                  self::validate_context($context);
    1096                  require_capability('mod/assign:grade', $context);
    1097              } catch (Exception $e) {
    1098                  $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
    1099                  $warning = array();
    1100                  $warning['item'] = 'assignment';
    1101                  $warning['itemid'] = $cm->instance;
    1102                  $warning['warningcode'] = '1';
    1103                  $warning['message'] = 'No access rights in module context';
    1104                  $warnings[] = $warning;
    1105              }
    1106          }
    1107  
    1108          // Create the query and populate an array of assign_user_flags records from the recordset results.
    1109          if (count ($requestedassignmentids) > 0) {
    1110              $placeholders = array();
    1111              list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
    1112  
    1113              $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,".
    1114                     "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ".
    1115                     "FROM {assign_user_flags} auf ".
    1116                     "WHERE auf.assignment ".$inorequalsql.
    1117                     " ORDER BY auf.assignment, auf.id";
    1118  
    1119              $rs = $DB->get_recordset_sql($sql, $placeholders);
    1120              $currentassignmentid = null;
    1121              $assignment = null;
    1122              foreach ($rs as $rd) {
    1123                  $userflag = array();
    1124                  $userflag['id'] = $rd->id;
    1125                  $userflag['userid'] = $rd->userid;
    1126                  $userflag['locked'] = $rd->locked;
    1127                  $userflag['mailed'] = $rd->mailed;
    1128                  $userflag['extensionduedate'] = $rd->extensionduedate;
    1129                  $userflag['workflowstate'] = $rd->workflowstate;
    1130                  $userflag['allocatedmarker'] = $rd->allocatedmarker;
    1131  
    1132                  if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
    1133                      if (!is_null($assignment)) {
    1134                          $assignments[] = $assignment;
    1135                      }
    1136                      $assignment = array();
    1137                      $assignment['assignmentid'] = $rd->assignment;
    1138                      $assignment['userflags'] = array();
    1139                      $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
    1140                  }
    1141                  $assignment['userflags'][] = $userflag;
    1142  
    1143                  $currentassignmentid = $rd->assignment;
    1144              }
    1145              if (!is_null($assignment)) {
    1146                  $assignments[] = $assignment;
    1147              }
    1148              $rs->close();
    1149  
    1150          }
    1151  
    1152          foreach ($requestedassignmentids as $assignmentid) {
    1153              $warning = array();
    1154              $warning['item'] = 'assignment';
    1155              $warning['itemid'] = $assignmentid;
    1156              $warning['warningcode'] = '3';
    1157              $warning['message'] = 'No user flags found';
    1158              $warnings[] = $warning;
    1159          }
    1160  
    1161          $result = array();
    1162          $result['assignments'] = $assignments;
    1163          $result['warnings'] = $warnings;
    1164          return $result;
    1165      }
    1166  
    1167      /**
    1168       * Creates an assign_user_flags external_single_structure
    1169       * @return external_single_structure
    1170       * @since  Moodle 2.6
    1171       */
    1172      private static function assign_user_flags() {
    1173          return new external_single_structure(
    1174              array (
    1175                  'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
    1176                  'userflags'   => new external_multiple_structure(new external_single_structure(
    1177                          array(
    1178                              'id'               => new external_value(PARAM_INT, 'user flag id'),
    1179                              'userid'           => new external_value(PARAM_INT, 'student id'),
    1180                              'locked'           => new external_value(PARAM_INT, 'locked'),
    1181                              'mailed'           => new external_value(PARAM_INT, 'mailed'),
    1182                              'extensionduedate' => new external_value(PARAM_INT, 'extension due date'),
    1183                              'workflowstate'    => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL),
    1184                              'allocatedmarker'  => new external_value(PARAM_INT, 'allocated marker')
    1185                          )
    1186                      )
    1187                  )
    1188              )
    1189          );
    1190      }
    1191  
    1192      /**
    1193       * Describes the get_user_flags return value
    1194       * @return external_single_structure
    1195       * @since  Moodle 2.6
    1196       */
    1197      public static function get_user_flags_returns() {
    1198          return new external_single_structure(
    1199              array(
    1200                  'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'),
    1201                  'warnings'      => new external_warnings('item is always \'assignment\'',
    1202                      'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
    1203                      'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)')
    1204              )
    1205          );
    1206      }
    1207  
    1208      /**
    1209       * Describes the parameters for get_user_mappings
    1210       * @return external_function_parameters
    1211       * @since  Moodle 2.6
    1212       */
    1213      public static function get_user_mappings_parameters() {
    1214          return new external_function_parameters(
    1215              array(
    1216                  'assignmentids' => new external_multiple_structure(
    1217                      new external_value(PARAM_INT, 'assignment id'),
    1218                      '1 or more assignment ids',
    1219                      VALUE_REQUIRED)
    1220              )
    1221          );
    1222      }
    1223  
    1224      /**
    1225       * Returns user mapping information from assign_user_mapping for the requested assignment ids
    1226       * @param int[] $assignmentids
    1227       * @return array of user mapping records for each requested assignment
    1228       * @since  Moodle 2.6
    1229       */
    1230      public static function get_user_mappings($assignmentids) {
    1231          global $DB;
    1232          $params = self::validate_parameters(self::get_user_mappings_parameters(),
    1233                          array('assignmentids' => $assignmentids));
    1234  
    1235          $assignments = array();
    1236          $warnings = array();
    1237          $requestedassignmentids = $params['assignmentids'];
    1238  
    1239          // Check the user is allowed to get the mappings for the assignments requested.
    1240          $placeholders = array();
    1241          list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
    1242          $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ".
    1243                 "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids;
    1244          $placeholders['modname'] = 'assign';
    1245          $cms = $DB->get_records_sql($sql, $placeholders);
    1246          foreach ($cms as $cm) {
    1247              try {
    1248                  $context = context_module::instance($cm->id);
    1249                  self::validate_context($context);
    1250                  require_capability('mod/assign:revealidentities', $context);
    1251              } catch (Exception $e) {
    1252                  $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance));
    1253                  $warning = array();
    1254                  $warning['item'] = 'assignment';
    1255                  $warning['itemid'] = $cm->instance;
    1256                  $warning['warningcode'] = '1';
    1257                  $warning['message'] = 'No access rights in module context';
    1258                  $warnings[] = $warning;
    1259              }
    1260          }
    1261  
    1262          // Create the query and populate an array of assign_user_mapping records from the recordset results.
    1263          if (count ($requestedassignmentids) > 0) {
    1264              $placeholders = array();
    1265              list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
    1266  
    1267              $sql = "SELECT aum.id,aum.assignment,aum.userid ".
    1268                     "FROM {assign_user_mapping} aum ".
    1269                     "WHERE aum.assignment ".$inorequalsql.
    1270                     " ORDER BY aum.assignment, aum.id";
    1271  
    1272              $rs = $DB->get_recordset_sql($sql, $placeholders);
    1273              $currentassignmentid = null;
    1274              $assignment = null;
    1275              foreach ($rs as $rd) {
    1276                  $mapping = array();
    1277                  $mapping['id'] = $rd->id;
    1278                  $mapping['userid'] = $rd->userid;
    1279  
    1280                  if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
    1281                      if (!is_null($assignment)) {
    1282                          $assignments[] = $assignment;
    1283                      }
    1284                      $assignment = array();
    1285                      $assignment['assignmentid'] = $rd->assignment;
    1286                      $assignment['mappings'] = array();
    1287                      $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment));
    1288                  }
    1289                  $assignment['mappings'][] = $mapping;
    1290  
    1291                  $currentassignmentid = $rd->assignment;
    1292              }
    1293              if (!is_null($assignment)) {
    1294                  $assignments[] = $assignment;
    1295              }
    1296              $rs->close();
    1297  
    1298          }
    1299  
    1300          foreach ($requestedassignmentids as $assignmentid) {
    1301              $warning = array();
    1302              $warning['item'] = 'assignment';
    1303              $warning['itemid'] = $assignmentid;
    1304              $warning['warningcode'] = '3';
    1305              $warning['message'] = 'No mappings found';
    1306              $warnings[] = $warning;
    1307          }
    1308  
    1309          $result = array();
    1310          $result['assignments'] = $assignments;
    1311          $result['warnings'] = $warnings;
    1312          return $result;
    1313      }
    1314  
    1315      /**
    1316       * Creates an assign_user_mappings external_single_structure
    1317       * @return external_single_structure
    1318       * @since  Moodle 2.6
    1319       */
    1320      private static function assign_user_mappings() {
    1321          return new external_single_structure(
    1322              array (
    1323                  'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
    1324                  'mappings'   => new external_multiple_structure(new external_single_structure(
    1325                          array(
    1326                              'id'     => new external_value(PARAM_INT, 'user mapping id'),
    1327                              'userid' => new external_value(PARAM_INT, 'student id')
    1328                          )
    1329                      )
    1330                  )
    1331              )
    1332          );
    1333      }
    1334  
    1335      /**
    1336       * Describes the get_user_mappings return value
    1337       * @return external_single_structure
    1338       * @since  Moodle 2.6
    1339       */
    1340      public static function get_user_mappings_returns() {
    1341          return new external_single_structure(
    1342              array(
    1343                  'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'),
    1344                  'warnings'      => new external_warnings('item is always \'assignment\'',
    1345                      'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id',
    1346                      'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)')
    1347              )
    1348          );
    1349      }
    1350  
    1351      /**
    1352       * Describes the parameters for lock_submissions
    1353       * @return external_function_parameters
    1354       * @since  Moodle 2.6
    1355       */
    1356      public static function lock_submissions_parameters() {
    1357          return new external_function_parameters(
    1358              array(
    1359                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    1360                  'userids' => new external_multiple_structure(
    1361                      new external_value(PARAM_INT, 'user id'),
    1362                      '1 or more user ids',
    1363                      VALUE_REQUIRED),
    1364              )
    1365          );
    1366      }
    1367  
    1368      /**
    1369       * Locks (prevent updates to) submissions in this assignment.
    1370       *
    1371       * @param int $assignmentid The id of the assignment
    1372       * @param array $userids Array of user ids to lock
    1373       * @return array of warnings for each submission that could not be locked.
    1374       * @since Moodle 2.6
    1375       */
    1376      public static function lock_submissions($assignmentid, $userids) {
    1377          global $CFG;
    1378  
    1379          $params = self::validate_parameters(self::lock_submissions_parameters(),
    1380                          array('assignmentid' => $assignmentid,
    1381                                'userids' => $userids));
    1382  
    1383          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1384  
    1385          $warnings = array();
    1386          foreach ($params['userids'] as $userid) {
    1387              if (!$assignment->lock_submission($userid)) {
    1388                  $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
    1389                  $warnings[] = self::generate_warning($params['assignmentid'],
    1390                                                       'couldnotlock',
    1391                                                       $detail);
    1392              }
    1393          }
    1394  
    1395          return $warnings;
    1396      }
    1397  
    1398      /**
    1399       * Describes the return value for lock_submissions
    1400       *
    1401       * @return external_single_structure
    1402       * @since Moodle 2.6
    1403       */
    1404      public static function lock_submissions_returns() {
    1405          return new external_warnings();
    1406      }
    1407  
    1408      /**
    1409       * Describes the parameters for revert_submissions_to_draft
    1410       * @return external_function_parameters
    1411       * @since  Moodle 2.6
    1412       */
    1413      public static function revert_submissions_to_draft_parameters() {
    1414          return new external_function_parameters(
    1415              array(
    1416                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    1417                  'userids' => new external_multiple_structure(
    1418                      new external_value(PARAM_INT, 'user id'),
    1419                      '1 or more user ids',
    1420                      VALUE_REQUIRED),
    1421              )
    1422          );
    1423      }
    1424  
    1425      /**
    1426       * Reverts a list of user submissions to draft for a single assignment.
    1427       *
    1428       * @param int $assignmentid The id of the assignment
    1429       * @param array $userids Array of user ids to revert
    1430       * @return array of warnings for each submission that could not be reverted.
    1431       * @since Moodle 2.6
    1432       */
    1433      public static function revert_submissions_to_draft($assignmentid, $userids) {
    1434          global $CFG;
    1435  
    1436          $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
    1437                          array('assignmentid' => $assignmentid,
    1438                                'userids' => $userids));
    1439  
    1440          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1441  
    1442          $warnings = array();
    1443          foreach ($params['userids'] as $userid) {
    1444              if (!$assignment->revert_to_draft($userid)) {
    1445                  $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
    1446                  $warnings[] = self::generate_warning($params['assignmentid'],
    1447                                                       'couldnotrevert',
    1448                                                       $detail);
    1449              }
    1450          }
    1451  
    1452          return $warnings;
    1453      }
    1454  
    1455      /**
    1456       * Describes the return value for revert_submissions_to_draft
    1457       *
    1458       * @return external_single_structure
    1459       * @since Moodle 2.6
    1460       */
    1461      public static function revert_submissions_to_draft_returns() {
    1462          return new external_warnings();
    1463      }
    1464  
    1465      /**
    1466       * Describes the parameters for unlock_submissions
    1467       * @return external_function_parameters
    1468       * @since  Moodle 2.6
    1469       */
    1470      public static function unlock_submissions_parameters() {
    1471          return new external_function_parameters(
    1472              array(
    1473                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    1474                  'userids' => new external_multiple_structure(
    1475                      new external_value(PARAM_INT, 'user id'),
    1476                      '1 or more user ids',
    1477                      VALUE_REQUIRED),
    1478              )
    1479          );
    1480      }
    1481  
    1482      /**
    1483       * Locks (prevent updates to) submissions in this assignment.
    1484       *
    1485       * @param int $assignmentid The id of the assignment
    1486       * @param array $userids Array of user ids to lock
    1487       * @return array of warnings for each submission that could not be locked.
    1488       * @since Moodle 2.6
    1489       */
    1490      public static function unlock_submissions($assignmentid, $userids) {
    1491          global $CFG;
    1492  
    1493          $params = self::validate_parameters(self::unlock_submissions_parameters(),
    1494                          array('assignmentid' => $assignmentid,
    1495                                'userids' => $userids));
    1496  
    1497          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1498  
    1499          $warnings = array();
    1500          foreach ($params['userids'] as $userid) {
    1501              if (!$assignment->unlock_submission($userid)) {
    1502                  $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'];
    1503                  $warnings[] = self::generate_warning($params['assignmentid'],
    1504                                                       'couldnotunlock',
    1505                                                       $detail);
    1506              }
    1507          }
    1508  
    1509          return $warnings;
    1510      }
    1511  
    1512      /**
    1513       * Describes the return value for unlock_submissions
    1514       *
    1515       * @return external_single_structure
    1516       * @since Moodle 2.6
    1517       */
    1518      public static function unlock_submissions_returns() {
    1519          return new external_warnings();
    1520      }
    1521  
    1522      /**
    1523       * Describes the parameters for submit_grading_form webservice.
    1524       * @return external_function_parameters
    1525       * @since  Moodle 3.1
    1526       */
    1527      public static function submit_grading_form_parameters() {
    1528          return new external_function_parameters(
    1529              array(
    1530                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    1531                  'userid' => new external_value(PARAM_INT, 'The user id the submission belongs to'),
    1532                  'jsonformdata' => new external_value(PARAM_RAW, 'The data from the grading form, encoded as a json array')
    1533              )
    1534          );
    1535      }
    1536  
    1537      /**
    1538       * Submit the logged in users assignment for grading.
    1539       *
    1540       * @param int $assignmentid The id of the assignment
    1541       * @param int $userid The id of the user the submission belongs to.
    1542       * @param string $jsonformdata The data from the form, encoded as a json array.
    1543       * @return array of warnings to indicate any errors.
    1544       * @since Moodle 3.1
    1545       */
    1546      public static function submit_grading_form($assignmentid, $userid, $jsonformdata) {
    1547          global $CFG, $USER;
    1548  
    1549          require_once($CFG->dirroot . '/mod/assign/locallib.php');
    1550          require_once($CFG->dirroot . '/mod/assign/gradeform.php');
    1551  
    1552          $params = self::validate_parameters(self::submit_grading_form_parameters(),
    1553                                              array(
    1554                                                  'assignmentid' => $assignmentid,
    1555                                                  'userid' => $userid,
    1556                                                  'jsonformdata' => $jsonformdata
    1557                                              ));
    1558  
    1559          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1560  
    1561          $serialiseddata = json_decode($params['jsonformdata']);
    1562  
    1563          $data = array();
    1564          parse_str($serialiseddata, $data);
    1565  
    1566          $warnings = array();
    1567  
    1568          $options = array(
    1569              'userid' => $params['userid'],
    1570              'attemptnumber' => $data['attemptnumber'],
    1571              'rownum' => 0,
    1572              'gradingpanel' => true
    1573          );
    1574  
    1575          if (WS_SERVER) {
    1576              // Assume form submission if coming from WS.
    1577              $USER->ignoresesskey = true;
    1578              $data['_qf__mod_assign_grade_form_'.$params['userid']] = 1;
    1579          }
    1580  
    1581          $customdata = (object) $data;
    1582          $formparams = array($assignment, $customdata, $options);
    1583  
    1584          // Data is injected into the form by the last param for the constructor.
    1585          $mform = new mod_assign_grade_form(null, $formparams, 'post', '', null, true, $data);
    1586          $validateddata = $mform->get_data();
    1587  
    1588          if ($validateddata) {
    1589              $assignment->save_grade($params['userid'], $validateddata);
    1590          } else {
    1591              $warnings[] = self::generate_warning($params['assignmentid'],
    1592                                                   'couldnotsavegrade',
    1593                                                   'Form validation failed.');
    1594          }
    1595  
    1596  
    1597          return $warnings;
    1598      }
    1599  
    1600      /**
    1601       * Describes the return for submit_grading_form
    1602       * @return external_function_parameters
    1603       * @since  Moodle 3.1
    1604       */
    1605      public static function submit_grading_form_returns() {
    1606          return new external_warnings();
    1607      }
    1608  
    1609      /**
    1610       * Describes the parameters for submit_for_grading
    1611       * @return external_function_parameters
    1612       * @since  Moodle 2.6
    1613       */
    1614      public static function submit_for_grading_parameters() {
    1615          return new external_function_parameters(
    1616              array(
    1617                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    1618                  'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement')
    1619              )
    1620          );
    1621      }
    1622  
    1623      /**
    1624       * Submit the logged in users assignment for grading.
    1625       *
    1626       * @param int $assignmentid The id of the assignment
    1627       * @return array of warnings to indicate any errors.
    1628       * @since Moodle 2.6
    1629       */
    1630      public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
    1631          global $CFG, $USER;
    1632  
    1633          $params = self::validate_parameters(self::submit_for_grading_parameters(),
    1634                                              array('assignmentid' => $assignmentid,
    1635                                                    'acceptsubmissionstatement' => $acceptsubmissionstatement));
    1636  
    1637          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1638  
    1639          $warnings = array();
    1640          $data = new stdClass();
    1641          $data->submissionstatement = $params['acceptsubmissionstatement'];
    1642          $notices = array();
    1643  
    1644          if (!$assignment->submit_for_grading($data, $notices)) {
    1645              $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices);
    1646              $warnings[] = self::generate_warning($params['assignmentid'],
    1647                                                   'couldnotsubmitforgrading',
    1648                                                   $detail);
    1649          }
    1650  
    1651          return $warnings;
    1652      }
    1653  
    1654      /**
    1655       * Describes the return value for submit_for_grading
    1656       *
    1657       * @return external_single_structure
    1658       * @since Moodle 2.6
    1659       */
    1660      public static function submit_for_grading_returns() {
    1661          return new external_warnings();
    1662      }
    1663  
    1664      /**
    1665       * Describes the parameters for save_user_extensions
    1666       * @return external_function_parameters
    1667       * @since  Moodle 2.6
    1668       */
    1669      public static function save_user_extensions_parameters() {
    1670          return new external_function_parameters(
    1671              array(
    1672                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    1673                  'userids' => new external_multiple_structure(
    1674                      new external_value(PARAM_INT, 'user id'),
    1675                      '1 or more user ids',
    1676                      VALUE_REQUIRED),
    1677                  'dates' => new external_multiple_structure(
    1678                      new external_value(PARAM_INT, 'dates'),
    1679                      '1 or more extension dates (timestamp)',
    1680                      VALUE_REQUIRED),
    1681              )
    1682          );
    1683      }
    1684  
    1685      /**
    1686       * Grant extension dates to students for an assignment.
    1687       *
    1688       * @param int $assignmentid The id of the assignment
    1689       * @param array $userids Array of user ids to grant extensions to
    1690       * @param array $dates Array of extension dates
    1691       * @return array of warnings for each extension date that could not be granted
    1692       * @since Moodle 2.6
    1693       */
    1694      public static function save_user_extensions($assignmentid, $userids, $dates) {
    1695          global $CFG;
    1696  
    1697          $params = self::validate_parameters(self::save_user_extensions_parameters(),
    1698                          array('assignmentid' => $assignmentid,
    1699                                'userids' => $userids,
    1700                                'dates' => $dates));
    1701  
    1702          if (count($params['userids']) != count($params['dates'])) {
    1703              $detail = 'Length of userids and dates parameters differ.';
    1704              $warnings[] = self::generate_warning($params['assignmentid'],
    1705                                                   'invalidparameters',
    1706                                                   $detail);
    1707  
    1708              return $warnings;
    1709          }
    1710  
    1711          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1712  
    1713          $warnings = array();
    1714          foreach ($params['userids'] as $idx => $userid) {
    1715              $duedate = $params['dates'][$idx];
    1716              if (!$assignment->save_user_extension($userid, $duedate)) {
    1717                  $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate;
    1718                  $warnings[] = self::generate_warning($params['assignmentid'],
    1719                                                       'couldnotgrantextensions',
    1720                                                       $detail);
    1721              }
    1722          }
    1723  
    1724          return $warnings;
    1725      }
    1726  
    1727      /**
    1728       * Describes the return value for save_user_extensions
    1729       *
    1730       * @return external_single_structure
    1731       * @since Moodle 2.6
    1732       */
    1733      public static function save_user_extensions_returns() {
    1734          return new external_warnings();
    1735      }
    1736  
    1737      /**
    1738       * Describes the parameters for reveal_identities
    1739       * @return external_function_parameters
    1740       * @since  Moodle 2.6
    1741       */
    1742      public static function reveal_identities_parameters() {
    1743          return new external_function_parameters(
    1744              array(
    1745                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on')
    1746              )
    1747          );
    1748      }
    1749  
    1750      /**
    1751       * Reveal the identities of anonymous students to markers for a single assignment.
    1752       *
    1753       * @param int $assignmentid The id of the assignment
    1754       * @return array of warnings to indicate any errors.
    1755       * @since Moodle 2.6
    1756       */
    1757      public static function reveal_identities($assignmentid) {
    1758          global $CFG, $USER;
    1759  
    1760          $params = self::validate_parameters(self::reveal_identities_parameters(),
    1761                                              array('assignmentid' => $assignmentid));
    1762  
    1763          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1764  
    1765          $warnings = array();
    1766          if (!$assignment->reveal_identities()) {
    1767              $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'];
    1768              $warnings[] = self::generate_warning($params['assignmentid'],
    1769                                                   'couldnotrevealidentities',
    1770                                                   $detail);
    1771          }
    1772  
    1773          return $warnings;
    1774      }
    1775  
    1776      /**
    1777       * Describes the return value for reveal_identities
    1778       *
    1779       * @return external_single_structure
    1780       * @since Moodle 2.6
    1781       */
    1782      public static function reveal_identities_returns() {
    1783          return new external_warnings();
    1784      }
    1785  
    1786      /**
    1787       * Describes the parameters for save_submission
    1788       * @return external_function_parameters
    1789       * @since  Moodle 2.6
    1790       */
    1791      public static function save_submission_parameters() {
    1792          global $CFG;
    1793          $instance = new assign(null, null, null);
    1794          $pluginsubmissionparams = array();
    1795  
    1796          foreach ($instance->get_submission_plugins() as $plugin) {
    1797              if ($plugin->is_visible()) {
    1798                  $pluginparams = $plugin->get_external_parameters();
    1799                  if (!empty($pluginparams)) {
    1800                      $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams);
    1801                  }
    1802              }
    1803          }
    1804  
    1805          return new external_function_parameters(
    1806              array(
    1807                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    1808                  'plugindata' => new external_single_structure(
    1809                      $pluginsubmissionparams
    1810                  )
    1811              )
    1812          );
    1813      }
    1814  
    1815      /**
    1816       * Save a student submission for a single assignment
    1817       *
    1818       * @param int $assignmentid The id of the assignment
    1819       * @param array $plugindata - The submitted data for plugins
    1820       * @return array of warnings to indicate any errors
    1821       * @since Moodle 2.6
    1822       */
    1823      public static function save_submission($assignmentid, $plugindata) {
    1824          global $CFG, $USER;
    1825  
    1826          $params = self::validate_parameters(self::save_submission_parameters(),
    1827                                              array('assignmentid' => $assignmentid,
    1828                                                    'plugindata' => $plugindata));
    1829  
    1830          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1831  
    1832          $notices = array();
    1833  
    1834          $assignment->update_effective_access($USER->id);
    1835          if (!$assignment->submissions_open($USER->id)) {
    1836              $notices[] = get_string('duedatereached', 'assign');
    1837          } else {
    1838              $submissiondata = (object)$params['plugindata'];
    1839              $assignment->save_submission($submissiondata, $notices);
    1840          }
    1841  
    1842          $warnings = array();
    1843          foreach ($notices as $notice) {
    1844              $warnings[] = self::generate_warning($params['assignmentid'],
    1845                                                   'couldnotsavesubmission',
    1846                                                   $notice);
    1847          }
    1848  
    1849          return $warnings;
    1850      }
    1851  
    1852      /**
    1853       * Describes the return value for save_submission
    1854       *
    1855       * @return external_single_structure
    1856       * @since Moodle 2.6
    1857       */
    1858      public static function save_submission_returns() {
    1859          return new external_warnings();
    1860      }
    1861  
    1862      /**
    1863       * Describes the parameters for save_grade
    1864       * @return external_function_parameters
    1865       * @since  Moodle 2.6
    1866       */
    1867      public static function save_grade_parameters() {
    1868          global $CFG;
    1869          require_once("$CFG->dirroot/grade/grading/lib.php");
    1870          $instance = new assign(null, null, null);
    1871          $pluginfeedbackparams = array();
    1872  
    1873          foreach ($instance->get_feedback_plugins() as $plugin) {
    1874              if ($plugin->is_visible()) {
    1875                  $pluginparams = $plugin->get_external_parameters();
    1876                  if (!empty($pluginparams)) {
    1877                      $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
    1878                  }
    1879              }
    1880          }
    1881  
    1882          $advancedgradingdata = array();
    1883          $methods = array_keys(grading_manager::available_methods(false));
    1884          foreach ($methods as $method) {
    1885              require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
    1886              $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
    1887              if (!empty($details)) {
    1888                  $items = array();
    1889                  foreach ($details as $key => $value) {
    1890                      $value->required = VALUE_OPTIONAL;
    1891                      unset($value->content->keys['id']);
    1892                      $items[$key] = new external_multiple_structure (new external_single_structure(
    1893                          array(
    1894                              'criterionid' => new external_value(PARAM_INT, 'criterion id'),
    1895                              'fillings' => $value
    1896                          )
    1897                      ));
    1898                  }
    1899                  $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
    1900              }
    1901          }
    1902  
    1903          return new external_function_parameters(
    1904              array(
    1905                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    1906                  'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
    1907                  'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'),
    1908                  'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
    1909                  'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'),
    1910                  'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
    1911                  'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
    1912                                                                 'to all members ' .
    1913                                                                 'of the group (for group assignments).'),
    1914                  'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()),
    1915                  'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
    1916                                                                         VALUE_DEFAULT, array())
    1917              )
    1918          );
    1919      }
    1920  
    1921      /**
    1922       * Save a student grade for a single assignment.
    1923       *
    1924       * @param int $assignmentid The id of the assignment
    1925       * @param int $userid The id of the user
    1926       * @param float $grade The grade (ignored if the assignment uses advanced grading)
    1927       * @param int $attemptnumber The attempt number
    1928       * @param bool $addattempt Allow another attempt
    1929       * @param string $workflowstate New workflow state
    1930       * @param bool $applytoall Apply the grade to all members of the group
    1931       * @param array $plugindata Custom data used by plugins
    1932       * @param array $advancedgradingdata Advanced grading data
    1933       * @return null
    1934       * @since Moodle 2.6
    1935       */
    1936      public static function save_grade($assignmentid,
    1937                                        $userid,
    1938                                        $grade,
    1939                                        $attemptnumber,
    1940                                        $addattempt,
    1941                                        $workflowstate,
    1942                                        $applytoall,
    1943                                        $plugindata = array(),
    1944                                        $advancedgradingdata = array()) {
    1945          global $CFG, $USER;
    1946  
    1947          $params = self::validate_parameters(self::save_grade_parameters(),
    1948                                              array('assignmentid' => $assignmentid,
    1949                                                    'userid' => $userid,
    1950                                                    'grade' => $grade,
    1951                                                    'attemptnumber' => $attemptnumber,
    1952                                                    'workflowstate' => $workflowstate,
    1953                                                    'addattempt' => $addattempt,
    1954                                                    'applytoall' => $applytoall,
    1955                                                    'plugindata' => $plugindata,
    1956                                                    'advancedgradingdata' => $advancedgradingdata));
    1957  
    1958          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    1959  
    1960          $gradedata = (object)$params['plugindata'];
    1961  
    1962          $gradedata->addattempt = $params['addattempt'];
    1963          $gradedata->attemptnumber = $params['attemptnumber'];
    1964          $gradedata->workflowstate = $params['workflowstate'];
    1965          $gradedata->applytoall = $params['applytoall'];
    1966          $gradedata->grade = $params['grade'];
    1967  
    1968          if (!empty($params['advancedgradingdata'])) {
    1969              $advancedgrading = array();
    1970              $criteria = reset($params['advancedgradingdata']);
    1971              foreach ($criteria as $key => $criterion) {
    1972                  $details = array();
    1973                  foreach ($criterion as $value) {
    1974                      foreach ($value['fillings'] as $filling) {
    1975                          $details[$value['criterionid']] = $filling;
    1976                      }
    1977                  }
    1978                  $advancedgrading[$key] = $details;
    1979              }
    1980              $gradedata->advancedgrading = $advancedgrading;
    1981          }
    1982  
    1983          $assignment->save_grade($params['userid'], $gradedata);
    1984  
    1985          return null;
    1986      }
    1987  
    1988      /**
    1989       * Describes the return value for save_grade
    1990       *
    1991       * @return external_single_structure
    1992       * @since Moodle 2.6
    1993       */
    1994      public static function save_grade_returns() {
    1995          return null;
    1996      }
    1997  
    1998      /**
    1999       * Describes the parameters for save_grades
    2000       * @return external_function_parameters
    2001       * @since  Moodle 2.7
    2002       */
    2003      public static function save_grades_parameters() {
    2004          global $CFG;
    2005          require_once("$CFG->dirroot/grade/grading/lib.php");
    2006          $instance = new assign(null, null, null);
    2007          $pluginfeedbackparams = array();
    2008  
    2009          foreach ($instance->get_feedback_plugins() as $plugin) {
    2010              if ($plugin->is_visible()) {
    2011                  $pluginparams = $plugin->get_external_parameters();
    2012                  if (!empty($pluginparams)) {
    2013                      $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
    2014                  }
    2015              }
    2016          }
    2017  
    2018          $advancedgradingdata = array();
    2019          $methods = array_keys(grading_manager::available_methods(false));
    2020          foreach ($methods as $method) {
    2021              require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
    2022              $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
    2023              if (!empty($details)) {
    2024                  $items = array();
    2025                  foreach ($details as $key => $value) {
    2026                      $value->required = VALUE_OPTIONAL;
    2027                      unset($value->content->keys['id']);
    2028                      $items[$key] = new external_multiple_structure (new external_single_structure(
    2029                          array(
    2030                              'criterionid' => new external_value(PARAM_INT, 'criterion id'),
    2031                              'fillings' => $value
    2032                          )
    2033                      ));
    2034                  }
    2035                  $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
    2036              }
    2037          }
    2038  
    2039          return new external_function_parameters(
    2040              array(
    2041                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    2042                  'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' .
    2043                                                                 'to all members ' .
    2044                                                                 'of the group (for group assignments).'),
    2045                  'grades' => new external_multiple_structure(
    2046                      new external_single_structure(
    2047                          array (
    2048                              'userid' => new external_value(PARAM_INT, 'The student id to operate on'),
    2049                              'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '.
    2050                                                                         'Ignored if advanced grading used'),
    2051                              'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'),
    2052                              'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'),
    2053                              'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'),
    2054                              'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data',
    2055                                                                            VALUE_DEFAULT, array()),
    2056                              'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data',
    2057                                                                                     VALUE_DEFAULT, array())
    2058                          )
    2059                      )
    2060                  )
    2061              )
    2062          );
    2063      }
    2064  
    2065      /**
    2066       * Save multiple student grades for a single assignment.
    2067       *
    2068       * @param int $assignmentid The id of the assignment
    2069       * @param boolean $applytoall If set to true and this is a team assignment,
    2070       * apply the grade to all members of the group
    2071       * @param array $grades grade data for one or more students that includes
    2072       *                  userid - The id of the student being graded
    2073       *                  grade - The grade (ignored if the assignment uses advanced grading)
    2074       *                  attemptnumber - The attempt number
    2075       *                  addattempt - Allow another attempt
    2076       *                  workflowstate - New workflow state
    2077       *                  plugindata - Custom data used by plugins
    2078       *                  advancedgradingdata - Optional Advanced grading data
    2079       * @throws invalid_parameter_exception if multiple grades are supplied for
    2080       * a team assignment that has $applytoall set to true
    2081       * @return null
    2082       * @since Moodle 2.7
    2083       */
    2084      public static function save_grades($assignmentid, $applytoall, $grades) {
    2085          global $CFG, $USER;
    2086  
    2087          $params = self::validate_parameters(self::save_grades_parameters(),
    2088                                              array('assignmentid' => $assignmentid,
    2089                                                    'applytoall' => $applytoall,
    2090                                                    'grades' => $grades));
    2091  
    2092          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    2093  
    2094          if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
    2095              // Check that only 1 user per submission group is provided.
    2096              $groupids = array();
    2097              foreach ($params['grades'] as $gradeinfo) {
    2098                  $group = $assignment->get_submission_group($gradeinfo['userid']);
    2099                  if (in_array($group->id, $groupids)) {
    2100                      throw new invalid_parameter_exception('Multiple grades for the same team have been supplied '
    2101                                                            .' this is not permitted when the applytoall flag is set');
    2102                  } else {
    2103                      $groupids[] = $group->id;
    2104                  }
    2105              }
    2106          }
    2107  
    2108          foreach ($params['grades'] as $gradeinfo) {
    2109              $gradedata = (object)$gradeinfo['plugindata'];
    2110              $gradedata->addattempt = $gradeinfo['addattempt'];
    2111              $gradedata->attemptnumber = $gradeinfo['attemptnumber'];
    2112              $gradedata->workflowstate = $gradeinfo['workflowstate'];
    2113              $gradedata->applytoall = $params['applytoall'];
    2114              $gradedata->grade = $gradeinfo['grade'];
    2115  
    2116              if (!empty($gradeinfo['advancedgradingdata'])) {
    2117                  $advancedgrading = array();
    2118                  $criteria = reset($gradeinfo['advancedgradingdata']);
    2119                  foreach ($criteria as $key => $criterion) {
    2120                      $details = array();
    2121                      foreach ($criterion as $value) {
    2122                          foreach ($value['fillings'] as $filling) {
    2123                              $details[$value['criterionid']] = $filling;
    2124                          }
    2125                      }
    2126                      $advancedgrading[$key] = $details;
    2127                  }
    2128                  $gradedata->advancedgrading = $advancedgrading;
    2129              }
    2130              $assignment->save_grade($gradeinfo['userid'], $gradedata);
    2131          }
    2132  
    2133          return null;
    2134      }
    2135  
    2136      /**
    2137       * Describes the return value for save_grades
    2138       *
    2139       * @return external_single_structure
    2140       * @since Moodle 2.7
    2141       */
    2142      public static function save_grades_returns() {
    2143          return null;
    2144      }
    2145  
    2146      /**
    2147       * Describes the parameters for copy_previous_attempt
    2148       * @return external_function_parameters
    2149       * @since  Moodle 2.6
    2150       */
    2151      public static function copy_previous_attempt_parameters() {
    2152          return new external_function_parameters(
    2153              array(
    2154                  'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'),
    2155              )
    2156          );
    2157      }
    2158  
    2159      /**
    2160       * Copy a students previous attempt to a new attempt.
    2161       *
    2162       * @param int $assignmentid
    2163       * @return array of warnings to indicate any errors.
    2164       * @since Moodle 2.6
    2165       */
    2166      public static function copy_previous_attempt($assignmentid) {
    2167  
    2168          $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
    2169                                              array('assignmentid' => $assignmentid));
    2170  
    2171          list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
    2172  
    2173          $notices = array();
    2174  
    2175          $assignment->copy_previous_attempt($notices);
    2176  
    2177          $warnings = array();
    2178          foreach ($notices as $notice) {
    2179              $warnings[] = self::generate_warning($assignmentid,
    2180                                                   'couldnotcopyprevioussubmission',
    2181                                                   $notice);
    2182          }
    2183  
    2184          return $warnings;
    2185      }
    2186  
    2187      /**
    2188       * Describes the return value for save_submission
    2189       *
    2190       * @return external_single_structure
    2191       * @since Moodle 2.6
    2192       */
    2193      public static function copy_previous_attempt_returns() {
    2194          return new external_warnings();
    2195      }
    2196  
    2197      /**
    2198       * Returns description of method parameters
    2199       *
    2200       * @return external_function_parameters
    2201       * @since Moodle 3.0
    2202       */
    2203      public static function view_grading_table_parameters() {
    2204          return new external_function_parameters(
    2205              array(
    2206                  'assignid' => new external_value(PARAM_INT, 'assign instance id')
    2207              )
    2208          );
    2209      }
    2210  
    2211      /**
    2212       * Trigger the grading_table_viewed event.
    2213       *
    2214       * @param int $assignid the assign instance id
    2215       * @return array of warnings and status result
    2216       * @since Moodle 3.0
    2217       * @throws moodle_exception
    2218       */
    2219      public static function view_grading_table($assignid) {
    2220  
    2221          $params = self::validate_parameters(self::view_grading_table_parameters(),
    2222                                              array(
    2223                                                  'assignid' => $assignid
    2224                                              ));
    2225          $warnings = array();
    2226  
    2227          list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
    2228  
    2229          $assign->require_view_grades();
    2230          \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger();
    2231  
    2232          $result = array();
    2233          $result['status'] = true;
    2234          $result['warnings'] = $warnings;
    2235          return $result;
    2236      }
    2237  
    2238      /**
    2239       * Returns description of method result value
    2240       *
    2241       * @return external_description
    2242       * @since Moodle 3.0
    2243       */
    2244      public static function view_grading_table_returns() {
    2245          return new external_single_structure(
    2246              array(
    2247                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
    2248                  'warnings' => new external_warnings()
    2249              )
    2250          );
    2251      }
    2252  
    2253      /**
    2254       * Describes the parameters for view_submission_status.
    2255       *
    2256       * @return external_function_parameters
    2257       * @since Moodle 3.1
    2258       */
    2259      public static function view_submission_status_parameters() {
    2260          return new external_function_parameters (
    2261              array(
    2262                  'assignid' => new external_value(PARAM_INT, 'assign instance id'),
    2263              )
    2264          );
    2265      }
    2266  
    2267      /**
    2268       * Trigger the submission status viewed event.
    2269       *
    2270       * @param int $assignid assign instance id
    2271       * @return array of warnings and status result
    2272       * @since Moodle 3.1
    2273       */
    2274      public static function view_submission_status($assignid) {
    2275  
    2276          $warnings = array();
    2277          $params = array(
    2278              'assignid' => $assignid,
    2279          );
    2280          $params = self::validate_parameters(self::view_submission_status_parameters(), $params);
    2281  
    2282          list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
    2283  
    2284          \mod_assign\event\submission_status_viewed::create_from_assign($assign)->trigger();
    2285  
    2286          $result = array();
    2287          $result['status'] = true;
    2288          $result['warnings'] = $warnings;
    2289          return $result;
    2290      }
    2291  
    2292      /**
    2293       * Describes the view_submission_status return value.
    2294       *
    2295       * @return external_single_structure
    2296       * @since Moodle 3.1
    2297       */
    2298      public static function view_submission_status_returns() {
    2299          return new external_single_structure(
    2300              array(
    2301                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
    2302                  'warnings' => new external_warnings(),
    2303              )
    2304          );
    2305      }
    2306  
    2307      /**
    2308       * Describes the parameters for get_submission_status.
    2309       *
    2310       * @return external_function_parameters
    2311       * @since Moodle 3.1
    2312       */
    2313      public static function get_submission_status_parameters() {
    2314          return new external_function_parameters (
    2315              array(
    2316                  'assignid' => new external_value(PARAM_INT, 'assignment instance id'),
    2317                  'userid' => new external_value(PARAM_INT, 'user id (empty for current user)', VALUE_DEFAULT, 0),
    2318                  'groupid' => new external_value(PARAM_INT, 'filter by users in group (used for generating the grading summary).
    2319                      0 for all groups information, any other empty value will calculate currrent group.', VALUE_DEFAULT, 0),
    2320              )
    2321          );
    2322      }
    2323  
    2324      /**
    2325       * Returns information about an assignment submission status for a given user.
    2326       *
    2327       * @param int $assignid assignment instance id
    2328       * @param int $userid user id (empty for current user)
    2329       * @param int $groupid filter by users in group id (used for generating the grading summary). Use 0 for all groups information.
    2330       * @return array of warnings and grading, status, feedback and previous attempts information
    2331       * @since Moodle 3.1
    2332       * @throws required_capability_exception
    2333       */
    2334      public static function get_submission_status($assignid, $userid = 0, $groupid = 0) {
    2335          global $USER;
    2336  
    2337          $warnings = array();
    2338  
    2339          $params = array(
    2340              'assignid' => $assignid,
    2341              'userid' => $userid,
    2342              'groupid' => $groupid,
    2343          );
    2344          $params = self::validate_parameters(self::get_submission_status_parameters(), $params);
    2345  
    2346          list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
    2347  
    2348          // Default value for userid.
    2349          if (empty($params['userid'])) {
    2350              $params['userid'] = $USER->id;
    2351          }
    2352          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
    2353          core_user::require_active_user($user);
    2354  
    2355          if (!$assign->can_view_submission($user->id)) {
    2356              throw new required_capability_exception($context, 'mod/assign:viewgrades', 'nopermission', '');
    2357          }
    2358  
    2359          $assign->update_effective_access($user->id);
    2360  
    2361          $gradingsummary = $lastattempt = $feedback = $previousattempts = null;
    2362  
    2363          // Get the renderable since it contais all the info we need.
    2364          if (!empty($params['groupid'])) {
    2365              $groupid = $params['groupid'];
    2366              // Determine is the group is visible to user.
    2367              if (!groups_group_visible($groupid, $course, $cm)) {
    2368                  throw new moodle_exception('notingroup');
    2369              }
    2370          } else {
    2371              // A null group means that following functions will calculate the current group.
    2372              // A groupid set to 0 means all groups.
    2373              $groupid = ($params['groupid'] == 0) ? 0 : null;
    2374          }
    2375          if ($assign->can_view_grades($groupid)) {
    2376              $gradingsummary = $assign->get_assign_grading_summary_renderable($groupid);
    2377          }
    2378  
    2379          // Retrieve the rest of the renderable objects.
    2380          if (has_capability('mod/assign:viewownsubmissionsummary', $context, $user, false)) {
    2381              // The user can view the submission summary.
    2382              $lastattempt = $assign->get_assign_submission_status_renderable($user, true);
    2383          }
    2384  
    2385          $feedback = $assign->get_assign_feedback_status_renderable($user);
    2386  
    2387          $previousattempts = $assign->get_assign_attempt_history_renderable($user);
    2388  
    2389          // Now, build the result.
    2390          $result = array();
    2391  
    2392          // First of all, grading summary, this is suitable for teachers/managers.
    2393          if ($gradingsummary) {
    2394              $result['gradingsummary'] = $gradingsummary;
    2395          }
    2396          // Show the grader's identity if 'Hide Grader' is disabled or has the 'Show Hidden Grader' capability.
    2397          $showgradername = (has_capability('mod/assign:showhiddengrader', $context) or
    2398              !$assign->is_hidden_grader());
    2399  
    2400          // Did we submit anything?
    2401          if ($lastattempt) {
    2402              $submissionplugins = $assign->get_submission_plugins();
    2403  
    2404              if (empty($lastattempt->submission)) {
    2405                  unset($lastattempt->submission);
    2406              } else {
    2407                  $lastattempt->submission->plugins = self::get_plugins_data($assign, $submissionplugins, $lastattempt->submission);
    2408              }
    2409  
    2410              if (empty($lastattempt->teamsubmission)) {
    2411                  unset($lastattempt->teamsubmission);
    2412              } else {
    2413                  $lastattempt->teamsubmission->plugins = self::get_plugins_data($assign, $submissionplugins,
    2414                                                                                  $lastattempt->teamsubmission);
    2415              }
    2416  
    2417              // We need to change the type of some of the structures retrieved from the renderable.
    2418              if (!empty($lastattempt->submissiongroup)) {
    2419                  $lastattempt->submissiongroup = $lastattempt->submissiongroup->id;
    2420              } else {
    2421                  unset($lastattempt->submissiongroup);
    2422              }
    2423  
    2424              if (!empty($lastattempt->usergroups)) {
    2425                  $lastattempt->usergroups = array_keys($lastattempt->usergroups);
    2426              }
    2427              // We cannot use array_keys here.
    2428              if (!empty($lastattempt->submissiongroupmemberswhoneedtosubmit)) {
    2429                  $lastattempt->submissiongroupmemberswhoneedtosubmit = array_map(
    2430                                                                              function($e){
    2431                                                                                  return $e->id;
    2432                                                                              },
    2433                                                                              $lastattempt->submissiongroupmemberswhoneedtosubmit);
    2434              }
    2435  
    2436              // Can edit its own submission?
    2437              $lastattempt->caneditowner = has_capability('mod/assign:submit', $context, $user, false)
    2438                  && $assign->submissions_open($user->id) && $assign->is_any_submission_plugin_enabled();
    2439  
    2440              $result['lastattempt'] = $lastattempt;
    2441          }
    2442  
    2443          // The feedback for our latest submission.
    2444          if ($feedback) {
    2445              if ($feedback->grade) {
    2446                  if (!$showgradername) {
    2447                      $feedback->grade->grader = -1;
    2448                  }
    2449                  $feedbackplugins = $assign->get_feedback_plugins();
    2450                  $feedback->plugins = self::get_plugins_data($assign, $feedbackplugins, $feedback->grade);
    2451              } else {
    2452                  unset($feedback->plugins);
    2453                  unset($feedback->grade);
    2454              }
    2455  
    2456              $result['feedback'] = $feedback;
    2457          }
    2458  
    2459          // Retrieve only previous attempts.
    2460          if ($previousattempts and count($previousattempts->submissions) > 1) {
    2461              // Don't show the last one because it is the current submission.
    2462              array_pop($previousattempts->submissions);
    2463  
    2464              // Show newest to oldest.
    2465              $previousattempts->submissions = array_reverse($previousattempts->submissions);
    2466  
    2467              foreach ($previousattempts->submissions as $i => $submission) {
    2468                  $attempt = array();
    2469  
    2470                  $grade = null;
    2471                  foreach ($previousattempts->grades as $onegrade) {
    2472                      if ($onegrade->attemptnumber == $submission->attemptnumber) {
    2473                          $grade = $onegrade;
    2474                          break;
    2475                      }
    2476                  }
    2477  
    2478                  $attempt['attemptnumber'] = $submission->attemptnumber;
    2479  
    2480                  if ($submission) {
    2481                      $submission->plugins = self::get_plugins_data($assign, $previousattempts->submissionplugins, $submission);
    2482                      $attempt['submission'] = $submission;
    2483                  }
    2484  
    2485                  if ($grade) {
    2486                      // From object to id.
    2487                      if (!$showgradername) {
    2488                          $grade->grader = -1;
    2489                      } else {
    2490                          $grade->grader = $grade->grader->id;
    2491                      }
    2492  
    2493                      $feedbackplugins = self::get_plugins_data($assign, $previousattempts->feedbackplugins, $grade);
    2494  
    2495                      $attempt['grade'] = $grade;
    2496                      $attempt['feedbackplugins'] = $feedbackplugins;
    2497                  }
    2498                  $result['previousattempts'][] = $attempt;
    2499              }
    2500          }
    2501  
    2502          $result['warnings'] = $warnings;
    2503          return $result;
    2504      }
    2505  
    2506      /**
    2507       * Describes the get_submission_status return value.
    2508       *
    2509       * @return external_single_structure
    2510       * @since Moodle 3.1
    2511       */
    2512      public static function get_submission_status_returns() {
    2513          return new external_single_structure(
    2514              array(
    2515                  'gradingsummary' => new external_single_structure(
    2516                      array(
    2517                          'participantcount' => new external_value(PARAM_INT, 'Number of users who can submit.'),
    2518                          'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'),
    2519                          'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'),
    2520                          'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'),
    2521                          'submissionssubmittedcount' => new external_value(PARAM_INT, 'Number of submissions in submitted status.'),
    2522                          'submissionsneedgradingcount' => new external_value(PARAM_INT, 'Number of submissions that need grading.'),
    2523                          'warnofungroupedusers' => new external_value(PARAM_ALPHA, 'Whether we need to warn people that there
    2524                                                                          are users without groups (\'warningrequired\'), warn
    2525                                                                          people there are users who will submit in the default
    2526                                                                          group (\'warningoptional\') or no warning (\'\').'),
    2527                      ), 'Grading information.', VALUE_OPTIONAL
    2528                  ),
    2529                  'lastattempt' => new external_single_structure(
    2530                      array(
    2531                          'submission' => self::get_submission_structure(VALUE_OPTIONAL),
    2532                          'teamsubmission' => self::get_submission_structure(VALUE_OPTIONAL),
    2533                          'submissiongroup' => new external_value(PARAM_INT, 'The submission group id (for group submissions only).',
    2534                                                                  VALUE_OPTIONAL),
    2535                          'submissiongroupmemberswhoneedtosubmit' => new external_multiple_structure(
    2536                              new external_value(PARAM_INT, 'USER id.'),
    2537                              'List of users who still need to submit (for group submissions only).',
    2538                              VALUE_OPTIONAL
    2539                          ),
    2540                          'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'),
    2541                          'locked' => new external_value(PARAM_BOOL, 'Whether new submissions are locked.'),
    2542                          'graded' => new external_value(PARAM_BOOL, 'Whether the submission is graded.'),
    2543                          'canedit' => new external_value(PARAM_BOOL, 'Whether the user can edit the current submission.'),
    2544                          'caneditowner' => new external_value(PARAM_BOOL, 'Whether the owner of the submission can edit it.'),
    2545                          'cansubmit' => new external_value(PARAM_BOOL, 'Whether the user can submit.'),
    2546                          'extensionduedate' => new external_value(PARAM_INT, 'Extension due date.'),
    2547                          'blindmarking' => new external_value(PARAM_BOOL, 'Whether blind marking is enabled.'),
    2548                          'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.'),
    2549                          'usergroups' => new external_multiple_structure(
    2550                              new external_value(PARAM_INT, 'Group id.'), 'User groups in the course.'
    2551                          ),
    2552                      ), 'Last attempt information.', VALUE_OPTIONAL
    2553                  ),
    2554                  'feedback' => new external_single_structure(
    2555                      array(
    2556                          'grade' => self::get_grade_structure(VALUE_OPTIONAL),
    2557                          'gradefordisplay' => new external_value(PARAM_RAW, 'Grade rendered into a format suitable for display.'),
    2558                          'gradeddate' => new external_value(PARAM_INT, 'The date the user was graded.'),
    2559                          'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'Plugins info.', VALUE_OPTIONAL),
    2560                      ), 'Feedback for the last attempt.', VALUE_OPTIONAL
    2561                  ),
    2562                  'previousattempts' => new external_multiple_structure(
    2563                      new external_single_structure(
    2564                          array(
    2565                              'attemptnumber' => new external_value(PARAM_INT, 'Attempt number.'),
    2566                              'submission' => self::get_submission_structure(VALUE_OPTIONAL),
    2567                              'grade' => self::get_grade_structure(VALUE_OPTIONAL),
    2568                              'feedbackplugins' => new external_multiple_structure(self::get_plugin_structure(), 'Feedback info.',
    2569                                                                                      VALUE_OPTIONAL),
    2570                          )
    2571                      ), 'List all the previous attempts did by the user.', VALUE_OPTIONAL
    2572                  ),
    2573                  'warnings' => new external_warnings(),
    2574              )
    2575          );
    2576      }
    2577  
    2578      /**
    2579       * Returns description of method parameters
    2580       *
    2581       * @return external_function_parameters
    2582       * @since Moodle 3.1
    2583       */
    2584      public static function list_participants_parameters() {
    2585          return new external_function_parameters(
    2586              array(
    2587                  'assignid' => new external_value(PARAM_INT, 'assign instance id'),
    2588                  'groupid' => new external_value(PARAM_INT, 'group id'),
    2589                  'filter' => new external_value(PARAM_RAW, 'search string to filter the results'),
    2590                  'skip' => new external_value(PARAM_INT, 'number of records to skip', VALUE_DEFAULT, 0),
    2591                  'limit' => new external_value(PARAM_INT, 'maximum number of records to return', VALUE_DEFAULT, 0),
    2592                  'onlyids' => new external_value(PARAM_BOOL, 'Do not return all user fields', VALUE_DEFAULT, false),
    2593                  'includeenrolments' => new external_value(PARAM_BOOL, 'Do return courses where the user is enrolled',
    2594                                                            VALUE_DEFAULT, true),
    2595                  'tablesort' => new external_value(PARAM_BOOL, 'Apply current user table sorting preferences.',
    2596                                                            VALUE_DEFAULT, false)
    2597              )
    2598          );
    2599      }
    2600  
    2601      /**
    2602       * Retrieves the list of students to be graded for the assignment.
    2603       *
    2604       * @param int $assignid the assign instance id
    2605       * @param int $groupid the current group id
    2606       * @param string $filter search string to filter the results.
    2607       * @param int $skip Number of records to skip
    2608       * @param int $limit Maximum number of records to return
    2609       * @param bool $onlyids Only return user ids.
    2610       * @param bool $includeenrolments Return courses where the user is enrolled.
    2611       * @param bool $tablesort Apply current user table sorting params from the grading table.
    2612       * @return array of warnings and status result
    2613       * @since Moodle 3.1
    2614       * @throws moodle_exception
    2615       */
    2616      public static function list_participants($assignid, $groupid, $filter, $skip,
    2617              $limit, $onlyids, $includeenrolments, $tablesort) {
    2618          global $DB, $CFG;
    2619          require_once($CFG->dirroot . "/mod/assign/locallib.php");
    2620          require_once($CFG->dirroot . "/user/lib.php");
    2621  
    2622          $params = self::validate_parameters(self::list_participants_parameters(),
    2623                                              array(
    2624                                                  'assignid' => $assignid,
    2625                                                  'groupid' => $groupid,
    2626                                                  'filter' => $filter,
    2627                                                  'skip' => $skip,
    2628                                                  'limit' => $limit,
    2629                                                  'onlyids' => $onlyids,
    2630                                                  'includeenrolments' => $includeenrolments,
    2631                                                  'tablesort' => $tablesort
    2632                                              ));
    2633          $warnings = array();
    2634  
    2635          list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
    2636  
    2637          require_capability('mod/assign:view', $context);
    2638  
    2639          $assign->require_view_grades();
    2640  
    2641          $participants = array();
    2642          if (groups_group_visible($params['groupid'], $course, $cm)) {
    2643              $participants = $assign->list_participants_with_filter_status_and_group($params['groupid'], $params['tablesort']);
    2644          }
    2645  
    2646          $userfields = user_get_default_fields();
    2647          if (!$params['includeenrolments']) {
    2648              // Remove enrolled courses from users fields to be returned.
    2649              $key = array_search('enrolledcourses', $userfields);
    2650              if ($key !== false) {
    2651                  unset($userfields[$key]);
    2652              } else {
    2653                  throw new moodle_exception('invaliduserfield', 'error', '', 'enrolledcourses');
    2654              }
    2655          }
    2656  
    2657          $result = array();
    2658          $index = 0;
    2659          foreach ($participants as $record) {
    2660              // Preserve the fullname set by the assignment.
    2661              $fullname = $record->fullname;
    2662              $searchable = $fullname;
    2663              $match = false;
    2664              if (empty($filter)) {
    2665                  $match = true;
    2666              } else {
    2667                  $filter = core_text::strtolower($filter);
    2668                  $value = core_text::strtolower($searchable);
    2669                  if (is_string($value) && (core_text::strpos($value, $filter) !== false)) {
    2670                      $match = true;
    2671                  }
    2672              }
    2673              if ($match) {
    2674                  $index++;
    2675                  if ($index <= $params['skip']) {
    2676                      continue;
    2677                  }
    2678                  if (($params['limit'] > 0) && (($index - $params['skip']) > $params['limit'])) {
    2679                      break;
    2680                  }
    2681                  // Now we do the expensive lookup of user details because we completed the filtering.
    2682                  if (!$assign->is_blind_marking() && !$params['onlyids']) {
    2683                      $userdetails = user_get_user_details($record, $course, $userfields);
    2684                  } else {
    2685                      $userdetails = array('id' => $record->id);
    2686                  }
    2687                  $userdetails['fullname'] = $fullname;
    2688                  $userdetails['submitted'] = $record->submitted;
    2689                  $userdetails['requiregrading'] = $record->requiregrading;
    2690                  $userdetails['grantedextension'] = $record->grantedextension;
    2691                  if (!empty($record->groupid)) {
    2692                      $userdetails['groupid'] = $record->groupid;
    2693                  }
    2694                  if (!empty($record->groupname)) {
    2695                      $userdetails['groupname'] = $record->groupname;
    2696                  }
    2697                  // Unique id is required for blind marking.
    2698                  $userdetails['recordid'] = -1;
    2699                  if (!empty($record->recordid)) {
    2700                      $userdetails['recordid'] = $record->recordid;
    2701                  }
    2702  
    2703                  $result[] = $userdetails;
    2704              }
    2705          }
    2706          return $result;
    2707      }
    2708  
    2709      /**
    2710       * Returns the description of the results of the mod_assign_external::list_participants() method.
    2711       *
    2712       * @return external_description
    2713       * @since Moodle 3.1
    2714       */
    2715      public static function list_participants_returns() {
    2716          // Get user description.
    2717          $userdesc = core_user_external::user_description();
    2718          // List unneeded properties.
    2719          $unneededproperties = [
    2720              'auth', 'confirmed', 'lang', 'calendartype', 'theme', 'timezone', 'mailformat'
    2721          ];
    2722          // Remove unneeded properties for consistency with the previous version.
    2723          foreach ($unneededproperties as $prop) {
    2724              unset($userdesc->keys[$prop]);
    2725          }
    2726  
    2727          // Override property attributes for consistency with the previous version.
    2728          $userdesc->keys['fullname']->type = PARAM_NOTAGS;
    2729          $userdesc->keys['profileimageurlsmall']->required = VALUE_OPTIONAL;
    2730          $userdesc->keys['profileimageurl']->required = VALUE_OPTIONAL;
    2731          $userdesc->keys['email']->desc = 'Email address';
    2732          $userdesc->keys['idnumber']->desc = 'The idnumber of the user';
    2733          $userdesc->keys['recordid'] = new external_value(PARAM_INT, 'record id');
    2734  
    2735          // Define other keys.
    2736          $otherkeys = [
    2737              'groups' => new external_multiple_structure(
    2738                  new external_single_structure(
    2739                      [
    2740                          'id' => new external_value(PARAM_INT, 'group id'),
    2741                          'name' => new external_value(PARAM_RAW, 'group name'),
    2742                          'description' => new external_value(PARAM_RAW, 'group description'),
    2743                      ]
    2744                  ), 'user groups', VALUE_OPTIONAL
    2745              ),
    2746              'roles' => new external_multiple_structure(
    2747                  new external_single_structure(
    2748                      [
    2749                          'roleid' => new external_value(PARAM_INT, 'role id'),
    2750                          'name' => new external_value(PARAM_RAW, 'role name'),
    2751                          'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
    2752                          'sortorder' => new external_value(PARAM_INT, 'role sortorder')
    2753                      ]
    2754                  ), 'user roles', VALUE_OPTIONAL
    2755              ),
    2756              'enrolledcourses' => new external_multiple_structure(
    2757                  new external_single_structure(
    2758                      [
    2759                          'id' => new external_value(PARAM_INT, 'Id of the course'),
    2760                          'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
    2761                          'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
    2762                      ]
    2763                  ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL
    2764              ),
    2765              'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'),
    2766              'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'),
    2767              'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'),
    2768              'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL),
    2769              'groupname' => new external_value(PARAM_NOTAGS, 'for group assignments this is the group name', VALUE_OPTIONAL),
    2770          ];
    2771  
    2772          // Merge keys.
    2773          $userdesc->keys = array_merge($userdesc->keys, $otherkeys);
    2774          return new external_multiple_structure($userdesc);
    2775      }
    2776  
    2777      /**
    2778       * Returns description of method parameters
    2779       *
    2780       * @return external_function_parameters
    2781       * @since Moodle 3.1
    2782       */
    2783      public static function get_participant_parameters() {
    2784          return new external_function_parameters(
    2785              array(
    2786                  'assignid' => new external_value(PARAM_INT, 'assign instance id'),
    2787                  'userid' => new external_value(PARAM_INT, 'user id'),
    2788                  'embeduser' => new external_value(PARAM_BOOL, 'user id', VALUE_DEFAULT, false),
    2789              )
    2790          );
    2791      }
    2792  
    2793      /**
    2794       * Get the user participating in the given assignment. An error with code 'usernotincourse'
    2795       * is thrown is the user isn't a participant of the given assignment.
    2796       *
    2797       * @param int $assignid the assign instance id
    2798       * @param int $userid the user id
    2799       * @param bool $embeduser return user details (only applicable if not blind marking)
    2800       * @return array of warnings and status result
    2801       * @since Moodle 3.1
    2802       * @throws moodle_exception
    2803       */
    2804      public static function get_participant($assignid, $userid, $embeduser) {
    2805          global $DB, $CFG;
    2806          require_once($CFG->dirroot . "/mod/assign/locallib.php");
    2807          require_once($CFG->dirroot . "/user/lib.php");
    2808  
    2809          $params = self::validate_parameters(self::get_participant_parameters(), array(
    2810              'assignid' => $assignid,
    2811              'userid' => $userid,
    2812              'embeduser' => $embeduser
    2813          ));
    2814  
    2815          list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
    2816          $assign->require_view_grades();
    2817  
    2818          $participant = $assign->get_participant($params['userid']);
    2819  
    2820          // Update assign with override information.
    2821          $assign->update_effective_access($params['userid']);
    2822  
    2823          if (!$participant) {
    2824              // No participant found so we can return early.
    2825              throw new moodle_exception('usernotincourse');
    2826          }
    2827  
    2828          $return = array(
    2829              'id' => $participant->id,
    2830              'fullname' => $participant->fullname,
    2831              'submitted' => $participant->submitted,
    2832              'requiregrading' => $participant->requiregrading,
    2833              'grantedextension' => $participant->grantedextension,
    2834              'blindmarking' => $assign->is_blind_marking(),
    2835              'allowsubmissionsfromdate' => $assign->get_instance($userid)->allowsubmissionsfromdate,
    2836              'duedate' => $assign->get_instance($userid)->duedate,
    2837              'cutoffdate' => $assign->get_instance($userid)->cutoffdate,
    2838              'duedatestr' => userdate($assign->get_instance($userid)->duedate, get_string('strftimedatetime', 'langconfig')),
    2839          );
    2840  
    2841          if (!empty($participant->groupid)) {
    2842              $return['groupid'] = $participant->groupid;
    2843          }
    2844          if (!empty($participant->groupname)) {
    2845              $return['groupname'] = $participant->groupname;
    2846          }
    2847  
    2848          // Skip the expensive lookup of user detail if we're blind marking or the caller
    2849          // hasn't asked for user details to be embedded.
    2850          if (!$assign->is_blind_marking() && $embeduser) {
    2851              if ($userdetails = user_get_user_details($participant, $course)) {
    2852                  $return['user'] = $userdetails;
    2853              }
    2854          }
    2855  
    2856          return $return;
    2857      }
    2858  
    2859      /**
    2860       * Returns description of method result value
    2861       *
    2862       * @return external_description
    2863       * @since Moodle 3.1
    2864       */
    2865      public static function get_participant_returns() {
    2866          $userdescription = core_user_external::user_description();
    2867          $userdescription->default = [];
    2868          $userdescription->required = VALUE_OPTIONAL;
    2869  
    2870          return new external_single_structure(array(
    2871              'id' => new external_value(PARAM_INT, 'ID of the user'),
    2872              'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
    2873              'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'),
    2874              'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'),
    2875              'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'),
    2876              'blindmarking' => new external_value(PARAM_BOOL, 'is blind marking enabled for this assignment'),
    2877              'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allowsubmissionsfromdate for the user'),
    2878              'duedate' => new external_value(PARAM_INT, 'duedate for the user'),
    2879              'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user'),
    2880              'duedatestr' => new external_value(PARAM_TEXT, 'duedate for the user'),
    2881              'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL),
    2882              'groupname' => new external_value(PARAM_NOTAGS, 'for group assignments this is the group name', VALUE_OPTIONAL),
    2883              'user' => $userdescription,
    2884          ));
    2885      }
    2886  
    2887      /**
    2888       * Utility function for validating an assign.
    2889       *
    2890       * @param int $assignid assign instance id
    2891       * @return array array containing the assign, course, context and course module objects
    2892       * @since  Moodle 3.2
    2893       */
    2894      protected static function validate_assign($assignid) {
    2895          global $DB;
    2896  
    2897          // Request and permission validation.
    2898          $assign = $DB->get_record('assign', array('id' => $assignid), 'id', MUST_EXIST);
    2899          list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
    2900  
    2901          $context = context_module::instance($cm->id);
    2902          // Please, note that is not required to check mod/assign:view because is done by validate_context->require_login.
    2903          self::validate_context($context);
    2904          $assign = new assign($context, $cm, $course);
    2905  
    2906          return array($assign, $course, $cm, $context);
    2907      }
    2908  
    2909      /**
    2910       * Describes the parameters for view_assign.
    2911       *
    2912       * @return external_function_parameters
    2913       * @since Moodle 3.2
    2914       */
    2915      public static function view_assign_parameters() {
    2916          return new external_function_parameters (
    2917              array(
    2918                  'assignid' => new external_value(PARAM_INT, 'assign instance id'),
    2919              )
    2920          );
    2921      }
    2922  
    2923      /**
    2924       * Update the module completion status.
    2925       *
    2926       * @param int $assignid assign instance id
    2927       * @return array of warnings and status result
    2928       * @since Moodle 3.2
    2929       */
    2930      public static function view_assign($assignid) {
    2931          $warnings = array();
    2932          $params = array(
    2933              'assignid' => $assignid,
    2934          );
    2935          $params = self::validate_parameters(self::view_assign_parameters(), $params);
    2936  
    2937          list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
    2938  
    2939          $assign->set_module_viewed();
    2940  
    2941          $result = array();
    2942          $result['status'] = true;
    2943          $result['warnings'] = $warnings;
    2944          return $result;
    2945      }
    2946  
    2947      /**
    2948       * Describes the view_assign return value.
    2949       *
    2950       * @return external_single_structure
    2951       * @since Moodle 3.2
    2952       */
    2953      public static function view_assign_returns() {
    2954          return new external_single_structure(
    2955              array(
    2956                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
    2957                  'warnings' => new external_warnings(),
    2958              )
    2959          );
    2960      }
    2961  }