Merge branch 'MDL-72172-master' of git://github.com/dravek/moodle

This commit is contained in:
Ilya Tregubov 2021-11-01 07:26:34 +01:00
commit 2b45c78426
6 changed files with 695 additions and 0 deletions

View File

@ -0,0 +1,280 @@
<?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/>.
declare(strict_types=1);
namespace core_cohort\local\entities;
use context;
use context_helper;
use lang_string;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\date;
use core_reportbuilder\local\filters\select;
use core_reportbuilder\local\filters\text;
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\column;
use core_reportbuilder\local\report\filter;
/**
* Cohort entity
*
* @package core_cohort
* @copyright 2021 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohort extends base {
/**
* Database tables that this entity uses and their default aliases
*
* @return array
*/
protected function get_default_table_aliases(): array {
return ['cohort' => 'c'];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('cohort', 'core_cohort');
}
/**
* 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 {
$tablealias = $this->get_table_alias('cohort');
// Category/context column.
$columns[] = (new column(
'context',
new lang_string('category'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_fields("{$tablealias}.contextid")
->set_is_sortable(true)
->add_callback(static function(int $contextid): string {
return context::instance_by_id($contextid)->get_context_name(false);
});
// Name column.
$columns[] = (new column(
'name',
new lang_string('name', 'core_cohort'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$tablealias}.name")
->set_is_sortable(true);
// ID number column.
$columns[] = (new column(
'idnumber',
new lang_string('idnumber', 'core_cohort'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$tablealias}.idnumber")
->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_TEXT)
->add_fields("{$tablealias}.description, {$tablealias}.descriptionformat, {$tablealias}.id, {$tablealias}.contextid")
->add_callback(static function(string $description, stdClass $cohort): string {
global $CFG;
require_once("{$CFG->libdir}/filelib.php");
$description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $cohort->contextid, 'cohort',
'description', $cohort->id);
return format_text($description, $cohort->descriptionformat, ['context' => $cohort->contextid]);
})
->set_is_sortable(false);
// Visible column.
$columns[] = (new column(
'visible',
new lang_string('visible', 'core_cohort'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_BOOLEAN)
->add_fields("{$tablealias}.visible")
->set_is_sortable(true)
->set_callback([format::class, 'boolean_as_text']);
// Time created column.
$columns[] = (new column(
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$tablealias}.timecreated")
->set_is_sortable(true)
->set_callback([format::class, 'userdate']);
// Time modified column.
$columns[] = (new column(
'timemodified',
new lang_string('timemodified', 'core_reportbuilder'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$tablealias}.timemodified")
->set_is_sortable(true)
->set_callback([format::class, 'userdate']);
// Component column.
$columns[] = (new column(
'component',
new lang_string('component', 'core_cohort'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$tablealias}.component")
->set_is_sortable(true)
->add_callback(static function(string $component): string {
return empty($component)
? get_string('nocomponent', 'cohort')
: get_string('pluginname', $component);
});
// Theme column.
$columns[] = (new column(
'theme',
new lang_string('theme'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$tablealias}.theme")
->set_is_sortable(true);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$tablealias = $this->get_table_alias('cohort');
// Context filter.
$filters[] = (new filter(
select::class,
'context',
new lang_string('category'),
$this->get_entity_name(),
"{$tablealias}.contextid"
))
->add_joins($this->get_joins())
->set_options_callback(static function(): array {
global $DB;
// Load all contexts in which there are cohorts.
$ctxfields = context_helper::get_preload_record_columns_sql('ctx');
$contexts = $DB->get_records_sql("
SELECT DISTINCT {$ctxfields}, c.contextid
FROM {context} ctx
JOIN {cohort} c ON c.contextid = ctx.id");
// Transform context record into it's name (used as the filter options).
return array_map(static function(stdClass $contextrecord): string {
context_helper::preload_from_record($contextrecord);
return context::instance_by_id($contextrecord->contextid)
->get_context_name(false);
}, $contexts);
});
// Name filter.
$filters[] = (new filter(
text::class,
'name',
new lang_string('name', 'core_cohort'),
$this->get_entity_name(),
"{$tablealias}.name"
))
->add_joins($this->get_joins());
// ID number filter.
$filters[] = (new filter(
text::class,
'idnumber',
new lang_string('idnumber', 'core_cohort'),
$this->get_entity_name(),
"{$tablealias}.idnumber"
))
->add_joins($this->get_joins());
// Time created filter.
$filters[] = (new filter(
date::class,
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
"{$tablealias}.timecreated"
))
->add_joins($this->get_joins());
return $filters;
}
}

View File

@ -0,0 +1,120 @@
<?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/>.
declare(strict_types=1);
namespace core_cohort\local\entities;
use lang_string;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\date;
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\column;
use core_reportbuilder\local\report\filter;
/**
* Cohort member entity
*
* @package core_cohort
* @copyright 2021 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohort_member extends base {
/**
* Database tables that this entity uses and their default aliases
*
* @return array
*/
protected function get_default_table_aliases(): array {
return ['cohort_members' => 'cm'];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('cohortmember', 'core_cohort');
}
/**
* 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 {
$tablealias = $this->get_table_alias('cohort_members');
// Time added column.
$columns[] = (new column(
'timeadded',
new lang_string('timeadded', 'core_reportbuilder'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$tablealias}.timeadded")
->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 {
$tablealias = $this->get_table_alias('cohort_members');
// Time added filter.
$filters[] = (new filter(
date::class,
'timeadded',
new lang_string('timeadded', 'core_reportbuilder'),
$this->get_entity_name(),
"{$tablealias}.timeadded"
))
->add_joins($this->get_joins());
return $filters;
}
}

View File

@ -0,0 +1,120 @@
<?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/>.
declare(strict_types=1);
namespace core_cohort\reportbuilder\datasource;
use core_cohort\local\entities\cohort;
use core_cohort\local\entities\cohort_member;
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\user;
/**
* Cohorts datasource
*
* @package core_cohort
* @copyright 2021 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohorts extends datasource {
/**
* Return user friendly name of the datasource
*
* @return string
*/
public static function get_name(): string {
return get_string('cohorts', 'core_cohort');
}
/**
* Initialise report
*/
protected function initialise(): void {
$cohortentity = new cohort();
$cohorttablealias = $cohortentity->get_table_alias('cohort');
$this->set_main_table('cohort', $cohorttablealias);
$this->add_entity($cohortentity);
// Join the cohort member entity to the cohort entity.
$cohortmemberentity = new cohort_member();
$cohortmembertablealias = $cohortmemberentity->get_table_alias('cohort_members');
$cohortmemberjoin = "LEFT JOIN {cohort_members} {$cohortmembertablealias}
ON {$cohortmembertablealias}.cohortid = {$cohorttablealias}.id";
$this->add_entity($cohortmemberentity->add_join($cohortmemberjoin));
// Join the user entity to the cohort member entity.
$userentity = new user();
$usertablealias = $userentity->get_table_alias('user');
$userjoin = "LEFT JOIN {user} {$usertablealias}
ON {$usertablealias}.id = {$cohortmembertablealias}.userid";
$this->add_entity($userentity->add_joins([$cohortmemberjoin, $userjoin]));
// Add all columns from entities to be available in custom reports.
$this->add_columns_from_entity($cohortentity->get_entity_name());
$this->add_columns_from_entity($cohortmemberentity->get_entity_name());
$this->add_columns_from_entity($userentity->get_entity_name());
// Add all filters from entities to be available in custom reports.
$this->add_filters_from_entity($cohortentity->get_entity_name());
$this->add_filters_from_entity($cohortmemberentity->get_entity_name());
$this->add_filters_from_entity($userentity->get_entity_name());
// Add all conditions from entities to be available in custom reports.
$this->add_conditions_from_entity($cohortentity->get_entity_name());
$this->add_conditions_from_entity($cohortmemberentity->get_entity_name());
$this->add_conditions_from_entity($userentity->get_entity_name());
}
/**
* Return the columns that will be added to the report as part of default setup
*
* @return string[]
*/
public function get_default_columns(): array {
return [
'cohort:context',
'cohort:name',
'cohort:idnumber',
'cohort:description',
];
}
/**
* Return the filters that will be added to the report once is created
*
* @return string[]
*/
public function get_default_filters(): array {
return ['cohort:context', 'cohort:name'];
}
/**
* Return the conditions that will be added to the report once is created
*
* @return string[]
*/
public function get_default_conditions(): array {
return [];
}
}

View File

@ -0,0 +1,100 @@
@core_reportbuilder @javascript
Feature: Manage custom reports for cohorts
In order to manage custom reports for cohorts
As an admin and user
I need to create new, view and edit existing reports
Background:
Given the following "cohorts" exist:
| name | idnumber | contextid |
| Another one | AO | 1 |
| MDL-62161 | 62161 | 1 |
| New system cohort | NSC | 1 |
| MDL-62162 | 62162 | 1 |
| Other cohort | LC | 3 |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | Alice | Last1 | user1@example.com |
| user2 | Carlos | Last2 | user2@example.com |
| user3 | Paul | Last3 | user3@example.com |
| user4 | Juan | Last4 | user4@example.com |
| user5 | Pedro | Last5 | user5@example.com |
| user6 | Luis | Last6 | user6@example.com |
| user7 | David | Last7 | user7@example.com |
| user8 | Zoe | Last8 | user8@example.com |
And the following "cohort members" exist:
| user | cohort |
| user1 | AO |
| user2 | AO |
| user3 | AO |
| user4 | AO |
| user5 | 62161 |
| user6 | 62161 |
| user7 | NSC |
| user8 | NSC |
And the following "core_reportbuilder > Reports" exist:
| name | source | default |
| My report | core_cohort\reportbuilder\datasource\cohorts | 0 |
And the following "core_reportbuilder > Columns" exist:
| report | uniqueidentifier |
| My report | cohort:context |
| My report | cohort:name |
Scenario: Add condition to cohorts report
Given I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
And I change window size to "large"
When I click on "Show/hide settings sidebar" "button"
And I click on "Show/hide 'Conditions'" "button"
Then I should see "There are no conditions selected" in the "[data-region='settings-conditions']" "css_element"
And I set the field "Select a condition" to "Category"
And I should see "Added condition 'Category'"
And I should not see "There are no conditions selected" in the "[data-region='settings-conditions']" "css_element"
And I set the following fields in the "Category" "core_reportbuilder > Condition" to these values:
| Category operator | Is equal to |
| Category value | 3 |
And I click on "Apply" "button" in the "[data-region='settings-conditions']" "css_element"
And I should see "Conditions applied"
And I should see "Other cohort" in the "reportbuilder-table" "table"
And I should not see "MDL-62162" in the "reportbuilder-table" "table"
Scenario: Use filters in cohorts report
Given I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
And I change window size to "large"
When I click on "Show/hide settings sidebar" "button"
And I click on "Show/hide 'Filters'" "button"
Then I should see "There are no filters selected" in the "[data-region='settings-filters']" "css_element"
And I set the field "Select a filter" to "Name"
And I should see "Other cohort" in the ".reportbuilder-table" "css_element"
And I should see "MDL-62162" in the ".reportbuilder-table" "css_element"
When I click on "Switch to preview mode" "button"
And I click on "Filters" "button" in the "[data-region='core_reportbuilder/report-header']" "css_element"
And I set the following fields in the "Name" "core_reportbuilder > Filter" to these values:
| Name operator | Contains |
| Name value | Another |
And I click on "Apply" "button" in the "[data-region='core_reportbuilder/report-header']" "css_element"
Then the following should exist in the "reportbuilder-table" table:
| Category | Name |
| System | Another one |
And the following should not exist in the "reportbuilder-table" table:
| Category | Name |
| Miscellaneous | Other cohort |
Scenario: Use sorting and aggregations in cohorts report
Given the following "core_reportbuilder > Columns" exist:
| report | uniqueidentifier |
| My report | user:lastname |
And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
And I set the field "Rename column 'Surname'" to "Members"
And I reload the page
And I set the field "Aggregate column 'Surname'" to "Comma separated distinct values"
And I click on "Show/hide settings sidebar" "button"
And I click on "Show/hide 'Sorting'" "button"
And I change window size to "large"
And I click on "Move sorting for column 'Surname'" "button"
And I click on "To the top of the list" "link" in the "Move sorting for column 'Surname'" "dialogue"
And I click on "Enable sorting for column 'Surname'" "checkbox"
And "Another one" "table_row" should appear before "MDL-62161" "table_row"
When I click on "Sort column 'Surname' descending" "button"
Then I should see "Updated sorting for column 'Surname'"
And I wait "1" seconds
And "MDL-62161" "table_row" should appear before "Another one" "table_row"

View File

@ -0,0 +1,74 @@
<?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/>.
declare(strict_types=1);
namespace core_cohort\reportbuilder\datasource;
use core_reportbuilder_testcase;
use core_reportbuilder_generator;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
/**
* Unit tests for cohorts datasource
*
* @package core_cohort
* @covers \core_cohort\reportbuilder\datasource\cohorts
* @copyright 2021 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class datasource_test extends core_reportbuilder_testcase {
/**
* Test cohorts datasource
*/
public function test_cohorts_datasource(): void {
$this->resetAfterTest();
// Test subject.
$cohort = $this->getDataGenerator()->create_cohort([
'name' => 'Legends',
'idnumber' => 'C101',
'description' => 'Cohort for the legends',
]);
$user = $this->getDataGenerator()->create_user(['firstname' => 'Lionel', 'lastname' => 'Richards']);
cohort_add_member($cohort->id, $user->id);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Cohorts', 'source' => cohorts::class]);
// Add user fullname column to the report.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname']);
$content = $this->get_custom_report_content($report->get('id'));
$this->assertCount(1, $content);
$contentrow = array_values(reset($content));
$this->assertEquals([
'System', // Context.
'Legends', // Name.
'C101', // ID number.
'<div class="text_to_html">Cohort for the legends</div>', // Description.
'Lionel Richards', // User.
], $contentrow);
}
}

View File

@ -33,6 +33,7 @@ $string['bulkadd'] = 'Add to cohort';
$string['bulknocohort'] = 'No available cohorts found';
$string['categorynotfound'] = 'Category <b>{$a}</b> not found or you don\'t have permission to create a cohort there. The default context will be used.';
$string['cohort'] = 'Cohort';
$string['cohortmember'] = 'Cohort member';
$string['cohorts'] = 'Cohorts';
$string['cohortsin'] = '{$a}: available cohorts';
$string['assigncohorts'] = 'Assign cohort members';