mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 00:12:56 +02:00
MDL-80669 reportbuilder: improved reporting of custom/profile fields.
Make the display of both custom and user profile fields consistent in column callbacks. The defaults for each should be considered when both rendering the column data and also when aggregating them (specifically for numeric columns to ensure calculations are accurate). When such fields are filtered, and they have user defined default values then we should also take that into account to ensure that the same values rendered in columns can always be filtered for. During this change, I've updated some of the variables to improve readability and future maintainability of these classes. Annoyingly there are a non-zero number of changes just for Oracle support.
This commit is contained in:
parent
554a790bf0
commit
892b68d58c
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -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',
|
||||
'<div class="text_to_html">Goodbye</div>',
|
||||
'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],
|
||||
|
@ -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' => '<div class="no-overflow">Goodbye</div>',
|
||||
'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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user