AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** Communicate with backpacks.** @copyright 2020 Tung Thai based on Totara Learning Solutions Ltd {@link http://www.totaralms.com/} dode* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>*/namespace core_badges;defined('MOODLE_INTERNAL') || die();require_once($CFG->libdir . '/filelib.php');use cache;use coding_exception;use context_system;use moodle_url;use core_badges\backpack_api2p1_mapping;use core_badges\oauth2\client;use curl;use stdClass;use core\oauth2\issuer;use core\oauth2\endpoint;use core\oauth2\discovery\imsbadgeconnect;/*** To process badges with backpack and control api request and this class using for Open Badge API v2.1 methods.** @package core_badges* @copyright 2020 Tung Thai* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class backpack_api2p1 {/** @var object is the external backpack. */private $externalbackpack;/** @var array define api mapping. */private $mappings = [];/** @var false|null|stdClass|\core_badges\backpack_api2p1 to */private $tokendata;/** @var null clienid. */private $clientid = null;/** @var null version api of the backpack. */protected $backpackapiversion;/** @var issuer The OAuth2 Issuer for this backpack */protected issuer $issuer;/** @var endpoint The apiBase endpoint */protected endpoint $apibase;/*** backpack_api2p1 constructor.** @param object $externalbackpack object* @throws coding_exception error message*/public function __construct($externalbackpack) {if (!empty($externalbackpack)) {$this->externalbackpack = $externalbackpack;$this->backpackapiversion = $externalbackpack->apiversion;$this->get_clientid($externalbackpack->oauth2_issuerid);if (!($this->tokendata = $this->get_stored_token($externalbackpack->id))&& $this->backpackapiversion != OPEN_BADGES_V2P1) {throw new coding_exception('Backpack incorrect');}}$this->define_mappings();}/*** Initialises or returns the OAuth2 issuer associated to this backpack.** @return issuer*/protected function get_issuer(): issuer {if (!isset($this->issuer)) {$this->issuer = new \core\oauth2\issuer($this->externalbackpack->oauth2_issuerid);}return $this->issuer;}/*** Gets the apiBase url associated to this backpack.** @return string*/protected function get_api_base_url(): string {if (!isset($this->apibase)) {$apibase = endpoint::get_record(['issuerid' => $this->externalbackpack->oauth2_issuerid,'name' => 'apiBase',]);if (empty($apibase)) {imsbadgeconnect::create_endpoints($this->get_issuer());$apibase = endpoint::get_record(['issuerid' => $this->externalbackpack->oauth2_issuerid,'name' => 'apiBase',]);}$this->apibase = $apibase;}return $this->apibase->get('url');}/*** Define the mappings supported by this usage and api version.*/private function define_mappings() {if ($this->backpackapiversion == OPEN_BADGES_V2P1) {$mapping = [];$mapping[] = ['post.assertions', // Action.'[URL]/assertions', // URL'[PARAM]', // Post params.false, // Multiple.'post', // Method.true, // JSON Encoded.true // Auth required.];$mapping[] = ['get.assertions', // Action.'[URL]/assertions', // URL'[PARAM]', // Post params.false, // Multiple.'get', // Method.true, // JSON Encoded.true // Auth required.];foreach ($mapping as $map) {$map[] = false; // Site api function.$map[] = OPEN_BADGES_V2P1; // V2 function.$this->mappings[] = new backpack_api2p1_mapping(...$map);}}}/*** Disconnect the backpack from this user.** @param object $backpack to disconnect.* @return bool* @throws \dml_exception*/public function disconnect_backpack($backpack) {global $USER, $DB;if ($backpack) {$DB->delete_records_select('badge_external', 'backpackid = :backpack', ['backpack' => $backpack->id]);$DB->delete_records('badge_backpack', ['id' => $backpack->id]);$DB->delete_records('badge_backpack_oauth2', ['externalbackpackid' => $this->externalbackpack->id,'userid' => $USER->id]);return true;}return false;}/*** Make an api request.** @param string $action The api function.* @param string $postdata The body of the api request.* @return mixed*/public function curl_request($action, $postdata = null) {$tokenkey = $this->tokendata->token;foreach ($this->mappings as $mapping) {if ($mapping->is_match($action)) {return $mapping->request($this->get_api_base_url(),$tokenkey,$postdata);}}throw new coding_exception('Unknown request');}/*** Get token.** @param int $externalbackpackid ID of external backpack.* @return oauth2\badge_backpack_oauth2|false|stdClass|null*/protected function get_stored_token($externalbackpackid) {global $USER;$token = \core_badges\oauth2\badge_backpack_oauth2::get_record(['externalbackpackid' => $externalbackpackid, 'userid' => $USER->id]);if ($token !== false) {$token = $token->to_record();return $token;}return null;}/*** Get client id.** @param int $issuerid id of Oauth2 service.* @throws coding_exception*/private function get_clientid($issuerid) {$issuer = \core\oauth2\api::get_issuer($issuerid);if (!empty($issuer)) {$this->issuer = $issuer;$this->clientid = $issuer->get('clientid');}}/*** Export a badge to the backpack site.** @param string $hash of badge issued.* @return array* @throws \moodle_exception* @throws coding_exception*/public function put_assertions($hash) {$data = [];if (!$hash) {return false;}$issuer = $this->get_issuer();$client = new client($issuer, new moodle_url('/badges/mybadges.php'), '', $this->externalbackpack);if (!$client->is_logged_in()) {$redirecturl = new moodle_url('/badges/mybadges.php', ['error' => 'backpackexporterror']);redirect($redirecturl);}$this->tokendata = $this->get_stored_token($this->externalbackpack->id);$assertion = new \core_badges_assertion($hash, OPEN_BADGES_V2);$data['assertion'] = $assertion->get_badge_assertion();$response = $this->curl_request('post.assertions', $data);if ($response && isset($response->status->statusCode) && $response->status->statusCode == 200) {$msg['status'] = \core\output\notification::NOTIFY_SUCCESS;$msg['message'] = get_string('addedtobackpack', 'badges');} else {if ($response) {// Although the specification defines that status error is a string, some providers, like Badgr, are wrongly// returning an array. It has been reported, but adding these extra checks doesn't hurt, just in case.if (property_exists($response, 'status') &&is_object($response->status) &&property_exists($response->status, 'error')) {$statuserror = $response->status->error;if (is_array($statuserror)) {$statuserror = implode($statuserror);}} else if (property_exists($response, 'error')) {$statuserror = $response->error;if (property_exists($response, 'message')) {$statuserror .= '. Message: ' . $response->message;}}} else {$statuserror = 'Empty response';}$data = ['badgename' => $data['assertion']['badge']['name'],'error' => $statuserror,];$msg['status'] = \core\output\notification::NOTIFY_ERROR;$msg['message'] = get_string('backpackexporterrorwithinfo', 'badges', $data);}return $msg;}}