mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 00:12:56 +02:00
Merge branch 'MDL-66992-master' of https://github.com/tungthai/moodle
This commit is contained in:
commit
7ce7d21503
58
badges/backpack-connect.php
Normal file
58
badges/backpack-connect.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Connect to backpack site.
|
||||
*
|
||||
* @package core_badges
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
require_once($CFG->libdir . '/badgeslib.php');
|
||||
|
||||
$scope = optional_param('scope', '', PARAM_RAW);
|
||||
$action = optional_param('action', null, PARAM_RAW);
|
||||
|
||||
if (badges_open_badges_backpack_api() != OPEN_BADGES_V2P1) {
|
||||
throw new coding_exception('backpacks only support Open Badges V2.1');
|
||||
}
|
||||
|
||||
require_login();
|
||||
|
||||
$externalbackpack = badges_get_site_backpack($CFG->badges_site_backpack);
|
||||
$persistedissuer = \core\oauth2\issuer::get_record(['id' => $externalbackpack->oauth2_issuerid]);
|
||||
if ($persistedissuer) {
|
||||
$issuer = new \core\oauth2\issuer($externalbackpack->oauth2_issuerid);
|
||||
$returnurl = new moodle_url('/badges/backpack-connect.php',
|
||||
['action' => 'authorization', 'sesskey' => sesskey()]);
|
||||
|
||||
$client = new core_badges\oauth2\client($issuer, $returnurl, $scope, $externalbackpack);
|
||||
if ($client) {
|
||||
if (!$client->is_logged_in()) {
|
||||
redirect($client->get_login_url());
|
||||
}
|
||||
$wantsurl = new moodle_url('/badges/mybadges.php');
|
||||
$auth = new \core_badges\oauth2\auth();
|
||||
$auth->complete_data($client, $wantsurl);
|
||||
} else {
|
||||
throw new moodle_exception('Could not get an OAuth client.');
|
||||
}
|
||||
} else {
|
||||
throw new moodle_exception('Unknown OAuth client.');
|
||||
}
|
62
badges/backpack-export.php
Normal file
62
badges/backpack-export.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Export badges to the backpack site.
|
||||
*
|
||||
* @package core_badges
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
require_once($CFG->libdir . '/badgeslib.php');
|
||||
|
||||
if (badges_open_badges_backpack_api() != OPEN_BADGES_V2P1) {
|
||||
throw new coding_exception('backpacks only support Open Badges V2.1');
|
||||
}
|
||||
$hash = optional_param('hash', null, PARAM_RAW);
|
||||
|
||||
$PAGE->set_pagelayout('admin');
|
||||
$url = new moodle_url('/badges/backpack-export.php');
|
||||
|
||||
require_login();
|
||||
if (empty($CFG->badges_allowexternalbackpack) || empty($CFG->enablebadges)) {
|
||||
redirect($CFG->wwwroot);
|
||||
}
|
||||
$backpack = badges_get_site_backpack($CFG->badges_site_backpack);
|
||||
$userbadges = badges_get_user_badges($USER->id);
|
||||
$context = context_user::instance($USER->id);
|
||||
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_url($url);
|
||||
$title = get_string('badges', 'badges');
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading(fullname($USER));
|
||||
$PAGE->set_pagelayout('standard');
|
||||
|
||||
$redirecturl = new moodle_url('/badges/mybadges.php');
|
||||
if ($hash) {
|
||||
$backpack = badges_get_site_backpack($CFG->badges_site_backpack);
|
||||
$api = new core_badges\backpack_api2p1($backpack);
|
||||
$notify = $api->put_assertions($hash);
|
||||
if (!empty($notify['status']) && $notify['status'] == \core\output\notification::NOTIFY_SUCCESS) {
|
||||
redirect($redirecturl, $notify['message'], null, \core\output\notification::NOTIFY_SUCCESS);
|
||||
} else if (!empty($notify['status']) && $notify['status'] == \core\output\notification::NOTIFY_ERROR) {
|
||||
redirect($redirecturl, $notify['message'], null, \core\output\notification::NOTIFY_ERROR);
|
||||
}
|
||||
}
|
||||
redirect($redirecturl);
|
236
badges/classes/backpack_api2p1.php
Normal file
236
badges/classes/backpack_api2p1.php
Normal file
@ -0,0 +1,236 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 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 null api URL of the backpack. */
|
||||
protected $backpackapiurl = '';
|
||||
|
||||
/**
|
||||
* 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->backpackapiurl = $externalbackpack->backpackapiurl;
|
||||
$this->get_clientid = $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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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->backpackapiurl,
|
||||
$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->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 = new \core\oauth2\issuer($this->externalbackpack->oauth2_issuerid);
|
||||
$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 {
|
||||
$msg['status'] = \core\output\notification::NOTIFY_ERROR;
|
||||
$msg['message'] = get_string('backpackexporterror', 'badges', $data['assertion']['badge']['name']);
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
}
|
178
badges/classes/backpack_api2p1_mapping.php
Normal file
178
badges/classes/backpack_api2p1_mapping.php
Normal file
@ -0,0 +1,178 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Represent the url for each method and the encoding of the parameters and response.
|
||||
*
|
||||
* The code is based on badges/classes/backpack_api_mapping.php by Yuliya Bozhko <yuliya.bozhko@totaralms.com>.
|
||||
*
|
||||
* @copyright 2020 Tung Thai
|
||||
* @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();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
use context_system;
|
||||
use curl;
|
||||
|
||||
/**
|
||||
* Represent a single method for the remote api 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_mapping {
|
||||
|
||||
/** @var string The action of this method. */
|
||||
public $action;
|
||||
|
||||
/** @var string The base url of this backpack. */
|
||||
private $url;
|
||||
|
||||
/** @var array List of parameters for this method. */
|
||||
public $params;
|
||||
|
||||
/** @var boolean This method returns an array of responses. */
|
||||
public $multiple;
|
||||
|
||||
/** @var string get or post methods. */
|
||||
public $method;
|
||||
|
||||
/** @var boolean json decode the response. */
|
||||
public $json;
|
||||
|
||||
/** @var boolean Authentication is required for this request. */
|
||||
public $authrequired;
|
||||
|
||||
/** @var boolean Differentiate the function that can be called on a user backpack or a site backpack. */
|
||||
private $isuserbackpack;
|
||||
|
||||
/**
|
||||
* Create a mapping.
|
||||
*
|
||||
* @param string $action The action of this method.
|
||||
* @param string $url The base url of this backpack.
|
||||
* @param mixed $postparams List of parameters for this method.
|
||||
* @param boolean $multiple This method returns an array of responses.
|
||||
* @param string $method get or post methods.
|
||||
* @param boolean $json json decode the response.
|
||||
* @param boolean $authrequired Authentication is required for this request.
|
||||
* @param boolean $isuserbackpack user backpack or a site backpack.
|
||||
* @param integer $backpackapiversion OpenBadges version 1 or 2.
|
||||
*/
|
||||
public function __construct($action, $url, $postparams,
|
||||
$multiple, $method, $json, $authrequired, $isuserbackpack, $backpackapiversion) {
|
||||
$this->action = $action;
|
||||
$this->url = $url;
|
||||
$this->postparams = $postparams;
|
||||
$this->multiple = $multiple;
|
||||
$this->method = $method;
|
||||
$this->json = $json;
|
||||
$this->authrequired = $authrequired;
|
||||
$this->isuserbackpack = $isuserbackpack;
|
||||
$this->backpackapiversion = $backpackapiversion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the action match this mapping?
|
||||
*
|
||||
* @param string $action The action.
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_match($action) {
|
||||
return $this->action == $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the method url and insert parameters.
|
||||
*
|
||||
* @param string $apiurl The raw apiurl.
|
||||
* @return string
|
||||
*/
|
||||
private function get_url($apiurl) {
|
||||
$urlscheme = parse_url($apiurl, PHP_URL_SCHEME);
|
||||
$urlhost = parse_url($apiurl, PHP_URL_HOST);
|
||||
|
||||
$url = $this->url;
|
||||
$url = str_replace('[SCHEME]', $urlscheme, $url);
|
||||
$url = str_replace('[HOST]', $urlhost, $url);
|
||||
$url = str_replace('[URL]', $apiurl, $url);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard options used for all curl requests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_curl_options() {
|
||||
return array(
|
||||
'FRESH_CONNECT' => true,
|
||||
'RETURNTRANSFER' => true,
|
||||
'FORBID_REUSE' => true,
|
||||
'HEADER' => 0,
|
||||
'CONNECTTIMEOUT' => 3,
|
||||
'CONNECTTIMEOUT' => 3,
|
||||
// Follow redirects with the same type of request when sent 301, or 302 redirects.
|
||||
'CURLOPT_POSTREDIR' => 3,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an api request and parse the response.
|
||||
*
|
||||
* @param string $apiurl Raw request url.
|
||||
* @param string $tokenkey to verify authorization.
|
||||
* @param array $post request method.
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function request($apiurl, $tokenkey, $post = []) {
|
||||
$curl = new curl();
|
||||
$url = $this->get_url($apiurl);
|
||||
if ($tokenkey) {
|
||||
$curl->setHeader('Authorization: Bearer ' . $tokenkey);
|
||||
}
|
||||
|
||||
if ($this->json) {
|
||||
$curl->setHeader(array('Content-type: application/json'));
|
||||
if ($this->method == 'post') {
|
||||
$post = json_encode($post);
|
||||
}
|
||||
}
|
||||
|
||||
$curl->setHeader(array('Accept: application/json', 'Expect:'));
|
||||
$options = $this->get_curl_options();
|
||||
if ($this->method == 'get') {
|
||||
$response = $curl->get($url, $post, $options);
|
||||
} else if ($this->method == 'post') {
|
||||
$response = $curl->post($url, $post, $options);
|
||||
}
|
||||
$response = json_decode($response);
|
||||
if (isset($response->result)) {
|
||||
$response = $response->result;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -88,16 +88,18 @@ class backpack extends moodleform {
|
||||
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
|
||||
array('class' => 'notconnected', 'id' => 'connection-status'));
|
||||
$mform->addElement('static', 'status', get_string('status'), $status);
|
||||
$mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="30"');
|
||||
$mform->addHelpButton('email', 'backpackemail', 'badges');
|
||||
$mform->addRule('email', get_string('required'), 'required', null, 'client');
|
||||
$mform->setType('email', PARAM_EMAIL);
|
||||
if (badges_open_badges_backpack_api() == OPEN_BADGES_V2) {
|
||||
$mform->addElement('passwordunmask', 'backpackpassword', get_string('password'));
|
||||
$mform->setType('backpackpassword', PARAM_RAW);
|
||||
} else {
|
||||
$mform->addElement('hidden', 'backpackpassword', '');
|
||||
$mform->setType('backpackpassword', PARAM_RAW);
|
||||
if (badges_open_badges_backpack_api() != OPEN_BADGES_V2P1) {
|
||||
$mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="30"');
|
||||
$mform->addHelpButton('email', 'backpackemail', 'badges');
|
||||
$mform->addRule('email', get_string('required'), 'required', null, 'client');
|
||||
$mform->setType('email', PARAM_EMAIL);
|
||||
if (badges_open_badges_backpack_api() == OPEN_BADGES_V2) {
|
||||
$mform->addElement('passwordunmask', 'backpackpassword', get_string('password'));
|
||||
$mform->setType('backpackpassword', PARAM_RAW);
|
||||
} else {
|
||||
$mform->addElement('hidden', 'backpackpassword', '');
|
||||
$mform->setType('backpackpassword', PARAM_RAW);
|
||||
}
|
||||
}
|
||||
$this->add_action_buttons(false, get_string('backpackconnectionconnect', 'badges'));
|
||||
}
|
||||
@ -107,7 +109,12 @@ class backpack extends moodleform {
|
||||
* Validates form data
|
||||
*/
|
||||
public function validation($data, $files) {
|
||||
global $CFG;
|
||||
|
||||
$errors = parent::validation($data, $files);
|
||||
if (badges_open_badges_backpack_api() == OPEN_BADGES_V2P1) {
|
||||
return $errors;
|
||||
}
|
||||
// We don't need to verify the email address if we're clearing a pending email verification attempt.
|
||||
if (!isset($data['revertbutton'])) {
|
||||
$check = new stdClass();
|
||||
|
@ -68,7 +68,7 @@ class external_backpack extends \moodleform {
|
||||
$label = $options[$backpack->apiversion];
|
||||
$mform->addElement('static', 'apiversioninfo', get_string('apiversion', 'core_badges'), $label);
|
||||
$mform->addElement('hidden', 'apiversion', $backpack->apiversion);
|
||||
$mform->setType('apiversion', PARAM_INTEGER);
|
||||
$mform->setType('apiversion', PARAM_RAW);
|
||||
|
||||
$mform->addElement('hidden', 'id', $backpack->id);
|
||||
$mform->setType('id', PARAM_INTEGER);
|
||||
@ -81,12 +81,16 @@ class external_backpack extends \moodleform {
|
||||
|
||||
$issuercontact = $CFG->badges_defaultissuercontact;
|
||||
$mform->addElement('static', 'issuerinfo', get_string('defaultissuercontact', 'core_badges'), $issuercontact);
|
||||
|
||||
$mform->addElement('passwordunmask', 'password', get_string('defaultissuerpassword', 'core_badges'));
|
||||
$mform->setType('password', PARAM_RAW);
|
||||
$mform->addHelpButton('password', 'defaultissuerpassword', 'badges');
|
||||
$mform->hideIf('password', 'apiversion', 'eq', 1);
|
||||
|
||||
if ($backpack->apiversion != OPEN_BADGES_V2P1) {
|
||||
$mform->addElement('passwordunmask', 'password', get_string('defaultissuerpassword', 'core_badges'));
|
||||
$mform->setType('password', PARAM_RAW);
|
||||
$mform->addHelpButton('password', 'defaultissuerpassword', 'badges');
|
||||
$mform->hideIf('password', 'apiversion', 'eq', 1);
|
||||
} else {
|
||||
$oauth2options = badges_get_oauth2_service_options();
|
||||
$mform->addElement('select', 'oauth2_issuerid', get_string('oauth2issuer', 'core_badges'), $oauth2options);
|
||||
$mform->setType('oauth2_issuerid', PARAM_INT);
|
||||
}
|
||||
$this->set_data($backpack);
|
||||
|
||||
// Disable short forms.
|
||||
|
96
badges/classes/oauth2/auth.php
Normal file
96
badges/classes/oauth2/auth.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file to proccess Oauth2 connects for backpack.
|
||||
*
|
||||
* @package core_badges
|
||||
* @subpackage badges
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
|
||||
namespace core_badges\oauth2;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir.'/authlib.php');
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Proccess Oauth2 connects to backpack site.
|
||||
*
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
class auth extends \auth_oauth2\auth {
|
||||
|
||||
/**
|
||||
* To complete data after login.
|
||||
*
|
||||
* @param client $client object.
|
||||
* @param string $redirecturl the url redirect.
|
||||
*/
|
||||
public function complete_data(\core_badges\oauth2\client $client, $redirecturl) {
|
||||
global $DB, $USER;
|
||||
|
||||
$userinfo = $client->get_userinfo();
|
||||
$badgebackpack = new stdClass();
|
||||
$badgebackpack->userid = $USER->id;
|
||||
if ($userinfo && isset($userinfo->email)) {
|
||||
$badgebackpack->email = $userinfo->email;
|
||||
} else {
|
||||
$badgebackpack->email = $USER->email;
|
||||
}
|
||||
$badgebackpack->externalbackpackid = $client->backpack->id;
|
||||
$badgebackpack->backpackuid = 0;
|
||||
$badgebackpack->autosync = 0;
|
||||
$badgebackpack->password = '';
|
||||
$record = $DB->get_record('badge_backpack', ['userid' => $USER->id,
|
||||
'externalbackpackid' => $client->backpack->id]);
|
||||
if (!$record) {
|
||||
$DB->insert_record('badge_backpack', $badgebackpack);
|
||||
} else {
|
||||
$badgebackpack->id = $record->id;
|
||||
$DB->update_record('badge_backpack', $badgebackpack);
|
||||
}
|
||||
|
||||
redirect($redirecturl, get_string('backpackconnected', 'badges'), null,
|
||||
\core\output\notification::NOTIFY_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user has been logged the backpack site.
|
||||
*
|
||||
* @param int $externalbackpackid ID of external backpack.
|
||||
* @param int $userid ID of user.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_logged_oauth2($externalbackpackid, $userid) {
|
||||
global $USER;
|
||||
if (empty($userid)) {
|
||||
$userid = $USER->id;
|
||||
}
|
||||
$persistedtoken = badge_backpack_oauth2::get_record(['externalbackpackid' => $externalbackpackid, 'userid' => $userid]);
|
||||
if ($persistedtoken) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
77
badges/classes/oauth2/badge_backpack_oauth2.php
Normal file
77
badges/classes/oauth2/badge_backpack_oauth2.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains the form add/update oauth2 for backpack is connected.
|
||||
*
|
||||
* @package core_badges
|
||||
* @subpackage badges
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
|
||||
namespace core_badges\oauth2;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\persistent;
|
||||
|
||||
/**
|
||||
* Class badge_backpack_oauth2 for backpack is connected.
|
||||
*
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
class badge_backpack_oauth2 extends persistent {
|
||||
|
||||
/**
|
||||
* The table name.
|
||||
*/
|
||||
const TABLE = 'badge_backpack_oauth2';
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return array(
|
||||
'userid' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'issuerid' => array(
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
'externalbackpackid' => array(
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
'token' => array(
|
||||
'type' => PARAM_TEXT
|
||||
),
|
||||
'refreshtoken' => array(
|
||||
'type' => PARAM_TEXT
|
||||
),
|
||||
'expires' => array(
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
'scope' => array(
|
||||
'type' => PARAM_TEXT
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
348
badges/classes/oauth2/client.php
Normal file
348
badges/classes/oauth2/client.php
Normal file
@ -0,0 +1,348 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Configurable OAuth2 client class.
|
||||
*
|
||||
* @package core_badges
|
||||
* @subpackage badges
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
|
||||
namespace core_badges\oauth2;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/oauthlib.php');
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
require_once('badge_backpack_oauth2.php');
|
||||
|
||||
use moodle_url;
|
||||
use moodle_exception;
|
||||
use stdClass;
|
||||
|
||||
define('BACKPACK_CHALLENGE_METHOD', 'S256');
|
||||
define('BACKPACK_CODE_VERIFIER_TIME', 60);
|
||||
|
||||
/**
|
||||
* Configurable OAuth2 client to request authorization and store token. Use the PKCE method to verifier authorization.
|
||||
*
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
class client extends \core\oauth2\client {
|
||||
|
||||
/** @var \core\oauth2\issuer */
|
||||
private $issuer;
|
||||
|
||||
/** @var string $clientid client identifier issued to the client */
|
||||
private $clientid = '';
|
||||
|
||||
/** @var string $clientsecret The client secret. */
|
||||
private $clientsecret = '';
|
||||
|
||||
/** @var moodle_url $returnurl URL to return to after authenticating */
|
||||
private $returnurl = null;
|
||||
|
||||
/** @var string $grantscope */
|
||||
protected $grantscope = '';
|
||||
|
||||
/** @var string $scope */
|
||||
protected $scope = '';
|
||||
|
||||
/** @var bool basicauth */
|
||||
protected $basicauth = true;
|
||||
|
||||
/** @var string|null backpack object */
|
||||
public $backpack = '';
|
||||
|
||||
/**
|
||||
* client constructor.
|
||||
*
|
||||
* @param issuer $issuer oauth2 service.
|
||||
* @param string $returnurl return url after login
|
||||
* @param string $additionalscopes the scopes has been granted
|
||||
* @param null $backpack backpack object.
|
||||
* @throws \coding_exception error message.
|
||||
*/
|
||||
public function __construct(\core\oauth2\issuer $issuer, $returnurl = '', $additionalscopes = '',
|
||||
$backpack = null) {
|
||||
$this->issuer = $issuer;
|
||||
$this->clientid = $issuer->get('clientid');
|
||||
$this->returnurl = $returnurl;
|
||||
$this->clientsecret = $issuer->get('clientsecret');
|
||||
$this->backpack = $backpack;
|
||||
$this->grantscope = $additionalscopes;
|
||||
$this->scope = $additionalscopes;
|
||||
parent::__construct($issuer, $returnurl, $additionalscopes, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get login url.
|
||||
*
|
||||
* @return moodle_url
|
||||
* @throws \coding_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public function get_login_url() {
|
||||
$callbackurl = self::callback_url();
|
||||
$scopes = $this->issuer->get('scopessupported');
|
||||
|
||||
// Removed the scopes does not support in authorization.
|
||||
$excludescopes = ['profile', 'openid'];
|
||||
$arrascopes = explode(' ', $scopes);
|
||||
foreach ($excludescopes as $exscope) {
|
||||
$key = array_search($exscope, $arrascopes);
|
||||
if (isset($key)) {
|
||||
unset($arrascopes[$key]);
|
||||
}
|
||||
}
|
||||
$scopes = implode(' ', $arrascopes);
|
||||
|
||||
$params = array_merge(
|
||||
[
|
||||
'client_id' => $this->clientid,
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => $callbackurl->out(false),
|
||||
'state' => $this->returnurl->out_as_local_url(false),
|
||||
'scope' => $scopes,
|
||||
'code_challenge' => $this->code_challenge(),
|
||||
'code_challenge_method' => BACKPACK_CHALLENGE_METHOD,
|
||||
]
|
||||
);
|
||||
return new moodle_url($this->auth_url(), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate code challenge.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function code_challenge() {
|
||||
$random = bin2hex(openssl_random_pseudo_bytes(43));
|
||||
$verifier = $this->base64url_encode(pack('H*', $random));
|
||||
$challenge = $this->base64url_encode(pack('H*', hash('sha256', $verifier)));
|
||||
$_SESSION['SESSION']->code_verifier = $verifier;
|
||||
return $challenge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get code verifier.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function code_verifier() {
|
||||
if (isset($_SESSION['SESSION']) && !empty($_SESSION['SESSION']->code_verifier)) {
|
||||
return $_SESSION['SESSION']->code_verifier;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate base64url encode.
|
||||
*
|
||||
* @param string $plaintext text to convert.
|
||||
* @return string
|
||||
*/
|
||||
public function base64url_encode($plaintext) {
|
||||
$base64 = base64_encode($plaintext);
|
||||
$base64 = trim($base64, "=");
|
||||
$base64url = strtr($base64, '+/', '-_');
|
||||
return ($base64url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback url where the request is returned to.
|
||||
*
|
||||
* @return moodle_url url of callback
|
||||
*/
|
||||
public static function callback_url() {
|
||||
return new moodle_url('/badges/oauth2callback.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and refresh token to keep login on backpack site.
|
||||
*
|
||||
* @return bool
|
||||
* @throws \coding_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public function is_logged_in() {
|
||||
|
||||
// Has the token expired?
|
||||
if (isset($this->accesstoken->expires) && time() >= $this->accesstoken->expires) {
|
||||
if (isset($this->accesstoken->refreshtoken)) {
|
||||
return $this->upgrade_token($this->accesstoken->refreshtoken, 'refresh_token');
|
||||
} else {
|
||||
throw new moodle_exception('Could not refresh oauth token, please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->accesstoken->token) && isset($this->accesstoken->scope)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we've been passed then authorization code generated by the
|
||||
// authorization server try and upgrade the token to an access token.
|
||||
$code = optional_param('oauth2code', null, PARAM_RAW);
|
||||
// Note - sometimes we may call is_logged_in twice in the same request - we don't want to attempt
|
||||
// to upgrade the same token twice.
|
||||
if ($code && $this->upgrade_token($code, 'authorization_code')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request new token.
|
||||
*
|
||||
* @param string $code code verify from Auth site.
|
||||
* @param string $granttype grant type.
|
||||
* @return bool
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public function upgrade_token($code, $granttype = 'authorization_code') {
|
||||
$callbackurl = self::callback_url();
|
||||
|
||||
if ($granttype == 'authorization_code') {
|
||||
$params = array('code' => $code,
|
||||
'grant_type' => $granttype,
|
||||
'redirect_uri' => $callbackurl->out(false),
|
||||
'scope' => $this->get_scopes(),
|
||||
'code_verifier' => $this->code_verifier()
|
||||
);
|
||||
} else if ($granttype == 'refresh_token') {
|
||||
$this->basicauth = false;
|
||||
$params = array('refresh_token' => $code,
|
||||
'grant_type' => $granttype,
|
||||
'scope' => $this->get_scopes(),
|
||||
);
|
||||
}
|
||||
if ($this->basicauth) {
|
||||
$idsecret = urlencode($this->clientid) . ':' . urlencode($this->clientsecret);
|
||||
$this->setHeader('Authorization: Basic ' . base64_encode($idsecret));
|
||||
} else {
|
||||
$params['client_id'] = $this->clientid;
|
||||
$params['client_secret'] = $this->clientsecret;
|
||||
}
|
||||
// Requests can either use http GET or POST.
|
||||
$response = $this->post($this->token_url(), $this->build_post_data($params));
|
||||
$r = json_decode($response);
|
||||
if ($this->info['http_code'] !== 200) {
|
||||
throw new moodle_exception('Could not upgrade oauth token');
|
||||
}
|
||||
|
||||
if (is_null($r)) {
|
||||
throw new moodle_exception("Could not decode JSON token response");
|
||||
}
|
||||
|
||||
if (!empty($r->error)) {
|
||||
throw new moodle_exception($r->error . ' ' . $r->error_description);
|
||||
}
|
||||
|
||||
if (!isset($r->access_token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the token an expiry time.
|
||||
$accesstoken = new stdClass;
|
||||
$accesstoken->token = $r->access_token;
|
||||
if (isset($r->expires_in)) {
|
||||
// Expires 10 seconds before actual expiry.
|
||||
$accesstoken->expires = (time() + ($r->expires_in - 10));
|
||||
}
|
||||
if (isset($r->refresh_token)) {
|
||||
$this->refreshtoken = $r->refresh_token;
|
||||
$accesstoken->refreshtoken = $r->refresh_token;
|
||||
}
|
||||
$accesstoken->scope = $r->scope;
|
||||
|
||||
// Also add the scopes.
|
||||
$this->store_token($accesstoken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a token to verify for send request.
|
||||
*
|
||||
* @param null|stdClass $token
|
||||
*/
|
||||
protected function store_token($token) {
|
||||
global $USER;
|
||||
|
||||
$this->accesstoken = $token;
|
||||
// Create or update a DB record with the new token.
|
||||
$persistedtoken = badge_backpack_oauth2::get_record(['externalbackpackid' => $this->backpack->id, 'userid' => $USER->id]);
|
||||
if ($token !== null) {
|
||||
if (!$persistedtoken) {
|
||||
$persistedtoken = new badge_backpack_oauth2();
|
||||
$persistedtoken->set('issuerid', $this->backpack->oauth2_issuerid);
|
||||
$persistedtoken->set('externalbackpackid', $this->backpack->id);
|
||||
$persistedtoken->set('userid', $USER->id);
|
||||
} else {
|
||||
$persistedtoken->set('timemodified', time());
|
||||
}
|
||||
// Update values from $token. Don't use from_record because that would skip validation.
|
||||
$persistedtoken->set('usermodified', $USER->id);
|
||||
$persistedtoken->set('token', $token->token);
|
||||
$persistedtoken->set('refreshtoken', $token->refreshtoken);
|
||||
$persistedtoken->set('expires', $token->expires);
|
||||
$persistedtoken->set('scope', $token->scope);
|
||||
$persistedtoken->save();
|
||||
} else {
|
||||
if ($persistedtoken) {
|
||||
$persistedtoken->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token of current user.
|
||||
*
|
||||
* @return stdClass|null token object
|
||||
*/
|
||||
protected function get_stored_token() {
|
||||
global $USER;
|
||||
|
||||
$token = badge_backpack_oauth2::get_record(['externalbackpackid' => $this->backpack->id, 'userid' => $USER->id]);
|
||||
if ($token !== false) {
|
||||
$token = $token->to_record();
|
||||
return $token;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scopes granted.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_scopes() {
|
||||
if (!empty($this->grantscope)) {
|
||||
return $this->grantscope;
|
||||
}
|
||||
$token = $this->get_stored_token();
|
||||
if ($token) {
|
||||
return $token->scope;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -63,7 +63,7 @@ class external_backpacks_page implements \renderable {
|
||||
foreach ($this->backpacks as $backpack) {
|
||||
$exporter = new backpack_exporter($backpack);
|
||||
$backpack = $exporter->export($output);
|
||||
if ($backpack->apiversion == OPEN_BADGES_V2) {
|
||||
if ($backpack->apiversion == OPEN_BADGES_V2 || $backpack->apiversion == OPEN_BADGES_V2P1) {
|
||||
$backpack->canedit = true;
|
||||
} else {
|
||||
$backpack->canedit = false;
|
||||
|
@ -105,6 +105,14 @@ class provider implements
|
||||
'issuer' => 'privacy:metadata:external:backpacks:issuer',
|
||||
], 'privacy:metadata:external:backpacks');
|
||||
|
||||
$collection->add_database_table('badge_backpack_oauth2', [
|
||||
'userid' => 'privacy:metadata:backpackoauth2:userid',
|
||||
'usermodified' => 'privacy:metadata:backpackoauth2:usermodified',
|
||||
'token' => 'privacy:metadata:backpackoauth2:token',
|
||||
'issuerid' => 'privacy:metadata:backpackoauth2:issuerid',
|
||||
'scope' => 'privacy:metadata:backpackoauth2:scope',
|
||||
], 'privacy:metadata:backpackoauth2');
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
|
@ -56,10 +56,17 @@ $badgescache = cache::make('core', 'externalbadges');
|
||||
if ($disconnect && $backpack) {
|
||||
require_sesskey();
|
||||
$sitebackpack = badges_get_site_backpack($backpack->externalbackpackid);
|
||||
// If backpack is connected, need to select collections.
|
||||
$bp = new \core_badges\backpack_api($sitebackpack, $backpack);
|
||||
$bp->disconnect_backpack($USER->id, $backpack->id);
|
||||
redirect(new moodle_url('/badges/mybackpack.php'));
|
||||
if ($sitebackpack->apiversion == OPEN_BADGES_V2P1) {
|
||||
$bp = new \core_badges\backpack_api2p1($sitebackpack);
|
||||
$bp->disconnect_backpack($backpack);
|
||||
redirect(new moodle_url('/badges/mybackpack.php'), get_string('backpackdisconnected', 'badges'), null,
|
||||
\core\output\notification::NOTIFY_SUCCESS);
|
||||
} else {
|
||||
// If backpack is connected, need to select collections.
|
||||
$bp = new \core_badges\backpack_api($sitebackpack, $backpack);
|
||||
$bp->disconnect_backpack($USER->id, $backpack->id);
|
||||
redirect(new moodle_url('/badges/mybackpack.php'));
|
||||
}
|
||||
}
|
||||
$warning = '';
|
||||
if ($backpack) {
|
||||
@ -100,6 +107,16 @@ if ($backpack) {
|
||||
$bp->set_backpack_collections($backpack->id, $groups);
|
||||
redirect(new moodle_url('/badges/mybadges.php'));
|
||||
}
|
||||
} else if (badges_open_badges_backpack_api() == OPEN_BADGES_V2P1) {
|
||||
// If backpack is version 2.1 to redirect on the backpack site to login.
|
||||
// User input username/email/password on the backpack site
|
||||
// After confirm the scopes.
|
||||
$form = new \core_badges\form\backpack(new moodle_url('/badges/mybackpack.php'));
|
||||
if ($form->is_cancelled()) {
|
||||
redirect(new moodle_url('/badges/mybadges.php'));
|
||||
} else if ($data = $form->get_submitted_data()) {
|
||||
redirect(new moodle_url('/badges/backpack-connect.php'));
|
||||
}
|
||||
} else {
|
||||
// If backpack is not connected, need to connect first.
|
||||
// To create a new connection to the backpack, first we need to verify the user's email address:
|
||||
|
57
badges/oauth2callback.php
Normal file
57
badges/oauth2callback.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Verify authorization callback.
|
||||
*
|
||||
* @package core_badges
|
||||
* @copyright 2020 Tung Thai
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Tung Thai <Tung.ThaiDuc@nashtechglobal.com>
|
||||
*/
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
|
||||
$error = optional_param('error', '', PARAM_RAW);
|
||||
|
||||
if ($error) {
|
||||
$message = optional_param('error_description', '', PARAM_RAW);
|
||||
if ($message) {
|
||||
print_error($message);
|
||||
} else {
|
||||
print_error($error);
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
require_login();
|
||||
|
||||
// The authorization code generated by the authorization server.
|
||||
$code = required_param('code', PARAM_RAW);
|
||||
$scope = required_param('scope', PARAM_RAW);
|
||||
|
||||
// The state parameter we've given (used in moodle as a redirect url).
|
||||
$state = required_param('state', PARAM_LOCALURL);
|
||||
|
||||
$redirecturl = new moodle_url($state);
|
||||
$params = $redirecturl->params();
|
||||
|
||||
if (isset($params['sesskey']) and confirm_sesskey($params['sesskey'])) {
|
||||
$redirecturl->param('oauth2code', $code);
|
||||
$redirecturl->param('scope', $scope);
|
||||
redirect($redirecturl);
|
||||
} else {
|
||||
print_error('invalidsesskey');
|
||||
}
|
@ -87,6 +87,8 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
if (badges_open_badges_backpack_api() == OPEN_BADGES_V1) {
|
||||
$action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
|
||||
$addurl = new moodle_url('#');
|
||||
} else if (badges_open_badges_backpack_api() == OPEN_BADGES_V2P1) {
|
||||
$addurl = new moodle_url('/badges/backpack-export.php', array('hash' => $badge->uniquehash));
|
||||
} else {
|
||||
$addurl = new moodle_url('/badges/backpack-add.php', array('hash' => $badge->uniquehash));
|
||||
}
|
||||
@ -354,7 +356,11 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
$this->output->add_action_handler($action, 'addbutton');
|
||||
$output .= $tobackpack;
|
||||
} else {
|
||||
$assertion = new moodle_url('/badges/backpack-add.php', array('hash' => $ibadge->hash));
|
||||
if (badges_open_badges_backpack_api() == OPEN_BADGES_V2P1) {
|
||||
$assertion = new moodle_url('/badges/backpack-export.php', array('hash' => $ibadge->hash));
|
||||
} else {
|
||||
$assertion = new moodle_url('/badges/backpack-add.php', array('hash' => $ibadge->hash));
|
||||
}
|
||||
$attributes = ['class' => 'btn btn-secondary m-1', 'role' => 'button'];
|
||||
$tobackpack = html_writer::link($assertion, get_string('addtobackpack', 'badges'), $attributes);
|
||||
$output .= $tobackpack;
|
||||
|
94
badges/tests/behat/backpack.feature
Normal file
94
badges/tests/behat/backpack.feature
Normal file
@ -0,0 +1,94 @@
|
||||
@core @core_badges @_file_upload
|
||||
Feature: Backpack badges
|
||||
The settings to connect to backpack with OAuth2 service
|
||||
As an learner
|
||||
I need to verify display backpack in the my profile
|
||||
|
||||
Background:
|
||||
Given the following "badge external backpack" exist:
|
||||
| backpackapiurl | backpackweburl | apiversion |
|
||||
| https://dc.imsglobal.org/obchost/ims/ob/v2p1 | https://dc.imsglobal.org | 2.1 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
|
||||
@javascript
|
||||
Scenario: Verify backback settings
|
||||
Given I am on homepage
|
||||
And I log in as "admin"
|
||||
And I navigate to "Badges > Backpack settings" in site administration
|
||||
And I set the following fields to these values:
|
||||
| External backpack connection | 1 |
|
||||
| Active external backpack | https://dc.imsglobal.org |
|
||||
And I press "Save changes"
|
||||
And I navigate to "Badges > Add a new badge" in site administration
|
||||
And I set the following fields to these values:
|
||||
| Name | Test badge verify backpack |
|
||||
| Version | v1 |
|
||||
| Language | English |
|
||||
| Description | Test badge description |
|
||||
| Image author | http://author.example.com |
|
||||
| Image caption | Test caption image |
|
||||
| issuername | Test Badge Site |
|
||||
| issuercontact | testuser@example.com |
|
||||
And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
|
||||
And I press "Create badge"
|
||||
And I set the field "type" to "Manual issue by role"
|
||||
And I set the field "Manager" to "1"
|
||||
And I press "Save"
|
||||
And I press "Enable access"
|
||||
And I press "Continue"
|
||||
And I follow "Recipients (0)"
|
||||
And I press "Award badge"
|
||||
And I set the field "potentialrecipients[]" to "Student 1 (student1@example.com)"
|
||||
And I press "Award badge"
|
||||
And I log out
|
||||
When I am on homepage
|
||||
And I log in as "student1"
|
||||
And I follow "Preferences" in the user menu
|
||||
And I follow "Backpack settings"
|
||||
Then I should see "https://dc.imsglobal.org"
|
||||
And I should see "Not connected"
|
||||
|
||||
@javascript
|
||||
Scenario: User has been connected backpack
|
||||
Given I am on homepage
|
||||
And I log in as "admin"
|
||||
And I navigate to "Badges > Backpack settings" in site administration
|
||||
And I set the following fields to these values:
|
||||
| External backpack connection | 1 |
|
||||
| Active external backpack | https://dc.imsglobal.org |
|
||||
And I press "Save changes"
|
||||
And I navigate to "Badges > Add a new badge" in site administration
|
||||
And I set the following fields to these values:
|
||||
| Name | Test badge verify backpack |
|
||||
| Version | v1 |
|
||||
| Language | English |
|
||||
| Description | Test badge description |
|
||||
| Image author | http://author.example.com |
|
||||
| Image caption | Test caption image |
|
||||
| issuername | Test Badge Site |
|
||||
| issuercontact | testuser@example.com |
|
||||
And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
|
||||
And I press "Create badge"
|
||||
And I set the field "type" to "Manual issue by role"
|
||||
And I set the field "Manager" to "1"
|
||||
And I press "Save"
|
||||
And I press "Enable access"
|
||||
And I press "Continue"
|
||||
And I follow "Recipients (0)"
|
||||
And I press "Award badge"
|
||||
And I set the field "potentialrecipients[]" to "Student 1 (student1@example.com)"
|
||||
And I press "Award badge"
|
||||
And I log out
|
||||
And the following "setup backpack connected" exist:
|
||||
| user | externalbackpack |
|
||||
| student1 | https://dc.imsglobal.org |
|
||||
When I log in as "student1"
|
||||
And I follow "Preferences" in the user menu
|
||||
And I follow "Backpack settings"
|
||||
Then I should see "Connected"
|
||||
And I follow "Preferences" in the user menu
|
||||
And I follow "Manage badges"
|
||||
And I should see "Test badge verify backpack"
|
||||
And "Add to backpack" "link" should exist
|
@ -98,6 +98,7 @@ The only URL required for verification is [your-site-url]/badges/assertion.php s
|
||||
$string['backpackbadgessummary'] = 'You have {$a->totalbadges} badge(s) displayed from {$a->totalcollections} collection(s).';
|
||||
$string['backpackbadgessettings'] = 'Change backpack settings';
|
||||
$string['backpackcannotsendverification'] = 'Cannot send verification email';
|
||||
$string['backpackconnected'] = 'Backpack has been connected';
|
||||
$string['backpackconnection'] = 'Backpack connection';
|
||||
$string['backpackconnection_help'] = 'Connecting to a backpack enables you to share your badges from this site, and display public badge collections from your backpack on your profile page on this site.';
|
||||
$string['backpackconnectioncancelattempt'] = 'Connect using a different email address';
|
||||
@ -106,6 +107,7 @@ $string['backpackconnectionresendemail'] = 'Resend verification email';
|
||||
$string['backpackconnectionunexpectedresult'] = 'There was a problem connecting to your backpack. Please check the credentials and try again.';
|
||||
$string['backpackconnectionunexpectedmessage'] = 'The backpack returned the error: "{$a}".';
|
||||
$string['backpackdetails'] = 'Backpack settings';
|
||||
$string['backpackdisconnected'] = 'Backpack has been disconnected';
|
||||
$string['backpackemail'] = 'Email address';
|
||||
$string['backpackemail_help'] = 'The email address associated with your backpack. While you are connected, any badges earned on this site will be associated with this email address.';
|
||||
$string['backpackemailverificationpending'] = 'Verification pending';
|
||||
@ -125,6 +127,7 @@ $string['backpackemailverifyemailsubject'] = '{$a}: Badges backpack email verifi
|
||||
$string['backpackemailverifypending'] = 'A verification email has been sent to <strong>{$a}</strong>. Click on the verification link in the email to activate your Backpack connection.';
|
||||
$string['backpackemailverifysuccess'] = 'Thanks for verifying your email address. You are now connected to your backpack.';
|
||||
$string['backpackemailverifytokenmismatch'] = 'The token in the link you clicked does not match the stored token. Make sure you clicked the link in most recent email you received.';
|
||||
$string['backpackexporterror'] = 'Can\'t export the badge to backpack';
|
||||
$string['backpackimport'] = 'Badge import settings';
|
||||
$string['backpackimport_help'] = 'After the backpack connection is successfully established, badges from your backpack can be displayed on your badges page and your profile page.
|
||||
|
||||
@ -442,8 +445,10 @@ $string['notifyweekly'] = 'Weekly';
|
||||
$string['numawards'] = 'This badge has been issued to <a href="{$a->link}">{$a->count}</a> user(s).';
|
||||
$string['numawardstat'] = 'This badge has been issued {$a} user(s).';
|
||||
$string['overallcrit'] = 'of the selected criteria are complete.';
|
||||
$string['oauth2issuer'] = 'OAuth 2 services';
|
||||
$string['openbadgesv1'] = 'Open Badges v1.0';
|
||||
$string['openbadgesv2'] = 'Open Badges v2.0';
|
||||
$string['openbadgesv2p1'] = 'Open Badges v2.1';
|
||||
$string['potentialrecipients'] = 'Potential badge recipients';
|
||||
$string['preferences'] = 'Badge preferences';
|
||||
$string['privacy:metadata:backpack'] = 'A record of user\'s backpacks';
|
||||
@ -465,6 +470,12 @@ $string['privacy:metadata:external:backpacks:description'] = 'The description of
|
||||
$string['privacy:metadata:external:backpacks:image'] = 'The image of the badge';
|
||||
$string['privacy:metadata:external:backpacks:issuer'] = 'Some information about the issuer';
|
||||
$string['privacy:metadata:external:backpacks:url'] = 'The Moodle URL where the issued badge information can be seen';
|
||||
$string['privacy:metadata:backpackoauth2'] = 'Information oauthorization when user connect to an external backpack';
|
||||
$string['privacy:metadata:backpackoauth2:userid'] = 'The ID of the user connect to backpack';
|
||||
$string['privacy:metadata:backpackoauth2:usermodified'] = 'The ID of the user modified connect';
|
||||
$string['privacy:metadata:backpackoauth2:token'] = 'The token of backpack connect';
|
||||
$string['privacy:metadata:backpackoauth2:issuerid'] = 'The ID of Oauth2 service';
|
||||
$string['privacy:metadata:backpackoauth2:scope'] = 'List scope of backpack connect';
|
||||
$string['privacy:metadata:issued'] = 'A record of badges awarded';
|
||||
$string['privacy:metadata:issued:dateexpire'] = 'The date when the badge expires';
|
||||
$string['privacy:metadata:issued:dateissued'] = 'The date of the award';
|
||||
|
@ -115,6 +115,7 @@ define('BADGE_BACKPACKWEBURL', 'https://backpack.openbadges.org');
|
||||
*/
|
||||
define('OPEN_BADGES_V1', 1);
|
||||
define('OPEN_BADGES_V2', 2);
|
||||
define('OPEN_BADGES_V2P1', 2.1);
|
||||
|
||||
/*
|
||||
* Only use for Open Badges 2.0 specification
|
||||
@ -800,7 +801,8 @@ function badges_update_site_backpack($id, $data) {
|
||||
$backpack->apiversion = $data->apiversion;
|
||||
$backpack->backpackweburl = $data->backpackweburl;
|
||||
$backpack->backpackapiurl = $data->backpackapiurl;
|
||||
$backpack->password = $data->password;
|
||||
$backpack->password = !empty($data->password) ? $data->password : '';
|
||||
$backpack->oauth2_issuerid = !empty($data->oauth2_issuerid) ? $data->oauth2_issuerid : '';
|
||||
$DB->update_record('badge_external_backpack', $backpack);
|
||||
return true;
|
||||
}
|
||||
@ -815,7 +817,6 @@ function badges_open_badges_backpack_api() {
|
||||
global $CFG;
|
||||
|
||||
$backpack = badges_get_site_backpack($CFG->badges_site_backpack);
|
||||
|
||||
if (empty($backpack->apiversion)) {
|
||||
return OPEN_BADGES_V2;
|
||||
}
|
||||
@ -861,8 +862,9 @@ function badges_get_site_backpacks() {
|
||||
*/
|
||||
function badges_get_badge_api_versions() {
|
||||
return [
|
||||
OPEN_BADGES_V1 => get_string('openbadgesv1', 'badges'),
|
||||
OPEN_BADGES_V2 => get_string('openbadgesv2', 'badges')
|
||||
(string)OPEN_BADGES_V1 => get_string('openbadgesv1', 'badges'),
|
||||
(string)OPEN_BADGES_V2 => get_string('openbadgesv2', 'badges'),
|
||||
(string)OPEN_BADGES_V2P1 => get_string('openbadgesv2p1', 'badges')
|
||||
];
|
||||
}
|
||||
|
||||
@ -1180,3 +1182,21 @@ function badges_verify_site_backpack() {
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OAuth2 services for the external backpack.
|
||||
*
|
||||
* @return array
|
||||
* @throws coding_exception
|
||||
*/
|
||||
function badges_get_oauth2_service_options() {
|
||||
global $DB;
|
||||
|
||||
$issuers = core\oauth2\api::get_all_issuers();
|
||||
$options = ['' => 'None'];
|
||||
foreach ($issuers as $issuer) {
|
||||
$options[$issuer->get('id')] = $issuer->get('name');
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
@ -222,6 +222,15 @@ class behat_core_generator extends behat_generator_base {
|
||||
'required' => array('contextlevel', 'reference', 'contenttype', 'user', 'contentname'),
|
||||
'switchids' => array('user' => 'userid')
|
||||
],
|
||||
'badge external backpack' => [
|
||||
'datagenerator' => 'badge_external_backpack',
|
||||
'required' => ['backpackapiurl', 'backpackweburl', 'apiversion']
|
||||
],
|
||||
'setup backpack connected' => [
|
||||
'datagenerator' => 'setup_backpack_connected',
|
||||
'required' => ['user', 'externalbackpack'],
|
||||
'switchids' => ['user' => 'userid', 'externalbackpack' => 'externalbackpackid']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@ -869,4 +878,56 @@ class behat_core_generator extends behat_generator_base {
|
||||
throw new Exception('The specified "' . $data['contenttype'] . '" contenttype does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a exetrnal backpack.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
protected function process_badge_external_backpack(array $data) {
|
||||
global $DB;
|
||||
$DB->insert_record('badge_external_backpack', $data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a backpack connected for user.
|
||||
*
|
||||
* @param array $data
|
||||
* @throws dml_exception
|
||||
*/
|
||||
protected function process_setup_backpack_connected(array $data) {
|
||||
global $DB;
|
||||
|
||||
if (empty($data['userid'])) {
|
||||
throw new Exception('\'setup backpack connected\' requires the field \'user\' to be specified');
|
||||
}
|
||||
if (empty($data['externalbackpackid'])) {
|
||||
throw new Exception('\'setup backpack connected\' requires the field \'externalbackpack\' to be specified');
|
||||
}
|
||||
// Dummy badge_backpack_oauth2 data.
|
||||
$timenow = time();
|
||||
$backpackoauth2 = new stdClass();
|
||||
$backpackoauth2->usermodified = $data['userid'];
|
||||
$backpackoauth2->timecreated = $timenow;
|
||||
$backpackoauth2->timemodified = $timenow;
|
||||
$backpackoauth2->userid = $data['userid'];
|
||||
$backpackoauth2->issuerid = 1;
|
||||
$backpackoauth2->externalbackpackid = $data['externalbackpackid'];
|
||||
$backpackoauth2->token = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$backpackoauth2->refreshtoken = '0123456789abcdefghijk';
|
||||
$backpackoauth2->expires = $timenow + 3600;
|
||||
$backpackoauth2->scope = 'https://purl.imsglobal.org/spec/ob/v2p1/scope/assertion.create';
|
||||
$backpackoauth2->scope .= ' https://purl.imsglobal.org/spec/ob/v2p1/scope/assertion.readonly offline_access';
|
||||
$DB->insert_record('badge_backpack_oauth2', $backpackoauth2);
|
||||
|
||||
// Dummy badge_backpack data.
|
||||
$backpack = new stdClass();
|
||||
$backpack->userid = $data['userid'];
|
||||
$backpack->email = 'student@behat.moodle';
|
||||
$backpack->backpackuid = 0;
|
||||
$backpack->autosync = 0;
|
||||
$backpack->password = '';
|
||||
$backpack->externalbackpackid = $data['externalbackpackid'];
|
||||
$DB->insert_record('badge_backpack', $backpack);
|
||||
}
|
||||
}
|
||||
|
@ -524,4 +524,18 @@ abstract class behat_generator_base {
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the external backpack id from it's backpackweburl.
|
||||
* @param string $backpackweburl
|
||||
* @return mixed
|
||||
* @throws dml_exception
|
||||
*/
|
||||
protected function get_externalbackpack_id($backpackweburl) {
|
||||
global $DB;
|
||||
if (!$id = $DB->get_field('badge_external_backpack', 'id', ['backpackweburl' => $backpackweburl])) {
|
||||
throw new Exception('The specified external backpack with backpackweburl "' . $username . '" does not exist');
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
@ -3183,12 +3183,35 @@
|
||||
<KEY NAME="externalbackpack" TYPE="foreign" FIELDS="externalbackpackid" REFTABLE="badge_external_backpack" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="badge_backpack_oauth2" COMMENT="Default comment for the table, please edit me">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="issuerid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="externalbackpackid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="token" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="refreshtoken" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="expires" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="scope" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="usermodified" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id"/>
|
||||
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" COMMENT="foreign (userid) references user (id)"/>
|
||||
<KEY NAME="issuerid" TYPE="foreign" FIELDS="issuerid" REFTABLE="oauth2_issuer" REFFIELDS="id" COMMENT="foreign (issuerid) references oauth2_issuer (id)"/>
|
||||
<KEY NAME="externalbackpackid" TYPE="foreign" FIELDS="externalbackpackid" REFTABLE="badge_external_backpack" REFFIELDS="id" COMMENT="foreign (externalbackpackid) references badge_external_backpack(id)"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="badge_external" COMMENT="Setting for external badges display">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="backpackid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of a backpack"/>
|
||||
<FIELD NAME="collectionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Badge collection id in the backpack"/>
|
||||
<FIELD NAME="entityid" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="assertion" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Assertion of external badge"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
@ -3245,11 +3268,13 @@
|
||||
<FIELD NAME="apiversion" TYPE="char" LENGTH="12" NOTNULL="true" DEFAULT="1.0" SEQUENCE="false"/>
|
||||
<FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="password" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Password to login into external backpack and issue badges."/>
|
||||
<FIELD NAME="oauth2_issuerid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="OAuth 2 Issuer"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="backpackapiurlkey" TYPE="unique" FIELDS="backpackapiurl"/>
|
||||
<KEY NAME="backpackweburlkey" TYPE="unique" FIELDS="backpackweburl"/>
|
||||
<KEY NAME="backpackoauth2key" TYPE="foreign" FIELDS="oauth2_issuerid" REFTABLE="oauth2_issuer" REFFIELDS="id" COMMENT="foreign (oauth2_issuerid) references oauth2_issuer (id)"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="user_devices" COMMENT="This table stores user's mobile devices information in order to send PUSH notifications">
|
||||
|
@ -2338,5 +2338,59 @@ function xmldb_main_upgrade($oldversion) {
|
||||
upgrade_main_savepoint(true, 2020051900.01);
|
||||
}
|
||||
|
||||
if ($oldversion < 2020052000.00) {
|
||||
// Define table badge_backpack_oauth2 to be created.
|
||||
$table = new xmldb_table('badge_backpack_oauth2');
|
||||
|
||||
// Adding fields to table badge_backpack_oauth2.
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
|
||||
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
|
||||
$table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
|
||||
$table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('externalbackpackid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('token', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('refreshtoken', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('expires', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
|
||||
$table->add_field('scope', XMLDB_TYPE_TEXT, null, null, null, null, null);
|
||||
|
||||
// Adding keys to table badge_backpack_oauth2.
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
|
||||
$table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
|
||||
$table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
|
||||
$table->add_key('issuerid', XMLDB_KEY_FOREIGN, ['issuerid'], 'oauth2_issuer', ['id']);
|
||||
$table->add_key('externalbackpackid', XMLDB_KEY_FOREIGN, ['externalbackpackid'], 'badge_external_backpack', ['id']);
|
||||
// Conditionally launch create table for badge_backpack_oauth2.
|
||||
if (!$dbman->table_exists($table)) {
|
||||
$dbman->create_table($table);
|
||||
}
|
||||
|
||||
// Define field oauth2_issuerid to be added to badge_external_backpack.
|
||||
$tablebadgeexternalbackpack = new xmldb_table('badge_external_backpack');
|
||||
$fieldoauth2issuerid = new xmldb_field('oauth2_issuerid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'password');
|
||||
$keybackpackoauth2key = new xmldb_key('backpackoauth2key', XMLDB_KEY_FOREIGN, ['oauth2_issuerid'], 'oauth2_issuer', ['id']);
|
||||
|
||||
// Conditionally launch add field oauth2_issuerid.
|
||||
if (!$dbman->field_exists($tablebadgeexternalbackpack, $fieldoauth2issuerid)) {
|
||||
$dbman->add_field($tablebadgeexternalbackpack, $fieldoauth2issuerid);
|
||||
|
||||
// Launch add key backpackoauth2key.
|
||||
$dbman->add_key($tablebadgeexternalbackpack, $keybackpackoauth2key);
|
||||
}
|
||||
|
||||
// Define field assertion to be added to badge_external.
|
||||
$tablebadgeexternal = new xmldb_table('badge_external');
|
||||
$fieldassertion = new xmldb_field('assertion', XMLDB_TYPE_TEXT, null, null, null, null, null, 'entityid');
|
||||
|
||||
// Conditionally launch add field assertion.
|
||||
if (!$dbman->field_exists($tablebadgeexternal, $fieldassertion)) {
|
||||
$dbman->add_field($tablebadgeexternal, $fieldassertion);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2020052000.00);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2020051900.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2020052000.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '3.9dev+ (Build: 20200519)'; // Human-friendly version name
|
||||
|
Loading…
x
Reference in New Issue
Block a user