From 9e7ca660c276193681cf319e63cae34882cc3bc3 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Fri, 7 Feb 2025 09:06:10 +0100 Subject: [PATCH] MDL-83026 badges: Added full name of the course to badge exporters --- .upgradenotes/MDL-83026-2024091612113951.yml | 14 +++++++ .../classes/external/badgeclass_exporter.php | 10 +++++ .../classes/external/user_badge_exporter.php | 5 +++ badges/tests/classes/external_helper.php | 41 +++++++++++++++++++ badges/tests/external/get_badge_test.php | 40 ++++++++---------- lib/badgeslib.php | 18 +++++++- 6 files changed, 103 insertions(+), 25 deletions(-) create mode 100644 .upgradenotes/MDL-83026-2024091612113951.yml diff --git a/.upgradenotes/MDL-83026-2024091612113951.yml b/.upgradenotes/MDL-83026-2024091612113951.yml new file mode 100644 index 00000000000..2e0d0cf3da4 --- /dev/null +++ b/.upgradenotes/MDL-83026-2024091612113951.yml @@ -0,0 +1,14 @@ +issueNumber: MDL-83026 +notes: + core_badges: + - message: + Added fields `courseid` and `coursefullname` to + `badgeclass_exporter`, which is used in the return structure + of external function `core_badges_get_badge`. + type: improved + - message: + Added field `coursefullname` to `user_badge_exporter`, which + is used in the return structure of external functions + `core_badges_get_user_badge_by_hash` and + `core_badges_get_user_badges`. + type: improved diff --git a/badges/classes/external/badgeclass_exporter.php b/badges/classes/external/badgeclass_exporter.php index 9aee9568256..35f2a8c32eb 100644 --- a/badges/classes/external/badgeclass_exporter.php +++ b/badges/classes/external/badgeclass_exporter.php @@ -167,6 +167,16 @@ class badgeclass_exporter extends exporter { 'description' => 'Identifier of the open badge for this assertion', 'optional' => true, ], + 'courseid' => [ + 'type' => PARAM_INT, + 'description' => 'Id of the course', + 'optional' => true, + ], + 'coursefullname' => [ + 'type' => PARAM_TEXT, + 'description' => 'Full name of the course', + 'optional' => true, + ], ]; } diff --git a/badges/classes/external/user_badge_exporter.php b/badges/classes/external/user_badge_exporter.php index 23a522c2d7d..4e97fa25814 100644 --- a/badges/classes/external/user_badge_exporter.php +++ b/badges/classes/external/user_badge_exporter.php @@ -122,6 +122,11 @@ class user_badge_exporter extends exporter { 'optional' => true, 'null' => NULL_ALLOWED, ], + 'coursefullname' => [ + 'type' => PARAM_TEXT, + 'description' => 'Full name of the course', + 'optional' => true, + ], 'message' => [ 'type' => PARAM_RAW, 'description' => 'Message', diff --git a/badges/tests/classes/external_helper.php b/badges/tests/classes/external_helper.php index 16f970d1248..b75e063119c 100644 --- a/badges/tests/classes/external_helper.php +++ b/badges/tests/classes/external_helper.php @@ -25,6 +25,44 @@ namespace core_badges\tests; */ trait external_helper { + /** + * Asserts that a badge class returned by an external function matches the given data. + * + * @param array $expected Expected badge data. + * @param array $actual Actual badge class data returned by the external function. + * @param bool $canconfiguredetails True if user has capability "moodle/badges:configuredetails". + * @throws \PHPUnit\Framework\ExpectationFailedException + */ + private function assert_badge_class(array $expected, array $actual, bool $canconfiguredetails): void { + $this->assertEquals('BadgeClass', $actual['type']); + $badgeurl = new \moodle_url('/badges/badgeclass.php', ['id' => $expected['id']]); + $this->assertEquals($badgeurl->out(false), $actual['id']); + $this->assertEquals($expected['issuername'], $actual['issuer']); + $this->assertEquals($expected['name'], $actual['name']); + $this->assertEquals($expected['badgeurl'], $actual['image']); + $this->assertEquals($expected['description'], $actual['description']); + $this->assertEquals($expected['issuerurl'], $actual['hostedUrl']); + $this->assertEquals($expected['coursefullname'], $actual['coursefullname'] ?? null); + if ($canconfiguredetails) { + $this->assertEquals($expected['courseid'], $actual['courseid'] ?? null); + } else { + $this->assertArrayNotHasKey('courseid', $actual); + } + + $alignments = $expected['alignment']; + if (!$canconfiguredetails) { + foreach ($alignments as $index => $alignment) { + $alignments[$index] = [ + 'id' => $alignment['id'], + 'badgeid' => $alignment['badgeid'], + 'targetName' => $alignment['targetName'], + 'targetUrl' => $alignment['targetUrl'], + ]; + } + } + $this->assertEquals($alignments, $actual['alignment'] ?? []); + } + /** * Asserts that an issued badge returned by an external function matches the given data. * @@ -51,6 +89,7 @@ trait external_helper { $this->assertEquals($expected['badgeurl'], $actual['badgeurl']); $this->assertEquals($expected['recipientid'], $actual['recipientid']); $this->assertEquals($expected['recipientfullname'], $actual['recipientfullname']); + $this->assertEquals($expected['coursefullname'], $actual['coursefullname'] ?? null); $this->assertEquals($expected['endorsement'] ?? null, $actual['endorsement'] ?? null); if ($isrecipient || $canconfiguredetails) { @@ -176,6 +215,7 @@ trait external_helper { 'recipientid' => $student->id, 'recipientfullname' => fullname($student), 'email' => $student->email, + 'coursefullname' => null, 'endorsement' => null, 'alignment' => [], 'relatedbadges' => [], @@ -258,6 +298,7 @@ trait external_helper { 'recipientid' => $student->id, 'recipientfullname' => fullname($student), 'email' => $student->email, + 'coursefullname' => \core_external\util::format_string($course->fullname, $coursecontext), 'endorsement' => null, 'alignment' => [], 'relatedbadges' => [], diff --git a/badges/tests/external/get_badge_test.php b/badges/tests/external/get_badge_test.php index 7cf373867d0..a236d023da8 100644 --- a/badges/tests/external/get_badge_test.php +++ b/badges/tests/external/get_badge_test.php @@ -60,18 +60,16 @@ final class get_badge_test extends externallib_advanced_testcase { public function test_get_badge(): void { $data = $this->prepare_test_data(); - // Test with an existing badge. + // Test with an existing site badge. $result = get_badge::execute($data['sitebadge']['id']); $result = \core_external\external_api::clean_returnvalue(get_badge::execute_returns(), $result); - $this->assertEquals('BadgeClass', $result['badge']['type']); - $badgeurl = new \moodle_url('/badges/badgeclass.php', ['id' => $data['sitebadge']['id']]); - $this->assertEquals($badgeurl->out(false), $result['badge']['id']); - $this->assertEquals($data['sitebadge']['issuername'], $result['badge']['issuer']); - $this->assertEquals($data['sitebadge']['name'], $result['badge']['name']); - $this->assertEquals($data['sitebadge']['badgeurl'], $result['badge']['image']); - $this->assertEquals($data['sitebadge']['description'], $result['badge']['description']); - $this->assertEquals($data['sitebadge']['issuerurl'], $result['badge']['hostedUrl']); - $this->assertEquals($data['sitebadge']['alignment'], $result['badge']['alignment']); + $this->assert_badge_class($data['sitebadge'], $result['badge'], true); + $this->assertEmpty($result['warnings']); + + // Test with an existing course badge. + $result = get_badge::execute($data['coursebadge']['id']); + $result = \core_external\external_api::clean_returnvalue(get_badge::execute_returns(), $result); + $this->assert_badge_class($data['coursebadge'], $result['badge'], true); $this->assertEmpty($result['warnings']); } @@ -81,24 +79,18 @@ final class get_badge_test extends externallib_advanced_testcase { */ public function test_get_badge_with_unprivileged_user(): void { $data = $this->prepare_test_data(); - foreach ($data['sitebadge']['alignment'] as &$alignment) { - unset($alignment['targetDescription']); - unset($alignment['targetFramework']); - unset($alignment['targetCode']); - } $this->setUser($this->getDataGenerator()->create_user()); + // Site badge. $result = get_badge::execute($data['sitebadge']['id']); $result = \core_external\external_api::clean_returnvalue(get_badge::execute_returns(), $result); - $this->assertEquals('BadgeClass', $result['badge']['type']); - $badgeurl = new \moodle_url('/badges/badgeclass.php', ['id' => $data['sitebadge']['id']]); - $this->assertEquals($badgeurl->out(false), $result['badge']['id']); - $this->assertEquals($data['sitebadge']['issuername'], $result['badge']['issuer']); - $this->assertEquals($data['sitebadge']['name'], $result['badge']['name']); - $this->assertEquals($data['sitebadge']['badgeurl'], $result['badge']['image']); - $this->assertEquals($data['sitebadge']['description'], $result['badge']['description']); - $this->assertEquals($data['sitebadge']['issuerurl'], $result['badge']['hostedUrl']); - $this->assertEquals($data['sitebadge']['alignment'], $result['badge']['alignment']); + $this->assert_badge_class($data['sitebadge'], $result['badge'], false); + $this->assertEmpty($result['warnings']); + + // Course badge. + $result = get_badge::execute($data['coursebadge']['id']); + $result = \core_external\external_api::clean_returnvalue(get_badge::execute_returns(), $result); + $this->assert_badge_class($data['coursebadge'], $result['badge'], false); $this->assertEmpty($result['warnings']); } diff --git a/lib/badgeslib.php b/lib/badgeslib.php index d75049cba05..9940d6f3c52 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -448,6 +448,12 @@ function badges_prepare_badge_for_external(stdClass $badge, stdClass $user): obj ]; } + // Course. + if ($badge->type == BADGE_TYPE_COURSE) { + $course = get_course($context->instanceid); + $badge->coursefullname = \core_external\util::format_string($course->fullname, $context); + } + // Recipient (the badge was awarded to this person). $badge->recipientid = $user->id; if ($user->deleted) { @@ -510,6 +516,7 @@ function badges_prepare_badge_for_external(stdClass $badge, stdClass $user): obj function badges_prepare_badgeclass_for_external(core_badges\output\badgeclass $badgeclass): stdClass { global $PAGE; $context = $badgeclass->context; + $canconfiguredetails = has_capability('moodle/badges:configuredetails', $context); $badgeurl = new \moodle_url('/badges/badgeclass.php', [ 'id' => $badgeclass->badge->id, @@ -534,11 +541,20 @@ function badges_prepare_badgeclass_for_external(core_badges\output\badgeclass $b 'hostedUrl' => $badgeclass->badge->issuerurl, 'image' => $image, ]; + + // Course. + if ($badgeclass->badge->type == BADGE_TYPE_COURSE) { + $course = get_course($badgeclass->badge->courseid); + $badge->coursefullname = \core_external\util::format_string($course->fullname, $context); + if ($canconfiguredetails) { + $badge->courseid = $course->id; + } + } + // Create a badge instance to be able to get the endorsement and other info. $badgeinstance = new badge($badgeclass->badge->id); $endorsement = $badgeinstance->get_endorsement(); $relatedbadges = $badgeinstance->get_related_badges(); - $canconfiguredetails = has_capability('moodle/badges:configuredetails', $context); $alignments = []; foreach ($badgeinstance->get_alignments() as $alignment) { $alignmentobj = (object) [