MDL-65959 core_badges: Allow ability to upload badges cross domain.

This commit is contained in:
Peter 2019-10-02 12:28:45 +08:00 committed by Peter Dias
parent 1c4620bce6
commit b6435e0934
7 changed files with 125 additions and 17 deletions

View File

@ -48,7 +48,8 @@ if (!empty($issuedbadge->recipient->id)) {
$badgeid = $issuedbadge->badgeid;
$badge = new badge($badgeid);
$backpack = $DB->get_record('badge_backpack', array('userid' => $USER->id));
$sitebackpack = badges_get_site_backpack($backpack->externalbackpackid);
$sitebackpack = badges_get_site_primary_backpack();
$userbackpack = badges_get_site_backpack($backpack->externalbackpackid);
$assertion = new core_badges_assertion($id, $sitebackpack->apiversion);
$api = new \core_badges\backpack_api($sitebackpack);
$api->authenticate();
@ -61,7 +62,8 @@ if (!empty($issuedbadge->recipient->id)) {
throw new moodle_exception('invalidrequest', 'error');
}
$issuerentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email'], $issuerentityid);
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email'],
$issuerentityid, $response->openBadgeId);
}
// Create badge.
$badge = $assertion->get_badge_class(false);
@ -72,25 +74,55 @@ if (!empty($issuedbadge->recipient->id)) {
throw new moodle_exception('invalidrequest', 'error');
}
$badgeentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid, $badgeentityid);
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid,
$badgeentityid, $response->hostedUrl);
}
// Create assertion (Award the badge!).
$assertiondata = $assertion->get_badge_assertion(false, false);
$assertionid = $assertion->get_assertion_hash();
$assertionentityid = badges_external_get_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid
);
if (!($assertionentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid))) {
if ($assertionentityid && strpos($sitebackpack->backpackapiurl, 'badgr')) {
$assertionentityid = badges_generate_badgr_open_url(
$sitebackpack,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionentityid
);
}
// Create an assertion for the recipient in the issuer's account.
if (!$assertionentityid) {
$response = $api->put_badgeclass_assertion($badgeentityid, $assertiondata);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$assertionentityid = badges_generate_badgr_open_url($sitebackpack, OPEN_BADGES_V2_TYPE_ASSERTION, $response->id);
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid,
$response->id);
}
// Now award/upload the badge to the user's account.
if ($assertionentityid && !badges_external_get_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid)) {
$userapi = new \core_badges\backpack_api($userbackpack, $backpack);
$userapi->authenticate();
$response = $userapi->import_badge_assertion($assertionentityid);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$assertionentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid, $assertionentityid);
badges_external_create_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid,
$assertionentityid);
$response = ['success' => 'addedtobackpack'];
} else {
$response = ['warning' => 'existsinbackpack'];
}
redirect(new moodle_url('/badges/mybadges.php', $response));
} else {
redirect(new moodle_url('/badges/mybadges.php'));

View File

@ -87,7 +87,6 @@ class backpack_api {
global $CFG;
$admin = get_admin();
$this->backpackapiurl = $sitebackpack->backpackapiurl;
$this->backpackapiurl = $sitebackpack->backpackapiurl;
$this->backpackapiversion = $sitebackpack->apiversion;
$this->password = $sitebackpack->password;
@ -152,6 +151,21 @@ class backpack_api {
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'importbadge', // Action.
// Badgr.io does not return the public information about a badge
// if the issuer is associated with another user. We need to pass
// the expand parameters which are not in any specification to get
// additional information about the assertion in a single request.
'[URL]/backpack/import',
['url' => '[PARAM]'], // Post params.
'', // Request exporter.
'core_badges\external\assertion_exporter', // Response exporter.
false, // Multiple.
'post', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'badges', // Action.
'[URL]/backpack/collections/[PARAM1]', // URL
@ -408,6 +422,22 @@ class backpack_api {
return $this->curl_request('assertions', null, $entityid, $data);
}
/**
* Import a badge assertion into a backpack. This is used to handle cross domain backpacks.
*
* @param string $data The structure of the badge class assertion.
* @return mixed
* @throws coding_exception
*/
public function import_badge_assertion(string $data) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}
return $this->curl_request('importbadge', null, null, $data);
}
/**
* Select collections from a backpack.
*
@ -570,7 +600,6 @@ class backpack_api {
*
* @param integer $userid The user in Moodle
* @param integer $backpackid The backpack to disconnect
* @param integer $externalbackupid The external backpack to disconnect
* @return boolean
*/
public function disconnect_backpack($userid, $backpackid, $externalbackupid) {

View File

@ -209,6 +209,8 @@ class backpack_api_mapping {
} else if ($value == '[PASSWORD]') {
$value = $password;
$request[$key] = $value;
} else if ($value == '[PARAM]') {
$request[$key] = is_array($param) ? $param[0] : $param;
}
}
}
@ -312,6 +314,7 @@ class backpack_api_mapping {
return array(
'FRESH_CONNECT' => true,
'RETURNTRANSFER' => true,
'FOLLOWLOCATION' => true,
'FORBID_REUSE' => true,
'HEADER' => 0,
'CONNECTTIMEOUT' => 3,

View File

@ -650,10 +650,6 @@ class core_badges_renderer extends plugin_renderer_base {
$externalhtml .= html_writer::start_tag('div', array('class' => 'generalbox'));
$externalhtml .= $this->output->heading_with_help(get_string('externalbadges', 'badges'), 'externalbadges', 'badges');
if (!is_null($backpack)) {
if ($backpack->backpackid != $CFG->badges_site_backpack) {
$externalhtml .= $this->output->notification(get_string('backpackneedsupdate', 'badges'), 'warning');
}
if ($backpack->totalcollections == 0) {
$externalhtml .= get_string('nobackpackcollectionssummary', 'badges', $backpack);
} else {

View File

@ -1,6 +1,22 @@
This files describes API changes in /badges/*,
information provided here is intended especially for developers.
=== 3.10 ===
* Users can now specify a backpack that differs from the site backpack. In order to do this, connection details need to
be set in 'Manage backpacks' with OR without auth details.
* Introduced new functions in backpack_api
** 'import_badge_assertion' to facilitate cross domain badge imports.
** 'update_assertion' updates a previously defined/created assertion.
* New badge lib functions introduced
** badges_save_external_backpack() - This method handles inserts/updates to the site wide backpacks' configuration details.
** badges_save_backpack_credentials() - This method handles inserts/updates any authentication details to connect to the backpacks created. This can either be site OR user backpack authentication details
** badges_get_user_backpack() - Gets a specific user's backpack. Defaults to current user's backpack if none provided.
** badges_get_site_primary_backpack() - Get the primary backpack set for the site as defined in $CFG->badges_site_backpack
* badges_open_badges_backpack_api() - Now accepts a backpackid(badge_external_backpack id) to check whether the version of the provided backpack.
This was introduced because now there is a difference between a site and user backpack. If null, defaults to site_backpack.
* badges_get_site_backpack() - Accepts an additional $userid param if we want to get a specific user's backpack. Defaults to 0 if we are trying to get the site/admin level backpack
* badges_external_get_mapping() - Accepts an additional argument to indicate which value it wants returned. Defaults to 'externalid' which contains the OBv2 badge URL
=== 3.9 ===
* BADGE_BACKPACKAPIURL and BADGE_BACKPACKWEBURL are deprecated and should not be used.
* OBv2 has been set to the default value when the obversion is not defined.

View File

@ -936,6 +936,17 @@ function badges_get_site_backpack($id) {
return $DB->get_record('badge_external_backpack', ['id' => $id]);
}
/**
* Get the primary backpack for the site
*
* @return array(stdClass)
*/
function badges_get_site_primary_backpack() {
global $CFG;
return badges_get_site_backpack($CFG->badges_site_backpack);
}
/**
* List the backpacks at site level.
*
@ -1026,9 +1037,10 @@ function badges_disconnect_user_backpack($userid) {
* @param integer $sitebackpackid The site backpack to connect to.
* @param string $type The type of this remote object.
* @param string $internalid The id for this object on the Moodle site.
* @param string $param The param we need to return. Defaults to the externalid.
* @return mixed The id or false if it doesn't exist.
*/
function badges_external_get_mapping($sitebackpackid, $type, $internalid) {
function badges_external_get_mapping($sitebackpackid, $type, $internalid, $param = 'externalid') {
global $DB;
// Return externalid if it exists.
$params = [
@ -1037,9 +1049,9 @@ function badges_external_get_mapping($sitebackpackid, $type, $internalid) {
'internalid' => $internalid
];
$record = $DB->get_record('badge_external_identifier', $params, 'externalid', IGNORE_MISSING);
$record = $DB->get_record('badge_external_identifier', $params, $param, IGNORE_MISSING);
if ($record) {
return $record->externalid;
return $record->$param;
}
return false;
}
@ -1320,3 +1332,26 @@ function badges_get_oauth2_service_options() {
return $options;
}
/**
* Generate a public badgr URL that conforms to OBv2. This is done because badgr responses do not currently conform to
* the spec.
*
* WARNING: This is an extremely hacky way of implementing this and should be removed once the standards are conformed to.
*
* @param stdClass $backpack The Badgr backpack we are pushing to
* @param string $type The type of object we are dealing with either Issuer, Assertion OR Badge.
* @param string $externalid The externalid as provided by the backpack
* @return string The public URL to access Badgr objects
*/
function badges_generate_badgr_open_url($backpack, $type, $externalid) {
if (badges_open_badges_backpack_api($backpack->id) == OPEN_BADGES_V2) {
$entity = strtolower($type);
if ($type == OPEN_BADGES_V2_TYPE_BADGE) {
$entity = "badge";
}
$url = new moodle_url($backpack->backpackapiurl);
return "{$url->get_scheme()}://{$url->get_host()}/public/{$entity}s/$externalid";
}
}

View File

@ -2868,9 +2868,6 @@ function xmldb_main_upgrade($oldversion) {
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
$table->deleteKey('backpackapiurlkey');
$table->deleteKey('backpackweburlkey');
$table->add_key('backpackapiurlkey', XMLDB_KEY_UNIQUE, ['backpackapiurl', 'backpackemail']);
// Main savepoint reached.
upgrade_main_savepoint(true, 2021052500.32);