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

This commit is contained in:
Jun Pataleta 2024-10-30 11:32:24 +08:00 committed by Huong Nguyen
commit 9ece615f6c
No known key found for this signature in database
GPG Key ID: 40D88AB693A3E72A
10 changed files with 217 additions and 90 deletions

View File

@ -0,0 +1,8 @@
issueNumber: MDL-82809
notes:
core_reportbuilder:
- message: >-
The `get_active_conditions` method of the base report class has a new
`$checkavailable` parameter to determine whether to check the returned
conditions availability
type: changed

View File

@ -194,8 +194,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);
}
}
@ -277,9 +276,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.
@ -294,15 +294,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);
}
}

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

View File

@ -257,13 +257,12 @@ class custom_fields {
$customdataparams,
))
->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;

View File

@ -56,11 +56,7 @@ class user_profile_fields {
/** @var string The entity name used when adding columns and filters */
private readonly string $entityname,
) {
// Retrieve the list of available/visible user profile fields.
$this->userprofilefields = array_filter(
profile_get_user_fields_with_data(0),
fn(profile_field_base $field) => $field->is_visible(),
);
$this->userprofilefields = profile_get_user_fields_with_data(0);
}
/**
@ -152,7 +148,8 @@ class user_profile_fields {
);
return $field->display_data();
}, $profilefield);
}, $profilefield)
->set_is_available($profilefield->is_visible());
}
return $columns;
@ -222,11 +219,12 @@ class user_profile_fields {
$userinfoparams,
))
->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;

View File

@ -553,9 +553,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()) {

View File

@ -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());

View File

@ -288,6 +288,40 @@ final class datasource_test extends advanced_testcase {
$this->assertCount($expectedcountconditions, $instance->get_conditions());
}
/**
* 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
*

View File

@ -65,9 +65,10 @@ final class custom_fields_test extends core_reportbuilder_testcase {
['categoryid' => $category->get('id'), 'type' => 'textarea', 'name' => 'Textarea', 'shortname' => 'textarea',
'configdata' => ['defaultvalue' => 'Default']]);
// This field is available only to course teachers.
$generator->create_field(
['categoryid' => $category->get('id'), 'type' => 'checkbox', 'name' => 'Checkbox', 'shortname' => 'checkbox',
'configdata' => ['checkbydefault' => 1]]);
'configdata' => ['checkbydefault' => 1, 'visibility' => 1]]);
$generator->create_field(
['categoryid' => $category->get('id'), 'type' => 'date', 'name' => 'Date', 'shortname' => 'date']);
@ -88,10 +89,11 @@ final 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);
@ -112,6 +114,19 @@ final 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()),
);
}
/**
@ -150,18 +165,32 @@ final 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()),
);
}
/**
@ -169,6 +198,7 @@ final class custom_fields_test extends core_reportbuilder_testcase {
*/
public function test_custom_report_content(): void {
$this->resetAfterTest();
$this->setAdminUser();
$category = $this->getDataGenerator()->create_category(['name' => 'Zebras']);
$courseone = $this->getDataGenerator()->create_course(['category' => $category->id, 'fullname' => 'C1']);
@ -301,6 +331,7 @@ final class custom_fields_test extends core_reportbuilder_testcase {
*/
public function test_custom_report_filter(string $filtername, array $filtervalues, ?string $expectmatch = null): void {
$this->resetAfterTest();
$this->setAdminUser();
$this->getDataGenerator()->create_course(['fullname' => 'C1']);

View File

@ -49,9 +49,10 @@ final class user_profile_fields_test extends core_reportbuilder_testcase {
$this->getDataGenerator()->create_custom_profile_field([
'shortname' => 'checkbox', 'name' => 'Checkbox field', 'datatype' => 'checkbox', 'defaultdata' => 1]);
// This field is available only to admins.
$this->getDataGenerator()->create_custom_profile_field([
'shortname' => 'datetime', 'name' => 'Date field', 'datatype' => 'datetime', 'param2' => 2022, 'param3' => 0,
'defaultdata' => 0]);
'defaultdata' => 0, 'visible' => PROFILE_VISIBLE_NONE]);
$this->getDataGenerator()->create_custom_profile_field([
'shortname' => 'menu', 'name' => 'Menu field', 'datatype' => 'menu', 'param1' => "Cat\nDog", 'defaultdata' => 'Cat']);
@ -77,55 +78,88 @@ final 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)),
));
}
/**
@ -164,38 +198,62 @@ final 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)),
));
}
/**
@ -203,6 +261,7 @@ final class user_profile_fields_test extends core_reportbuilder_testcase {
*/
public function test_custom_report_content(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create test subject with user profile fields content.
$this->generate_userprofilefields();
@ -330,6 +389,7 @@ final class user_profile_fields_test extends core_reportbuilder_testcase {
*/
public function test_custom_report_filter(string $filtername, array $filtervalues, ?string $expectmatch = null): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create test subject with user profile fields content.
$this->generate_userprofilefields();