Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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   * Class communication
  19   *
  20   * @package    core
  21   * @copyright  2017 Marina Glancy
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core\hub;
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  use webservice_xmlrpc_client;
  29  use moodle_exception;
  30  use curl;
  31  use stdClass;
  32  use coding_exception;
  33  use moodle_url;
  34  
  35  /**
  36   * Provides methods to communicate with the hub (sites directory) web services.
  37   *
  38   * @package    core
  39   * @copyright  2017 Marina Glancy
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class api {
  43  
  44      /** @var File type: Course screenshot */
  45      const HUB_SCREENSHOT_FILE_TYPE = 'screenshot';
  46  
  47      /** @var File type: Hub screenshot */
  48      const HUB_HUBSCREENSHOT_FILE_TYPE = 'hubscreenshot';
  49  
  50      /** @var File type: Backup */
  51      const HUB_BACKUP_FILE_TYPE = 'backup';
  52  
  53      /**
  54       * Calls a remote function exposed via web services on the hub.
  55       *
  56       * @param string $function name of WS function
  57       * @param array $data parameters of WS function
  58       * @param bool $allowpublic allow request without registration on the hub
  59       * @return mixed depends on the function
  60       * @throws moodle_exception
  61       */
  62      protected static function call($function, array $data = [], $allowpublic = false) {
  63  
  64          $token = registration::get_token() ?: 'publichub';
  65          if (!$allowpublic && $token === 'publichub') {
  66              // This will throw an exception.
  67              registration::require_registration();
  68          }
  69  
  70          return self::call_rest($token, $function, $data);
  71      }
  72  
  73      /**
  74       * Performs a REST request to the hub site (using the GET method).
  75       *
  76       * @param string $token
  77       * @param string $function
  78       * @param array $data
  79       * @return mixed
  80       * @throws moodle_exception
  81       */
  82      protected static function call_rest($token, $function, array $data) {
  83          $params = [
  84                  'wstoken' => $token,
  85                  'wsfunction' => $function,
  86                  'moodlewsrestformat' => 'json'
  87              ] + $data;
  88  
  89          $curl = new curl();
  90          $serverurl = HUB_MOODLEORGHUBURL . "/local/hub/webservice/webservices.php";
  91          $query = http_build_query($params, '', '&');
  92          $curloutput = @json_decode($curl->post($serverurl, $query), true);
  93          $info = $curl->get_info();
  94          if ($curl->get_errno()) {
  95              // Connection error.
  96              throw new moodle_exception('errorconnect', 'hub', '', $curl->error);
  97          } else if (isset($curloutput['exception'])) {
  98              // Exception occurred on the remote side.
  99              self::process_curl_exception($token, $curloutput);
 100          } else if ($info['http_code'] != 200) {
 101              throw new moodle_exception('errorconnect', 'hub', '', $info['http_code']);
 102          } else {
 103              return $curloutput;
 104          }
 105      }
 106  
 107      /**
 108       * Analyses exception received from the hub server.
 109       *
 110       * @param string $token token used for CURL request
 111       * @param array $curloutput output from CURL request
 112       * @throws moodle_exception
 113       */
 114      protected static function process_curl_exception($token, $curloutput) {
 115          if (!isset($curloutput['exception'])) {
 116              return;
 117          }
 118          if ($token === registration::get_token()) {
 119              // Check if registration token was rejected or there are other problems with registration.
 120              if (($curloutput['exception'] === 'moodle_exception' && $curloutput['errorcode'] === 'invalidtoken')
 121                      || $curloutput['exception'] === 'registration_exception') {
 122                  // Force admin to repeat site registration process.
 123                  registration::reset_token();
 124                  throw new moodle_exception('errorwstokenreset', 'hub', '', $curloutput['message']);
 125              }
 126          }
 127          throw new moodle_exception('errorws', 'hub', '', $curloutput['message']);
 128      }
 129  
 130      /**
 131       * Update site registration on the hub.
 132       *
 133       * @param array $siteinfo
 134       * @throws moodle_exception
 135       */
 136      public static function update_registration(array $siteinfo) {
 137          $params = array('siteinfo' => $siteinfo, 'validateurl' => 1);
 138          self::call('hub_update_site_info', $params);
 139      }
 140  
 141      /**
 142       * Returns information about the hub.
 143       *
 144       * Example of the return array:
 145       * {
 146       *     "courses": 384,
 147       *     "description": "Official Moodle sites directory.",
 148       *     "downloadablecourses": 0,
 149       *     "enrollablecourses": 0,
 150       *     "hublogo": 1,
 151       *     "language": "en",
 152       *     "name": "moodle",
 153       *     "sites": 274175,
 154       *     "url": "https://stats.moodle.org",
 155       *     "imgurl": "https://stats.moodle.org/local/hub/webservice/download.php?filetype=hubscreenshot"
 156       * }
 157       *
 158       * @return array
 159       * @throws moodle_exception
 160       */
 161      public static function get_hub_info() {
 162          $info = self::call('hub_get_info', [], true);
 163          $info['imgurl'] = new moodle_url(HUB_MOODLEORGHUBURL . '/local/hub/webservice/download.php',
 164              ['filetype' => self::HUB_HUBSCREENSHOT_FILE_TYPE]);
 165          return $info;
 166      }
 167  
 168      /**
 169       * Calls WS function hub_get_courses
 170       *
 171       * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
 172       *
 173       * Parameter $options may have any of these fields:
 174       * [
 175       *     'ids' => new external_multiple_structure(new external_value(PARAM_INTEGER, 'id of a course in the hub course
 176       *          directory'), 'ids of course', VALUE_OPTIONAL),
 177       *     'sitecourseids' => new external_multiple_structure(new external_value(PARAM_INTEGER, 'id of a course in the
 178       *          site'), 'ids of course in the site', VALUE_OPTIONAL),
 179       *     'coverage' => new external_value(PARAM_TEXT, 'coverage', VALUE_OPTIONAL),
 180       *     'licenceshortname' => new external_value(PARAM_ALPHANUMEXT, 'licence short name', VALUE_OPTIONAL),
 181       *     'subject' => new external_value(PARAM_ALPHANUM, 'subject', VALUE_OPTIONAL),
 182       *     'audience' => new external_value(PARAM_ALPHA, 'audience', VALUE_OPTIONAL),
 183       *     'educationallevel' => new external_value(PARAM_ALPHA, 'educational level', VALUE_OPTIONAL),
 184       *     'language' => new external_value(PARAM_ALPHANUMEXT, 'language', VALUE_OPTIONAL),
 185       *     'orderby' => new external_value(PARAM_ALPHA, 'orderby method: newest, eldest, publisher, fullname,
 186       *          ratingaverage', VALUE_OPTIONAL),
 187       *     'givememore' => new external_value(PARAM_INT, 'next range of result - range size being set by the hub
 188       *          server ', VALUE_OPTIONAL),
 189       *     'allsitecourses' => new external_value(PARAM_INTEGER,
 190       *          'if 1 return all not visible and visible courses whose siteid is the site
 191       *          matching token. Only courses of this site are returned.
 192       *          givememore parameter is ignored if this param = 1.
 193       *          In case of public token access, this param option is ignored', VALUE_DEFAULT, 0),
 194       * ]
 195       *
 196       * Each course in the returned array of courses will have fields:
 197       * [
 198       *     'id' => new external_value(PARAM_INTEGER, 'id'),
 199       *     'fullname' => new external_value(PARAM_TEXT, 'course name'),
 200       *     'shortname' => new external_value(PARAM_TEXT, 'course short name'),
 201       *     'description' => new external_value(PARAM_TEXT, 'course description'),
 202       *     'language' => new external_value(PARAM_ALPHANUMEXT, 'course language'),
 203       *     'publishername' => new external_value(PARAM_TEXT, 'publisher name'),
 204       *     'publisheremail' => new external_value(PARAM_EMAIL, 'publisher email', VALUE_OPTIONAL),
 205       *     'privacy' => new external_value(PARAM_INT, 'privacy: published or not', VALUE_OPTIONAL),
 206       *     'sitecourseid' => new external_value(PARAM_INT, 'course id on the site', VALUE_OPTIONAL),
 207       *     'contributornames' => new external_value(PARAM_TEXT, 'contributor names', VALUE_OPTIONAL),
 208       *     'coverage' => new external_value(PARAM_TEXT, 'coverage', VALUE_OPTIONAL),
 209       *     'creatorname' => new external_value(PARAM_TEXT, 'creator name'),
 210       *     'licenceshortname' => new external_value(PARAM_ALPHANUMEXT, 'licence short name'),
 211       *     'subject' => new external_value(PARAM_ALPHANUM, 'subject'),
 212       *     'audience' => new external_value(PARAM_ALPHA, 'audience'),
 213       *     'educationallevel' => new external_value(PARAM_ALPHA, 'educational level'),
 214       *     'creatornotes' => new external_value(PARAM_RAW, 'creator notes'),
 215       *     'creatornotesformat' => new external_value(PARAM_INTEGER, 'notes format'),
 216       *     'demourl' => new external_value(PARAM_URL, 'demo URL', VALUE_OPTIONAL),
 217       *     'courseurl' => new external_value(PARAM_URL, 'course URL', VALUE_OPTIONAL),
 218       *     'backupsize' => new external_value(PARAM_INT, 'course backup size in bytes', VALUE_OPTIONAL),
 219       *     'enrollable' => new external_value(PARAM_BOOL, 'is the course enrollable'),
 220       *     'screenshots' => new external_value(PARAM_INT, 'total number of screenshots'),
 221       *     'timemodified' => new external_value(PARAM_INT, 'time of last modification - timestamp'),
 222       *     'contents' => new external_multiple_structure(new external_single_structure(
 223       *         array(
 224       *             'moduletype' => new external_value(PARAM_ALPHA, 'the type of module (activity/block)'),
 225       *             'modulename' => new external_value(PARAM_TEXT, 'the name of the module (forum, resource etc)'),
 226       *             'contentcount' => new external_value(PARAM_INT, 'how many time the module is used in the course'),
 227       *         )), 'contents', VALUE_OPTIONAL),
 228       *     'rating' => new external_single_structure (
 229       *         array(
 230       *              'aggregate' =>  new external_value(PARAM_FLOAT, 'Rating average', VALUE_OPTIONAL),
 231       *              'scaleid' => new external_value(PARAM_INT, 'Rating scale'),
 232       *              'count' => new external_value(PARAM_INT, 'Rating count'),
 233       *         ), 'rating', VALUE_OPTIONAL),
 234       *     'comments' => new external_multiple_structure(new external_single_structure (
 235       *          array(
 236       *              'comment' => new external_value(PARAM_TEXT, 'the comment'),
 237       *              'commentator' => new external_value(PARAM_TEXT, 'the name of commentator'),
 238       *              'date' => new external_value(PARAM_INT, 'date of the comment'),
 239       *         )), 'contents', VALUE_OPTIONAL),
 240       *     'outcomes' => new external_multiple_structure(new external_single_structure(
 241       *          array(
 242       *              'fullname' => new external_value(PARAM_TEXT, 'the outcome fullname')
 243       *          )), 'outcomes', VALUE_OPTIONAL)
 244       * ]
 245       *
 246       * Additional fields for each course:
 247       *      'screenshotbaseurl' (moodle_url) URL of the first screenshot, only set if $course['screenshots']>0
 248       *      'commenturl' (moodle_url) URL for comments
 249       *
 250       * @param string $search search string
 251       * @param bool $downloadable return downloadable courses
 252       * @param bool $enrollable return enrollable courses
 253       * @param array|\stdClass $options other options from the list of allowed options:
 254       *              'ids', 'sitecourseids', 'coverage', 'licenceshortname', 'subject', 'audience',
 255       *              'educationallevel', 'language', 'orderby', 'givememore', 'allsitecourses'
 256       * @return array of two elements: [$courses, $coursetotal]
 257       * @throws \coding_exception
 258       * @throws moodle_exception
 259       */
 260      public static function get_courses($search, $downloadable, $enrollable, $options) {
 261          debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
 262          return [[], 0];
 263      }
 264  
 265      /**
 266       * Unregister the site
 267       *
 268       * @throws moodle_exception
 269       */
 270      public static function unregister_site() {
 271          global $CFG;
 272          self::call('hub_unregister_site', ['url' => [$CFG->wwwroot]]);
 273      }
 274  
 275      /**
 276       * Unpublish courses
 277       *
 278       * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
 279       *
 280       * @param int[]|int $courseids
 281       * @throws moodle_exception
 282       */
 283      public static function unregister_courses($courseids) {
 284          debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
 285      }
 286  
 287      /**
 288       * Publish one course
 289       *
 290       * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
 291       *
 292       * Expected contents of $courseinfo:
 293       * [
 294       *     'sitecourseid' => new external_value(PARAM_INT, 'the id of the course on the publishing site'),
 295       *     'fullname' => new external_value(PARAM_TEXT, 'course name'),
 296       *     'shortname' => new external_value(PARAM_TEXT, 'course short name'),
 297       *     'description' => new external_value(PARAM_TEXT, 'course description'),
 298       *     'language' => new external_value(PARAM_ALPHANUMEXT, 'course language'),
 299       *     'publishername' => new external_value(PARAM_TEXT, 'publisher name'),
 300       *     'publisheremail' => new external_value(PARAM_EMAIL, 'publisher email'),
 301       *     'contributornames' => new external_value(PARAM_TEXT, 'contributor names'),
 302       *     'coverage' => new external_value(PARAM_TEXT, 'coverage'),
 303       *     'creatorname' => new external_value(PARAM_TEXT, 'creator name'),
 304       *     'licenceshortname' => new external_value(PARAM_ALPHANUMEXT, 'licence short name'),
 305       *     'subject' => new external_value(PARAM_ALPHANUM, 'subject'),
 306       *     'audience' => new external_value(PARAM_ALPHA, 'audience'),
 307       *     'educationallevel' => new external_value(PARAM_ALPHA, 'educational level'),
 308       *     'creatornotes' => new external_value(PARAM_RAW, 'creator notes'),
 309       *     'creatornotesformat' => new external_value(PARAM_INTEGER, 'notes format'),
 310       *     'demourl' => new external_value(PARAM_URL, 'demo URL', VALUE_OPTIONAL),
 311       *     'courseurl' => new external_value(PARAM_URL, 'course URL', VALUE_OPTIONAL),
 312       *     'enrollable' => new external_value(PARAM_BOOL, 'is the course enrollable', VALUE_DEFAULT, 0),
 313       *     'screenshots' => new external_value(PARAM_INT, 'the number of screenhots', VALUE_OPTIONAL),
 314       *     'deletescreenshots' => new external_value(PARAM_INT, 'ask to delete all the existing screenshot files
 315       *          (it does not reset the screenshot number)', VALUE_DEFAULT, 0),
 316       *     'contents' => new external_multiple_structure(new external_single_structure(
 317       *          array(
 318       *              'moduletype' => new external_value(PARAM_ALPHA, 'the type of module (activity/block)'),
 319       *              'modulename' => new external_value(PARAM_TEXT, 'the name of the module (forum, resource etc)'),
 320       *              'contentcount' => new external_value(PARAM_INT, 'how many time the module is used in the course'),
 321       *          )), 'contents', VALUE_OPTIONAL),
 322       *     'outcomes' => new external_multiple_structure(new external_single_structure(
 323       *         array(
 324       *              'fullname' => new external_value(PARAM_TEXT, 'the outcome fullname')
 325       *          )), 'outcomes', VALUE_OPTIONAL)
 326       * ]
 327       *
 328       * @param array|\stdClass $courseinfo
 329       * @return int id of the published course on the hub
 330       * @throws moodle_exception if the communication with the hub failed or the course could not be published
 331       */
 332      public static function register_course($courseinfo) {
 333          debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
 334          throw new moodle_exception('errorcoursewronglypublished', 'hub');
 335      }
 336  
 337      /**
 338       * Uploads a screenshot for the published course
 339       *
 340       * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
 341       *
 342       * @param int $hubcourseid id of the published course on the hub, it must be published from this site
 343       * @param \stored_file $file
 344       * @param int $screenshotnumber ordinal number of the screenshot
 345       */
 346      public static function add_screenshot($hubcourseid, \stored_file $file, $screenshotnumber) {
 347          debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
 348      }
 349  
 350      /**
 351       * Downloads course backup
 352       *
 353       * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
 354       *
 355       * @param int $hubcourseid id of the course on the hub
 356       * @param string $path local path (in tempdir) to save the downloaded backup to.
 357       */
 358      public static function download_course_backup($hubcourseid, $path) {
 359          debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
 360      }
 361  
 362      /**
 363       * Uploads a course backup
 364       *
 365       * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
 366       *
 367       * @param int $hubcourseid id of the published course on the hub, it must be published from this site
 368       * @param \stored_file $backupfile
 369       */
 370      public static function upload_course_backup($hubcourseid, \stored_file $backupfile) {
 371          debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
 372      }
 373  }