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 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  namespace tool_brickfield;
  18  
  19  defined('MOODLE_INTERNAL') || die;
  20  
  21  // The curl class is in filelib.
  22  global $CFG;
  23  require_once("{$CFG->libdir}/filelib.php");
  24  
  25  use curl;
  26  use moodle_url;
  27  
  28  /**
  29   * Class brickfieldconnect. Contains all function to connect to Brickfield external services.
  30   *
  31   * @package     tool_brickfield
  32   * @author      2020 Onwards Mike Churchward <mike@brickfieldlabs.ie>
  33   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL
  34   */
  35  class brickfieldconnect extends curl {
  36  
  37      /** @var string The base api uri. */
  38      private static $baseapiuri = 'https://api.mybrickfield.ie/moodle/';
  39  
  40      /** @var array Endpoint details for setting and checking a site registration */
  41      const ACTION_CHECK_REGISTRATION = [
  42          'endpoint' => 'checkRegister',
  43          'method' => 'get',
  44      ];
  45  
  46      /** @var array Endpoint details for sending site summary data */
  47      const ACTION_SEND_SUMMARY = [
  48          'endpoint' => 'summary',
  49          'method' => 'post',
  50      ];
  51  
  52      /**
  53       * Object method to test whether site is already registered.
  54       * @return bool
  55       */
  56      public function is_registered(): bool {
  57          return !empty($this->get_registration_id_for_credentials());
  58      }
  59  
  60      /**
  61       * Update registration of this site.
  62       * @param   string $apikey The API key to use for the registration attempt
  63       * @param   string $secretkey The secret key to use
  64       * @return  bool
  65       */
  66      public function update_registration(string $apikey, string $secretkey): bool {
  67          $registrationid = $this->get_registration_id_for_credentials($apikey, $secretkey);
  68          if (empty($registrationid)) {
  69              return false;
  70          }
  71  
  72          (new registration())->set_siteid($registrationid);
  73          return true;
  74      }
  75  
  76      /**
  77       * Send the summary data to Brickfield.
  78       * @return bool
  79       * @throws \dml_exception
  80       */
  81      public function send_summary(): bool {
  82          // Run a registration check.
  83          if (!(new registration())->validate()) {
  84              return false;
  85          }
  86  
  87          $headers = $this->get_common_headers();
  88  
  89          // Sanity-check $headers 'id' value.
  90          if (!isset($headers['id'])) {
  91              return false;
  92          }
  93  
  94          $this->set_headers($headers);
  95          $summary = accessibility::get_summary_data($headers['id']);
  96          $body = json_encode($summary, JSON_UNESCAPED_SLASHES);
  97          $result = json_decode($this->call(self::ACTION_SEND_SUMMARY, $body));
  98          if (is_object($result) && ((int)$result->statusCode === 200)) {
  99              return true;
 100          } else {
 101              return false;
 102          }
 103      }
 104  
 105      /**
 106       * Get the URL required for the command.
 107       *
 108       * @param   array $command The command to call, for example see self::ACTION_REGISTER
 109       * @return  string The complete URL
 110       */
 111      protected function get_url_for_command(array $command): string {
 112          return $this->get_baseapiuri() . $command['endpoint'];
 113      }
 114  
 115      /**
 116       * Call the specified command.
 117       *
 118       * @param array $command The command to call, for example see self::ACTION_REGISTER
 119       * @param array|string $params The params provided to the call
 120       * @return  string The response body
 121       */
 122      protected function call(array $command, $params = ''): string {
 123          $url = $this->get_url_for_command($command);
 124          if ($command['method'] === 'get') {
 125              return $this->get($url, $params);
 126          }
 127  
 128          if ($command['method'] === 'post') {
 129              return $this->post($url, $params);
 130          }
 131  
 132          return '';
 133      }
 134  
 135      /**
 136       * Get the common headers used for all calls to the Brickfields endpoints.
 137       *
 138       * @return  array
 139       */
 140      protected function get_common_headers(): array {
 141          $headers = [
 142              'Cache-Control' => 'no-cache',
 143              'Content-Type' => 'application/json',
 144              'siteurl' => static::get_siteurl(),
 145          ];
 146  
 147          if (static::has_registration_key()) {
 148              $registration = new registration();
 149              $headers['secret'] = $registration->get_api_key();
 150              $headers['userhash'] = $registration->get_secret_key();
 151              $headers['id'] = $registration->get_siteid();
 152          }
 153  
 154          return $headers;
 155      }
 156  
 157      /**
 158       * Set headers on the request from the specified list of headers.
 159       *
 160       * @param   array $headers An array of header name => value
 161       */
 162      protected function set_headers(array $headers) {
 163          foreach ($headers as $key => $value) {
 164              $this->setHeader("{$key}: {$value}");
 165          }
 166      }
 167  
 168      /**
 169       * Whether the site currently has a registration key stored.
 170       *
 171       * @return  bool
 172       */
 173      protected function has_registration_key(): bool {
 174          $registration = new registration();
 175          $localkey = $registration->get_api_key();
 176          $localhash = $registration->get_secret_key();
 177          $localid = $registration->get_siteid();
 178  
 179          if (!$localhash || !$localkey || !$localid) {
 180              return false;
 181          }
 182  
 183          return true;
 184      }
 185  
 186      /**
 187       * Get a normalised URL for the site.
 188       *
 189       * @return  string
 190       */
 191      protected function get_siteurl(): string {
 192          return (new moodle_url('/'))->out(false);
 193      }
 194  
 195      /**
 196       * Get the registration ID for the given set of credentials.
 197       * @param   null|string $apikey The API key to use for the registration attempt
 198       * @param   null|string $secretkey The secret key to use
 199       * @return  null|string The registration ID if registration was successful, or null if not
 200       */
 201      protected function get_registration_id_for_credentials(string $apikey = null, string $secretkey = null): string {
 202          $headers = $this->get_common_headers();
 203          if ($apikey || $secretkey) {
 204              $headers['secret'] = $apikey;
 205              $headers['userhash'] = $secretkey;
 206          } else if (!$this->has_registration_key()) {
 207              return '';
 208          }
 209  
 210          $this->set_headers($headers);
 211          $response = $this->call(self::ACTION_CHECK_REGISTRATION);
 212  
 213          if ((int)$this->info['http_code'] !== 200) {
 214              // The response was unsuccessful.
 215              return '';
 216          }
 217  
 218          $result = json_decode($response);
 219          if (!$result) {
 220              // The response could not be decoded.
 221              return '';
 222          }
 223  
 224          if ((int)$result->statusCode !== 200) {
 225              // The data from the response suggests a failure.
 226              return '';
 227          }
 228  
 229          // Decode the actual result.
 230          $registrationdata = json_decode($result->body);
 231          if (empty($registrationdata) || !is_array($registrationdata)) {
 232              // Unable to decode the body of the response.
 233              return '';
 234          }
 235  
 236          if (!property_exists($registrationdata[0], 'id') || !property_exists($registrationdata[0]->id, 'N')) {
 237              // Unable to find a valid id in the response.
 238              return '';
 239          }
 240  
 241          return $registrationdata[0]->id->N;
 242      }
 243  
 244      /**
 245       * Get the check registration API URI.
 246       * @return string
 247       * @throws dml_exception
 248       */
 249      protected function get_baseapiuri(): string {
 250          $baseapiuri = get_config(manager::PLUGINNAME, 'baseapiuri');
 251          if (!empty($baseapiuri)) {
 252              return $baseapiuri;
 253          } else {
 254              set_config('baseapiuri', self::$baseapiuri, manager::PLUGINNAME);
 255              return self::$baseapiuri;
 256          }
 257      }
 258  }