MDL-71070 report_configlog: convert report to system report.

Convert to system report API provided by Report builder. The entity
defines the columns and filters, which are used by the system
report class we have created.
This commit is contained in:
Paul Holden
2021-08-03 12:17:11 +01:00
parent a4a3721081
commit 07611b5f12
8 changed files with 350 additions and 378 deletions
-72
View File
@@ -1,72 +0,0 @@
<?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/>.
/**
* Report search form class.
*
* @package report_configlog
* @copyright 2019 Paul Holden ([email protected])
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace report_configlog\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
/**
* Report search form class.
*
* @package report_configlog
* @copyright 2019 Paul Holden ([email protected])
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class search extends \moodleform {
/**
* Form definition
*
* @return void
*/
public function definition() {
$mform = $this->_form;
// By default just show the 'setting' field.
$mform->addElement('header', 'heading', get_string('search'));
$mform->addElement('text', 'setting', get_string('setting', 'report_configlog'));
$mform->setType('setting', PARAM_TEXT);
// Rest of the search fields.
$mform->addElement('text', 'value', get_string('value', 'report_configlog'));
$mform->setType('value', PARAM_TEXT);
$mform->addHelpButton('value', 'value', 'report_configlog');
$mform->setAdvanced('value', true);
$mform->addElement('text', 'user', get_string('user', 'report_configlog'));
$mform->setType('user', PARAM_TEXT);
$mform->addHelpButton('user', 'user', 'report_configlog');
$mform->setAdvanced('user', true);
$mform->addElement('date_selector', 'datefrom', get_string('datefrom', 'report_configlog'), ['optional' => true]);
$mform->setAdvanced('datefrom', true);
$mform->addElement('date_selector', 'dateto', get_string('dateto', 'report_configlog'), ['optional' => true]);
$mform->setAdvanced('dateto', true);
$this->add_action_buttons(false, get_string('search'));
}
}
@@ -0,0 +1,207 @@
<?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 report_configlog\local\entities;
use lang_string;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\column;
use core_reportbuilder\local\report\filter;
use core_reportbuilder\local\filters\date;
use core_reportbuilder\local\filters\text;
/**
* Config change entity class implementation
*
* Defines all the columns and filters that can be added to reports that use this entity.
*
* @package report_configlog
* @copyright 2020 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class config_change extends base {
/**
* Database tables that this entity uses and their default aliases
*
* @return array
*/
protected function get_default_table_aliases(): array {
return ['config_log' => 'cl'];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('entityconfigchange', 'report_configlog');
}
/**
* Initialize the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this->add_filter($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
$tablealias = $this->get_table_alias('config_log');
// Time modified column.
$columns[] = (new column(
'timemodified',
new lang_string('timemodified', 'report_configlog'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$tablealias}.timemodified")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
// Plugin column.
$columns[] = (new column(
'plugin',
new lang_string('plugin', 'report_configlog'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$tablealias}.plugin")
->set_is_sortable(true)
->add_callback(static function(?string $plugin): string {
return $plugin ?? 'core';
});
// Setting column.
$columns[] = (new column(
'setting',
new lang_string('setting', 'report_configlog'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$tablealias}.name")
->set_is_sortable(true);
// New value column.
$columns[] = (new column(
'newvalue',
new lang_string('valuenew', 'report_configlog'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$tablealias}.value")
->set_is_sortable(true)
->add_callback(static function(?string $value): string {
return format_text($value, FORMAT_PLAIN);
});
// Old value column.
$columns[] = (new column(
'oldvalue',
new lang_string('valueold', 'report_configlog'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$tablealias}.oldvalue")
->set_is_sortable(true)
->add_callback(static function(?string $oldvalue): string {
return format_text($oldvalue, FORMAT_PLAIN);
});
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
global $DB;
$tablealias = $this->get_table_alias('config_log');
// Time modified filter.
$filters[] = (new filter(
date::class,
'timemodified',
new lang_string('timemodified', 'report_configlog'),
$this->get_entity_name(),
"{$tablealias}.timemodified"
))
->add_joins($this->get_joins())
->set_limited_operators([
date::DATE_ANY,
date::DATE_RANGE,
]);
// Setting filter.
$filters[] = (new filter(
text::class,
'setting',
new lang_string('setting', 'report_configlog'),
$this->get_entity_name(),
"{$tablealias}.name"
))
->add_joins($this->get_joins());
// New value filter.
$filters[] = (new filter(
text::class,
'value',
new lang_string('valuenew', 'report_configlog'),
$this->get_entity_name(),
"{$tablealias}.value"
))
->add_joins($this->get_joins());
// Old value filter.
$filters[] = (new filter(
text::class,
'oldvalue',
new lang_string('valueold', 'report_configlog'),
$this->get_entity_name(),
"{$tablealias}.oldvalue"
))
->add_joins($this->get_joins());
return $filters;
}
}
@@ -0,0 +1,115 @@
<?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 report_configlog\local\systemreports;
use context_system;
use report_configlog\local\entities\config_change as config_change_entity;
use core_reportbuilder\system_report;
use core_reportbuilder\local\entities\user as user_entity;
use stdClass;
/**
* Config changes system report class implementation
*
* @package report_configlog
* @copyright 2020 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class config_changes extends system_report {
/**
* Initialise report, we need to set the main table, load our entities and set columns/filters
*/
protected function initialise(): void {
// Our main entity, it contains all of the column definitions that we need.
$entitymain = new config_change_entity();
$entitymainalias = $entitymain->get_table_alias('config_log');
$this->set_main_table('config_log', $entitymainalias);
$this->add_entity($entitymain);
// We can join the "user" entity to our "main" entity using standard SQL JOIN.
$entityuser = new user_entity();
$entityuseralias = $entityuser->get_table_alias('user');
$this->add_entity($entityuser
->add_join("LEFT JOIN {user} {$entityuseralias} ON {$entityuseralias}.id = {$entitymainalias}.userid")
);
// Now we can call our helper methods to add the content we want to include in the report.
$this->add_columns();
$this->add_filters();
// Set if report can be downloaded.
$this->set_downloadable(true, get_string('pluginname', 'report_configlog'));
}
/**
* Validates access to view this report
*
* @return bool
*/
protected function can_view(): bool {
return has_capability('moodle/site:config', context_system::instance());
}
/**
* Adds the columns we want to display in the report
*
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
* unique identifier
*/
protected function add_columns(): void {
$columns = [
'config_change:timemodified',
'user:fullnamewithlink',
'config_change:plugin',
'config_change:setting',
'config_change:newvalue',
'config_change:oldvalue',
];
$this->add_columns_from_entities($columns);
// Default sorting.
$this->set_initial_sort_column('config_change:timemodified', SORT_DESC);
// Custom callback to show 'CLI or install' in fullname column when there is no user.
if ($column = $this->get_column('user:fullnamewithlink')) {
$column->add_callback(static function(string $fullname, stdClass $row): string {
return $fullname ?: get_string('usernone', 'report_configlog');
});
}
}
/**
* Adds the filters we want to display in the report
*
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
* unique identifier
*/
protected function add_filters(): void {
$filters = [
'config_change:setting',
'config_change:value',
'config_change:oldvalue',
'user:fullname',
'config_change:timemodified',
];
$this->add_filters_from_entities($filters);
}
}
@@ -1,57 +0,0 @@
<?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/>.
/**
* Plugin renderer class.
*
* @package report_configlog
* @copyright 2019 Paul Holden ([email protected])
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace report_configlog\output;
defined('MOODLE_INTERNAL') || die();
/**
* Plugin renderer class.
*
* @package report_configlog
* @copyright 2019 Paul Holden ([email protected])
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends \plugin_renderer_base {
/** @var int Page size for displaying report table. */
const REPORT_TABLE_PAGESIZE = 30;
/**
* Return output to be rendered to page
*
* @param report_table $table
* @return string HTML rendered table
*/
protected function render_report_table(report_table $table) {
ob_start();
$table->out(self::REPORT_TABLE_PAGESIZE, false);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
}
@@ -1,194 +0,0 @@
<?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/>.
/**
* Report table class.
*
* @package report_configlog
* @copyright 2019 Paul Holden ([email protected])
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace report_configlog\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/searchlib.php');
require_once($CFG->libdir . '/tablelib.php');
/**
* Report table class.
*
* @package report_configlog
* @copyright 2019 Paul Holden ([email protected])
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class report_table extends \table_sql implements \renderable {
/** @var string $search */
protected $search;
/**
* Constructor
*
* @param string $search
*/
public function __construct(string $search) {
parent::__construct('report-configlog-report-table');
$this->search = trim($search);
// Define columns.
$columns = [
'timemodified' => get_string('timemodified', 'report_configlog'),
'fullname' => get_string('name'),
'plugin' => get_string('plugin', 'report_configlog'),
'name' => get_string('setting', 'report_configlog'),
'value' => get_string('valuenew', 'report_configlog'),
'oldvalue' => get_string('valueold', 'report_configlog'),
];
$this->define_columns(array_keys($columns));
$this->define_headers(array_values($columns));
// Table configuration.
$this->set_attribute('id', $this->uniqueid);
$this->set_attribute('cellspacing', '0');
$this->sortable(true, 'timemodified', SORT_DESC);
$this->initialbars(false);
$this->collapsible(false);
$this->useridfield = 'userid';
// Initialize table SQL properties.
$this->init_sql();
}
/**
* Initializes table SQL properties
*
* @return void
*/
protected function init_sql() {
global $DB;
$userfieldsapi = \core_user\fields::for_name();
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
$fields = 'cl.id, cl.timemodified, cl.plugin, cl.name, cl.value, cl.oldvalue, cl.userid, ' . $userfields;
$from = '{config_log} cl
LEFT JOIN {user} u ON u.id = cl.userid';
// Report search.
$where = '1=1';
$params = [];
if (!empty($this->search)) {
// Clean quotes, allow search by 'setting:' prefix.
$searchstring = str_replace(["\\\"", 'setting:'], ["\"", 'subject:'], $this->search);
$parser = new \search_parser();
$lexer = new \search_lexer($parser);
if ($lexer->parse($searchstring)) {
$parsearray = $parser->get_parsed_array();
// Data fields should contain both value/oldvalue.
$datafields = $DB->sql_concat_join("':'", ['cl.value', 'cl.oldvalue']);
list($where, $params) = search_generate_SQL($parsearray, $datafields, 'cl.name', 'cl.userid', 'u.id',
'u.firstname', 'u.lastname', 'cl.timemodified', 'cl.id');
}
}
$this->set_sql($fields, $from, $where, $params);
$this->set_count_sql('SELECT COUNT(1) FROM ' . $from . ' WHERE ' . $where, $params);
}
/**
* Cross DB text-compatible sorting for value/oldvalue fields
*
* @return string
*/
public function get_sql_sort() {
global $DB;
$sort = preg_replace_callback('/\b(value|oldvalue)\b/', function(array $matches) use ($DB) {
return $DB->sql_order_by_text($matches[1], 255);
}, parent::get_sql_sort());
return $sort;
}
/**
* Format report timemodified field
*
* @param stdClass $row
* @return string
*/
public function col_timemodified(\stdClass $row) {
return userdate($row->timemodified);
}
/**
* Format fullname field
*
* @param stdClass $row
* @return string
*/
public function col_fullname($row) {
$userid = $row->{$this->useridfield};
if (empty($userid)) {
// If the user id is empty it must have been set via the
// admin/cli/cfg.php script or during the initial install.
return get_string('usernone', 'report_configlog');
} else {
return parent::col_fullname($row);
}
}
/**
* Format report plugin field
*
* @param stdClass $row
* @return string
*/
public function col_plugin(\stdClass $row) {
return $row->plugin ?? 'core';
}
/**
* Format report value field
*
* @param stdClass $row
* @return string
*/
public function col_value(\stdClass $row) {
return $this->format_text($row->value, FORMAT_PLAIN);
}
/**
* Format report old value field
*
* @param stdClass $row
* @return string
*/
public function col_oldvalue(\stdClass $row) {
return $this->format_text($row->oldvalue, FORMAT_PLAIN);;
}
}
+12 -43
View File
@@ -23,6 +23,10 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core_reportbuilder\system_report_factory;
use core_reportbuilder\local\filters\text;
use report_configlog\local\systemreports\config_changes;
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/adminlib.php');
@@ -34,50 +38,15 @@ admin_externalpage_setup('reportconfiglog', '', ['search' => $search], '', ['pag
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('configlog', 'report_configlog'));
/** @var cache_session $cache */
$cache = cache::make_from_params(cache_store::MODE_SESSION, 'report_customlog', 'search');
// Create out report instance, setting initial filtering if required.
$report = system_report_factory::create(config_changes::class, context_system::instance());
if (!empty($search)) {
$searchdata = (object) ['setting' => $search];
} else {
$searchdata = $cache->get('data');
$report->set_filter_values([
'config_change:setting_operator' => text::IS_EQUAL_TO,
'config_change:setting_value' => $search,
]);
}
$mform = new \report_configlog\form\search();
$mform->set_data($searchdata);
echo $report->output();
$searchclauses = [];
// Check if we have a form submission, or a cached submission.
$data = ($mform->is_submitted() ? $mform->get_data() : fullclone($searchdata));
if ($data instanceof stdClass) {
if (!empty($data->value)) {
$searchclauses[] = $data->value;
}
if (!empty($data->setting)) {
$searchclauses[] = "setting:{$data->setting}";
}
if (!empty($data->user)) {
$searchclauses[] = "user:{$data->user}";
}
if (!empty($data->datefrom)) {
$searchclauses[] = "datefrom:{$data->datefrom}";
}
if (!empty($data->dateto)) {
$dateto = $data->dateto + DAYSECS - 1;
$searchclauses[] = "dateto:{$dateto}";
}
// Cache form submission so that it is preserved while paging through the report.
unset($data->submitbutton);
$cache->set('data', $data);
}
$mform->display();
$table = new \report_configlog\output\report_table(implode(' ', $searchclauses));
$table->define_baseurl($PAGE->url);
echo $PAGE->get_renderer('report_configlog')->render($table);
echo $OUTPUT->footer();
echo $OUTPUT->footer();
@@ -26,6 +26,7 @@
$string['configlog'] = 'Config changes';
$string['datefrom'] = 'Date from';
$string['dateto'] = 'Date to';
$string['entityconfigchange'] = 'Config change';
$string['plugin'] = 'Plugin';
$string['pluginname'] = 'Config changes';
$string['setting'] = 'Setting';
@@ -1,4 +1,4 @@
@report @report_configlog
@report @report_configlog @core_reportbuilder
Feature: In a report, admin can see configuration changes
In order see configuration changes
As an admin
@@ -16,7 +16,7 @@ Feature: In a report, admin can see configuration changes
@javascript
Scenario: Display configuration changes report
When I navigate to "Reports > Config changes" in site administration
Then the following should exist in the "report-configlog-report-table" table:
Then the following should exist in the "reportbuilder-table" table:
| User | Plugin | Setting | New value | Original value |
| Admin User | quiz | initialnumfeedbacks | 5 | 2 |
| Admin User | folder | maxsizetodownload | 2048 | 0 |
@@ -25,16 +25,19 @@ Feature: In a report, admin can see configuration changes
@javascript
Scenario Outline: Search configuration changes report
When I navigate to "Reports > Config changes" in site administration
And I click on "Show more..." "link"
And I set the field "<field>" to "<search>"
And I click on "Search" "button" in the "#fitem_id_submitbutton" "css_element"
Then the following should exist in the "report-configlog-report-table" table:
And I click on "Filters" "button"
And I set the following fields in the "<field>" "core_reportbuilder > Filter" to these values:
| <field> operator | Contains |
| <field> value | <search> |
And I click on "Apply" "button" in the "[data-region='report-filters']" "css_element"
And I should see "Filters applied"
Then the following should exist in the "reportbuilder-table" table:
| Plugin | Setting | New value |
| <plugin> | <setting> | <value> |
And I should not see "<excluded>" in the "report-configlog-report-table" "table"
And I should not see "<excluded>" in the "reportbuilder-table" "table"
Examples:
| field | search | plugin | setting | value | excluded |
| Setting | initialnumfeedbacks | quiz | initialnumfeedbacks | 5 | maxsizetodownload |
| Setting | maxsizetodownload | folder | maxsizetodownload | 2048 | initialnumfeedbacks |
| Value | Perth | core | defaultcity | Perth | maxsizetodownload |
| User | Admin | core | defaultcity | Perth | zzzzzzzzz |
| field | search | plugin | setting | value | excluded |
| Setting | initialnumfeedbacks | quiz | initialnumfeedbacks | 5 | maxsizetodownload |
| Setting | maxsizetodownload | folder | maxsizetodownload | 2048 | initialnumfeedbacks |
| New value | Perth | core | defaultcity | Perth | maxsizetodownload |
| Full name | Admin User | core | defaultcity | Perth | zzzzzzzzz |