This commit is contained in:
Ilya Tregubov 2022-08-30 14:59:50 +04:00
commit 96dbcc0840
21 changed files with 1437 additions and 165 deletions

View file

@ -20,12 +20,10 @@ namespace core_reportbuilder\local\entities;
use context_course;
use context_helper;
use core_tag_tag;
use core_reportbuilder\local\filters\boolean_select;
use core_reportbuilder\local\filters\course_selector;
use core_reportbuilder\local\filters\date;
use core_reportbuilder\local\filters\select;
use core_reportbuilder\local\filters\tags;
use core_reportbuilder\local\filters\text;
use core_reportbuilder\local\helpers\custom_fields;
use core_reportbuilder\local\helpers\format;
@ -202,7 +200,7 @@ class course extends base {
*
* @return string[]
*/
private function get_tag_joins(): array {
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');
@ -296,22 +294,6 @@ class course extends base {
$columns[] = $column;
}
// Tags.
$tag = $this->get_table_alias('tag');
$columns[] = (new column(
'tags',
new lang_string('tags'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->add_joins($this->get_tag_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$tag}.name, {$tag}.rawname")
->set_is_sortable(true)
->add_callback(static function($value, stdClass $tag): string {
return core_tag_tag::make_display_name($tag);
});
return $columns;
}
@ -363,22 +345,6 @@ class course extends base {
$filters[] = $filter;
}
// Tags.
$tag = $this->get_table_alias('tag');
$filters[] = (new filter(
tags::class,
'tags',
new lang_string('tags'),
$this->get_entity_name(),
"{$tag}.id"
))
->add_joins($this->get_joins())
->add_joins($this->get_tag_joins())
->set_options([
'component' => 'core',
'itemtype' => 'course',
]);
// We add our own custom course selector filter.
$filters[] = (new filter(
course_selector::class,

View file

@ -22,7 +22,6 @@ use context_helper;
use context_system;
use context_user;
use core_component;
use core_tag_tag;
use html_writer;
use lang_string;
use moodle_url;
@ -31,7 +30,6 @@ use core_user\fields;
use core_reportbuilder\local\filters\boolean_select;
use core_reportbuilder\local\filters\date;
use core_reportbuilder\local\filters\select;
use core_reportbuilder\local\filters\tags;
use core_reportbuilder\local\filters\text;
use core_reportbuilder\local\filters\user as user_filter;
use core_reportbuilder\local\helpers\user_profile_fields;
@ -143,7 +141,7 @@ class user extends base {
*
* @return string[]
*/
private function get_tag_joins(): array {
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');
@ -272,22 +270,6 @@ class user extends base {
return !empty($row->id) ? $OUTPUT->user_picture($row, ['link' => false, 'alttext' => false]) : '';
});
// Interests (tags).
$tag = $this->get_table_alias('tag');
$columns[] = (new column(
'interests',
new lang_string('interests'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->add_joins($this->get_tag_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$tag}.name, {$tag}.rawname")
->set_is_sortable(true)
->add_callback(static function($value, stdClass $tag): string {
return core_tag_tag::make_display_name($tag);
});
// Add all other user fields.
$userfields = $this->get_user_fields();
foreach ($userfields as $userfield => $userfieldlang) {
@ -529,22 +511,6 @@ class user extends base {
$filters[] = $filter;
}
// Interests (tags).
$tag = $this->get_table_alias('tag');
$filters[] = (new filter(
tags::class,
'interests',
new lang_string('interests'),
$this->get_entity_name(),
"{$tag}.id"
))
->add_joins($this->get_joins())
->add_joins($this->get_tag_joins())
->set_options([
'component' => 'core',
'itemtype' => 'user',
]);
// User select filter.
$filters[] = (new filter(
user_filter::class,

View file

@ -30,11 +30,6 @@ use core_reportbuilder\local\helpers\database;
*
* The field SQL should be the field containing the ID of the {tag} table
*
* The following array properties must be passed to the {@see \core_reportbuilder\local\report\filter::set_options} method when
* defining this filter, to define the component/itemtype you are using for tags:
*
* ['component' => 'core', 'itemtype' => 'user']
*
* @package core_reportbuilder
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@ -69,30 +64,22 @@ class tags extends base {
* Setup form
*
* @param MoodleQuickForm $mform
* @throws coding_exception If component/itemtype options are missing
*/
public function setup_form(MoodleQuickForm $mform): void {
global $DB;
$options = $this->filter->get_options();
if (!array_key_exists('component', $options) || !array_key_exists('itemtype', $options)) {
throw new coding_exception('Missing \'component\' and/or \'itemtype\' in filter options');
}
$operatorlabel = get_string('filterfieldoperator', 'core_reportbuilder', $this->get_header());
$mform->addElement('select', "{$this->name}_operator", $operatorlabel, $this->get_operators())
->setHiddenLabel(true);
$sql = 'SELECT DISTINCT t.id, t.name, t.rawname
FROM {tag} t
JOIN {tag_instance} ti ON ti.tagid = t.id
WHERE ti.component = :component AND ti.itemtype = :itemtype
ORDER BY t.name';
// Transform tag records into appropriate display name, for selection in the autocomplete element.
$tags = array_map(static function(stdClass $record): string {
return core_tag_tag::make_display_name($record);
}, $DB->get_records_sql($sql, ['component' => $options['component'], 'itemtype' => $options['itemtype']]));
}, $DB->get_records_sql($sql));
$valuelabel = get_string('filterfieldvalue', 'core_reportbuilder', $this->get_header());
$mform->addElement('autocomplete', "{$this->name}_value", $valuelabel, $tags, ['multiple' => true])

View file

@ -35,13 +35,13 @@ Feature: Manage custom report conditions
Scenario: Add tags condition to report
Given the following "core_reportbuilder > Condition" exists:
| report | My report |
| uniqueidentifier | user:interests |
| report | My report |
| uniqueidentifier | tag:name |
And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
When I click on "Show/hide 'Conditions'" "button"
And I set the following fields in the "Interests" "core_reportbuilder > Condition" to these values:
| Interests operator | Is equal to |
| Interests value | dancing |
And I set the following fields in the "Tag name" "core_reportbuilder > Condition" to these values:
| Tag name operator | Is equal to |
| Tag name value | dancing |
And I click on "Apply" "button" in the "[data-region='settings-conditions']" "css_element"
Then I should see "Conditions applied"
And I should see "User One" in the "reportbuilder-table" "table"

View file

@ -50,8 +50,9 @@ class custom_report_column_cards_exporter_test extends advanced_testcase {
$exporter = new custom_report_column_cards_exporter(null, ['report' => $reportinstance]);
$export = $exporter->export($PAGE->get_renderer('core_reportbuilder'));
$this->assertCount(2, $export->menucards);
[$menucardcategory, $menucardcourse] = $export->menucards;
// The root of the menu cards property should contain each entity.
$this->assertCount(3, $export->menucards);
[$menucardcategory, $menucardcourse, $menucardtag] = $export->menucards;
// Course category entity menu card.
$this->assertEquals('Course category', $menucardcategory['name']);
@ -80,5 +81,19 @@ class custom_report_column_cards_exporter_test extends advanced_testcase {
'title' => 'Add column \'Course full name with link\'',
'action' => 'report-add-column',
], $menucarditem);
// Tag entity menu card.
$this->assertEquals('Tag', $menucardtag['name']);
$this->assertEquals('tag', $menucardtag['key']);
$this->assertNotEmpty($menucardtag['items']);
// Test the structure of the first menu card item.
$menucarditem = reset($menucardtag['items']);
$this->assertEquals([
'name' => 'Tag name',
'identifier' => 'tag:name',
'title' => 'Add column \'Tag name\'',
'action' => 'report-add-column',
], $menucarditem);
}
}

View file

@ -51,11 +51,11 @@ class custom_report_conditions_exporter_test extends advanced_testcase {
$exporter = new custom_report_conditions_exporter(null, ['report' => $reportinstance]);
$export = $exporter->export($PAGE->get_renderer('core_reportbuilder'));
// The root of the available conditions property should contain two entities.
$this->assertTrue($export->hasavailableconditions);
$this->assertCount(2, $export->availableconditions);
[$conditionscategory, $conditionscourse] = $export->availableconditions;
// The root of the available conditions property should contain each entity.
$this->assertCount(3, $export->availableconditions);
[$conditionscategory, $conditionscourse, $conditionstag] = $export->availableconditions;
// Course category conditions, assert structure of first item.
$this->assertEquals('Course category', $conditionscategory['optiongroup']['text']);
@ -76,6 +76,14 @@ class custom_report_conditions_exporter_test extends advanced_testcase {
// Make sure the active condition we added, isn't present in available conditions.
$this->assertNotContains('course:shortname', array_column($conditionscourse['optiongroup']['values'], 'value'));
// Tag conditions, assert structure of first item.
$this->assertEquals('Tag', $conditionstag['optiongroup']['text']);
$this->assertGreaterThanOrEqual(1, $conditionstag['optiongroup']['values']);
$this->assertEquals([
'value' => 'tag:name',
'visiblename' => 'Tag name',
], $conditionstag['optiongroup']['values'][0]);
// The active conditions are contained inside form HTML, just assert there's something present.
$this->assertTrue($export->hasactiveconditions);
$this->assertNotEmpty($export->activeconditionsform);

View file

@ -64,11 +64,11 @@ class custom_report_filters_exporter_test extends advanced_testcase {
$exporter = new custom_report_filters_exporter(null, ['report' => $reportinstance]);
$export = $exporter->export($PAGE->get_renderer('core_reportbuilder'));
// The root of the available filters property should contain two entities.
$this->assertTrue($export->hasavailablefilters);
$this->assertCount(2, $export->availablefilters);
[$filterscategory, $filterscourse] = $export->availablefilters;
// The root of the available filters property should contain each entity.
$this->assertCount(3, $export->availablefilters);
[$filterscategory, $filterscourse, $filterstag] = $export->availablefilters;
// Course category filters, assert structure of first item.
$this->assertEquals('Course category', $filterscategory['optiongroup']['text']);
@ -91,6 +91,14 @@ class custom_report_filters_exporter_test extends advanced_testcase {
$this->assertNotContains('course_category:name', $filterscourseavailable);
$this->assertNotContains('course:idnumber', $filterscourseavailable);
// Tag filters, assert structure of first item.
$this->assertEquals('Tag', $filterstag['optiongroup']['text']);
$this->assertGreaterThanOrEqual(1, $filterstag['optiongroup']['values']);
$this->assertEquals([
'value' => 'tag:name',
'visiblename' => 'Tag name',
], $filterstag['optiongroup']['values'][0]);
$this->assertTrue($export->hasactivefilters);
$this->assertCount(2, $export->activefilters);
[$activefiltercourseidnumber, $activefiltercategoryname] = $export->activefilters;

View file

@ -156,7 +156,6 @@ class course_test extends advanced_testcase {
$this->assertEquals('Gregorian', $courserow['calendartype']);
$this->assertEquals('afterburner', $courserow['theme']);
$this->assertEquals(get_string_manager()->get_list_of_translations()['en'], $courserow['lang']);
$this->assertEquals('dancing', $courserow['tags']);
$expected = '<a href="https://www.example.com/moodle/course/view.php?id=' . $course1->id . '">Course 1</a>';
$this->assertEquals($expected, $courserow['coursefullnamewithlink']);
$expected = '<a href="https://www.example.com/moodle/course/view.php?id=' . $course1->id . '">C1</a>';
@ -248,15 +247,6 @@ class course_test extends advanced_testcase {
$this->assertEquals([
'Course 1',
], array_column($tablerows, 'fullname'));
// Filter by tags field.
$tablerows = $this->get_report_table_rows([
'course:tags_operator' => tags::EQUAL_TO,
'course:tags_value' => [
$DB->get_field('tag', 'id', ['name' => 'dancing'], MUST_EXIST),
],
]);
$this->assertEquals(['Course 1'], array_column($tablerows, 'fullname'));
}
/**

View file

@ -85,7 +85,6 @@ class user_test extends advanced_testcase {
$this->assertEquals('Yes', $userrow['suspended']);
$this->assertEquals('No', $userrow['confirmed']);
$this->assertEquals('Spain', $userrow['country']);
$this->assertEquals('dancing', $userrow['interests']);
$this->assertEquals('Blue', $userrow['profilefield_favcolor']);
$this->assertEquals('Time travel', $userrow['profilefield_favsuperpower']);
}
@ -230,15 +229,6 @@ class user_test extends advanced_testcase {
$this->assertEquals([
'Daffy Duck',
], array_column($tablerows, 'fullname'));
// Filter by interests (tags) field.
$tablerows = $this->get_report_table_rows([
'user:interests_operator' => tags::EQUAL_TO,
'user:interests_value' => [
$DB->get_field('tag', 'id', ['name' => 'dancing'], MUST_EXIST),
],
]);
$this->assertEquals(['Daffy Duck'], array_column($tablerows, 'fullname'));
}
/**