[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/assign/ -> externallib.php (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1