Search moodle.org's
Developer Documentation

See Release Notes

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

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

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