diff --git a/badges/backpack-add.php b/badges/backpack-add.php index 1ff7795ae7c..7b4fc37ce6b 100644 --- a/badges/backpack-add.php +++ b/badges/backpack-add.php @@ -60,6 +60,7 @@ if (!empty($issuedbadge->recipient->id)) { $assertionid = $assertion->get_assertion_hash(); $assertionentityid = $assertiondata['id']; $badgeadded = false; + $issuerexists = false; if (badges_open_badges_backpack_api() == OPEN_BADGES_V2) { $sitebackpack = badges_get_site_primary_backpack(); $api = new \core_badges\backpack_api($sitebackpack); @@ -71,62 +72,75 @@ if (!empty($issuedbadge->recipient->id)) { $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'); + if ($response) { + $issuerexists = true; + $issuerentityid = $response->id; + badges_external_create_mapping( + $sitebackpack->id, + OPEN_BADGES_V2_TYPE_ISSUER, + $issuer['email'], + $issuerentityid, + ); } - $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'); + if ($issuerexists) { + // 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) { + $badgeentityid = $response->id; + badges_external_create_mapping( + $sitebackpack->id, + OPEN_BADGES_V2_TYPE_BADGE, + $badgeid, + $badgeentityid, + ); + } } - $badgeentityid = $response->id; - badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid, - $badgeentityid); - } - // Create assertion (Award the badge!). - $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); - $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( + // Create assertion (Award the badge!). + $assertionentityid = badges_external_get_mapping( $sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, - $assertionid, - 'externalid' + $assertionid ); - $response = $api->update_assertion($internalid, $assertiondata); - if (!$response) { - throw new moodle_exception('invalidrequest', 'error'); + + 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) { + $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); } } } @@ -136,21 +150,59 @@ if (!empty($issuedbadge->recipient->id)) { // - 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)) { + if ( + $assertionentityid + && (!$issuerexists || !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'); + if ($response) { + $assertionentityid = $response->id; + $badgeadded = true; + badges_external_create_mapping( + $userbackpack->id, + OPEN_BADGES_V2_TYPE_ASSERTION, + $assertionid, + $assertionentityid, + ); } - $assertionentityid = $response->id; - $badgeadded = true; - badges_external_create_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid, - $assertionentityid); } - $response = $badgeadded ? ['success' => 'addedtobackpack'] : ['warning' => 'existsinbackpack']; - redirect(new moodle_url('/badges/mybadges.php', $response)); + $message = ''; + $messagetype = \core\output\notification::NOTIFY_INFO; + if ($badgeadded) { + $message = get_string('addedtobackpack', 'badges'); + $messagetype = \core\output\notification::NOTIFY_SUCCESS; + } else { + if (isset($userapi) && !empty($userapi->get_errors())) { + // If the api used to import the badge to the backpack has errors, show them to inform the user. + if (array_filter($userapi->get_errors(), fn($element) => str_contains($element, "DUPLICATE_BADGE"))) { + // Duplicated badges are displayed as a warning. + $message = get_string('existsinbackpack', 'badges'); + $messagetype = \core\output\notification::NOTIFY_WARNING; + } else { + // If the userapi has any other errors, we will use those to inform the user. + $message = get_string( + 'error:cannotsendtobackpack', + 'badges', + implode($userapi->get_errors()), + ); + $messagetype = \core\output\notification::NOTIFY_ERROR; + } + } else if (isset($api) && !empty($api->get_errors())) { + // If the api used to create/update the issuer has errors, show them to inform the user. + $errors = $api->get_errors() ?? [get_string('invalidrequest', 'error')]; + $message = get_string( + 'error:cannotsendtobackpack', + 'badges', + implode($errors), + ); + $messagetype = \core\output\notification::NOTIFY_ERROR; + } + } + + redirect(new moodle_url('/badges/mybadges.php'), $message, null, $messagetype); } else { redirect(new moodle_url('/badges/mybadges.php')); } diff --git a/badges/classes/backpack_api.php b/badges/classes/backpack_api.php index c7c49cec06e..4ea4b0f78cd 100644 --- a/badges/classes/backpack_api.php +++ b/badges/classes/backpack_api.php @@ -667,6 +667,20 @@ class backpack_api { return backpack_api_mapping::get_authentication_error(); } + /** + * List all errors occurred during the requests to the backpack. + * + * @return array The list of errors. + */ + public function get_errors(): array { + $errors = []; + foreach ($this->mappings as $mapping) { + $errors = array_merge($errors, $mapping->get_errors()); + } + + return $errors; + } + /** * Get the list of badges in a collection. * diff --git a/badges/classes/backpack_api_mapping.php b/badges/classes/backpack_api_mapping.php index 4c10f672a77..c8a81139a80 100644 --- a/badges/classes/backpack_api_mapping.php +++ b/badges/classes/backpack_api_mapping.php @@ -79,6 +79,9 @@ class backpack_api_mapping { /** @var string Error string from authentication request. */ private static $authenticationerror = ''; + /** @var array Errors encountered during the request. */ + protected $errors = []; + /** * Create a mapping. * @@ -144,6 +147,26 @@ class backpack_api_mapping { return self::$authenticationerror; } + /** + * Get the errors encountered during the request. + * + * @return array The list of errors. + */ + public function get_errors() { + return $this->errors; + } + + /** + * Add an error to the list of errors. + * + * @param string $error The error message. + * @return self This instance for method chaining. + */ + public function add_error(string $error): self { + $this->errors[] = $error; + return $this; + } + /** * Does the action match this mapping? * @@ -366,6 +389,23 @@ class backpack_api_mapping { $response = $curl->put($url, $post, $options); } $response = json_decode($response); + if ($response === null) { + $this->add_error(get_string('invalidrequest', 'error')); + return null; + } + if (isset($response->status) && isset($response->status->success) && $response->status->success != true) { + // If the response wasn't successful, store the errors and return null. + if (isset($response->validationErrors)) { + $error = implode(', ', $response->validationErrors); + } else if (isset($response->status)) { + $error = $response->status->description; + } else { + $error = get_string('invalidrequest', 'error'); + } + $this->add_error($error); + return null; + } + if (isset($response->result)) { $response = $response->result; } diff --git a/lang/en/badges.php b/lang/en/badges.php index b42b0484161..ebb109dd8e9 100644 --- a/lang/en/badges.php +++ b/lang/en/badges.php @@ -294,6 +294,9 @@ $string['error:cannotact'] = 'Cannot activate the badge. '; $string['error:cannotawardbadge'] = 'Cannot award badge to a user.'; $string['error:cannotrevokebadge'] = 'Cannot revoke badge from a user.'; $string['error:cannotdeletecriterion'] = 'This criterion cannot be deleted. '; +$string['error:cannotsendtobackpack'] = 'Can\'t add badge to backpack
+Your backpack provider is sending this message: \'{$a}\'.

+You can try downloading the badge and adding it manually to your backpack, or try again later.'; $string['error:connectionunknownreason'] = 'The connection was unsuccessful but no reason was given.'; $string['error:clone'] = 'Cannot clone the badge.'; $string['error:duplicatename'] = 'Badge with such name already exists in the system.';