Merge branch 'MDL-82809-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE

This commit is contained in:
Jun Pataleta
2024-10-31 11:22:59 +07:00
committed by Huong Nguyen
10 changed files with 216 additions and 97 deletions
+6 -7
View File
@@ -264,8 +264,7 @@ abstract class datasource extends base {
" {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER);
}
$this->activefilters['values'][$instance->get_unique_identifier()] =
$instance->set_persistent($filter);
$this->activefilters['values'][$instance->get_unique_identifier()] = $instance->set_persistent($filter);
}
}
@@ -347,9 +346,10 @@ abstract class datasource extends base {
* Override parent method, returning only those conditions specifically added to the custom report (rather than all that are
* available)
*
* @param bool $checkavailable
* @return filter[]
*/
public function get_active_conditions(): array {
public function get_active_conditions(bool $checkavailable = true): array {
$reportid = $this->get_report_persistent()->get('id');
// Determine whether we already retrieved the conditions since the report was last modified.
@@ -364,15 +364,14 @@ abstract class datasource extends base {
foreach ($activeconditions as $condition) {
$instance = $this->get_condition($condition->get('uniqueidentifier'));
// Ensure the condition is still present and available.
if ($instance !== null && $instance->get_is_available()) {
// Ensure the condition is still present and available (if checking available status).
if ($instance !== null && (!$checkavailable || $instance->get_is_available())) {
if ($instance->get_is_deprecated()) {
debugging("The condition '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." .
" {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER);
}
$this->activeconditions['values'][$instance->get_unique_identifier()] =
$instance->set_persistent($condition);
$this->activeconditions['values'][$instance->get_unique_identifier()] = $instance->set_persistent($condition);
}
}
+11 -14
View File
@@ -74,30 +74,27 @@ class course extends base {
}
/**
* Get custom fields helper
*
* @return custom_fields
*/
protected function get_custom_fields(): custom_fields {
$customfields = new custom_fields($this->get_table_alias('course') . '.id', $this->get_entity_name(),
'core_course', 'course');
$customfields->add_joins($this->get_joins());
return $customfields;
}
/**
* Initialise the entity, adding all course and custom course fields
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$customfields = $this->get_custom_fields();
$tablealias = $this->get_table_alias('course');
$customfields = (new custom_fields(
"{$tablealias}.id",
$this->get_entity_name(),
'core_course',
'course',
))
->add_joins($this->get_joins());
$columns = array_merge($this->get_all_columns(), $customfields->get_columns());
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = array_merge($this->get_all_filters(), $customfields->get_filters());
foreach ($filters as $filter) {
$this
@@ -266,13 +266,12 @@ class custom_fields {
$datafieldsql
))
->add_joins($this->get_joins())
->add_join($this->get_table_join($field));
->add_join($this->get_table_join($field))
->set_is_available($this->handler->can_view($field, 0));
// Options are stored inside configdata json string and we need to convert it to array.
if ($field->get('type') === 'select') {
$filter->set_options_callback(static function() use ($field): array {
return $field->get_options();
});
// If using a select filter, then populate the options.
if ($filter->get_filter_class() === select::class) {
$filter->set_options_callback(fn(): array => $field->get_options());
}
$filters[] = $filter;
@@ -64,18 +64,7 @@ class user_profile_fields {
public function __construct(string $usertablefieldalias, string $entityname) {
$this->usertablefieldalias = $usertablefieldalias;
$this->entityname = $entityname;
$this->userprofilefields = $this->get_user_profile_fields();
}
/**
* Retrieves the list of available/visible user profile fields
*
* @return profile_field_base[]
*/
private function get_user_profile_fields(): array {
return array_filter(profile_get_user_fields_with_data(0), static function(profile_field_base $profilefield): bool {
return $profilefield->is_visible();
});
$this->userprofilefields = profile_get_user_fields_with_data(0);
}
/**
@@ -183,7 +172,8 @@ class user_profile_fields {
$field->data = $value;
return (string) $field->display_data();
}, $profilefield);
}, $profilefield)
->set_is_available($profilefield->is_visible());
}
return $columns;
@@ -240,11 +230,12 @@ class user_profile_fields {
$params
))
->add_joins($this->get_joins())
->add_join($this->get_table_join($profilefield));
->add_join($this->get_table_join($profilefield))
->set_is_available($profilefield->is_visible());
// If menu type then set filter options as appropriate.
if ($profilefield->field->datatype === 'menu') {
$filter->set_options($profilefield->options);
// If using a select filter, then populate the options.
if ($filter->get_filter_class() === select::class) {
$filter->set_options_callback(fn(): array => $profilefield->options);
}
$filters[] = $filter;
+2 -1
View File
@@ -518,9 +518,10 @@ abstract class base {
/**
* Return all active report conditions (by default, all available conditions)
*
* @param bool $checkavailable
* @return filter[]
*/
public function get_active_conditions(): array {
public function get_active_conditions(bool $checkavailable = true): array {
$conditions = $this->get_conditions();
foreach ($conditions as $condition) {
if ($condition->get_is_deprecated()) {
@@ -79,7 +79,7 @@ abstract class base_report_table extends table_sql implements dynamic, renderabl
// For each condition, we need to ensure their values are always accounted for in the report.
$conditionvalues = $this->report->get_condition_values();
foreach ($this->report->get_active_conditions() as $condition) {
foreach ($this->report->get_active_conditions(false) as $condition) {
[$conditionsql, $conditionparams] = $this->get_filter_sql($condition, $conditionvalues, 'c' . $conditionindex++);
if ($conditionsql !== '') {
$joins = array_merge($joins, $condition->get_joins());
+34
View File
@@ -238,6 +238,40 @@ final class datasource_test extends advanced_testcase {
$this->assertNull($instance->get_condition('datasource_test_entity:extra2'));
}
/**
* Test getting active conditions
*
* @covers ::get_active_conditions
*/
public function test_get_active_conditions(): void {
$instance = $this->get_datasource_test_source();
$method = (new ReflectionClass($instance))->getMethod('add_conditions_from_entity');
$method->invoke($instance, 'datasource_test_entity');
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$reportid = $instance->get_report_persistent()->get('id');
$generator->create_condition(['reportid' => $reportid, 'uniqueidentifier' => 'datasource_test_entity:first']);
$generator->create_condition(['reportid' => $reportid, 'uniqueidentifier' => 'datasource_test_entity:second']);
// Set the second condition as unavailable.
$instance->get_condition('datasource_test_entity:second')->set_is_available(false);
$this->assertEquals([
'datasource_test_entity:first',
], array_keys($instance->get_active_conditions(true)));
// Ensure report elements are reloaded.
$instance::report_elements_modified($reportid);
$this->assertEquals([
'datasource_test_entity:first',
'datasource_test_entity:second',
], array_keys($instance->get_active_conditions(false)));
}
/**
* Create and return our test datasource instance
*
@@ -67,8 +67,10 @@ class custom_fields_test extends core_reportbuilder_testcase {
$generator->create_field(
['categoryid' => $category->get('id'), 'type' => 'textarea', 'name' => 'Textarea', 'shortname' => 'textarea']);
// This field is available only to course teachers.
$generator->create_field(
['categoryid' => $category->get('id'), 'type' => 'checkbox', 'name' => 'Checkbox', 'shortname' => 'checkbox']);
['categoryid' => $category->get('id'), 'type' => 'checkbox', 'name' => 'Checkbox', 'shortname' => 'checkbox',
'configdata' => ['visibility' => 1]]);
$generator->create_field(
['categoryid' => $category->get('id'), 'type' => 'date', 'name' => 'Date', 'shortname' => 'date']);
@@ -90,10 +92,11 @@ class custom_fields_test extends core_reportbuilder_testcase {
*/
public function test_get_columns(): void {
$this->resetAfterTest();
$this->setAdminUser();
$customfields = $this->generate_customfields();
$columns = $customfields->get_columns();
$columns = $customfields->get_columns();
$this->assertCount(5, $columns);
$this->assertContainsOnlyInstancesOf(column::class, $columns);
@@ -114,6 +117,19 @@ class custom_fields_test extends core_reportbuilder_testcase {
[true, false, true, true, true],
array_map(fn(column $column) => $column->get_is_sortable(), $columns)
);
// Column available.
$this->assertEquals(
[true, true, true, true, true],
array_map(fn(column $column) => $column->get_is_available(), $columns),
);
// Column available, for non-privileged user.
$this->setUser(null);
$this->assertEquals(
[true, true, false, true, true],
array_map(fn(column $column) => $column->get_is_available(), $customfields->get_columns()),
);
}
/**
@@ -167,18 +183,32 @@ class custom_fields_test extends core_reportbuilder_testcase {
*/
public function test_get_filters(): void {
$this->resetAfterTest();
$this->setAdminUser();
$customfields = $this->generate_customfields();
$filters = $customfields->get_filters();
$filters = $customfields->get_filters();
$this->assertCount(5, $filters);
$this->assertContainsOnlyInstancesOf(filter::class, $filters);
// Filter titles.
// Filter headers.
$this->assertEquals(
['Text', 'Textarea', 'Checkbox', 'Date', 'Select'],
array_map(fn(filter $filter) => $filter->get_header(), $filters)
);
// Filter available.
$this->assertEquals(
[true, true, true, true, true],
array_map(fn(filter $filter) => $filter->get_is_available(), $filters),
);
// Filter available, for non-privileged user.
$this->setUser(null);
$this->assertEquals(
[true, true, false, true, true],
array_map(fn(filter $filter) => $filter->get_is_available(), $customfields->get_filters()),
);
}
/**
@@ -186,6 +216,7 @@ class custom_fields_test extends core_reportbuilder_testcase {
*/
public function test_custom_report_content(): void {
$this->resetAfterTest();
$this->setAdminUser();
$this->generate_customfields();
@@ -308,6 +339,7 @@ class custom_fields_test extends core_reportbuilder_testcase {
*/
public function test_custom_report_filter(string $filtername, array $filtervalues, bool $expectmatch): void {
$this->resetAfterTest();
$this->setAdminUser();
$this->generate_customfields();
@@ -53,8 +53,10 @@ class user_profile_fields_test extends core_reportbuilder_testcase {
$this->getDataGenerator()->create_custom_profile_field([
'shortname' => 'checkbox', 'name' => 'Checkbox field', 'datatype' => 'checkbox']);
// This field is available only to admins.
$this->getDataGenerator()->create_custom_profile_field([
'shortname' => 'datetime', 'name' => 'Date field', 'datatype' => 'datetime', 'param2' => 2022, 'param3' => 0]);
'shortname' => 'datetime', 'name' => 'Date field', 'datatype' => 'datetime', 'param2' => 2022, 'param3' => 0,
'visible' => PROFILE_VISIBLE_NONE]);
$this->getDataGenerator()->create_custom_profile_field([
'shortname' => 'menu', 'name' => 'Menu field', 'datatype' => 'menu', 'param1' => "Cat\nDog"]);
@@ -80,55 +82,88 @@ class user_profile_fields_test extends core_reportbuilder_testcase {
*/
public function test_get_columns(): void {
$this->resetAfterTest();
$userentity = new user();
$useralias = $userentity->get_table_alias('user');
$this->setAdminUser();
// Get pre-existing user profile fields.
$initialuserprofilefields = new user_profile_fields("$useralias.id", $userentity->get_entity_name());
$initialcolumns = $initialuserprofilefields->get_columns();
$initialcolumntitles = array_map(static function(column $column): string {
return $column->get_title();
}, $initialcolumns);
$initialcolumntypes = array_map(static function(column $column): int {
return $column->get_type();
}, $initialcolumns);
$userentity = new user();
$initialcolumns = (new user_profile_fields(
$userentity->get_table_alias('user') . '.id',
$userentity->get_entity_name(),
))->get_columns();
// Add new custom profile fields.
$userprofilefields = $this->generate_userprofilefields();
$columns = $userprofilefields->get_columns();
// Columns count should be equal to start + 6.
$this->assertCount(count($initialcolumns) + 6, $columns);
// Ensure pre-existing fields are ignored in subsequent assertions.
$columns = array_slice($userprofilefields->get_columns(), count($initialcolumns));
$this->assertCount(6, $columns);
$this->assertContainsOnlyInstancesOf(column::class, $columns);
// Assert column titles.
$columntitles = array_map(static function(column $column): string {
return $column->get_title();
}, $columns);
$expectedcolumntitles = array_merge($initialcolumntitles, [
// Column titles.
$this->assertEquals([
'Checkbox field',
'Date field',
'Menu field',
'MSN ID',
'Text field',
'Textarea field',
]);
$this->assertEquals($expectedcolumntitles, $columntitles);
], array_map(
fn(column $column): string => $column->get_title(),
$columns,
));
// Assert column types.
$columntypes = array_map(static function(column $column): int {
return $column->get_type();
}, $columns);
$expectedcolumntypes = array_merge($initialcolumntypes, [
// Column types.
$this->assertEquals([
column::TYPE_BOOLEAN,
column::TYPE_TIMESTAMP,
column::TYPE_TEXT,
column::TYPE_TEXT,
column::TYPE_TEXT,
column::TYPE_LONGTEXT,
]);
$this->assertEquals($expectedcolumntypes, $columntypes);
], array_map(
fn(column $column): int => $column->get_type(),
$columns,
));
// Column sortable.
$this->assertEquals([
true,
true,
true,
true,
true,
false,
], array_map(
fn(column $column): bool => $column->get_is_sortable(),
$columns,
));
// Column available.
$this->assertEquals([
true,
true,
true,
true,
true,
true,
], array_map(
fn(column $column): bool => $column->get_is_available(),
$columns,
));
// Column available, for non-privileged user.
$this->setUser(null);
$this->assertEquals([
true,
false,
true,
true,
true,
true,
], array_map(
fn(column $column): bool => $column->get_is_available(),
array_slice($userprofilefields->get_columns(), count($initialcolumns)),
));
}
/**
@@ -166,38 +201,62 @@ class user_profile_fields_test extends core_reportbuilder_testcase {
*/
public function test_get_filters(): void {
$this->resetAfterTest();
$userentity = new user();
$useralias = $userentity->get_table_alias('user');
$this->setAdminUser();
// Get pre-existing user profile fields.
$initialuserprofilefields = new user_profile_fields("$useralias.id", $userentity->get_entity_name());
$initialfilters = $initialuserprofilefields->get_filters();
$initialfilterheaders = array_map(static function(filter $filter): string {
return $filter->get_header();
}, $initialfilters);
$userentity = new user();
$initialfilters = (new user_profile_fields(
$userentity->get_table_alias('user') . '.id',
$userentity->get_entity_name(),
))->get_filters();
// Add new custom profile fields.
$userprofilefields = $this->generate_userprofilefields();
$filters = $userprofilefields->get_filters();
// Filters count should be equal to start + 6.
$this->assertCount(count($initialfilters) + 6, $filters);
// Ensure pre-existing fields are ignored in subsequent assertions.
$filters = array_slice($userprofilefields->get_filters(), count($initialfilters));
$this->assertCount(6, $filters);
$this->assertContainsOnlyInstancesOf(filter::class, $filters);
// Assert filter headers.
$filterheaders = array_map(static function(filter $filter): string {
return $filter->get_header();
}, $filters);
$expectedfilterheaders = array_merge($initialfilterheaders, [
// Filter headers.
$this->assertEquals([
'Checkbox field',
'Date field',
'Menu field',
'MSN ID',
'Text field',
'Textarea field',
]);
$this->assertEquals($expectedfilterheaders, $filterheaders);
], array_map(
fn(filter $filter): string => $filter->get_header(),
$filters,
));
// Filter available.
$this->assertEquals([
true,
true,
true,
true,
true,
true,
], array_map(
fn(filter $filter): bool => $filter->get_is_available(),
$filters,
));
// Filter available, for non-privileged user.
$this->setUser(null);
$this->assertEquals([
true,
false,
true,
true,
true,
true,
], array_map(
fn(filter $filter): bool => $filter->get_is_available(),
array_slice($userprofilefields->get_filters(), count($initialfilters)),
));
}
/**
@@ -205,6 +264,7 @@ class user_profile_fields_test extends core_reportbuilder_testcase {
*/
public function test_custom_report_content(): void {
$this->resetAfterTest();
$this->setAdminUser();
$userprofilefields = $this->generate_userprofilefields();
@@ -322,6 +382,7 @@ class user_profile_fields_test extends core_reportbuilder_testcase {
*/
public function test_custom_report_filter(string $filtername, array $filtervalues, string $expectmatchuser): void {
$this->resetAfterTest();
$this->setAdminUser();
$userprofilefields = $this->generate_userprofilefields();
+5
View File
@@ -1,6 +1,11 @@
This file describes API changes in /reportbuilder/*
Information provided here is intended especially for developers.
=== 4.4.5 ===
* The `get_active_conditions` method of the base report class has a new `$checkavailable` parameter to determine whether to check
the returned conditions availability
=== 4.4.1 ===
* New database helper method `sql_replace_parameters` to help ensure uniqueness of parameters within a SQL expression