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 400] [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   * Communicate with backpacks.
  19   *
  20   * @copyright  2020 Tung Thai based on Totara Learning Solutions Ltd {@link http://www.totaralms.com/} dode
  21   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22   * @author     Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
  23   */
  24  
  25  namespace core_badges;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  require_once($CFG->libdir . '/filelib.php');
  30  
  31  use cache;
  32  use coding_exception;
  33  use context_system;
  34  use moodle_url;
  35  use core_badges\backpack_api2p1_mapping;
  36  use core_badges\oauth2\client;
  37  use curl;
  38  use stdClass;
  39  
  40  /**
  41   * To process badges with backpack and control api request and this class using for Open Badge API v2.1 methods.
  42   *
  43   * @package   core_badges
  44   * @copyright  2020 Tung Thai
  45   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  46   */
  47  class backpack_api2p1 {
  48  
  49      /** @var object is the external backpack. */
  50      private $externalbackpack;
  51  
  52      /** @var array define api mapping. */
  53      private $mappings = [];
  54  
  55      /** @var false|null|stdClass|\core_badges\backpack_api2p1 to */
  56      private $tokendata;
  57  
  58      /** @var null clienid. */
  59      private $clientid = null;
  60  
  61      /** @var null version api of the backpack. */
  62      protected $backpackapiversion;
  63  
  64      /** @var null api URL of the backpack. */
  65      protected $backpackapiurl = '';
  66  
  67      /**
  68       * backpack_api2p1 constructor.
  69       *
  70       * @param object $externalbackpack object
  71       * @throws coding_exception error message
  72       */
  73      public function __construct($externalbackpack) {
  74  
  75          if (!empty($externalbackpack)) {
  76              $this->externalbackpack = $externalbackpack;
  77              $this->backpackapiversion = $externalbackpack->apiversion;
  78              $this->backpackapiurl = $externalbackpack->backpackapiurl;
  79              $this->get_clientid = $this->get_clientid($externalbackpack->oauth2_issuerid);
  80  
  81              if (!($this->tokendata = $this->get_stored_token($externalbackpack->id))
  82                  && $this->backpackapiversion != OPEN_BADGES_V2P1) {
  83                  throw new coding_exception('Backpack incorrect');
  84              }
  85          }
  86  
  87          $this->define_mappings();
  88      }
  89  
  90  
  91      /**
  92       * Define the mappings supported by this usage and api version.
  93       */
  94      private function define_mappings() {
  95          if ($this->backpackapiversion == OPEN_BADGES_V2P1) {
  96  
  97              $mapping = [];
  98              $mapping[] = [
  99                  'post.assertions',                               // Action.
 100                  '[URL]/assertions',   // URL
 101                  '[PARAM]',                                  // Post params.
 102                  false,                                      // Multiple.
 103                  'post',                                     // Method.
 104                  true,                                       // JSON Encoded.
 105                  true                                        // Auth required.
 106              ];
 107  
 108              $mapping[] = [
 109                  'get.assertions',                               // Action.
 110                  '[URL]/assertions',   // URL
 111                  '[PARAM]',                                  // Post params.
 112                  false,                                      // Multiple.
 113                  'get',                                     // Method.
 114                  true,                                       // JSON Encoded.
 115                  true                                        // Auth required.
 116              ];
 117  
 118              foreach ($mapping as $map) {
 119                  $map[] = false; // Site api function.
 120                  $map[] = OPEN_BADGES_V2P1; // V2 function.
 121                  $this->mappings[] = new backpack_api2p1_mapping(...$map);
 122              }
 123  
 124          }
 125      }
 126  
 127      /**
 128       * Disconnect the backpack from this user.
 129       *
 130       * @param object $backpack to disconnect.
 131       * @return bool
 132       * @throws \dml_exception
 133       */
 134      public function disconnect_backpack($backpack) {
 135          global $USER, $DB;
 136  
 137          if ($backpack) {
 138              $DB->delete_records_select('badge_external', 'backpackid = :backpack', ['backpack' => $backpack->id]);
 139              $DB->delete_records('badge_backpack', ['id' => $backpack->id]);
 140              $DB->delete_records('badge_backpack_oauth2', ['externalbackpackid' => $this->externalbackpack->id,
 141                  'userid' => $USER->id]);
 142  
 143              return true;
 144          }
 145          return false;
 146      }
 147  
 148      /**
 149       * Make an api request.
 150       *
 151       * @param string $action The api function.
 152       * @param string $postdata The body of the api request.
 153       * @return mixed
 154       */
 155      public function curl_request($action, $postdata = null) {
 156          $tokenkey = $this->tokendata->token;
 157          foreach ($this->mappings as $mapping) {
 158              if ($mapping->is_match($action)) {
 159                  return $mapping->request(
 160                      $this->backpackapiurl,
 161                      $tokenkey,
 162                      $postdata
 163                  );
 164              }
 165          }
 166  
 167          throw new coding_exception('Unknown request');
 168      }
 169  
 170      /**
 171       * Get token.
 172       *
 173       * @param int $externalbackpackid ID of external backpack.
 174       * @return oauth2\badge_backpack_oauth2|false|stdClass|null
 175       */
 176      protected function get_stored_token($externalbackpackid) {
 177          global $USER;
 178  
 179          $token = \core_badges\oauth2\badge_backpack_oauth2::get_record(
 180              ['externalbackpackid' => $externalbackpackid, 'userid' => $USER->id]);
 181          if ($token !== false) {
 182              $token = $token->to_record();
 183              return $token;
 184          }
 185          return null;
 186      }
 187  
 188      /**
 189       * Get client id.
 190       *
 191       * @param int $issuerid id of Oauth2 service.
 192       * @throws coding_exception
 193       */
 194      private function get_clientid($issuerid) {
 195          $issuer = \core\oauth2\api::get_issuer($issuerid);
 196          if (!empty($issuer)) {
 197              $this->clientid = $issuer->get('clientid');
 198          }
 199      }
 200  
 201      /**
 202       * Export a badge to the backpack site.
 203       *
 204       * @param string $hash of badge issued.
 205       * @return array
 206       * @throws \moodle_exception
 207       * @throws coding_exception
 208       */
 209      public function put_assertions($hash) {
 210          $data = [];
 211          if (!$hash) {
 212              return false;
 213          }
 214  
 215          $issuer = new \core\oauth2\issuer($this->externalbackpack->oauth2_issuerid);
 216          $client = new client($issuer, new moodle_url('/badges/mybadges.php'), '', $this->externalbackpack);
 217          if (!$client->is_logged_in()) {
 218              $redirecturl = new moodle_url('/badges/mybadges.php', ['error' => 'backpackexporterror']);
 219              redirect($redirecturl);
 220          }
 221  
 222          $this->tokendata = $this->get_stored_token($this->externalbackpack->id);
 223  
 224          $assertion = new \core_badges_assertion($hash, OPEN_BADGES_V2);
 225          $data['assertion'] = $assertion->get_badge_assertion();
 226          $response = $this->curl_request('post.assertions', $data);
 227          if ($response && isset($response->status->statusCode) && $response->status->statusCode == 200) {
 228              $msg['status'] = \core\output\notification::NOTIFY_SUCCESS;
 229              $msg['message'] = get_string('addedtobackpack', 'badges');
 230          } else {
 231              $msg['status'] = \core\output\notification::NOTIFY_ERROR;
 232              $msg['message'] = get_string('backpackexporterror', 'badges', $data['assertion']['badge']['name']);
 233          }
 234          return $msg;
 235      }
 236  }