This commit is contained in:
Ilya Tregubov 2023-05-18 09:22:15 +08:00
commit 63290dc925
No known key found for this signature in database
GPG Key ID: 0F58186F748E55C1
18 changed files with 734 additions and 54 deletions

View File

@ -0,0 +1,164 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_role\reportbuilder\local\entities;
use context;
use context_helper;
use lang_string;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\select;
use core_reportbuilder\local\report\{column, filter};
/**
* Role entity
*
* @package core_role
* @copyright 2023 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class role extends base {
/**
* Database tables that this entity uses and their default aliases
*
* @return array
*/
protected function get_default_table_aliases(): array {
return [
'context' => 'rctx',
'role' => 'r',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('role');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
$contextalias = $this->get_table_alias('context');
$rolealias = $this->get_table_alias('role');
// Name column.
$columns[] = (new column(
'name',
new lang_string('rolefullname', 'core_role'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$rolealias}.name, {$rolealias}.shortname, {$rolealias}.id, {$contextalias}.id AS contextid")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->set_is_sortable(true)
->set_callback(static function($name, stdClass $role): string {
if ($name === null) {
return '';
}
context_helper::preload_from_record($role);
$context = context::instance_by_id($role->contextid);
return role_get_name($role, $context, ROLENAME_BOTH);
});
// Short name column.
$columns[] = (new column(
'shortname',
new lang_string('roleshortname', 'core_role'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$rolealias}.shortname")
->set_is_sortable(true);
// Description column.
$columns[] = (new column(
'description',
new lang_string('description'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_LONGTEXT)
->add_fields("{$rolealias}.description, {$rolealias}.shortname")
->set_callback(static function($description, stdClass $role): string {
if ($description === null) {
return '';
}
return role_get_description($role);
});
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$rolealias = $this->get_table_alias('role');
// Name filter.
$filters[] = (new filter(
select::class,
'name',
new lang_string('rolefullname', 'core_role'),
$this->get_entity_name(),
"{$rolealias}.id"
))
->add_joins($this->get_joins())
->set_options_callback(static function(): array {
return role_get_names(null, ROLENAME_ORIGINAL, true);
});
return $filters;
}
}

View File

@ -22,11 +22,13 @@ use core_course\reportbuilder\local\entities\course_category;
use core_course\reportbuilder\local\entities\access;
use core_course\reportbuilder\local\entities\completion;
use core_course\reportbuilder\local\entities\enrolment;
use core_enrol\reportbuilder\local\entities\enrol;
use core_group\reportbuilder\local\entities\group;
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\course;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\helpers\database;
use core_role\reportbuilder\local\entities\role;
/**
* Course participants datasource
@ -42,9 +44,10 @@ class participants extends datasource {
*/
protected function initialise(): void {
$courseentity = new course();
$course = $courseentity->get_table_alias('course');
$this->add_entity($courseentity);
$context = $courseentity->get_table_alias('context');
$course = $courseentity->get_table_alias('course');
$this->set_main_table('course', $course);
// Exclude site course.
@ -57,14 +60,19 @@ class participants extends datasource {
$this->add_entity($coursecatentity
->add_join("JOIN {course_categories} {$categories} ON {$categories}.id = {$course}.category"));
// Join the enrolment method entity.
$enrolentity = new enrol();
$enrol = $enrolentity->get_table_alias('enrol');
$this->add_entity($enrolentity
->add_join("LEFT JOIN {enrol} {$enrol} ON {$enrol}.courseid = {$course}.id"));
// Join the enrolments entity.
$enrolmententity = new enrolment();
$enrolmententity = (new enrolment())
->set_table_alias('enrol', $enrol);
$userenrolment = $enrolmententity->get_table_alias('user_enrolments');
$enrol = $enrolmententity->get_table_alias('enrol');
$enroljoin = "LEFT JOIN {enrol} {$enrol} ON {$enrol}.courseid = {$course}.id";
$userenrolmentjoin = " LEFT JOIN {user_enrolments} {$userenrolment} ON {$userenrolment}.enrolid = {$enrol}.id";
$enrolmententity->add_joins([$enroljoin, $userenrolmentjoin]);
$this->add_entity($enrolmententity);
$this->add_entity($enrolmententity
->add_joins($enrolentity->get_joins())
->add_join("LEFT JOIN {user_enrolments} {$userenrolment} ON {$userenrolment}.enrolid = {$enrol}.id"));
// Join user entity.
$userentity = new user();
@ -73,9 +81,20 @@ class participants extends datasource {
$userentity->add_join("LEFT JOIN {user} {$user} ON {$userenrolment}.userid = {$user}.id AND {$user}.deleted = 0");
$this->add_entity($userentity);
// Join the role entity.
$roleentity = (new role())
->set_table_alias('context', $context);
$role = $roleentity->get_table_alias('role');
$this->add_entity($roleentity
->add_joins($userentity->get_joins())
->add_join($courseentity->get_context_join())
->add_join("LEFT JOIN {role_assignments} ras ON ras.contextid = {$context}.id AND ras.userid = {$user}.id")
->add_join("LEFT JOIN {role} {$role} ON {$role}.id = ras.roleid")
);
// Join group entity.
$groupentity = (new group())
->set_table_alias('context', $courseentity->get_table_alias('context'));
->set_table_alias('context', $context);
$groups = $groupentity->get_table_alias('groups');
// Sub-select for all course group members.
@ -135,7 +154,7 @@ class participants extends datasource {
public function get_default_columns(): array {
return [
'course:coursefullnamewithlink',
'enrolment:method',
'enrol:name',
'user:fullnamewithlink',
];
}

View File

@ -88,7 +88,7 @@ class enrolment extends base {
$userenrolments = $this->get_table_alias('user_enrolments');
$enrol = $this->get_table_alias('enrol');
// Enrolment method column.
// Enrolment method column (Deprecated since Moodle 4.3, to remove in MDL-78118).
$columns[] = (new column(
'method',
new lang_string('method', 'enrol'),
@ -98,6 +98,7 @@ class enrolment extends base {
->set_type(column::TYPE_TEXT)
->add_fields("{$enrol}.enrol, {$enrol}.id")
->set_is_sortable(true)
->set_is_deprecated('See \'enrol:name\' for replacement')
->add_callback([enrolment_formatter::class, 'enrolment_name']);
// Enrolment time created.
@ -153,7 +154,7 @@ class enrolment extends base {
->set_is_sortable(true)
->add_callback([enrolment_formatter::class, 'enrolment_status']);
// Role method column.
// Role column (Deprecated since Moodle 4.3, to remove in MDL-78118).
$ctx = database::generate_alias();
$ra = database::generate_alias();
$r = database::generate_alias();
@ -171,6 +172,7 @@ class enrolment extends base {
->set_type(column::TYPE_TEXT)
->add_fields("{$r}.id, {$r}.name, {$r}.shortname, {$ctx}.instanceid")
->set_is_sortable(true, ["{$r}.shortname"])
->set_is_deprecated('See \'role:name\' for replacement')
->add_callback(static function(?string $value, stdClass $row): string {
if (!$row->id) {
return '';
@ -213,7 +215,7 @@ class enrolment extends base {
$userenrolments = $this->get_table_alias('user_enrolments');
$enrol = $this->get_table_alias('enrol');
// Enrolment method.
// Enrolment method (Deprecated since Moodle 4.3, to remove in MDL-78118).
$enrolmentmethods = static function(): array {
return array_map(static function(enrol_plugin $plugin): string {
return get_string('pluginname', 'enrol_' . $plugin->get_name());
@ -227,6 +229,7 @@ class enrolment extends base {
"{$enrol}.enrol"
))
->add_joins($this->get_joins())
->set_is_deprecated('See \'enrol:plugin\' for replacement')
->set_options_callback($enrolmentmethods);
// Enrolment time created.

View File

@ -37,6 +37,8 @@ class enrolment {
* @param string|null $value
* @param stdClass $row
* @return string
*
* @deprecated since Moodle 4.3 - please do not use this function any more (to remove in MDL-78118)
*/
public static function enrolment_name(?string $value, stdClass $row): string {
global $DB;

View File

@ -19,12 +19,15 @@ declare(strict_types=1);
namespace core_course\reportbuilder\datasource;
use completion_completion;
use core_collator;
use core_reportbuilder\local\filters\boolean_select;
use core_reportbuilder\local\filters\date;
use core_reportbuilder\local\filters\duration;
use core_reportbuilder\local\filters\select;
use core_reportbuilder_generator;
use core_reportbuilder_testcase;
use grade_item;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
@ -45,9 +48,40 @@ require_once("{$CFG->libdir}/gradelib.php");
class participants_test extends core_reportbuilder_testcase {
/**
* Test participants datasource
* Test default datasource
*/
public function test_participants_datasource(): void {
public function test_datasource_default(): void {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_and_enrol($course, 'student');
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Participants', 'source' => participants::class, 'default' => 1]);
$content = $this->get_custom_report_content($report->get('id'));
// Consistent order, just in case.
core_collator::asort_array_of_arrays_by_key($content, 'c1_enrol');
$content = array_values($content);
$courseurl = course_get_url($course);
$userurl = new moodle_url('/user/profile.php', ['id' => $user->id]);
$this->assertEquals([
["<a href=\"{$courseurl}\">{$course->fullname}</a>", 'Guest access', ''],
["<a href=\"{$courseurl}\">{$course->fullname}</a>", 'Manual enrolments',
"<a href=\"{$userurl}\">" . fullname($user) . "</a>"],
["<a href=\"{$courseurl}\">{$course->fullname}</a>", 'Self enrolment (Student)', ''],
], array_map('array_values', $content));
}
/**
* Test datasource columns that aren't added by default
*/
public function test_datasource_non_default_columns(): void {
global $DB;
$this->resetAfterTest();
@ -96,8 +130,20 @@ class participants_test extends core_reportbuilder_testcase {
'uniqueidentifier' => 'course_category:name']);
$generator->create_column(['reportid' => $report->get('id'),
'uniqueidentifier' => 'user:fullname']);
// Order by enrolment method.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrolment:method', 'sortenabled' => 1]);
// Enrol entity (report ordering by enrolment name).
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:name', 'sortenabled' => 1]);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:plugin']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:enabled']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:period']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:startdate']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:enddate']);
// Role entity.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:name']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:shortname']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:description']);
$generator->create_column(['reportid' => $report->get('id'),
'uniqueidentifier' => 'group:name']);
$generator->create_column(['reportid' => $report->get('id'),
@ -122,7 +168,7 @@ class participants_test extends core_reportbuilder_testcase {
'uniqueidentifier' => 'completion:grade']);
// Add filter to the report.
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrolment:method']);
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:plugin']);
$content = $this->get_custom_report_content($report->get('id'));
@ -131,8 +177,8 @@ class participants_test extends core_reportbuilder_testcase {
// Filter by Manual enrolment method.
$content = $this->get_custom_report_content($report->get('id'), 30, [
'enrolment:method_operator' => select::EQUAL_TO,
'enrolment:method_value' => 'manual',
'enrol:plugin_operator' => select::EQUAL_TO,
'enrol:plugin_value' => 'manual',
]);
$this->assertCount(1, $content);
@ -142,6 +188,14 @@ class participants_test extends core_reportbuilder_testcase {
'Music', // Course category name.
fullname($user1), // User fullname.
'Manual enrolments', // Enrolment method.
'Manual enrolments', // Enrolment plugin.
'Yes', // Enrolment enabled.
'', // Enrolment period.
'', // Enrolment start date.
'', // Enrolment end date.
'Student', // Role name.
'student', // Role shortname.
'Students generally have fewer privileges within a course.', // Role description.
$group->name, // Group name.
'Yes', // Course completed.
userdate($timelastaccess), // Time last access.
@ -150,18 +204,20 @@ class participants_test extends core_reportbuilder_testcase {
'', // Time started.
userdate($timecompleted), // Time completed.
'', // Reagreggate.
'2', // Days taking course.
'2', // Days until completion.
2, // Days taking course.
2, // Days until completion.
'80.00', // Grade.
], array_values($content[0]));
}
/**
* Data provider for {@see test_report_filters}
* Data provider for {@see test_datasource_filters}
*
* @return array
*/
public function filters_data_provider(): array {
public function datasource_filters_provider(): array {
global $DB;
return [
[
'enrolment:status',
@ -169,7 +225,7 @@ class participants_test extends core_reportbuilder_testcase {
'enrolment:status_operator' => select::EQUAL_TO,
'enrolment:status_value' => 1,
],
'Luna'
['Luna'],
],
[
'enrolment:timecreated',
@ -177,7 +233,7 @@ class participants_test extends core_reportbuilder_testcase {
'enrolment:timecreated_operator' => date::DATE_CURRENT,
'enrolment:timecreated_unit' => date::DATE_UNIT_DAY,
],
'Kira'
['Kira'],
],
[
'enrolment:timestarted',
@ -185,7 +241,7 @@ class participants_test extends core_reportbuilder_testcase {
'enrolment:timestarted_operator' => date::DATE_CURRENT,
'enrolment:timecreated_unit' => date::DATE_UNIT_DAY,
],
'Luna'
['Luna'],
],
[
'enrolment:timeended',
@ -193,43 +249,80 @@ class participants_test extends core_reportbuilder_testcase {
'enrolment:timeended_operator' => date::DATE_CURRENT,
'enrolment:timeended_unit' => date::DATE_UNIT_DAY,
],
'Luna'
['Luna'],
],
[
'enrol:enabled',
[
'completion:enabled_operator' => boolean_select::CHECKED,
],
['Lionel', 'Kira', 'Luna'],
],
[
'enrol:period',
[
'enrol:period_operator' => duration::DURATION_MAXIMUM,
'enrol:period_unit' => MINSECS,
'enrol:period_value' => 2,
],
['Lionel', 'Kira', 'Luna'],
],
[
'enrol:startdate',
[
'enrol:startdate_operator' => date::DATE_EMPTY,
],
['Lionel', 'Kira', 'Luna'],
],
[
'enrol:enddate',
[
'enrol:enddate_operator' => date::DATE_EMPTY,
],
['Lionel', 'Kira', 'Luna'],
],
[
'role:name',
[
'role:name_operator' => select::EQUAL_TO,
'role:name_value' => $DB->get_field('role', 'id', ['shortname' => 'editingteacher']),
],
['Luna'],
],
[
'completion:completed',
[
'completion:completed_operator' => boolean_select::CHECKED,
'completion:completed_unit' => 1,
],
'Lionel'
['Lionel'],
],
[
'completion:timecompleted',
[
'completion:timecompleted_operator' => date::DATE_NOT_EMPTY,
],
'Lionel'
['Lionel'],
],
[
'completion:timeenrolled',
[
'completion:timeenrolled_operator' => date::DATE_NOT_EMPTY,
],
'Lionel'
['Lionel'],
],
[
'completion:timestarted',
[
'completion:timestarted_operator' => date::DATE_NOT_EMPTY,
],
'Lionel'
['Lionel'],
],
[
'completion:reaggregate',
[
'completion:reaggregate_operator' => date::DATE_NOT_EMPTY,
],
'Lionel'
['Lionel'],
],
];
}
@ -239,11 +332,11 @@ class participants_test extends core_reportbuilder_testcase {
*
* @param string $filter
* @param array $filtervalues
* @param string $expected
* @param string[] $expected
*
* @dataProvider filters_data_provider
* @dataProvider datasource_filters_provider
*/
public function test_report_filters(string $filter, array $filtervalues, string $expected): void {
public function test_datasource_filters(string $filter, array $filtervalues, array $expected): void {
global $DB;
$this->resetAfterTest();
@ -269,7 +362,7 @@ class participants_test extends core_reportbuilder_testcase {
'manual', $timestart - 8 * DAYSECS, $timeend, ENROL_USER_ACTIVE);
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student',
'manual', $timestart, $timeend, ENROL_USER_ACTIVE);
$this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student',
$this->getDataGenerator()->enrol_user($user3->id, $course->id, 'editingteacher',
'manual', time(), time(), ENROL_USER_SUSPENDED);
// Mark course as completed for the user.
@ -293,15 +386,14 @@ class participants_test extends core_reportbuilder_testcase {
$DB->set_field('user_enrolments', 'timecreated', 0, ['userid' => $user3->id]);
// Add filters to the report.
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrolment:method']);
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:plugin']);
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filter]);
// Apply filters.
$filtermanual = ['enrolment:method_operator' => select::EQUAL_TO, 'enrolment:method_value' => 'manual'];
$filtermanual = ['enrol:plugin_operator' => select::EQUAL_TO, 'enrol:plugin_value' => 'manual'];
$content = $this->get_custom_report_content($report->get('id'), 30, $filtermanual + $filtervalues);
$this->assertCount(1, $content);
$this->assertEquals($expected, $content[0]['c0_firstname']);
$this->assertEqualsCanonicalizing($expected, array_column($content, 'c0_firstname'));
}
/**

View File

@ -0,0 +1,250 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_enrol\reportbuilder\local\entities;
use enrol_plugin;
use lang_string;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{boolean_select, date, duration, select};
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\{column, filter};
/**
* Enrolment method entity
*
* @package core_enrol
* @copyright 2023 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol extends base {
/**
* Database tables that this entity uses and their default aliases
*
* @return array
*/
protected function get_default_table_aliases(): array {
return [
'enrol' => 'e',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('enrolmentmethod', 'core_enrol');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
global $DB;
$enrolalias = $this->get_table_alias('enrol');
// Plugin column.
$columns[] = (new column(
'plugin',
new lang_string('plugin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$enrolalias}.enrol")
->set_is_sortable(true)
->set_callback(static function(?string $enrol): string {
if ($enrol === null || !$plugin = enrol_get_plugin($enrol)) {
return '';
}
return $plugin->get_instance_name(null);
});
// Name column.
$columns[] = (new column(
'name',
new lang_string('name'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$enrolalias}.enrol, {$enrolalias}.name, {$enrolalias}.courseid, " .
"{$enrolalias}.roleid, {$enrolalias}.customint1")
->set_is_sortable(true)
->set_callback(static function(?string $enrol, stdClass $instance): string {
if ($enrol === null || !$plugin = enrol_get_plugin($enrol)) {
return '';
}
return $plugin->get_instance_name($instance);
});
// Enabled column.
$columns[] = (new column(
'enabled',
new lang_string('enabled', 'core_admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_BOOLEAN)
// For accurate aggregation, we need to return boolean enabled = true by xor'ing the field value.
->add_field($DB->sql_bitxor("{$enrolalias}.status", 1), 'status')
->set_is_sortable(true)
->set_callback([format::class, 'boolean_as_text']);
// Period column.
$columns[] = (new column(
'period',
new lang_string('enrolperiod', 'core_enrol'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$enrolalias}.enrolperiod")
->set_is_sortable(true)
->set_callback(static function(?int $enrolperiod): string {
if (!$enrolperiod) {
return '';
}
return format_time($enrolperiod);
});
// Start date column.
$columns[] = (new column(
'startdate',
new lang_string('enroltimestart', 'core_enrol'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$enrolalias}.enrolstartdate")
->set_is_sortable(true)
->set_callback([format::class, 'userdate']);
// End date column.
$columns[] = (new column(
'enddate',
new lang_string('enroltimeend', 'core_enrol'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$enrolalias}.enrolenddate")
->set_is_sortable(true)
->set_callback([format::class, 'userdate']);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
global $DB;
$enrolalias = $this->get_table_alias('enrol');
// Plugin filter.
$filters[] = (new filter(
select::class,
'plugin',
new lang_string('plugin'),
$this->get_entity_name(),
"{$enrolalias}.enrol"
))
->add_joins($this->get_joins())
->set_options_callback(static function(): array {
return array_map(static function(enrol_plugin $plugin): string {
return $plugin->get_instance_name(null);
}, enrol_get_plugins(true));
});
// Enabled filter.
$filters[] = (new filter(
boolean_select::class,
'enabled',
new lang_string('enabled', 'core_admin'),
$this->get_entity_name(),
$DB->sql_bitxor("{$enrolalias}.status", 1)
))
->add_joins($this->get_joins());
// Period filter.
$filters[] = (new filter(
duration::class,
'period',
new lang_string('enrolperiod', 'core_enrol'),
$this->get_entity_name(),
"{$enrolalias}.enrolperiod"
))
->add_joins($this->get_joins());
// Start date filter.
$filters[] = (new filter(
date::class,
'startdate',
new lang_string('enroltimestart', 'core_enrol'),
$this->get_entity_name(),
"{$enrolalias}.enrolstartdate"
))
->add_joins($this->get_joins());
// End date filter.
$filters[] = (new filter(
date::class,
'enddate',
new lang_string('enroltimeend', 'core_enrol'),
$this->get_entity_name(),
"{$enrolalias}.enrolenddate"
))
->add_joins($this->get_joins());
return $filters;
}
}

View File

@ -136,7 +136,8 @@ abstract class datasource extends base {
}
/**
* Return all configured report columns
* Override parent method, returning only those columns specifically added to the custom report (rather than all that are
* available)
*
* @return column[]
*/
@ -157,6 +158,10 @@ abstract class datasource extends base {
// Ensure the column is still present and available.
if ($instance !== null && $instance->get_is_available()) {
if ($instance->get_is_deprecated()) {
debugging("The column '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." .
" {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER);
}
// We should clone the report column to ensure if it's added twice to a report, each operates independently.
$this->activecolumns['values'][] = clone $instance
@ -224,7 +229,8 @@ abstract class datasource extends base {
abstract public function get_default_filters(): array;
/**
* Return all configured report filters
* Override parent method, returning only those filters specifically added to the custom report (rather than all that are
* available)
*
* @return filter[]
*/
@ -245,6 +251,11 @@ abstract class datasource extends base {
// Ensure the filter is still present and available.
if ($instance !== null && $instance->get_is_available()) {
if ($instance->get_is_deprecated()) {
debugging("The filter '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." .
" {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER);
}
$this->activefilters['values'][$instance->get_unique_identifier()] =
$instance->set_persistent($filter);
}
@ -323,7 +334,8 @@ abstract class datasource extends base {
}
/**
* Return all configured report conditions
* Override parent method, returning only those conditions specifically added to the custom report (rather than all that are
* available)
*
* @return filter[]
*/
@ -344,6 +356,11 @@ abstract class datasource extends base {
// Ensure the condition is still present and available.
if ($instance !== null && $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);
}

View File

@ -54,6 +54,9 @@ class custom_report_column_cards_exporter extends custom_report_menu_cards_expor
$menucards = [];
foreach ($report->get_columns() as $column) {
if ($column->get_is_deprecated()) {
continue;
}
// New menu card per entity.
$entityname = $column->get_entity_name();

View File

@ -107,7 +107,9 @@ class custom_report_conditions_exporter extends exporter {
// Populate available conditions.
foreach ($report->get_conditions() as $condition) {
if (in_array($condition->get_unique_identifier(), $conditionidentifiers)) {
// Conditions can only be added once per report, skip if it already exists.
if (in_array($condition->get_unique_identifier(), $conditionidentifiers) || $condition->get_is_deprecated()) {
continue;
}

View File

@ -111,7 +111,9 @@ class custom_report_filters_exporter extends exporter {
// Populate available filters.
foreach ($report->get_filters() as $filter) {
if (in_array($filter->get_unique_identifier(), $filteridentifiers)) {
// Filters can only be added once per report, skip if it already exists.
if (in_array($filter->get_unique_identifier(), $filteridentifiers) || $filter->get_is_deprecated()) {
continue;
}

View File

@ -88,6 +88,7 @@ class duration extends base {
MINSECS => get_string('filterdateminutes', 'core_reportbuilder'),
HOURSECS => get_string('filterdatehours', 'core_reportbuilder'),
DAYSECS => get_string('filterdatedays', 'core_reportbuilder'),
WEEKSECS => get_string('filterdateweeks', 'core_reportbuilder'),
];
$elements[] = $mform->createElement('select', "{$this->name}_unit", $unitlabel, $units);

View File

@ -399,7 +399,15 @@ abstract class base {
* @return column[]
*/
public function get_active_columns(): array {
return $this->get_columns();
$columns = $this->get_columns();
foreach ($columns as $column) {
if ($column->get_is_deprecated()) {
debugging("The column '{$column->get_unique_identifier()}' is deprecated, please do not use it any more." .
" {$column->get_is_deprecated_message()}", DEBUG_DEVELOPER);
}
}
return $columns;
}
/**
@ -498,7 +506,15 @@ abstract class base {
* @return filter[]
*/
public function get_active_conditions(): array {
return $this->get_conditions();
$conditions = $this->get_conditions();
foreach ($conditions as $condition) {
if ($condition->get_is_deprecated()) {
debugging("The condition '{$condition->get_unique_identifier()}' is deprecated, please do not use it any more." .
" {$condition->get_is_deprecated_message()}", DEBUG_DEVELOPER);
}
}
return $conditions;
}
/**
@ -645,7 +661,15 @@ abstract class base {
* @return filter[]
*/
public function get_active_filters(): array {
return $this->get_filters();
$filters = $this->get_filters();
foreach ($filters as $filter) {
if ($filter->get_is_deprecated()) {
debugging("The filter '{$filter->get_unique_identifier()}' is deprecated, please do not use it any more." .
" {$filter->get_is_deprecated_message()}", DEBUG_DEVELOPER);
}
}
return $filters;
}
/**

View File

@ -103,6 +103,12 @@ final class column {
/** @var bool $available Used to know if column is available to the current user or not */
protected $available = true;
/** @var bool $deprecated */
protected $deprecated = false;
/** @var string $deprecatedmessage */
protected $deprecatedmessage;
/** @var column_model $persistent */
protected $persistent;
@ -737,6 +743,37 @@ final class column {
return $this;
}
/**
* Set deprecated state of the column, in which case it will still be shown when already present in existing reports but
* won't be available for selection in the report editor
*
* @param string $deprecatedmessage
* @return self
*/
public function set_is_deprecated(string $deprecatedmessage = ''): self {
$this->deprecated = true;
$this->deprecatedmessage = $deprecatedmessage;
return $this;
}
/**
* Return deprecated state of the column
*
* @return bool
*/
public function get_is_deprecated(): bool {
return $this->deprecated;
}
/**
* Return deprecated message of the column
*
* @return string
*/
public function get_is_deprecated_message(): string {
return $this->deprecatedmessage;
}
/**
* Set column persistent
*

View File

@ -57,6 +57,12 @@ final class filter {
/** @var bool $available */
protected $available = true;
/** @var bool $deprecated */
protected $deprecated = false;
/** @var string $deprecatedmessage */
protected $deprecatedmessage;
/** @var mixed $options */
protected $options;
@ -279,6 +285,37 @@ final class filter {
return $this;
}
/**
* Set deprecated state of the filter, in which case it will still be shown when already present in existing reports but
* won't be available for selection in the report editor
*
* @param string $deprecatedmessage
* @return self
*/
public function set_is_deprecated(string $deprecatedmessage = ''): self {
$this->deprecated = true;
$this->deprecatedmessage = $deprecatedmessage;
return $this;
}
/**
* Return deprecated state of the filter
*
* @return bool
*/
public function get_is_deprecated(): bool {
return $this->deprecated;
}
/**
* Return deprecated message of the filter
*
* @return string
*/
public function get_is_deprecated_message(): string {
return $this->deprecatedmessage;
}
/**
* Set the options for the filter in the format that the filter class expected (e.g. the "select" filter expects an array)
*

View File

@ -23,7 +23,7 @@ use html_writer;
use moodle_exception;
use moodle_url;
use stdClass;
use core_reportbuilder\manager;
use core_reportbuilder\{datasource, manager};
use core_reportbuilder\local\models\report;
use core_reportbuilder\local\report\column;
use core_reportbuilder\output\column_aggregation_editable;
@ -38,6 +38,9 @@ use core_reportbuilder\output\column_heading_editable;
*/
class custom_report_table extends base_report_table {
/** @var datasource $report */
protected $report;
/** @var string Unique ID prefix for the table */
private const UNIQUEID_PREFIX = 'custom-report-table-';

View File

@ -94,7 +94,7 @@ class system_report_table extends base_report_table {
$this->is_downloading($parameters['download'] ?? null, $this->report->get_downloadfilename());
// Retrieve all report columns. If we are downloading the report, remove as required.
$columns = $this->report->get_columns();
$columns = $this->report->get_active_columns();
if ($this->is_downloading()) {
$columns = array_diff_key($columns,
array_flip($this->report->get_exclude_columns_for_download()));

View File

@ -87,6 +87,11 @@ abstract class core_reportbuilder_testcase extends advanced_testcase {
try {
$content = $this->get_custom_report_content($report->get('id'));
$this->assertNotEmpty($content);
// Ensure appropriate debugging was triggered for deprecated column.
if ($columninstance->get_is_deprecated()) {
$this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
}
} catch (Throwable $exception) {
$this->fail("Error for column '{$columnidentifier}': " . $exception->getMessage());
}
@ -109,13 +114,17 @@ abstract class core_reportbuilder_testcase extends advanced_testcase {
$instance = manager::get_report_from_persistent($report);
// Add every column.
$columnidentifiers = array_keys($instance->get_columns());
foreach ($columnidentifiers as $columnidentifier) {
$columndeprecatedcount = 0;
foreach ($instance->get_columns() as $columnidentifier => $column) {
$columndeprecatedcount += (int) $column->get_is_deprecated();
report::add_report_column($report->get('id'), $columnidentifier);
}
// Now iterate over each column, and apply all suitable aggregation types.
foreach ($instance->get_active_columns() as $column) {
$columns = $instance->get_active_columns();
$this->assertDebuggingCalledCount($columndeprecatedcount, null,
array_fill(0, $columndeprecatedcount, DEBUG_DEVELOPER));
foreach ($columns as $column) {
$aggregations = aggregation::get_column_aggregations($column->get_type(), $column->get_disabled_aggregation());
foreach (array_keys($aggregations) as $aggregation) {
$column->get_persistent()->set('aggregation', $aggregation)->update();
@ -124,6 +133,10 @@ abstract class core_reportbuilder_testcase extends advanced_testcase {
try {
$content = $this->get_custom_report_content($report->get('id'));
$this->assertNotEmpty($content);
// Ensure appropriate debugging was triggered for deprecated columns.
$this->assertDebuggingCalledCount($columndeprecatedcount, null,
array_fill(0, $columndeprecatedcount, DEBUG_DEVELOPER));
} catch (Throwable $exception) {
$this->fail("Error for column '{$column->get_unique_identifier()}' with aggregation '{$aggregation}': " .
$exception->getMessage());
@ -173,6 +186,11 @@ abstract class core_reportbuilder_testcase extends advanced_testcase {
try {
$content = $this->get_custom_report_content($report->get('id'));
$this->assertIsArray($content);
// Ensure appropriate debugging was triggered for deprecated condition.
if ($conditioninstance->get_is_deprecated()) {
$this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
}
} catch (Throwable $exception) {
$this->fail("Error for condition '{$conditionidentifier}': " . $exception->getMessage());
}

View File

@ -3,6 +3,12 @@ Information provided here is intended especially for developers.
=== 4.3 ===
* New `set_is_deprecated` method in base `local\report\[column|filter]` classes to deprecate report entity columns and filters
* The following report entity columns have been deprecated, with replacements as follows:
- `enrolment:method` => `enrol:name` (plus enrolment formatter `enrolment_name` method)
- 'enrolment:role` => `role:name`
* The following report entity filters/conditions have been deprecated, with replacements as follows:
- `enrolment:method` => `enrol:plugin`
* Trying to add/annotate duplicate entity names to a report will now throw a coding exception
* The `get_default_entity_name` method of the base entity class is now private, and shouldn't be overridden in extending classes