Skip to content

Commit

Permalink
MDL-83906 core_badges: Merge backpack-add and backpack-export
Browse files Browse the repository at this point in the history
  • Loading branch information
sarjona committed Jan 27, 2025
1 parent 89431ef commit 90107c5
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 135 deletions.
144 changes: 25 additions & 119 deletions badges/backpack-add.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,134 +25,40 @@
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');

use core_badges\backpack;
use core_badges\local\backpack\ob\api_base;

require_login();

$userbackpack = badges_get_user_backpack();
if (badges_open_badges_backpack_api($userbackpack->id) != OPEN_BADGES_V2) {
throw new coding_exception('No backpacks support Open Badges V2.');
// Check if badges and the external backpack are enabled.
if (empty($CFG->badges_allowexternalbackpack) || empty($CFG->enablebadges)) {
redirect($CFG->wwwroot);
}

$id = required_param('hash', PARAM_ALPHANUM);

$PAGE->set_url('/badges/backpack-add.php', array('hash' => $id));
$PAGE->set_context(context_system::instance());
$output = $PAGE->get_renderer('core', 'badges');

$issuedbadge = new \core_badges\output\issued_badge($id);
if (!empty($issuedbadge->recipient->id)) {
// The flow for issuing a badge is:
// * Create issuer
// * Create badge
// * Create assertion (Award the badge!)

// With the introduction OBv2.1 and MDL-65959 to allow cross region Badgr imports the above (old) procedure will
// only be completely performed if both the site and user backpacks conform to the same apiversion.
// Else we will attempt at pushing the assertion to the user's backpack. In this case, the id set against the assertion
// has to be a publicly accessible resource.

// Get the backpack.
$badgeid = $issuedbadge->badgeid;
$badge = new badge($badgeid);
$backpack = $DB->get_record('badge_backpack', array('userid' => $USER->id));
$userbackpack = badges_get_site_backpack($backpack->externalbackpackid, $USER->id);
$assertion = new core_badges_assertion($id, OPEN_BADGES_V2);
$assertiondata = $assertion->get_badge_assertion(false, false);
$assertionid = $assertion->get_assertion_hash();
$assertionentityid = $assertiondata['id'];
$badgeadded = false;
if (badges_open_badges_backpack_api() == OPEN_BADGES_V2) {
$sitebackpack = badges_get_site_primary_backpack();
$api = api_base::create_from_externalbackpack($userbackpack);
$response = $api->authenticate();

// A numeric response indicates a valid successful authentication. Else an error object will be returned.
if (is_numeric($response)) {
// Create issuer.
$issuer = $assertion->get_issuer();
if (!($issuerentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email']))) {
$response = $api->put_issuer($issuer);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$issuerentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email'],
$issuerentityid);
}
// Create badge.
$badge = $assertion->get_badge_class(false);
$badgeid = $assertion->get_badge_id();
if (!($badgeentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid))) {
$response = $api->put_badgeclass($issuerentityid, $badge);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$badgeentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid,
$badgeentityid);
}
// Check the user has a backpack.
$backpack = backpack::get_user_backpack();
if (empty($backpack)) {
throw new coding_exception('This user has no backpack associated with their account.');
}

// Create assertion (Award the badge!).
$assertionentityid = badges_external_get_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid
);
$hash = required_param('hash', PARAM_ALPHANUM);

if ($assertionentityid && strpos($sitebackpack->backpackapiurl, 'badgr')) {
$assertionentityid = badges_generate_badgr_open_url(
$sitebackpack,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionentityid
);
}
$PAGE->set_url('/badges/backpack-add.php', ['hash' => $hash]);
$PAGE->set_context(context_user::instance($USER->id));
$output = $PAGE->get_renderer('core', 'badges');

// 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);
$badgeadded = true;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid,
$response->id);
} else {
// An assertion already exists. Make sure it's up to date.
$internalid = badges_external_get_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid,
'externalid'
);
$response = $api->update_assertion($internalid, $assertiondata);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
}
}
}
// Check the assertion belongs to the current user.
$assertion = new core_badges_assertion($hash, $backpack->apiversion);
if ($assertion->get_userid() != $USER->id) {
throw new coding_exception('This assertion does not belong to the current user.');
}

// Now award/upload the badge to the user's account.
// - If a user and site backpack have the same provider we can skip this as Badgr automatically maps recipients
// based on email address.
// - This is only needed when the backpacks are from different regions.
if ($assertionentityid && !badges_external_get_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid)) {
$userapi = api_base::create_from_externalbackpack($userbackpack);
$userapi->authenticate();
$response = $userapi->import_badge_assertion($assertionentityid);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$assertionentityid = $response->id;
$badgeadded = true;
badges_external_create_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid,
$assertionentityid);
}
// Send the assertion to the backpack.
$api = api_base::create_from_externalbackpack($backpack);
$notify = $api->put_assertions($hash);

$response = $badgeadded ? ['success' => 'addedtobackpack'] : ['warning' => 'existsinbackpack'];
redirect(new moodle_url('/badges/mybadges.php', $response));
} else {
redirect(new moodle_url('/badges/mybadges.php'));
$redirecturl = new moodle_url('/badges/mybadges.php');
if (!empty($notify['status'])) {
redirect($redirecturl, $notify['message'], null, $notify['status']);
}
redirect($redirecturl);
1 change: 1 addition & 0 deletions badges/backpack-export.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

/**
* Export badges to the backpack site.
* TODO: Deprecate or delete this file.
*
* @package core_badges
* @copyright 2020 Tung Thai
Expand Down
9 changes: 7 additions & 2 deletions badges/classes/assertion.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public function __construct($hash, $obversion = OPEN_BADGES_V2) {
bi.dateexpire,
bi.uniquehash,
u.email,
u.id as userid,
b.*,
bb.email as backpackemail
FROM
Expand Down Expand Up @@ -119,7 +120,7 @@ public function get_badge_assertion($issued = true, $usesalt = true) {
$this->_data->uniquehash,
exporter_base::convert_apiversion(OPEN_BADGES_V2),
);
return $assertionexporter->export();
return $assertionexporter->export($issued, $usesalt);
}

/**
Expand All @@ -135,7 +136,7 @@ public function get_badge_class($issued = true) {
$this->get_badge_id(),
exporter_base::convert_apiversion(OPEN_BADGES_V2),
);
return $badgeexporter->export();
return $badgeexporter->export($issued);
}

/**
Expand Down Expand Up @@ -295,4 +296,8 @@ protected function embed_data_badge_version2(&$json, $type = OPEN_BADGES_V2_TYPE
public function get_tags(): array {
return array_values(\core_tag_tag::get_item_tags_array('core_badges', 'badge', $this->get_badge_id()));
}

public function get_userid(): int {
return $this->_data->userid;
}
}
59 changes: 59 additions & 0 deletions badges/classes/backpack.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?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/>.

namespace core_badges;

/**
* Class that represents a backpack.
* TODO: Review/improve this class because it's not clear how to create backpacks (id's, hash...) and what represents.
*
* @package core_badges
* @copyright 2025 Sara Arjona <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backpack {

/**
* Constructs with badge details.
*
* @param int $badgeid badge ID.
*/
public function __construct(string $hash) {
}


/**
* Get the user backpack for the currently logged in user OR the provided user
*
* @param int|null $userid The user whose backpack you're requesting for. If null, get the logged in user's backpack
* @return mixed The user's backpack or none.
*/
public static function get_user_backpack(?int $userid = 0) {
global $DB;

if (!$userid) {
global $USER;
$userid = $USER->id;
}

$sql = "SELECT beb.*, bb.id AS badgebackpack, bb.password, bb.email AS backpackemail
FROM {badge_external_backpack} beb
JOIN {badge_backpack} bb ON bb.externalbackpackid = beb.id AND bb.userid=:userid";

return $DB->get_record_sql($sql, ['userid' => $userid]);
}

}
8 changes: 8 additions & 0 deletions badges/classes/local/backpack/ob/api_base.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ abstract protected function get_mappings(): array;
*/
abstract public function disconnect_backpack(): bool;

/**
* Send an assertion to the backpack.
*
* @param string $hash The assertion hash
* @return array
*/
abstract public function put_assertions(string $hash): array;

/**
* Create a new backpackapi instance from an external backpack record.
*
Expand Down
Loading

0 comments on commit 90107c5

Please sign in to comment.