\ No newline at end of file
diff --git a/reportbuilder/tests/behat/filtereditor.feature b/reportbuilder/tests/behat/filtereditor.feature
new file mode 100644
index 00000000000..9e4a6cf07fc
--- /dev/null
+++ b/reportbuilder/tests/behat/filtereditor.feature
@@ -0,0 +1,152 @@
+@core_reportbuilder @javascript
+Feature: Manage custom report filters
+ In order to manage the filters of custom reports
+ As an admin
+ I need to add, edit and delete filters in a report
+
+ Scenario: Add filter to report
+ Given the following "core_reportbuilder > Reports" exist:
+ | name | source | default |
+ | My report | core_user\reportbuilder\datasource\users | 0 |
+ And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
+ When I click on "Show/hide settings sidebar" "button"
+ And I click on "Show/hide 'Filters'" "button"
+ Then I should see "There are no filters selected" in the "[data-region='active-filters']" "css_element"
+ And I set the field "Select a filter" to "Email address"
+ And I should see "Added filter 'Email address'"
+ And I should not see "There are no filters selected" in the "[data-region='active-filters']" "css_element"
+ And I should see "Email address" in the "[data-region='active-filters']" "css_element"
+
+ Scenario: Rename filter in report
+ Given the following "core_reportbuilder > Reports" exist:
+ | name | source | default |
+ | My report | core_user\reportbuilder\datasource\users | 0 |
+ And the following "core_reportbuilder > Filters" exist:
+ | report | uniqueidentifier |
+ | My report | user:email |
+ And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
+ When I click on "Show/hide settings sidebar" "button"
+ And I click on "Show/hide 'Filters'" "button"
+ And I set the field "Rename filter 'Email address'" to "My Email filter"
+ And I reload the page
+ And I click on "Show/hide settings sidebar" "button"
+ And I click on "Show/hide 'Filters'" "button"
+ Then I should see "My Email filter" in the "[data-region='active-filters']" "css_element"
+
+ Scenario: Move filter in report
+ Given the following "core_reportbuilder > Reports" exist:
+ | name | source | default |
+ | My report | core_user\reportbuilder\datasource\users | 0 |
+ And the following "core_reportbuilder > Filters" exist:
+ | report | uniqueidentifier |
+ | My report | user:fullname |
+ | My report | user:email |
+ | My report | user:country |
+ And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
+ When I click on "Show/hide settings sidebar" "button"
+ And I click on "Show/hide 'Filters'" "button"
+ And I click on "Move filter 'Country'" "button"
+ And I click on "After \"Full name\"" "link" in the "Move filter 'Country'" "dialogue"
+ Then I should see "Moved filter 'Country'"
+ And "Country" "text" should appear before "Email address" "text"
+
+ Scenario: Delete filter from report
+ Given the following "core_reportbuilder > Reports" exist:
+ | name | source | default |
+ | My report | core_user\reportbuilder\datasource\users | 0 |
+ And the following "core_reportbuilder > Filters" exist:
+ | report | uniqueidentifier |
+ | My report | user:email |
+ And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
+ When I click on "Show/hide settings sidebar" "button"
+ And I click on "Show/hide 'Filters'" "button"
+ And I click on "Delete filter 'Email address'" "link"
+ And I click on "Delete" "button" in the "Delete filter 'Email address'" "dialogue"
+ Then I should see "Deleted filter 'Email address'"
+ And I should see "There are no filters selected" in the "[data-region='active-filters']" "css_element"
+ And I should not see "Email address" in the "[data-region='active-filters']" "css_element"
+
+ Scenario: Use report filters when previewing report
+ Given the following "users" exist:
+ | username | firstname | lastname |
+ | user1 | User | 1 |
+ | user2 | User | 2 |
+ | user3 | User | 3 |
+ And the following "core_reportbuilder > Reports" exist:
+ | name | source | default |
+ | My report | core_user\reportbuilder\datasource\users | 0 |
+ And the following "core_reportbuilder > Columns" exist:
+ | report | uniqueidentifier |
+ | My report | user:fullname |
+ | My report | user:email |
+ And the following "core_reportbuilder > Filters" exist:
+ | report | uniqueidentifier |
+ | My report | user:fullname |
+ And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
+ And I change window size to "large"
+ And I should see "user1@example.com" in the ".reportbuilder-table" "css_element"
+ And I should see "user2@example.com" in the ".reportbuilder-table" "css_element"
+ And I should see "user3@example.com" in the ".reportbuilder-table" "css_element"
+ When I click on "Switch to preview mode" "button"
+ And I click on "Filters" "button"
+ And I set the following fields in the "Full name" "core_reportbuilder > Filter" to these values:
+ | Full name operator | Does not contain |
+ | Full name value | User 2 |
+ And I click on "Apply" "button" in the "[data-region='report-filters']" "css_element"
+ Then I should see "Filters applied"
+ And I should see "Filters (1)" in the "#dropdownFiltersButton" "css_element"
+ And the following should exist in the "reportbuilder-table" table:
+ | Full name | Email address |
+ | User 1 | user1@example.com |
+ | User 3 | user3@example.com |
+ And the following should not exist in the "reportbuilder-table" table:
+ | Full name | Email address |
+ | User 2 | user2@example.com |
+
+ Scenario: Use report filters when previewing report that contains same condition
+ Given the following "users" exist:
+ | username | firstname | lastname |
+ | user1 | User | 1 |
+ | user2 | User | 2 |
+ | user3 | User | 3 |
+ And the following "core_reportbuilder > Reports" exist:
+ | name | source | default |
+ | My report | core_user\reportbuilder\datasource\users | 0 |
+ And the following "core_reportbuilder > Columns" exist:
+ | report | uniqueidentifier |
+ | My report | user:fullname |
+ | My report | user:email |
+ And the following "core_reportbuilder > Conditions" exist:
+ | report | uniqueidentifier |
+ | My report | user:email |
+ And the following "core_reportbuilder > Filters" exist:
+ | report | uniqueidentifier |
+ | My report | user:email |
+ And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
+ And I change window size to "large"
+ And I should see "user1@example.com" in the ".reportbuilder-table" "css_element"
+ And I should see "user2@example.com" in the ".reportbuilder-table" "css_element"
+ And I should see "user3@example.com" in the ".reportbuilder-table" "css_element"
+ # Set a condition to the report.
+ When I click on "Show/hide settings sidebar" "button"
+ And I click on "Show/hide 'Conditions'" "button"
+ And I set the following fields in the "Email address" "core_reportbuilder > Condition" to these values:
+ | Email address operator | Is not equal to |
+ | Email address value | user3@example.com |
+ And I click on "Apply" "button" in the "#settingsconditions" "css_element"
+ And I click on "Switch to preview mode" "button"
+ And I click on "Filters" "button"
+ And I set the following fields in the "Email address" "core_reportbuilder > Filter" to these values:
+ | Email address operator | Is not equal to |
+ | Email address value | user2@example.com |
+ And I click on "Apply" "button" in the "[data-region='report-filters']" "css_element"
+ Then I should see "Filters applied"
+ And I should see "Filters (1)" in the "#dropdownFiltersButton" "css_element"
+ # Assert we haven't overridden the condition and user3 is still not showing in the report.
+ And the following should exist in the "reportbuilder-table" table:
+ | Full name | Email address |
+ | User 1 | user1@example.com |
+ And the following should not exist in the "reportbuilder-table" table:
+ | Full name | Email address |
+ | User 2 | user2@example.com |
+ | User 3 | user3@example.com |
diff --git a/reportbuilder/tests/external/filters/add_test.php b/reportbuilder/tests/external/filters/add_test.php
new file mode 100644
index 00000000000..5daf87cd1d6
--- /dev/null
+++ b/reportbuilder/tests/external/filters/add_test.php
@@ -0,0 +1,93 @@
+.
+
+declare(strict_types=1);
+
+namespace core_reportbuilder\external\filters;
+
+use core_reportbuilder\manager;
+use core_reportbuilder_generator;
+use external_api;
+use externallib_advanced_testcase;
+use core_reportbuilder\report_access_exception;
+use core_reportbuilder\local\models\filter;
+use core_user\reportbuilder\datasource\users;
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once("{$CFG->dirroot}/webservice/tests/helpers.php");
+
+/**
+ * Unit tests of external class for adding report filters
+ *
+ * @package core_reportbuilder
+ * @covers \core_reportbuilder\external\filters\add
+ * @copyright 2021 Paul Holden
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class add_test extends externallib_advanced_testcase {
+
+ /**
+ * Text execute method
+ */
+ public function test_execute(): void {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ /** @var core_reportbuilder_generator $generator */
+ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
+
+ $report = $generator->create_report([
+ 'name' => 'My report',
+ 'source' => users::class,
+ 'default' => false,
+ ]);
+
+ // Add filter.
+ $result = add::execute($report->get('id'), 'user:fullname');
+ $result = external_api::clean_returnvalue(add::execute_returns(), $result);
+ $this->assertTrue($result['hasavailablefilters']);
+ $this->assertEquals('User', $result['availablefilters'][0]['optiongroup']['text']);
+ $this->assertNotEmpty($result['availablefilters'][0]['optiongroup']['values']);
+ $this->assertTrue($result['hasactivefilters']);
+ $this->assertCount(1, $result['activefilters']);
+ $this->assertEquals('Full name', $result['activefilters'][0]['heading']);
+
+ // Assert report filters.
+ $filters = filter::get_filter_records($report->get('id'));
+ $this->assertCount(1, $filters);
+ $this->assertEquals('user:fullname', reset($filters)->get('uniqueidentifier'));
+ }
+
+ /**
+ * Test execute method for a user without permission to edit reports
+ */
+ public function test_execute_access_exception(): void {
+ $this->resetAfterTest();
+
+ /** @var core_reportbuilder_generator $generator */
+ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
+ $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
+
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
+
+ $this->expectException(report_access_exception::class);
+ $this->expectExceptionMessage('You can not edit this report');
+ add::execute($report->get('id'), 'user:fullname');
+ }
+}
diff --git a/reportbuilder/tests/external/filters/delete_test.php b/reportbuilder/tests/external/filters/delete_test.php
new file mode 100644
index 00000000000..aca9a7603a4
--- /dev/null
+++ b/reportbuilder/tests/external/filters/delete_test.php
@@ -0,0 +1,98 @@
+.
+
+declare(strict_types=1);
+
+namespace core_reportbuilder\external\filters;
+
+use core_reportbuilder_generator;
+use external_api;
+use externallib_advanced_testcase;
+use core_reportbuilder\report_access_exception;
+use core_reportbuilder\local\models\filter;
+use core_user\reportbuilder\datasource\users;
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once("{$CFG->dirroot}/webservice/tests/helpers.php");
+
+/**
+ * Unit tests of external class for deleting report filters
+ *
+ * @package core_reportbuilder
+ * @covers \core_reportbuilder\external\filters\delete
+ * @copyright 2021 Paul Holden
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class delete_test extends externallib_advanced_testcase {
+
+ /**
+ * Text execute method
+ */
+ public function test_execute(): void {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ /** @var core_reportbuilder_generator $generator */
+ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
+
+ $report = $generator->create_report([
+ 'name' => 'My report',
+ 'source' => users::class,
+ 'default' => false,
+ ]);
+
+ // Add two filters.
+ $filterfullname = $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname']);
+ $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:email']);
+
+ // Delete the first filter.
+ $result = delete::execute($report->get('id'), $filterfullname->get('id'));
+ $result = external_api::clean_returnvalue(delete::execute_returns(), $result);
+ $this->assertTrue($result['hasavailablefilters']);
+ $this->assertEquals('User', $result['availablefilters'][0]['optiongroup']['text']);
+ $this->assertNotEmpty($result['availablefilters'][0]['optiongroup']['values']);
+ $this->assertTrue($result['hasactivefilters']);
+ $this->assertCount(1, $result['activefilters']);
+ $this->assertEquals('Email address', $result['activefilters'][0]['heading']);
+
+ // Assert report filters.
+ $filters = filter::get_filter_records($report->get('id'));
+ $this->assertCount(1, $filters);
+ $this->assertEquals('user:email', reset($filters)->get('uniqueidentifier'));
+ }
+
+ /**
+ * Test execute method for a user without permission to edit reports
+ */
+ public function test_execute_access_exception(): void {
+ $this->resetAfterTest();
+
+ /** @var core_reportbuilder_generator $generator */
+ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
+
+ $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
+ $filter = $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:email']);
+
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
+
+ $this->expectException(report_access_exception::class);
+ $this->expectExceptionMessage('You can not edit this report');
+ delete::execute($report->get('id'), $filter->get('id'));
+ }
+}
diff --git a/reportbuilder/tests/external/filters/reorder_test.php b/reportbuilder/tests/external/filters/reorder_test.php
new file mode 100644
index 00000000000..9b6c06d9957
--- /dev/null
+++ b/reportbuilder/tests/external/filters/reorder_test.php
@@ -0,0 +1,108 @@
+.
+
+declare(strict_types=1);
+
+namespace core_reportbuilder\external\filters;
+
+use core_reportbuilder_generator;
+use external_api;
+use externallib_advanced_testcase;
+use core_reportbuilder\report_access_exception;
+use core_reportbuilder\local\models\filter;
+use core_user\reportbuilder\datasource\users;
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once("{$CFG->dirroot}/webservice/tests/helpers.php");
+
+/**
+ * Unit tests of external class for re-ordering report filters
+ *
+ * @package core_reportbuilder
+ * @covers \core_reportbuilder\external\filters\reorder
+ * @copyright 2021 Paul Holden
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class reorder_test extends externallib_advanced_testcase {
+
+ /**
+ * Text execute method
+ */
+ public function test_execute(): void {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ /** @var core_reportbuilder_generator $generator */
+ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
+
+ $report = $generator->create_report([
+ 'name' => 'My report',
+ 'source' => users::class,
+ 'default' => false,
+ ]);
+
+ // Add four filters.
+ $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname']);
+ $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:email']);
+ $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:country']);
+ $filtercity = $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:city']);
+
+ // Move the city filter to second position.
+ $result = reorder::execute($report->get('id'), $filtercity->get('id'), 2);
+ $result = external_api::clean_returnvalue(reorder::execute_returns(), $result);
+ $this->assertTrue($result['hasavailablefilters']);
+ $this->assertEquals('User', $result['availablefilters'][0]['optiongroup']['text']);
+ $this->assertNotEmpty($result['availablefilters'][0]['optiongroup']['values']);
+ $this->assertTrue($result['hasactivefilters']);
+ $this->assertCount(4, $result['activefilters']);
+
+ // Assert report filters order.
+ $filters = filter::get_filter_records($report->get('id'), 'filterorder');
+
+ $filteridentifiers = array_map(static function(filter $filter): string {
+ return $filter->get('uniqueidentifier');
+ }, $filters);
+
+ $this->assertEquals([
+ 'user:fullname',
+ 'user:city',
+ 'user:email',
+ 'user:country',
+ ], $filteridentifiers);
+ }
+
+ /**
+ * Test execute method for a user without permission to edit reports
+ */
+ public function test_execute_access_exception(): void {
+ $this->resetAfterTest();
+
+ /** @var core_reportbuilder_generator $generator */
+ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
+
+ $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
+ $filter = $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:email']);
+
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
+
+ $this->expectException(report_access_exception::class);
+ $this->expectExceptionMessage('You can not edit this report');
+ reorder::execute($report->get('id'), $filter->get('id'), 1);
+ }
+}
diff --git a/reportbuilder/tests/fixtures/testable_system_report_table.php b/reportbuilder/tests/fixtures/testable_system_report_table.php
index f6f76ee5e4d..ff1495d9d03 100644
--- a/reportbuilder/tests/fixtures/testable_system_report_table.php
+++ b/reportbuilder/tests/fixtures/testable_system_report_table.php
@@ -40,7 +40,7 @@ class testable_system_report_table extends system_report_table {
$record = parent::format_row($row);
$result = [];
- $columns = $this->systemreport->get_columns();
+ $columns = $this->report->get_columns();
foreach ($columns as $column) {
$result[$column->get_name()] = $record[$column->get_column_alias()];
}
diff --git a/user/classes/reportbuilder/datasource/users.php b/user/classes/reportbuilder/datasource/users.php
index 92a6e3eec01..c37eb9f5fce 100644
--- a/user/classes/reportbuilder/datasource/users.php
+++ b/user/classes/reportbuilder/datasource/users.php
@@ -61,6 +61,7 @@ class users extends datasource {
$userentityname = $userentity->get_entity_name();
$this->add_columns_from_entity($userentityname);
+ $this->add_filters_from_entity($userentityname);
$this->add_conditions_from_entity($userentityname);
}
@@ -73,6 +74,15 @@ class users extends datasource {
return ['user:fullname', 'user:username', 'user:email'];
}
+ /**
+ * Return the filters that will be added to the report once is created
+ *
+ * @return string[]
+ */
+ public function get_default_filters(): array {
+ return ['user:fullname', 'user:username', 'user:email'];
+ }
+
/**
* Return the conditions that will be added to the report once is created
*