Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Completion external API
  19   *
  20   * @package    core_completion
  21   * @category   external
  22   * @copyright  2015 Juan Leyva <juan@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   * @since      Moodle 2.9
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die;
  28  
  29  require_once("$CFG->libdir/externallib.php");
  30  require_once("$CFG->libdir/completionlib.php");
  31  
  32  /**
  33   * Completion external functions
  34   *
  35   * @package    core_completion
  36   * @category   external
  37   * @copyright  2015 Juan Leyva <juan@moodle.com>
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   * @since      Moodle 2.9
  40   */
  41  class core_completion_external extends external_api {
  42  
  43      /**
  44       * Describes the parameters for update_activity_completion_status_manually.
  45       *
  46       * @return external_function_parameters
  47       * @since Moodle 2.9
  48       */
  49      public static function update_activity_completion_status_manually_parameters() {
  50          return new external_function_parameters (
  51              array(
  52                  'cmid' => new external_value(PARAM_INT, 'course module id'),
  53                  'completed' => new external_value(PARAM_BOOL, 'activity completed or not'),
  54              )
  55          );
  56      }
  57  
  58      /**
  59       * Update completion status for the current user in an activity, only for activities with manual tracking.
  60       * @param  int $cmid      Course module id
  61       * @param  bool $completed Activity completed or not
  62       * @return array            Result and possible warnings
  63       * @since Moodle 2.9
  64       * @throws moodle_exception
  65       */
  66      public static function update_activity_completion_status_manually($cmid,  $completed) {
  67  
  68          // Validate and normalize parameters.
  69          $params = self::validate_parameters(self::update_activity_completion_status_manually_parameters(),
  70              array('cmid' => $cmid, 'completed' => $completed));
  71          $cmid = $params['cmid'];
  72          $completed = $params['completed'];
  73  
  74          $warnings = array();
  75  
  76          $context = context_module::instance($cmid);
  77          self::validate_context($context);
  78          require_capability('moodle/course:togglecompletion', $context);
  79  
  80          list($course, $cm) = get_course_and_cm_from_cmid($cmid);
  81  
  82          // Set up completion object and check it is enabled.
  83          $completion = new completion_info($course);
  84          if (!$completion->is_enabled()) {
  85              throw new moodle_exception('completionnotenabled', 'completion');
  86          }
  87  
  88          // Check completion state is manual.
  89          if ($cm->completion != COMPLETION_TRACKING_MANUAL) {
  90              throw new moodle_exception('cannotmanualctrack', 'error');
  91          }
  92  
  93          $targetstate = ($completed) ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE;
  94          $completion->update_state($cm, $targetstate);
  95  
  96          $result = array();
  97          $result['status'] = true;
  98          $result['warnings'] = $warnings;
  99          return $result;
 100      }
 101  
 102      /**
 103       * Describes the update_activity_completion_status_manually return value.
 104       *
 105       * @return external_single_structure
 106       * @since Moodle 2.9
 107       */
 108      public static function update_activity_completion_status_manually_returns() {
 109  
 110          return new external_single_structure(
 111              array(
 112                  'status'    => new external_value(PARAM_BOOL, 'status, true if success'),
 113                  'warnings'  => new external_warnings(),
 114              )
 115          );
 116      }
 117  
 118      /**
 119       * Describes the parameters for override_activity_completion_status.
 120       *
 121       * @return external_external_function_parameters
 122       * @since Moodle 3.4
 123       */
 124      public static function override_activity_completion_status_parameters() {
 125          return new external_function_parameters (
 126              array(
 127                  'userid' => new external_value(PARAM_INT, 'user id'),
 128                  'cmid' => new external_value(PARAM_INT, 'course module id'),
 129                  'newstate' => new external_value(PARAM_INT, 'the new activity completion state'),
 130              )
 131          );
 132      }
 133  
 134      /**
 135       * Update completion status for a user in an activity.
 136       * @param  int $userid    User id
 137       * @param  int $cmid      Course module id
 138       * @param  int $newstate  Activity completion
 139       * @return array          Array containing the current (updated) completion status.
 140       * @since Moodle 3.4
 141       * @throws moodle_exception
 142       */
 143      public static function override_activity_completion_status($userid, $cmid, $newstate) {
 144          // Validate and normalize parameters.
 145          $params = self::validate_parameters(self::override_activity_completion_status_parameters(),
 146              array('userid' => $userid, 'cmid' => $cmid, 'newstate' => $newstate));
 147          $userid = $params['userid'];
 148          $cmid = $params['cmid'];
 149          $newstate = $params['newstate'];
 150  
 151          $context = context_module::instance($cmid);
 152          self::validate_context($context);
 153  
 154          list($course, $cm) = get_course_and_cm_from_cmid($cmid);
 155  
 156          // Set up completion object and check it is enabled.
 157          $completion = new completion_info($course);
 158          if (!$completion->is_enabled()) {
 159              throw new moodle_exception('completionnotenabled', 'completion');
 160          }
 161  
 162          // Update completion state and get the new state back.
 163          $completion->update_state($cm, $newstate, $userid, true);
 164          $completiondata = $completion->get_data($cm, false, $userid);
 165  
 166          // Return the current state of completion.
 167          return [
 168              'cmid' => $completiondata->coursemoduleid,
 169              'userid' => $completiondata->userid,
 170              'state' => $completiondata->completionstate,
 171              'timecompleted' => $completiondata->timemodified,
 172              'overrideby' => $completiondata->overrideby,
 173              'tracking' => $completion->is_enabled($cm)
 174          ];
 175      }
 176  
 177      /**
 178       * Describes the override_activity_completion_status return value.
 179       *
 180       * @return external_single_structure
 181       * @since Moodle 3.4
 182       */
 183      public static function override_activity_completion_status_returns() {
 184  
 185          return new external_single_structure(
 186              array(
 187                  'cmid' => new external_value(PARAM_INT, 'The course module id'),
 188                  'userid' => new external_value(PARAM_INT, 'The user id to which the completion info belongs'),
 189                  'state'   => new external_value(PARAM_INT, 'The current completion state.'),
 190                  'timecompleted' => new external_value(PARAM_INT, 'time of completion'),
 191                  'overrideby' => new external_value(PARAM_INT, 'The user id who has overriden the status, or null'),
 192                  'tracking'      => new external_value(PARAM_INT, 'type of tracking:
 193                                                                      0 means none, 1 manual, 2 automatic'),
 194              )
 195          );
 196      }
 197  
 198      /**
 199       * Returns description of method parameters
 200       *
 201       * @return external_function_parameters
 202       * @since Moodle 2.9
 203       */
 204      public static function get_activities_completion_status_parameters() {
 205          return new external_function_parameters(
 206              array(
 207                  'courseid' => new external_value(PARAM_INT, 'Course ID'),
 208                  'userid'   => new external_value(PARAM_INT, 'User ID'),
 209              )
 210          );
 211      }
 212  
 213      /**
 214       * Get Activities completion status
 215       *
 216       * @param int $courseid ID of the Course
 217       * @param int $userid ID of the User
 218       * @return array of activities progress and warnings
 219       * @throws moodle_exception
 220       * @since Moodle 2.9
 221       * @throws moodle_exception
 222       */
 223      public static function get_activities_completion_status($courseid, $userid) {
 224          global $CFG, $USER, $PAGE;
 225          require_once($CFG->libdir . '/grouplib.php');
 226  
 227          $warnings = array();
 228          $arrayparams = array(
 229              'courseid' => $courseid,
 230              'userid'   => $userid,
 231          );
 232  
 233          $params = self::validate_parameters(self::get_activities_completion_status_parameters(), $arrayparams);
 234  
 235          $course = get_course($params['courseid']);
 236          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
 237          core_user::require_active_user($user);
 238  
 239          $context = context_course::instance($course->id);
 240          self::validate_context($context);
 241  
 242          // Check that current user have permissions to see this user's activities.
 243          if ($user->id != $USER->id) {
 244              require_capability('report/progress:view', $context);
 245              if (!groups_user_groups_visible($course, $user->id)) {
 246                  // We are not in the same group!
 247                  throw new moodle_exception('accessdenied', 'admin');
 248              }
 249          }
 250  
 251          $completion = new completion_info($course);
 252          $activities = $completion->get_activities();
 253  
 254          $results = array();
 255          foreach ($activities as $activity) {
 256              // Check if current user has visibility on this activity.
 257              if (!$activity->uservisible) {
 258                  continue;
 259              }
 260              // Get progress information and state (we must use get_data because it works for all user roles in course).
 261              $exporter = new \core_completion\external\completion_info_exporter(
 262                  $course,
 263                  $activity,
 264                  $userid,
 265              );
 266              $renderer = $PAGE->get_renderer('core');
 267              $data = (array)$exporter->export($renderer);
 268              $results[] = array_merge([
 269                  'cmid'     => $activity->id,
 270                  'modname'  => $activity->modname,
 271                  'instance' => $activity->instance,
 272                  'tracking' => $activity->completion,
 273              ], $data);
 274          }
 275  
 276          $results = array(
 277              'statuses' => $results,
 278              'warnings' => $warnings
 279          );
 280          return $results;
 281      }
 282  
 283      /**
 284       * Returns description of method result value
 285       *
 286       * @return external_description
 287       * @since Moodle 2.9
 288       */
 289      public static function get_activities_completion_status_returns() {
 290          return new external_single_structure(
 291              array(
 292                  'statuses' => new external_multiple_structure(
 293                      new external_single_structure(
 294                          [
 295                              'cmid'          => new external_value(PARAM_INT, 'course module ID'),
 296                              'modname'       => new external_value(PARAM_PLUGIN, 'activity module name'),
 297                              'instance'      => new external_value(PARAM_INT, 'instance ID'),
 298                              'state'         => new external_value(PARAM_INT,
 299                                  "Completion state value:
 300                                      0 means incomplete,
 301                                      1 complete,
 302                                      2 complete pass,
 303                                      3 complete fail"
 304                                  ),
 305                              'timecompleted' => new external_value(PARAM_INT,
 306                                  'timestamp for completed activity'),
 307                              'tracking'      => new external_value(PARAM_INT,
 308                                  "type of tracking:
 309                                      0 means none,
 310                                      1 manual,
 311                                      2 automatic"
 312                                  ),
 313                              'overrideby' => new external_value(PARAM_INT,
 314                                  'The user id who has overriden the status, or null', VALUE_OPTIONAL),
 315                              'valueused' => new external_value(PARAM_BOOL,
 316                                  'Whether the completion status affects the availability of another activity.',
 317                                  VALUE_OPTIONAL),
 318                              'hascompletion' => new external_value(PARAM_BOOL,
 319                                  'Whether this activity module has completion enabled',
 320                                  VALUE_OPTIONAL),
 321                              'isautomatic' => new external_value(PARAM_BOOL,
 322                                  'Whether this activity module instance tracks completion automatically.',
 323                                  VALUE_OPTIONAL),
 324                              'istrackeduser' => new external_value(PARAM_BOOL,
 325                                  'Whether completion is being tracked for this user.',
 326                                  VALUE_OPTIONAL),
 327                              'uservisible' => new external_value(PARAM_BOOL,
 328                                  'Whether this activity is visible to the user.',
 329                                  VALUE_OPTIONAL),
 330                              'details' => new external_multiple_structure(
 331                                  new external_single_structure(
 332                                      [
 333                                          'rulename' => new external_value(PARAM_TEXT, 'Rule name'),
 334                                          'rulevalue' => new external_single_structure(
 335                                              [
 336                                                  'status' => new external_value(PARAM_INT, 'Completion status'),
 337                                                  'description' => new external_value(PARAM_TEXT, 'Completion description'),
 338                                              ]
 339                                          )
 340                                      ]
 341                                  ),
 342                                  'Completion status details',
 343                                  VALUE_DEFAULT,
 344                                  []
 345                              ),
 346  
 347                          ], 'Activity'
 348                      ), 'List of activities status'
 349                  ),
 350                  'warnings' => new external_warnings()
 351              )
 352          );
 353      }
 354  
 355      /**
 356       * Returns description of method parameters
 357       *
 358       * @return external_function_parameters
 359       * @since Moodle 2.9
 360       */
 361      public static function get_course_completion_status_parameters() {
 362          return new external_function_parameters(
 363              array(
 364                  'courseid' => new external_value(PARAM_INT, 'Course ID'),
 365                  'userid'   => new external_value(PARAM_INT, 'User ID'),
 366              )
 367          );
 368      }
 369      /**
 370       * Get Course completion status
 371       *
 372       * @param int $courseid ID of the Course
 373       * @param int $userid ID of the User
 374       * @return array of course completion status and warnings
 375       * @since Moodle 2.9
 376       * @throws moodle_exception
 377       */
 378      public static function get_course_completion_status($courseid, $userid) {
 379          global $CFG, $USER;
 380          require_once($CFG->libdir . '/grouplib.php');
 381  
 382          $warnings = array();
 383          $arrayparams = array(
 384              'courseid' => $courseid,
 385              'userid'   => $userid,
 386          );
 387          $params = self::validate_parameters(self::get_course_completion_status_parameters(), $arrayparams);
 388  
 389          $course = get_course($params['courseid']);
 390          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
 391          core_user::require_active_user($user);
 392  
 393          $context = context_course::instance($course->id);
 394          self::validate_context($context);
 395  
 396          // Can current user see user's course completion status?
 397          // This check verifies if completion is enabled because $course is mandatory.
 398          if (!completion_can_view_data($user->id, $course)) {
 399              throw new moodle_exception('cannotviewreport');
 400          }
 401  
 402          // The previous function doesn't check groups.
 403          if ($user->id != $USER->id) {
 404              if (!groups_user_groups_visible($course, $user->id)) {
 405                  // We are not in the same group!
 406                  throw new moodle_exception('accessdenied', 'admin');
 407              }
 408          }
 409  
 410          $info = new completion_info($course);
 411  
 412          // Check this user is enroled.
 413          if (!$info->is_tracked_user($user->id)) {
 414              if ($USER->id == $user->id) {
 415                  throw new moodle_exception('notenroled', 'completion');
 416              } else {
 417                  throw new moodle_exception('usernotenroled', 'completion');
 418              }
 419          }
 420  
 421          $completions = $info->get_completions($user->id);
 422          if (empty($completions)) {
 423              throw new moodle_exception('nocriteriaset', 'completion');
 424          }
 425  
 426          // Load course completion.
 427          $completionparams = array(
 428              'userid' => $user->id,
 429              'course' => $course->id,
 430          );
 431          $ccompletion = new completion_completion($completionparams);
 432  
 433          $completionrows = array();
 434          // Loop through course criteria.
 435          foreach ($completions as $completion) {
 436              $criteria = $completion->get_criteria();
 437  
 438              $completionrow = array();
 439              $completionrow['type'] = $criteria->criteriatype;
 440              $completionrow['title'] = $criteria->get_title();
 441              $completionrow['status'] = $completion->get_status();
 442              $completionrow['complete'] = $completion->is_complete();
 443              $completionrow['timecompleted'] = $completion->timecompleted;
 444              $completionrow['details'] = $criteria->get_details($completion);
 445              $completionrows[] = $completionrow;
 446          }
 447  
 448          $result = array(
 449                    'completed'   => $info->is_course_complete($user->id),
 450                    'aggregation' => $info->get_aggregation_method(),
 451                    'completions' => $completionrows
 452          );
 453  
 454          $results = array(
 455              'completionstatus' => $result,
 456              'warnings' => $warnings
 457          );
 458          return $results;
 459  
 460      }
 461      /**
 462       * Returns description of method result value
 463       *
 464       * @return external_description
 465       * @since Moodle 2.9
 466       */
 467      public static function get_course_completion_status_returns() {
 468          return new external_single_structure(
 469              array(
 470                  'completionstatus' => new external_single_structure(
 471                      array(
 472                          'completed'     => new external_value(PARAM_BOOL, 'true if the course is complete, false otherwise'),
 473                          'aggregation'   => new external_value(PARAM_INT, 'aggregation method 1 means all, 2 means any'),
 474                          'completions'   => new external_multiple_structure(
 475                              new external_single_structure(
 476                              array(
 477                                   'type'          => new external_value(PARAM_INT,   'Completion criteria type'),
 478                                   'title'         => new external_value(PARAM_TEXT,  'Completion criteria Title'),
 479                                   'status'        => new external_value(PARAM_NOTAGS, 'Completion status (Yes/No) a % or number'),
 480                                   'complete'      => new external_value(PARAM_BOOL,   'Completion status (true/false)'),
 481                                   'timecompleted' => new external_value(PARAM_INT,   'Timestamp for criteria completetion'),
 482                                   'details' => new external_single_structure(
 483                                       array(
 484                                           'type' => new external_value(PARAM_TEXT, 'Type description'),
 485                                           'criteria' => new external_value(PARAM_RAW, 'Criteria description'),
 486                                           'requirement' => new external_value(PARAM_TEXT, 'Requirement description'),
 487                                           'status' => new external_value(PARAM_RAW, 'Status description, can be anything'),
 488                                           ), 'details'),
 489                                   ), 'Completions'
 490                              ), ''
 491                           )
 492                      ), 'Course status'
 493                  ),
 494                  'warnings' => new external_warnings()
 495              ), 'Course completion status'
 496          );
 497      }
 498  
 499      /**
 500       * Describes the parameters for mark_course_self_completed.
 501       *
 502       * @return external_function_parameters
 503       * @since Moodle 3.0
 504       */
 505      public static function mark_course_self_completed_parameters() {
 506          return new external_function_parameters (
 507              array(
 508                  'courseid' => new external_value(PARAM_INT, 'Course ID')
 509              )
 510          );
 511      }
 512  
 513      /**
 514       * Update the course completion status for the current user (if course self-completion is enabled).
 515       *
 516       * @param  int $courseid    Course id
 517       * @return array            Result and possible warnings
 518       * @since Moodle 3.0
 519       * @throws moodle_exception
 520       */
 521      public static function mark_course_self_completed($courseid) {
 522          global $USER;
 523  
 524          $warnings = array();
 525          $params = self::validate_parameters(self::mark_course_self_completed_parameters(),
 526                                              array('courseid' => $courseid));
 527  
 528          $course = get_course($params['courseid']);
 529          $context = context_course::instance($course->id);
 530          self::validate_context($context);
 531  
 532          // Set up completion object and check it is enabled.
 533          $completion = new completion_info($course);
 534          if (!$completion->is_enabled()) {
 535              throw new moodle_exception('completionnotenabled', 'completion');
 536          }
 537  
 538          if (!$completion->is_tracked_user($USER->id)) {
 539              throw new moodle_exception('nottracked', 'completion');
 540          }
 541  
 542          $completion = $completion->get_completion($USER->id, COMPLETION_CRITERIA_TYPE_SELF);
 543  
 544          // Self completion criteria not enabled.
 545          if (!$completion) {
 546              throw new moodle_exception('noselfcompletioncriteria', 'completion');
 547          }
 548  
 549          // Check if the user has already marked himself as complete.
 550          if ($completion->is_complete()) {
 551              throw new moodle_exception('useralreadymarkedcomplete', 'completion');
 552          }
 553  
 554          // Mark the course complete.
 555          $completion->mark_complete();
 556  
 557          $result = array();
 558          $result['status'] = true;
 559          $result['warnings'] = $warnings;
 560          return $result;
 561      }
 562  
 563      /**
 564       * Describes the mark_course_self_completed return value.
 565       *
 566       * @return external_single_structure
 567       * @since Moodle 3.0
 568       */
 569      public static function mark_course_self_completed_returns() {
 570  
 571          return new external_single_structure(
 572              array(
 573                  'status'    => new external_value(PARAM_BOOL, 'status, true if success'),
 574                  'warnings'  => new external_warnings(),
 575              )
 576          );
 577      }
 578  
 579  }