From 697be4eaa32611f0b2e823d4e3f8a469a05288e0 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Fri, 16 Jun 2023 15:55:19 +0100 Subject: [PATCH 1/3] MDL-75810 badges: allow tags to be specified in test data generator. --- badges/tests/generator/lib.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/badges/tests/generator/lib.php b/badges/tests/generator/lib.php index 242b5d46a55..7d4ab149e5e 100644 --- a/badges/tests/generator/lib.php +++ b/badges/tests/generator/lib.php @@ -43,9 +43,10 @@ class core_badges_generator extends component_generator_base { $record = (array) $record; - // Save badge image path for later. + // Save badge image/tags for later. $badgeimage = $record['image'] ?? ''; - unset($record['image']); + $badgetags = $record['tags'] ?? ''; + unset($record['image'], $record['tags']); $record = (object) array_merge([ 'name' => 'Test badge', @@ -93,6 +94,14 @@ class core_badges_generator extends component_generator_base { badges_process_badge_image($badge, $file->copy_content_to_temp()); } + // Process badge tags (if supplied). + if ($badgetags !== '') { + if (!is_array($badgetags)) { + $badgetags = preg_split('/\s*,\s*/', $badgetags, -1, PREG_SPLIT_NO_EMPTY); + } + core_tag_tag::set_item_tags('core_badges', 'badge', $badge->id, $badge->get_context(), $badgetags); + } + return $badge; } From e8ae9680bc20f5acbf349bdd6184bd7b75ac44f8 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Thu, 15 Jun 2023 21:38:40 +0100 Subject: [PATCH 2/3] MDL-75810 reportbuilder: entity method for returning tag SQL joins. --- .../reportbuilder/local/entities/blog.php | 13 +--------- reportbuilder/classes/local/entities/base.php | 24 +++++++++++++++++++ .../classes/local/entities/course.php | 13 +--------- reportbuilder/classes/local/entities/user.php | 13 +--------- reportbuilder/upgrade.txt | 1 + 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/blog/classes/reportbuilder/local/entities/blog.php b/blog/classes/reportbuilder/local/entities/blog.php index 0c4af13975d..71051f0f2ec 100644 --- a/blog/classes/reportbuilder/local/entities/blog.php +++ b/blog/classes/reportbuilder/local/entities/blog.php @@ -305,17 +305,6 @@ class blog extends base { * @return string[] */ public function get_tag_joins(): array { - $postalias = $this->get_table_alias('post'); - $taginstancealias = $this->get_table_alias('tag_instance'); - $tagalias = $this->get_table_alias('tag'); - - return [ - "LEFT JOIN {tag_instance} {$taginstancealias} - ON {$taginstancealias}.component = 'core' - AND {$taginstancealias}.itemtype = 'post' - AND {$taginstancealias}.itemid = {$postalias}.id", - "LEFT JOIN {tag} {$tagalias} - ON {$tagalias}.id = {$taginstancealias}.tagid", - ]; + return $this->get_tag_joins_for_entity('core', 'post', $this->get_table_alias('post') . '.id'); } } diff --git a/reportbuilder/classes/local/entities/base.php b/reportbuilder/classes/local/entities/base.php index a32dfc02729..1dfcacefe94 100644 --- a/reportbuilder/classes/local/entities/base.php +++ b/reportbuilder/classes/local/entities/base.php @@ -215,6 +215,30 @@ abstract class base { return array_values($this->joins); } + /** + * Helper method for returning joins necessary for retrieving tags related to the current entity + * + * Both 'tag' and 'tag_instance' aliases must be returned by the entity {@see get_default_table_aliases} method + * + * @param string $component + * @param string $itemtype + * @param string $itemidfield + * @return string[] + */ + final protected function get_tag_joins_for_entity(string $component, string $itemtype, string $itemidfield): array { + $taginstancealias = $this->get_table_alias('tag_instance'); + $tagalias = $this->get_table_alias('tag'); + + return [ + "LEFT JOIN {tag_instance} {$taginstancealias} + ON {$taginstancealias}.component = '{$component}' + AND {$taginstancealias}.itemtype = '{$itemtype}' + AND {$taginstancealias}.itemid = {$itemidfield}", + "LEFT JOIN {tag} {$tagalias} + ON {$tagalias}.id = {$taginstancealias}.tagid", + ]; + } + /** * Add a column to the entity * diff --git a/reportbuilder/classes/local/entities/course.php b/reportbuilder/classes/local/entities/course.php index 5532f209f1f..787264c6360 100644 --- a/reportbuilder/classes/local/entities/course.php +++ b/reportbuilder/classes/local/entities/course.php @@ -206,18 +206,7 @@ class course extends base { * @return string[] */ public function get_tag_joins(): array { - $course = $this->get_table_alias('course'); - $taginstance = $this->get_table_alias('tag_instance'); - $tag = $this->get_table_alias('tag'); - - return [ - "LEFT JOIN {tag_instance} {$taginstance} - ON {$taginstance}.component = 'core' - AND {$taginstance}.itemtype = 'course' - AND {$taginstance}.itemid = {$course}.id", - "LEFT JOIN {tag} {$tag} - ON {$tag}.id = {$taginstance}.tagid", - ]; + return $this->get_tag_joins_for_entity('core', 'course', $this->get_table_alias('course') . '.id'); } /** diff --git a/reportbuilder/classes/local/entities/user.php b/reportbuilder/classes/local/entities/user.php index 881fd02d713..4e26f92d439 100644 --- a/reportbuilder/classes/local/entities/user.php +++ b/reportbuilder/classes/local/entities/user.php @@ -144,18 +144,7 @@ class user extends base { * @return string[] */ public function get_tag_joins(): array { - $user = $this->get_table_alias('user'); - $taginstance = $this->get_table_alias('tag_instance'); - $tag = $this->get_table_alias('tag'); - - return [ - "LEFT JOIN {tag_instance} {$taginstance} - ON {$taginstance}.component = 'core' - AND {$taginstance}.itemtype = 'user' - AND {$taginstance}.itemid = {$user}.id", - "LEFT JOIN {tag} {$tag} - ON {$tag}.id = {$taginstance}.tagid", - ]; + return $this->get_tag_joins_for_entity('core', 'user', $this->get_table_alias('user') . '.id'); } /** diff --git a/reportbuilder/upgrade.txt b/reportbuilder/upgrade.txt index f38b06c36d1..384a7c2970a 100644 --- a/reportbuilder/upgrade.txt +++ b/reportbuilder/upgrade.txt @@ -3,6 +3,7 @@ Information provided here is intended especially for developers. === 4.3 === +* New `get_tag_joins_for_entity` helper in base entity class, for returning SQL joins necessary for retrieving tags * New `set_is_deprecated` method in base `local\report\[column|filter]` classes to deprecate report entity columns and filters * The following report entity columns have been deprecated, with replacements as follows: - `enrolment:method` => `enrol:name` (plus enrolment formatter `enrolment_name` method) From ccafa6e657a01bb849b8a88590205af0fb2c9bd1 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Wed, 21 Sep 2022 09:49:23 +0100 Subject: [PATCH 3/3] MDL-75810 badges: tag support in badge custom report sources. --- .../reportbuilder/datasource/badges.php | 20 +- .../reportbuilder/datasource/users.php | 26 +- .../reportbuilder/local/entities/badge.php | 11 + .../reportbuilder/datasource/badges_test.php | 291 ++++++++++++++++-- .../reportbuilder/datasource/users_test.php | 79 +++-- lang/en/badges.php | 1 + 6 files changed, 374 insertions(+), 54 deletions(-) diff --git a/badges/classes/reportbuilder/datasource/badges.php b/badges/classes/reportbuilder/datasource/badges.php index 379d4cbc646..9944204ee65 100644 --- a/badges/classes/reportbuilder/datasource/badges.php +++ b/badges/classes/reportbuilder/datasource/badges.php @@ -22,6 +22,7 @@ use lang_string; use core_reportbuilder\datasource; use core_reportbuilder\local\entities\{course, user}; use core_badges\reportbuilder\local\entities\{badge, badge_issued}; +use core_tag\reportbuilder\local\entities\tag; /** * Badges datasource @@ -49,9 +50,15 @@ class badges extends datasource { $badgealias = $badgeentity->get_table_alias('badge'); $this->set_main_table('badge', $badgealias); - $this->add_entity($badgeentity); + // Join the tag entity. + $tagentity = (new tag()) + ->set_table_alias('tag', $badgeentity->get_table_alias('tag')) + ->set_entity_title(new lang_string('badgetags', 'core_badges')); + $this->add_entity($tagentity + ->add_joins($badgeentity->get_tag_joins())); + // Join the badge issued entity to the badge entity. $badgeissuedentity = new badge_issued(); $badgeissuedalias = $badgeissuedentity->get_table_alias('badge_issued'); @@ -81,7 +88,16 @@ class badges extends datasource { ); // Add report elements from each of the entities we added to the report. - $this->add_all_from_entities(); + $this->add_all_from_entity($badgeentity->get_entity_name()); + + // Add specific tag entity elements. + $this->add_columns_from_entity($tagentity->get_entity_name(), ['name', 'namewithlink']); + $this->add_filter($tagentity->get_filter('name')); + $this->add_condition($tagentity->get_condition('name')); + + $this->add_all_from_entity($badgeissuedentity->get_entity_name()); + $this->add_all_from_entity($userentity->get_entity_name()); + $this->add_all_from_entity($courseentity->get_entity_name()); } /** diff --git a/badges/classes/reportbuilder/datasource/users.php b/badges/classes/reportbuilder/datasource/users.php index 22cb74e94e5..6b693816846 100644 --- a/badges/classes/reportbuilder/datasource/users.php +++ b/badges/classes/reportbuilder/datasource/users.php @@ -18,10 +18,12 @@ declare(strict_types=1); namespace core_badges\reportbuilder\datasource; +use lang_string; use core_reportbuilder\datasource; use core_reportbuilder\local\entities\{course, user}; use core_reportbuilder\local\helpers\database; use core_badges\reportbuilder\local\entities\{badge, badge_issued}; +use core_tag\reportbuilder\local\entities\tag; /** * User badges datasource @@ -48,17 +50,16 @@ class users extends datasource { global $CFG; $userentity = new user(); - $useralias = $userentity->get_table_alias('user'); + $this->set_main_table('user', $useralias); + $this->add_entity($userentity); $paramguest = database::generate_param_name(); $this->add_base_condition_sql("{$useralias}.id != :{$paramguest} AND {$useralias}.deleted = 0", [ $paramguest => $CFG->siteguest, ]); - $this->add_entity($userentity); - // Join the badge issued entity to the user entity. $badgeissuedentity = new badge_issued(); $badgeissuedalias = $badgeissuedentity->get_table_alias('badge_issued'); @@ -71,6 +72,14 @@ class users extends datasource { ->add_joins($badgeissuedentity->get_joins()) ->add_join("LEFT JOIN {badge} {$badgealias} ON {$badgealias}.id = {$badgeissuedalias}.badgeid")); + // Join the tag entity. + $tagentity = (new tag()) + ->set_table_alias('tag', $badgeentity->get_table_alias('tag')) + ->set_entity_title(new lang_string('badgetags', 'core_badges')); + $this->add_entity($tagentity + ->add_joins($badgeentity->get_joins()) + ->add_joins($badgeentity->get_tag_joins())); + // Join the course entity to the badge entity, coalescing courseid with the siteid for site badges. $courseentity = new course(); $coursealias = $courseentity->get_table_alias('course'); @@ -80,7 +89,16 @@ class users extends datasource { CASE WHEN {$badgealias}.id IS NULL THEN 0 ELSE COALESCE({$badgealias}.courseid, 1) END")); // Add report elements from each of the entities we added to the report. - $this->add_all_from_entities(); + $this->add_all_from_entity($userentity->get_entity_name()); + $this->add_all_from_entity($badgeissuedentity->get_entity_name()); + $this->add_all_from_entity($badgeentity->get_entity_name()); + + // Add specific tag entity elements. + $this->add_columns_from_entity($tagentity->get_entity_name(), ['name', 'namewithlink']); + $this->add_filter($tagentity->get_filter('name')); + $this->add_condition($tagentity->get_condition('name')); + + $this->add_all_from_entity($courseentity->get_entity_name()); } /** diff --git a/badges/classes/reportbuilder/local/entities/badge.php b/badges/classes/reportbuilder/local/entities/badge.php index 9fa76e9cbb2..f55e258a58c 100644 --- a/badges/classes/reportbuilder/local/entities/badge.php +++ b/badges/classes/reportbuilder/local/entities/badge.php @@ -52,6 +52,8 @@ class badge extends base { return [ 'badge' => 'b', 'context' => 'bctx', + 'tag_instance' => 'bti', + 'tag' => 'bt', ]; } @@ -303,4 +305,13 @@ class badge extends base { return $filters; } + + /** + * Return joins necessary for retrieving tags + * + * @return string[] + */ + public function get_tag_joins(): array { + return $this->get_tag_joins_for_entity('core_badges', 'badge', $this->get_table_alias('badge') . '.id'); + } } diff --git a/badges/tests/reportbuilder/datasource/badges_test.php b/badges/tests/reportbuilder/datasource/badges_test.php index cacf1896187..b8494ff7a04 100644 --- a/badges/tests/reportbuilder/datasource/badges_test.php +++ b/badges/tests/reportbuilder/datasource/badges_test.php @@ -21,11 +21,13 @@ namespace core_badges\reportbuilder\datasource; use core_badges_generator; use core_reportbuilder_generator; use core_reportbuilder_testcase; +use core_reportbuilder\local\filters\{boolean_select, date, select, tags, text}; defined('MOODLE_INTERNAL') || die(); global $CFG; require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php"); +require_once("{$CFG->libdir}/badgeslib.php"); /** * Unit tests for badges datasource @@ -38,58 +40,287 @@ require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php"); class badges_test extends core_reportbuilder_testcase { /** - * Load required test libraries + * Test default datasource */ - public static function setUpBeforeClass(): void { - global $CFG; - require_once("{$CFG->libdir}/badgeslib.php"); - } - - /** - * Test datasource - */ - public function test_datasource(): void { + public function test_datasource_default(): void { $this->resetAfterTest(); - $this->setAdminUser(); - // Test users with a badge we can issue them with. $user1 = $this->getDataGenerator()->create_user(['firstname' => 'Alan', 'lastname' => 'Apple']); $user2 = $this->getDataGenerator()->create_user(['firstname' => 'Barry', 'lastname' => 'Banana']); /** @var core_badges_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); - $sitebadge = $generator->create_badge(['name' => 'Badge 1']); - $sitebadge->issue($user1->id, true); - $sitebadge->issue($user2->id, true); + $badgeone = $generator->create_badge(['name' => 'Badge 1', 'description' => 'My first badge']); + $badgeone->issue($user1->id, true); + $badgeone->issue($user2->id, true); - // Another badge, in a course, no issues. + // Second badge, not issued to any users. + $badgetwo = $generator->create_badge(['name' => 'Badge 2', 'description' => 'My second badge']); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'Badges', 'source' => badges::class, 'default' => 1]); + + $content = $this->get_custom_report_content($report->get('id')); + $this->assertCount(3, $content); + + // Default columns are badge, description, user, issued time. Sorted by badge, user, issued time. + [$badgename, $badgedescription, $userfullname, $badgeissued] = array_values($content[0]); + $this->assertEquals($badgeone->name, $badgename); + $this->assertEquals($badgeone->description, $badgedescription); + $this->assertEquals(fullname($user1), $userfullname); + $this->assertNotEmpty($badgeissued); + + [$badgename, $badgedescription, $userfullname, $badgeissued] = array_values($content[1]); + $this->assertEquals($badgeone->name, $badgename); + $this->assertEquals($badgeone->description, $badgedescription); + $this->assertEquals(fullname($user2), $userfullname); + $this->assertNotEmpty($badgeissued); + + [$badgename, $badgedescription, $userfullname, $badgeissued] = array_values($content[2]); + $this->assertEquals($badgetwo->name, $badgename); + $this->assertEquals($badgetwo->description, $badgedescription); + $this->assertEmpty($userfullname); + $this->assertEmpty($badgeissued); + } + + /** + * Test datasource columns that aren't added by default + */ + public function test_datasource_non_default_columns(): void { + global $DB; + + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(['firstname' => 'Alan', 'lastname' => 'Apple']); + $user2 = $this->getDataGenerator()->create_user(['firstname' => 'Barry', 'lastname' => 'Banana']); + + /** @var core_badges_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); + + $badgeone = $generator->create_badge([ + 'name' => 'Badge 1', + 'language' => 'de', + 'expireperiod' => HOURSECS, + 'tags' => ['cool'], + ]); + $badgeone->issue($user1->id, true); + $badgeone->issue($user2->id, true); + + // Course badge, not issued to any users. $course = $this->getDataGenerator()->create_course(); - $coursebadge = $generator->create_badge(['name' => 'Badge 2', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]); + $badgetwo = $generator->create_badge(['name' => 'Badge 2', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]); + + // Create criteria for manually awarding by role. + $managerrole = $DB->get_field('role', 'id', ['shortname' => 'manager']); + $generator->create_criteria(['badgeid' => $badgeone->id, 'roleid' => $managerrole]); /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); $report = $generator->create_report(['name' => 'Badges', 'source' => badges::class, 'default' => 0]); - // Badge course. - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname', 'sortenabled' => 1]); - - // Badge name. + // These two columns have been asserted previously, we're only adding them for consistent sorting. $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:name', 'sortenabled' => 1]); - - // User fullname. $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname', 'sortenabled' => 1]); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:criteria']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:image']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:language']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:version']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:status']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:expiry']); + + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:name']); + + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge_issued:expire']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge_issued:visible']); + + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']); + $content = $this->get_custom_report_content($report->get('id')); $this->assertCount(3, $content); - $this->assertEquals([ - ['PHPUnit test site', $sitebadge->name, fullname($user1, true)], - ['PHPUnit test site', $sitebadge->name, fullname($user2, true)], - [$course->fullname, $coursebadge->name, ''], - ], array_map(static function(array $row): array { - return array_values($row); - }, $content)); + // First badge, issued to user one. + [$badgename, $fullname, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires, $visible, $coursename] + = array_values($content[0]); + $this->assertEquals($badgeone->name, $badgename); + $this->assertEquals(fullname($user1), $fullname); + $this->assertStringContainsString('Awarded by: Manager', $criteria); + $this->assertStringContainsString('Image caption', $image); + $this->assertEquals('German', $language); + $this->assertEquals(2, $version); + $this->assertEquals('Available (criteria locked)', $status); + $this->assertEquals('1 hour', $expiry); + $this->assertEquals('cool', $tag); + $this->assertNotEmpty($expires); + $this->assertEquals('Yes', $visible); + $this->assertEquals('PHPUnit test site', $coursename); + + // First badge, issued to user two. + [$badgename, $fullname, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires, $visible, $coursename] + = array_values($content[1]); + $this->assertEquals($badgeone->name, $badgename); + $this->assertEquals(fullname($user2), $fullname); + $this->assertStringContainsString('Awarded by: Manager', $criteria); + $this->assertStringContainsString('Image caption', $image); + $this->assertEquals('German', $language); + $this->assertEquals(2, $version); + $this->assertEquals('Available (criteria locked)', $status); + $this->assertEquals('1 hour', $expiry); + $this->assertEquals('cool', $tag); + $this->assertNotEmpty($expires); + $this->assertEquals('Yes', $visible); + $this->assertEquals('PHPUnit test site', $coursename); + + // Course badge, not issues to any users. + [$badgename, $fullname, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires, $visible, $coursename] + = array_values($content[2]); + $this->assertEquals($badgetwo->name, $badgename); + $this->assertEmpty($fullname); + $this->assertEquals('Criteria for this badge have not been set up yet.', $criteria); + $this->assertStringContainsString('Image caption', $image); + $this->assertEquals('English', $language); + $this->assertEquals(2, $version); + $this->assertEquals('Available', $status); + $this->assertEquals('Never', $expiry); + $this->assertEmpty($tag); + $this->assertEmpty($expires); + $this->assertEmpty($visible); + $this->assertEquals($course->fullname, $coursename); + } + + /** + * Data provider for {@see test_datasource_filters} + * + * @return array[] + */ + public function datasource_filters_provider(): array { + return [ + // Badge. + 'Filter badge name' => ['badge:name', [ + 'badge:name_operator' => text::IS_EQUAL_TO, + 'badge:name_value' => 'Course badge', + ], true], + 'Filter badge name (no match)' => ['badge:name', [ + 'badge:name_operator' => text::IS_EQUAL_TO, + 'badge:name_value' => 'Other badge', + ], false], + 'Filter badge status' => ['badge:status', [ + 'badge:status_operator' => select::EQUAL_TO, + 'badge:status_value' => BADGE_STATUS_ACTIVE_LOCKED, + ], true], + 'Filter badge status (no match)' => ['badge:status', [ + 'badge:status_operator' => select::EQUAL_TO, + 'badge:status_value' => BADGE_STATUS_ACTIVE, + ], false], + 'Filter badge type' => ['badge:type', [ + 'badge:type_operator' => select::EQUAL_TO, + 'badge:type_value' => BADGE_TYPE_COURSE, + ], true], + 'Filter badge type (no match)' => ['badge:type', [ + 'badge:type_operator' => select::EQUAL_TO, + 'badge:type_value' => BADGE_TYPE_SITE, + ], false], + + // Badge tag. + 'Filter tag name' => ['tag:name', [ + 'tag:name_operator' => tags::NOT_EMPTY, + ], true], + 'Filter tag name (no match)' => ['tag:name', [ + 'tag:name_operator' => tags::EQUAL_TO, + 'tag:name_value' => [-1], + ], false], + + // User. + 'Filter user fullname' => ['user:fullname', [ + 'user:fullname_operator' => text::IS_EQUAL_TO, + 'user:fullname_value' => 'Zoe Zebra', + ], true], + 'Filter user fullname (no match)' => ['user:fullname', [ + 'user:fullname_operator' => text::IS_EQUAL_TO, + 'user:fullname_value' => 'Alice Aardvark', + ], false], + + // Badge issued. + 'Filter badge issued date' => ['badge_issued:issued', [ + 'badge_issued:issued_operator' => date::DATE_RANGE, + 'badge_issued:issued_from' => 1622502000, + ], true], + 'Filter badge issued date (no match)' => ['badge_issued:issued', [ + 'badge_issued:issued_operator' => date::DATE_RANGE, + 'badge_issued:issued_to' => 1622502000, + ], false], + 'Filter badge issued expires' => ['badge_issued:expires', [ + 'badge_issued:expires_operator' => date::DATE_RANGE, + 'badge_issued:expires_from' => 1622502000, + ], true], + 'Filter badge issued expires (no match)' => ['badge_issued:expires', [ + 'badge_issued:expires_operator' => date::DATE_RANGE, + 'badge_issued:expires_to' => 1622502000, + ], false], + 'Filter badge issued visible' => ['badge_issued:visible', [ + 'badge_issued:visible_operator' => boolean_select::CHECKED, + ], true], + 'Filter badge issued visible (no match)' => ['badge_issued:visible', [ + 'badge_issued:visible_operator' => boolean_select::NOT_CHECKED, + ], false], + + // Course. + 'Filter course fullname' => ['course:fullname', [ + 'course:fullname_operator' => text::IS_EQUAL_TO, + 'course:fullname_value' => 'Course 1', + ], true], + 'Filter course fullname (no match)' => ['course:fullname', [ + 'course:fullname_operator' => text::IS_EQUAL_TO, + 'course:fullname_value' => 'Course 2', + ], false], + ]; + } + + /** + * Test datasource filters + * + * @param string $filtername + * @param array $filtervalues + * @param bool $expectmatch + * + * @dataProvider datasource_filters_provider + */ + public function test_datasource_filters(string $filtername, array $filtervalues, bool $expectmatch): void { + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(['fullname' => 'Course 1']); + $user = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe', 'lastname' => 'Zebra']); + + /** @var core_badges_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); + $badge = $generator->create_badge([ + 'name' => 'Course badge', + 'type' => BADGE_TYPE_COURSE, + 'courseid' => $course->id, + 'expireperiod' => HOURSECS, + 'tags' => ['cool'], + ]); + $badge->issue($user->id, true); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + // Create report containing single username column, and given filter. + $report = $generator->create_report(['name' => 'My report', 'source' => badges::class, 'default' => 0]); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:name']); + + // Add filter, set it's values. + $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]); + $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); + + if ($expectmatch) { + $this->assertCount(1, $content); + $this->assertEquals($badge->name, reset($content[0])); + } else { + $this->assertEmpty($content); + } } /** diff --git a/badges/tests/reportbuilder/datasource/users_test.php b/badges/tests/reportbuilder/datasource/users_test.php index 414d86bba57..ea4d8ca0739 100644 --- a/badges/tests/reportbuilder/datasource/users_test.php +++ b/badges/tests/reportbuilder/datasource/users_test.php @@ -21,7 +21,7 @@ namespace core_badges\reportbuilder\datasource; use core_badges_generator; use core_reportbuilder_generator; use core_reportbuilder_testcase; -use core_reportbuilder\local\filters\{boolean_select, date, select, text}; +use core_reportbuilder\local\filters\{boolean_select, date, select, tags, text}; defined('MOODLE_INTERNAL') || die(); @@ -94,10 +94,21 @@ class users_test extends core_reportbuilder_testcase { /** @var core_badges_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); - ($badgesite = $generator->create_badge(['name' => 'Badge 1', 'language' => 'de', 'expireperiod' => HOURSECS])) - ->issue($user->id, true); - ($badgecourse = $generator->create_badge(['name' => 'Badge 2', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id])) - ->issue($user->id, true); + + $badgesite = $generator->create_badge([ + 'name' => 'Badge 1', + 'language' => 'de', + 'expireperiod' => HOURSECS, + 'tags' => ['cool'], + ]); + $badgecourse = $generator->create_badge([ + 'name' => 'Badge 2', + 'type' => BADGE_TYPE_COURSE, + 'courseid' => $course->id, + ]); + + $badgesite->issue($user->id, true); + $badgecourse->issue($user->id, true); // Create criteria for manually awarding by role. $managerrole = $DB->get_field('role', 'id', ['shortname' => 'manager']); @@ -118,6 +129,8 @@ class users_test extends core_reportbuilder_testcase { $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:status']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:expiry']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:name']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge_issued:expire']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge_issued:visible']); @@ -127,37 +140,49 @@ class users_test extends core_reportbuilder_testcase { $this->assertCount(3, $content); // Admin user, no badge issued. - [, , $criteria, $image, $language, $version, $status, $expiry, $expires, $visible, $coursename] = array_values($content[0]); + [$fullname, $badgename, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires, $visible, $coursename] + = array_values($content[0]); + $this->assertEquals('Admin User', $fullname); + $this->assertEmpty($badgename); $this->assertEmpty($criteria); $this->assertEmpty($image); $this->assertEmpty($language); $this->assertEmpty($version); $this->assertEmpty($status); $this->assertEmpty($expiry); + $this->assertEmpty($tag); $this->assertEmpty($expires); $this->assertEmpty($visible); $this->assertEmpty($coursename); - // Site badge issued. - [, , $criteria, $image, $language, $version, $status, $expiry, $expires, $visible, $coursename] = array_values($content[1]); + // User issued site badge. + [$fullname, $badgename, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires, $visible, $coursename] + = array_values($content[1]); + $this->assertEquals(fullname($user), $fullname); + $this->assertEquals($badgesite->name, $badgename); $this->assertStringContainsString('Awarded by: Manager', $criteria); $this->assertStringContainsString('Image caption', $image); $this->assertEquals('German', $language); $this->assertEquals(2, $version); $this->assertEquals('Available (criteria locked)', $status); $this->assertEquals('1 hour', $expiry); + $this->assertEquals('cool', $tag); $this->assertNotEmpty($expires); $this->assertEquals('Yes', $visible); $this->assertEquals('PHPUnit test site', $coursename); - // Course badge issued. - [, , $criteria, $image, $language, $version, $status, $expiry, $expires, $visible, $coursename] = array_values($content[2]); + // User issued site badge. + [$fullname, $badgename, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires, $visible, $coursename] + = array_values($content[2]); + $this->assertEquals(fullname($user), $fullname); + $this->assertEquals($badgecourse->name, $badgename); $this->assertEquals('Criteria for this badge have not been set up yet.', $criteria); $this->assertStringContainsString('Image caption', $image); $this->assertEquals('English', $language); $this->assertEquals(2, $version); $this->assertEquals('Available (criteria locked)', $status); $this->assertEquals('Never', $expiry); + $this->assertEmpty($tag); $this->assertEmpty($expires); $this->assertEquals('Yes', $visible); $this->assertEquals($course->fullname, $coursename); @@ -170,6 +195,16 @@ class users_test extends core_reportbuilder_testcase { */ public function datasource_filters_provider(): array { return [ + // User. + 'Filter user fullname' => ['user:fullname', [ + 'user:fullname_operator' => text::IS_EQUAL_TO, + 'user:fullname_value' => 'Zoe Zebra', + ], true], + 'Filter user fullname (no match)' => ['user:fullname', [ + 'user:fullname_operator' => text::IS_EQUAL_TO, + 'user:fullname_value' => 'Alice Aardvark', + ], false], + // Badge. 'Filter badge name' => ['badge:name', [ 'badge:name_operator' => text::IS_EQUAL_TO, @@ -196,6 +231,15 @@ class users_test extends core_reportbuilder_testcase { 'badge:type_value' => BADGE_TYPE_SITE, ], false], + // Badge tag. + 'Filter tag name' => ['tag:name', [ + 'tag:name_operator' => tags::NOT_EMPTY, + ], true], + 'Filter tag name (no match)' => ['tag:name', [ + 'tag:name_operator' => tags::EQUAL_TO, + 'tag:name_value' => [-1], + ], false], + // Badge issued. 'Filter badge issued date' => ['badge_issued:issued', [ 'badge_issued:issued_operator' => date::DATE_RANGE, @@ -245,16 +289,18 @@ class users_test extends core_reportbuilder_testcase { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(['fullname' => 'Course 1']); - $user = $this->getDataGenerator()->create_and_enrol($course); + $user = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe', 'lastname' => 'Zebra']); /** @var core_badges_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); - $generator->create_badge([ + $badge = $generator->create_badge([ 'name' => 'Course badge', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id, 'expireperiod' => HOURSECS, - ])->issue($user->id, true); + 'tags' => ['cool'], + ]); + $badge->issue($user->id, true); /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); @@ -292,11 +338,8 @@ class users_test extends core_reportbuilder_testcase { /** @var core_badges_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); - $generator->create_badge([ - 'name' => 'Course badge', - 'type' => BADGE_TYPE_COURSE, - 'courseid' => $course->id, - ])->issue($user->id, true); + $badge = $generator->create_badge(['name' => 'Course badge', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]); + $badge->issue($user->id, true); $this->datasource_stress_test_columns(users::class); $this->datasource_stress_test_columns_aggregation(users::class); diff --git a/lang/en/badges.php b/lang/en/badges.php index 84ca649db5e..d9421fb37a9 100644 --- a/lang/en/badges.php +++ b/lang/en/badges.php @@ -163,6 +163,7 @@ $string['badgestatus_3'] = 'Available (criteria locked)'; $string['badgestatus_4'] = 'Archived'; $string['badgestoearn'] = 'Number of badges available: {$a}'; $string['badgesview'] = 'Course badges'; +$string['badgetags'] = 'Badge tags'; $string['badgeurl'] = 'Issued badge link'; $string['bawards'] = 'Recipients ({$a})'; $string['bcriteria'] = 'Criteria';