diff --git a/reportbuilder/classes/local/helpers/custom_fields.php b/reportbuilder/classes/local/helpers/custom_fields.php index 3d078970f2f..a2e45a79141 100644 --- a/reportbuilder/classes/local/helpers/custom_fields.php +++ b/reportbuilder/classes/local/helpers/custom_fields.php @@ -18,18 +18,13 @@ declare(strict_types=1); namespace core_reportbuilder\local\helpers; -use core_reportbuilder\local\filters\boolean_select; -use core_reportbuilder\local\filters\date; -use core_reportbuilder\local\filters\number; -use core_reportbuilder\local\filters\select; -use core_reportbuilder\local\filters\text; -use core_reportbuilder\local\report\column; -use core_reportbuilder\local\report\filter; -use lang_string; -use stdClass; +use core\lang_string; use core_customfield\data_controller; use core_customfield\field_controller; use core_customfield\handler; +use core_reportbuilder\local\filters\{boolean_select, date, number, select, text}; +use core_reportbuilder\local\report\{column, filter}; +use stdClass; /** * Helper class for course custom fields. @@ -116,24 +111,31 @@ class custom_fields { foreach ($categorieswithfields as $fieldcategory) { $categoryfields = $fieldcategory->get_fields(); foreach ($categoryfields as $field) { - $customdatatablealias = $this->get_table_alias($field); - $datacontroller = data_controller::create(0, null, $field); - $datafield = $datacontroller->datafield(); - $datafieldsql = "{$customdatatablealias}.{$datafield}"; - // Long text fields should be cast for Oracle, for aggregation support. + $customdatatablealias = $this->get_table_alias($field); + $customdatasql = "{$customdatatablealias}.{$datafield}"; + + // Numeric column (non-text) should coalesce with default, as should text fields for Oracle, for aggregation. $columntype = $this->get_column_type($field, $datafield); - if ($columntype === column::TYPE_LONGTEXT && $DB->get_dbfamily() === 'oracle') { - $datafieldsql = $DB->sql_order_by_text($datafieldsql, 1024); + if (!in_array($columntype, [column::TYPE_TEXT, column::TYPE_LONGTEXT])) { + + // See MDL-78783 regarding no bound parameters, and Oracle limitations of GROUP BY. + $customdatasql = " + CASE WHEN {$this->tablefieldalias} IS NOT NULL + THEN COALESCE({$customdatasql}, " . (float) $datacontroller->get_default_value() . ") + ELSE NULL + END"; + } else if ($columntype === column::TYPE_LONGTEXT && $DB->get_dbfamily() === 'oracle') { + $customdatasql = $DB->sql_order_by_text($customdatasql, 1024); } // Select enough fields to re-create and format each custom field instance value. - $selectfields = "{$customdatatablealias}.id, {$customdatatablealias}.contextid"; + $customdatasqlextra = "{$customdatatablealias}.id, {$customdatatablealias}.contextid"; if ($datafield === 'value') { // We will take the format into account when displaying the individual values. - $selectfields .= ", {$customdatatablealias}.valueformat, {$customdatatablealias}.valuetrust"; + $customdatasqlextra .= ", {$customdatatablealias}.valueformat, {$customdatatablealias}.valuetrust"; } $columns[] = (new column( @@ -143,15 +145,19 @@ class custom_fields { )) ->add_joins($this->get_joins()) ->add_join($this->get_table_join($field)) - ->add_field($datafieldsql, $datafield) - ->add_fields($selectfields) + ->add_field($customdatasql, $datafield) + ->add_fields($customdatasqlextra) ->add_field($this->tablefieldalias, 'tablefieldalias') ->set_type($columntype) ->set_is_sortable($columntype !== column::TYPE_LONGTEXT) - ->add_callback(static function($value, stdClass $row, field_controller $field): string { - if ($row->tablefieldalias === null) { + ->add_callback(static function($value, stdClass $row, field_controller $field, ?string $aggregation): string { + if ($row->tablefieldalias === null && $value === null) { return ''; } + // If aggregating numeric column, populate row ID to ensure the controller is created correctly. + if (in_array((string) $aggregation, ['avg', 'max', 'min', 'sum'])) { + $row->id ??= -1; + } return (string) data_controller::create(0, $row, $field)->export_value(); }, $field) // Important. If the handler implements can_view() function, it will be called with parameter $instanceid=0. @@ -213,23 +219,42 @@ class custom_fields { foreach ($categorieswithfields as $fieldcategory) { $categoryfields = $fieldcategory->get_fields(); foreach ($categoryfields as $field) { - $customdatatablealias = $this->get_table_alias($field); - $datacontroller = data_controller::create(0, null, $field); - $datafield = $datacontroller->datafield(); - $datafieldsql = "{$customdatatablealias}.{$datafield}"; + + $customdatatablealias = $this->get_table_alias($field); + $customdatasql = "{$customdatatablealias}.{$datafield}"; + $customdataparams = []; + if ($datafield === 'value') { - $datafieldsql = $DB->sql_cast_to_char($datafieldsql); + $customdatasql = $DB->sql_cast_to_char($customdatasql); + } + + // Account for field default value, when joined to the instance table related to the custom fields. + if (($fielddefault = $datacontroller->get_default_value()) !== null) { + $paramdefault = database::generate_param_name(); + + // Oracle be crazy. + $paramdefaultsql = ":{$paramdefault}"; + if ($DB->get_dbfamily() === 'oracle' && in_array($datafield, ['intvalue', 'decvalue'])) { + $paramdefaultsql = $DB->sql_cast_char2int($paramdefaultsql); + } + + $customdatasql = " + CASE WHEN {$this->tablefieldalias} IS NOT NULL + THEN COALESCE({$customdatasql}, {$paramdefaultsql}) + ELSE NULL + END"; + $customdataparams[$paramdefault] = $fielddefault; } - $typeclass = $this->get_filter_class_type($datacontroller); $filter = (new filter( - $typeclass, + $this->get_filter_class_type($datacontroller), 'customfield_' . $field->get('shortname'), new lang_string('customfieldcolumn', 'core_reportbuilder', $field->get_formatted_name()), $this->entityname, - $datafieldsql + $customdatasql, + $customdataparams, )) ->add_joins($this->get_joins()) ->add_join($this->get_table_join($field)); diff --git a/reportbuilder/classes/local/helpers/user_profile_fields.php b/reportbuilder/classes/local/helpers/user_profile_fields.php index bb2addfc5c9..3899225976c 100644 --- a/reportbuilder/classes/local/helpers/user_profile_fields.php +++ b/reportbuilder/classes/local/helpers/user_profile_fields.php @@ -18,15 +18,11 @@ declare(strict_types=1); namespace core_reportbuilder\local\helpers; -use context_system; +use core\context\system; +use core\lang_string; use core_text; -use core_reportbuilder\local\filters\boolean_select; -use core_reportbuilder\local\filters\date; -use core_reportbuilder\local\filters\select; -use core_reportbuilder\local\filters\text; -use core_reportbuilder\local\report\column; -use core_reportbuilder\local\report\filter; -use lang_string; +use core_reportbuilder\local\filters\{boolean_select, date, select, text}; +use core_reportbuilder\local\report\{column, filter}; use profile_field_base; use stdClass; @@ -111,37 +107,53 @@ class user_profile_fields { global $DB; $columns = []; - foreach ($this->userprofilefields as $profilefield) { - $columntype = $this->get_user_field_type($profilefield->field->datatype); - $columnfieldsql = $this->get_table_alias($profilefield) . '.data'; - // Numeric (checkbox/time) fields should be cast, as should all fields for Oracle, for aggregation support. - if ($columntype === column::TYPE_BOOLEAN || $columntype === column::TYPE_TIMESTAMP) { - $columnfieldsql = "CASE WHEN {$columnfieldsql} IS NULL THEN NULL ELSE " . - $DB->sql_cast_char2int($columnfieldsql, true) . " END"; - } else if ($DB->get_dbfamily() === 'oracle') { - $columnfieldsql = $DB->sql_order_by_text($columnfieldsql, 1024); + foreach ($this->userprofilefields as $profilefield) { + $userinfotablealias = $this->get_table_alias($profilefield); + $userinfosql = "{$userinfotablealias}.data"; + + if ($DB->get_dbfamily() === 'oracle') { + $userinfosql = $DB->sql_order_by_text($userinfosql, 1024); + } + + // Numeric column (non-text) should cast/coalesce with default, as should all fields for Oracle, for aggregation. + $columntype = $this->get_user_field_type($profilefield->field->datatype); + if (!in_array($columntype, [column::TYPE_TEXT, column::TYPE_LONGTEXT])) { + + // See MDL-78783 regarding no bound parameters, and Oracle limitations of GROUP BY. + $userinfosql = " + CASE WHEN {$this->usertablefieldalias} IS NOT NULL + THEN " . + $DB->sql_cast_char2int("COALESCE({$userinfosql}, '" . (float) $profilefield->field->defaultdata . "')") + . " + ELSE NULL + END"; } $columns[] = (new column( 'profilefield_' . core_text::strtolower($profilefield->field->shortname), new lang_string('customfieldcolumn', 'core_reportbuilder', - format_string($profilefield->field->name, true, - ['escape' => false, 'context' => context_system::instance()])), + format_string($profilefield->field->name, true, ['escape' => false, 'context' => system::instance()])), $this->entityname )) ->add_joins($this->get_joins()) ->add_join($this->get_table_join($profilefield)) - ->add_field($columnfieldsql, 'data') + ->add_field($userinfosql, 'data') + ->add_field("{$userinfotablealias}.dataformat") + ->add_field($this->usertablefieldalias, 'userid') ->set_type($columntype) ->set_is_sortable($columntype !== column::TYPE_LONGTEXT) ->add_callback(static function($value, stdClass $row, profile_field_base $field): string { - if ($value === null) { + if ($row->userid === null && $value === null) { return ''; } - $field->data = $value; - return (string) $field->display_data(); + $field->set_user_data( + $row->data ?? $field->field->defaultdata, + $row->dataformat ?? $field->field->defaultdataformat, + ); + + return $field->display_data(); }, $profilefield); } @@ -157,48 +169,60 @@ class user_profile_fields { global $DB; $filters = []; - foreach ($this->userprofilefields as $profilefield) { - $field = $this->get_table_alias($profilefield) . '.data'; - $params = []; + foreach ($this->userprofilefields as $profilefield) { + $userinfotablealias = $this->get_table_alias($profilefield); + $userinfosql = "{$userinfotablealias}.data"; + $userinfoparams = []; + + // Perform casts where necessary, as this is a text DB field. switch ($profilefield->field->datatype) { case 'checkbox': $classname = boolean_select::class; - $fieldsql = "COALESCE(" . $DB->sql_cast_char2int($field, true) . ", 0)"; + $userinfosql = $DB->sql_cast_char2int($userinfosql, true); break; case 'datetime': $classname = date::class; - $fieldsql = $DB->sql_cast_char2int($field, true); + $userinfosql = $DB->sql_cast_char2int($userinfosql, true); break; case 'menu': $classname = select::class; - - $emptyparam = database::generate_param_name(); - $fieldsql = "COALESCE(" . $DB->sql_compare_text($field, 255) . ", :{$emptyparam})"; - $params[$emptyparam] = ''; - + $userinfosql = $DB->sql_cast_to_char($userinfosql); break; case 'text': case 'textarea': default: $classname = text::class; - - $emptyparam = database::generate_param_name(); - $fieldsql = "COALESCE(" . $DB->sql_compare_text($field, 255) . ", :{$emptyparam})"; - $params[$emptyparam] = ''; - + $userinfosql = $DB->sql_cast_to_char($userinfosql); break; } + // Account for field default value, when joined to the user table. + if (($fielddefault = $profilefield->field->defaultdata) !== null) { + $paramdefault = database::generate_param_name(); + + // Oracle be crazy. + $paramdefaultsql = ":{$paramdefault}"; + if ($DB->get_dbfamily() === 'oracle' && in_array($profilefield->field->datatype, ['checkbox', 'datetime'])) { + $paramdefaultsql = $DB->sql_cast_char2int($paramdefaultsql); + } + + $userinfosql = " + CASE WHEN {$this->usertablefieldalias} IS NOT NULL + THEN COALESCE({$userinfosql}, {$paramdefaultsql}) + ELSE NULL + END"; + $userinfoparams[$paramdefault] = $fielddefault; + } + $filter = (new filter( $classname, 'profilefield_' . core_text::strtolower($profilefield->field->shortname), new lang_string('customfieldcolumn', 'core_reportbuilder', - format_string($profilefield->field->name, true, - ['escape' => false, 'context' => context_system::instance()])), + format_string($profilefield->field->name, true, ['escape' => false, 'context' => system::instance()])), $this->entityname, - $fieldsql, - $params + $userinfosql, + $userinfoparams, )) ->add_joins($this->get_joins()) ->add_join($this->get_table_join($profilefield)); diff --git a/reportbuilder/tests/local/helpers/custom_fields_test.php b/reportbuilder/tests/local/helpers/custom_fields_test.php index 759857da88b..2e03c3cb262 100644 --- a/reportbuilder/tests/local/helpers/custom_fields_test.php +++ b/reportbuilder/tests/local/helpers/custom_fields_test.php @@ -22,12 +22,8 @@ use core_customfield_generator; use core_reportbuilder_generator; use core_reportbuilder_testcase; use core_reportbuilder\local\entities\course; -use core_reportbuilder\local\filters\boolean_select; -use core_reportbuilder\local\filters\date; -use core_reportbuilder\local\filters\select; -use core_reportbuilder\local\filters\text; -use core_reportbuilder\local\report\column; -use core_reportbuilder\local\report\filter; +use core_reportbuilder\local\filters\{boolean_select, date, select, text}; +use core_reportbuilder\local\report\{column, filter}; use core_course\reportbuilder\datasource\{categories, courses}; defined('MOODLE_INTERNAL') || die(); @@ -62,13 +58,16 @@ final class custom_fields_test extends core_reportbuilder_testcase { ]); $generator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Text', 'shortname' => 'text']); + ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Text', 'shortname' => 'text', + 'configdata' => ['defaultvalue' => 'default']]); $generator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'textarea', 'name' => 'Textarea', 'shortname' => 'textarea']); + ['categoryid' => $category->get('id'), 'type' => 'textarea', 'name' => 'Textarea', 'shortname' => 'textarea', + 'configdata' => ['defaultvalue' => 'Default']]); $generator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'checkbox', 'name' => 'Checkbox', 'shortname' => 'checkbox']); + ['categoryid' => $category->get('id'), 'type' => 'checkbox', 'name' => 'Checkbox', 'shortname' => 'checkbox', + 'configdata' => ['checkbydefault' => 1]]); $generator->create_field( ['categoryid' => $category->get('id'), 'type' => 'date', 'name' => 'Date', 'shortname' => 'date']); @@ -81,8 +80,7 @@ final class custom_fields_test extends core_reportbuilder_testcase { $coursealias = $courseentity->get_table_alias('course'); // Create an instance of the customfields helper. - return new custom_fields($coursealias . '.id', $courseentity->get_entity_name(), - 'core_course', 'course'); + return new custom_fields("{$coursealias}.id", $courseentity->get_entity_name(), 'core_course', 'course'); } /** @@ -172,51 +170,19 @@ final class custom_fields_test extends core_reportbuilder_testcase { public function test_custom_report_content(): void { $this->resetAfterTest(); - $this->generate_customfields(); + $category = $this->getDataGenerator()->create_category(['name' => 'Zebras']); + $courseone = $this->getDataGenerator()->create_course(['category' => $category->id, 'fullname' => 'C1']); - $course = $this->getDataGenerator()->create_course(['customfields' => [ + // Second course will populate each custom field. + $this->generate_customfields(); + $coursetwo = $this->getDataGenerator()->create_course(['category' => $category->id, 'fullname' => 'C2', 'customfields' => [ ['shortname' => 'text', 'value' => 'Hello'], ['shortname' => 'textarea_editor', 'value' => ['text' => 'Goodbye', 'format' => FORMAT_MOODLE]], - ['shortname' => 'checkbox', 'value' => true], + ['shortname' => 'checkbox', 'value' => 0], ['shortname' => 'date', 'value' => 1669852800], ['shortname' => 'select', 'value' => 2], ]]); - /** @var core_reportbuilder_generator $generator */ - $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); - $report = $generator->create_report(['name' => 'Courses', 'source' => courses::class, 'default' => 0]); - - // Add custom field columns to the report. - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']); - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_text']); - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_textarea']); - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_checkbox']); - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_date']); - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_select']); - - $content = $this->get_custom_report_content($report->get('id')); - - $this->assertEquals([ - $course->fullname, - 'Hello', - '
Goodbye
', - 'Yes', - userdate(1669852800), - 'Dog' - ], array_values($content[0])); - } - - /** - * Test that adding custom field columns to report returns expected default values for fields - */ - public function test_custom_report_content_column_defaults(): void { - $this->resetAfterTest(); - - $this->generate_customfields(); - - $category = $this->getDataGenerator()->create_category(['name' => 'Zebras']); - $course = $this->getDataGenerator()->create_course(['category' => $category->id]); - /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); $report = $generator->create_report(['name' => 'Categories', 'source' => categories::class, 'default' => 0]); @@ -224,13 +190,43 @@ final class custom_fields_test extends core_reportbuilder_testcase { // Add custom field columns to the report. $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:name', 'sortenabled' => 1]); - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname', + 'sortenabled' => 1]); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_text']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_textarea']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_checkbox']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_date']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_select']); $content = $this->get_custom_report_content($report->get('id')); $this->assertEquals([ - ['Category 1', '', ''], - [$category->name, $course->fullname, 'Cat'], + [ + 'Category 1', + '', + '', + '', + '', + '', + '', + ], + [ + $category->name, + $courseone->fullname, + 'default', + format_text('Default'), + 'Yes', + '', + 'Cat', + ], + [ + $category->name, + $coursetwo->fullname, + 'Hello', + format_text('Goodbye'), + 'No', + userdate(1669852800), + 'Dog', + ], ], array_map('array_values', $content)); } @@ -244,41 +240,53 @@ final class custom_fields_test extends core_reportbuilder_testcase { 'Filter by text custom field' => ['course:customfield_text', [ 'course:customfield_text_operator' => text::IS_EQUAL_TO, 'course:customfield_text_value' => 'Hello', - ], true], + ], 'C2'], + 'Filter by text custom field (default)' => ['course:customfield_text', [ + 'course:customfield_text_operator' => text::IS_EQUAL_TO, + 'course:customfield_text_value' => 'default', + ], 'C1'], 'Filter by text custom field (no match)' => ['course:customfield_text', [ 'course:customfield_text_operator' => text::IS_EQUAL_TO, 'course:customfield_text_value' => 'Goodbye', - ], false], + ]], 'Filter by textarea custom field' => ['course:customfield_textarea', [ 'course:customfield_textarea_operator' => text::IS_EQUAL_TO, 'course:customfield_textarea_value' => 'Goodbye', - ], true], + ], 'C2'], + 'Filter by textarea custom field (default)' => ['course:customfield_textarea', [ + 'course:customfield_textarea_operator' => text::IS_EQUAL_TO, + 'course:customfield_textarea_value' => 'Default', + ], 'C1'], 'Filter by textarea custom field (no match)' => ['course:customfield_textarea', [ 'course:customfield_textarea_operator' => text::IS_EQUAL_TO, 'course:customfield_textarea_value' => 'Hello', - ], false], + ]], 'Filter by checkbox custom field' => ['course:customfield_checkbox', [ - 'course:customfield_checkbox_operator' => boolean_select::CHECKED, - ], true], - 'Filter by checkbox custom field (no match)' => ['course:customfield_checkbox', [ 'course:customfield_checkbox_operator' => boolean_select::NOT_CHECKED, - ], false], + ], 'C2'], + 'Filter by checkbox custom field (default)' => ['course:customfield_checkbox', [ + 'course:customfield_checkbox_operator' => boolean_select::CHECKED, + ], 'C1'], 'Filter by date custom field' => ['course:customfield_date', [ 'course:customfield_date_operator' => date::DATE_RANGE, 'course:customfield_date_from' => 1622502000, - ], true], + ], 'C2'], 'Filter by date custom field (no match)' => ['course:customfield_date', [ 'course:customfield_date_operator' => date::DATE_RANGE, - 'course:customfield_date_to' => 1622502000, - ], false], + 'course:customfield_date_from' => 1672531200, + ]], 'Filter by select custom field' => ['course:customfield_select', [ 'course:customfield_select_operator' => select::EQUAL_TO, 'course:customfield_select_value' => 2, - ], true], - 'Filter by select custom field (no match)' => ['course:customfield_select', [ + ], 'C2'], + 'Filter by select custom field (default)' => ['course:customfield_select', [ 'course:customfield_select_operator' => select::EQUAL_TO, 'course:customfield_select_value' => 1, - ], false], + ], 'C1'], + 'Filter by select custom field (no match)' => ['course:customfield_select', [ + 'course:customfield_select_operator' => select::EQUAL_TO, + 'course:customfield_select_value' => 3, + ]], ]; } @@ -287,19 +295,21 @@ final class custom_fields_test extends core_reportbuilder_testcase { * * @param string $filtername * @param array $filtervalues - * @param bool $expectmatch + * @param string|null $expectmatch * * @dataProvider custom_report_filter_provider */ - public function test_custom_report_filter(string $filtername, array $filtervalues, bool $expectmatch): void { + public function test_custom_report_filter(string $filtername, array $filtervalues, ?string $expectmatch = null): void { $this->resetAfterTest(); - $this->generate_customfields(); + $this->getDataGenerator()->create_course(['fullname' => 'C1']); - $course = $this->getDataGenerator()->create_course(['customfields' => [ + // Second course will populate each custom field. + $this->generate_customfields(); + $this->getDataGenerator()->create_course(['fullname' => 'C2', 'customfields' => [ ['shortname' => 'text', 'value' => 'Hello'], ['shortname' => 'textarea_editor', 'value' => ['text' => 'Goodbye', 'format' => FORMAT_MOODLE]], - ['shortname' => 'checkbox', 'value' => true], + ['shortname' => 'checkbox', 'value' => 0], ['shortname' => 'date', 'value' => 1669852800], ['shortname' => 'select', 'value' => 2], ]]); @@ -315,9 +325,9 @@ final class custom_fields_test extends core_reportbuilder_testcase { $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]); $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); - if ($expectmatch) { + if ($expectmatch !== null) { $this->assertCount(1, $content); - $this->assertEquals($course->fullname, reset($content[0])); + $this->assertEquals($expectmatch, reset($content[0])); } else { $this->assertEmpty($content); } @@ -336,7 +346,7 @@ final class custom_fields_test extends core_reportbuilder_testcase { $this->resetAfterTest(); $this->generate_customfields(); - $course = $this->getDataGenerator()->create_course(['customfields' => [ + $this->getDataGenerator()->create_course(['customfields' => [ ['shortname' => 'text', 'value' => 'Hello'], ['shortname' => 'textarea_editor', 'value' => ['text' => 'Goodbye', 'format' => FORMAT_MOODLE]], ['shortname' => 'checkbox', 'value' => true], diff --git a/reportbuilder/tests/local/helpers/user_profile_fields_test.php b/reportbuilder/tests/local/helpers/user_profile_fields_test.php index 321a4603666..bdd7874c3e2 100644 --- a/reportbuilder/tests/local/helpers/user_profile_fields_test.php +++ b/reportbuilder/tests/local/helpers/user_profile_fields_test.php @@ -21,12 +21,8 @@ namespace core_reportbuilder\local\helpers; use core_reportbuilder_generator; use core_reportbuilder_testcase; use core_reportbuilder\local\entities\user; -use core_reportbuilder\local\filters\boolean_select; -use core_reportbuilder\local\filters\date; -use core_reportbuilder\local\filters\select; -use core_reportbuilder\local\filters\text; -use core_reportbuilder\local\report\column; -use core_reportbuilder\local\report\filter; +use core_reportbuilder\local\filters\{boolean_select, date, select, text}; +use core_reportbuilder\local\report\{column, filter}; use core_user\reportbuilder\datasource\users; defined('MOODLE_INTERNAL') || die(); @@ -51,28 +47,29 @@ final class user_profile_fields_test extends core_reportbuilder_testcase { */ private function generate_userprofilefields(): user_profile_fields { $this->getDataGenerator()->create_custom_profile_field([ - 'shortname' => 'checkbox', 'name' => 'Checkbox field', 'datatype' => 'checkbox']); + 'shortname' => 'checkbox', 'name' => 'Checkbox field', 'datatype' => 'checkbox', 'defaultdata' => 1]); $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, + 'defaultdata' => 0]); $this->getDataGenerator()->create_custom_profile_field([ - 'shortname' => 'menu', 'name' => 'Menu field', 'datatype' => 'menu', 'param1' => "Cat\nDog"]); + 'shortname' => 'menu', 'name' => 'Menu field', 'datatype' => 'menu', 'param1' => "Cat\nDog", 'defaultdata' => 'Cat']); $this->getDataGenerator()->create_custom_profile_field([ 'shortname' => 'Social', 'name' => 'msn', 'datatype' => 'social', 'param1' => 'msn']); $this->getDataGenerator()->create_custom_profile_field([ - 'shortname' => 'text', 'name' => 'Text field', 'datatype' => 'text']); + 'shortname' => 'text', 'name' => 'Text field', 'datatype' => 'text', 'defaultdata' => 'default']); $this->getDataGenerator()->create_custom_profile_field([ - 'shortname' => 'textarea', 'name' => 'Textarea field', 'datatype' => 'textarea']); + 'shortname' => 'textarea', 'name' => 'Textarea field', 'datatype' => 'textarea', 'defaultdata' => 'Default']); $userentity = new user(); $useralias = $userentity->get_table_alias('user'); // Create an instance of the userprofilefield helper. - return new user_profile_fields("$useralias.id", $userentity->get_entity_name()); + return new user_profile_fields("{$useralias}.id", $userentity->get_entity_name()); } /** @@ -207,14 +204,13 @@ final class user_profile_fields_test extends core_reportbuilder_testcase { public function test_custom_report_content(): void { $this->resetAfterTest(); - $userprofilefields = $this->generate_userprofilefields(); - // Create test subject with user profile fields content. - $user = $this->getDataGenerator()->create_user([ + $this->generate_userprofilefields(); + $this->getDataGenerator()->create_user([ 'firstname' => 'Zebedee', - 'profile_field_checkbox' => true, + 'profile_field_checkbox' => 0, 'profile_field_datetime' => '2021-12-09', - 'profile_field_menu' => 'Cat', + 'profile_field_menu' => 'Dog', 'profile_field_Social' => 12345, 'profile_field_text' => 'Hello', 'profile_field_textarea' => 'Goodbye', @@ -225,7 +221,7 @@ final class user_profile_fields_test extends core_reportbuilder_testcase { $report = $generator->create_report(['name' => 'Users', 'source' => users::class, 'default' => 0]); // Add user profile field columns to the report. - $firstname = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:firstname']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:firstname', 'sortenabled' => 1]); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_checkbox']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_datetime']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_menu']); @@ -233,29 +229,26 @@ final class user_profile_fields_test extends core_reportbuilder_testcase { $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_text']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_textarea']); - // Sort the report, Admin -> Zebedee for consistency. - report::toggle_report_column_sorting($report->get('id'), $firstname->get('id'), true); - $content = $this->get_custom_report_content($report->get('id')); $this->assertEquals([ [ - 'c0_firstname' => 'Admin', - 'c1_data' => '', - 'c2_data' => '', - 'c3_data' => '', - 'c4_data' => '', - 'c5_data' => '', - 'c6_data' => '', + 'Admin', + 'Yes', + 'Not set', + 'Cat', + '', + 'default', + format_text('Default', options: ['overflowdiv' => true]), ], [ - 'c0_firstname' => 'Zebedee', - 'c1_data' => 'Yes', - 'c2_data' => '9 December 2021', - 'c3_data' => 'Cat', - 'c4_data' => '12345', - 'c5_data' => 'Hello', - 'c6_data' => '
Goodbye
', + 'Zebedee', + 'No', + '9 December 2021', + 'Dog', + '12345', + 'Hello', + format_text('Goodbye', options: ['overflowdiv' => true]), ], - ], $content); + ], array_map('array_values', $content)); } /** @@ -266,49 +259,63 @@ final class user_profile_fields_test extends core_reportbuilder_testcase { public static function custom_report_filter_provider(): array { return [ 'Filter by checkbox profile field' => ['user:profilefield_checkbox', [ - 'user:profilefield_checkbox_operator' => boolean_select::CHECKED, - ], 'testuser'], - 'Filter by checkbox profile field (empty)' => ['user:profilefield_checkbox', [ 'user:profilefield_checkbox_operator' => boolean_select::NOT_CHECKED, + ], 'testuser'], + 'Filter by checkbox profile field (default)' => ['user:profilefield_checkbox', [ + 'user:profilefield_checkbox_operator' => boolean_select::CHECKED, ], 'admin'], 'Filter by datetime profile field' => ['user:profilefield_datetime', [ 'user:profilefield_datetime_operator' => date::DATE_RANGE, 'user:profilefield_datetime_from' => 1622502000, ], 'testuser'], - 'Filter by datetime profile field (empty)' => ['user:profilefield_datetime', [ - 'user:profilefield_datetime_operator' => date::DATE_EMPTY, - ], 'admin'], + 'Filter by datetime profile field (no match)' => ['user:profilefield_datetime', [ + 'user:profilefield_datetime_operator' => date::DATE_RANGE, + 'user:profilefield_datetime_from' => 1672531200, + ]], 'Filter by menu profile field' => ['user:profilefield_menu', [ 'user:profilefield_menu_operator' => select::EQUAL_TO, 'user:profilefield_menu_value' => 'Dog', ], 'testuser'], - 'Filter by menu profile field (empty)' => ['user:profilefield_menu', [ - 'user:profilefield_menu_operator' => select::NOT_EQUAL_TO, - 'user:profilefield_menu_value' => 'Dog', + 'Filter by menu profile field (default)' => ['user:profilefield_menu', [ + 'user:profilefield_menu_operator' => select::EQUAL_TO, + 'user:profilefield_menu_value' => 'Cat', ], 'admin'], + 'Filter by menu profile field (no match)' => ['user:profilefield_menu', [ + 'user:profilefield_menu_operator' => select::EQUAL_TO, + 'user:profilefield_menu_value' => 'Fish', + ]], 'Filter by social profile field' => ['user:profilefield_social', [ 'user:profilefield_social_operator' => text::IS_EQUAL_TO, 'user:profilefield_social_value' => '12345', ], 'testuser'], - 'Filter by social profile field (empty)' => ['user:profilefield_social', [ - 'user:profilefield_social_operator' => text::IS_EMPTY, - ], 'admin'], + 'Filter by social profile field (no match)' => ['user:profilefield_social', [ + 'user:profilefield_social_operator' => text::IS_EQUAL_TO, + 'user:profilefield_social_value' => '54321', + ]], 'Filter by text profile field' => ['user:profilefield_text', [ 'user:profilefield_text_operator' => text::IS_EQUAL_TO, 'user:profilefield_text_value' => 'Hello', ], 'testuser'], - 'Filter by text profile field (empty)' => ['user:profilefield_text', [ - 'user:profilefield_text_operator' => text::IS_NOT_EQUAL_TO, - 'user:profilefield_text_value' => 'Hello', + 'Filter by text profile field (default)' => ['user:profilefield_text', [ + 'user:profilefield_text_operator' => text::IS_EQUAL_TO, + 'user:profilefield_text_value' => 'default', ], 'admin'], + 'Filter by text profile field (no match)' => ['user:profilefield_text', [ + 'user:profilefield_text_operator' => text::IS_EQUAL_TO, + 'user:profilefield_text_value' => 'hola', + ]], 'Filter by textarea profile field' => ['user:profilefield_textarea', [ 'user:profilefield_textarea_operator' => text::IS_EQUAL_TO, 'user:profilefield_textarea_value' => 'Goodbye', ], 'testuser'], - 'Filter by textarea profile field (empty)' => ['user:profilefield_textarea', [ - 'user:profilefield_textarea_operator' => text::DOES_NOT_CONTAIN, - 'user:profilefield_textarea_value' => 'Goodbye', + 'Filter by textarea profile field (default)' => ['user:profilefield_textarea', [ + 'user:profilefield_textarea_operator' => text::IS_EQUAL_TO, + 'user:profilefield_textarea_value' => 'Default', ], 'admin'], + 'Filter by textarea profile field (no match)' => ['user:profilefield_textarea', [ + 'user:profilefield_textarea_operator' => text::IS_EMPTY, + 'user:profilefield_textarea_value' => 'Adios', + ]], ]; } @@ -317,19 +324,18 @@ final class user_profile_fields_test extends core_reportbuilder_testcase { * * @param string $filtername * @param array $filtervalues - * @param string $expectmatchuser + * @param string|null $expectmatch * * @dataProvider custom_report_filter_provider */ - public function test_custom_report_filter(string $filtername, array $filtervalues, string $expectmatchuser): void { + public function test_custom_report_filter(string $filtername, array $filtervalues, ?string $expectmatch = null): void { $this->resetAfterTest(); - $userprofilefields = $this->generate_userprofilefields(); - // Create test subject with user profile fields content. - $user = $this->getDataGenerator()->create_user([ + $this->generate_userprofilefields(); + $this->getDataGenerator()->create_user([ 'username' => 'testuser', - 'profile_field_checkbox' => true, + 'profile_field_checkbox' => 0, 'profile_field_datetime' => '2021-12-09', 'profile_field_menu' => 'Dog', 'profile_field_Social' => '12345', @@ -348,8 +354,12 @@ final class user_profile_fields_test extends core_reportbuilder_testcase { $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]); $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); - $this->assertCount(1, $content); - $this->assertEquals($expectmatchuser, reset($content[0])); + if ($expectmatch !== null) { + $this->assertCount(1, $content); + $this->assertEquals($expectmatch, reset($content[0])); + } else { + $this->assertEmpty($content); + } } /** @@ -364,8 +374,8 @@ final class user_profile_fields_test extends core_reportbuilder_testcase { $this->resetAfterTest(); - $userprofilefields = $this->generate_userprofilefields(); - $user = $this->getDataGenerator()->create_user([ + $this->generate_userprofilefields(); + $this->getDataGenerator()->create_user([ 'profile_field_checkbox' => true, 'profile_field_datetime' => '2021-12-09', 'profile_field_menu' => 'Dog',