first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,124 @@
<?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_cohort\customfield;
use core_customfield\handler;
use core_customfield\field_controller;
/**
* Cohort handler for custom fields.
*
* @package core_cohort
* @copyright 2023 Dmitrii Metelkin <dmitriim@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohort_handler extends handler {
/**
* @var cohort_handler
*/
static protected $singleton;
/**
* Returns a singleton.
*
* @param int $itemid
* @return \core_customfield\handler
*/
public static function create(int $itemid = 0): handler {
if (static::$singleton === null) {
self::$singleton = new static(0);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new \coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*
* @return bool true if the current can configure custom fields, false otherwise
*/
public function can_configure(): bool {
return has_capability('moodle/cohort:configurecustomfields', $this->get_configuration_context());
}
/**
* The current user can edit custom fields on the given cohort.
*
* @param field_controller $field
* @param int $instanceid id of the cohort to test edit permission
* @return bool true if the current can edit custom field, false otherwise
*/
public function can_edit(field_controller $field, int $instanceid = 0): bool {
return has_capability('moodle/cohort:manage', $this->get_instance_context($instanceid));
}
/**
* The current user can view custom fields on the given cohort.
*
* @param field_controller $field
* @param int $instanceid id of the cohort to test edit permission
* @return bool true if the current can view custom field, false otherwise
*/
public function can_view(field_controller $field, int $instanceid): bool {
return has_any_capability(['moodle/cohort:manage', 'moodle/cohort:view'], $this->get_instance_context($instanceid));
}
/**
* Context that should be used for new categories created by this handler.
*
* @return \context the context for configuration
*/
public function get_configuration_context(): \context {
return \context_system::instance();
}
/**
* URL for configuration of the fields on this handler.
*
* @return \moodle_url The URL to configure custom fields for this component
*/
public function get_configuration_url(): \moodle_url {
return new \moodle_url('/cohort/customfield.php');
}
/**
* Returns the context for the data associated with the given instanceid.
*
* @param int $instanceid id of the record to get the context for
* @return \context the context for the given record
*/
public function get_instance_context(int $instanceid = 0): \context {
global $DB;
if ($instanceid > 0) {
$cohort = $DB->get_record('cohort', ['id' => $instanceid], '*', MUST_EXIST);
return \context::instance_by_id($cohort->contextid, MUST_EXIST);
} else {
return \context_system::instance();
}
}
}
+89
View File
@@ -0,0 +1,89 @@
<?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/>.
/**
* Class for exporting a cohort summary from an stdClass.
*
* @package core_cohort
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_cohort\external;
defined('MOODLE_INTERNAL') || die();
use renderer_base;
/**
* Class for exporting a cohort summary from an stdClass.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohort_summary_exporter extends \core\external\exporter {
protected static function define_related() {
// Cohorts can exist on a category context.
return array('context' => '\\context');
}
public static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT,
),
'name' => array(
'type' => PARAM_TEXT,
),
'idnumber' => array(
'type' => PARAM_RAW, // ID numbers are plain text.
'default' => '',
'null' => NULL_ALLOWED
),
'description' => array(
'type' => PARAM_TEXT,
'default' => '',
'null' => NULL_ALLOWED
),
'descriptionformat' => array(
'type' => PARAM_INT,
'default' => FORMAT_HTML,
'null' => NULL_ALLOWED
),
'visible' => array(
'type' => PARAM_BOOL,
),
'theme' => array(
'type' => PARAM_THEME,
'null' => NULL_ALLOWED
)
);
}
public static function define_other_properties() {
return array(
'contextname' => array(
// The method context::get_context_name() already formats the string, and may return HTML.
'type' => PARAM_RAW
),
);
}
protected function get_other_values(renderer_base $output) {
return array(
'contextname' => $this->related['context']->get_context_name()
);
}
}
+74
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/>.
/**
* Contains class core_cohort\output\cohortidnumber
*
* @package core_cohort
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_cohort\output;
use core_external\external_api;
use lang_string;
/**
* Class to prepare a cohort idnumber for display.
*
* @package core_cohort
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohortidnumber extends \core\output\inplace_editable {
/**
* Constructor.
*
* @param stdClass $cohort
*/
public function __construct($cohort) {
$cohortcontext = \context::instance_by_id($cohort->contextid);
$editable = has_capability('moodle/cohort:manage', $cohortcontext);
$displayvalue = s($cohort->idnumber); // All idnumbers are plain text.
parent::__construct('core_cohort', 'cohortidnumber', $cohort->id, $editable,
$displayvalue,
$cohort->idnumber,
new lang_string('editcohortidnumber', 'cohort'),
new lang_string('newidnumberfor', 'cohort', $displayvalue));
}
/**
* Updates cohort name and returns instance of this object
*
* @param int $cohortid
* @param string $newvalue
* @return static
*/
public static function update($cohortid, $newvalue) {
global $DB;
$cohort = $DB->get_record('cohort', array('id' => $cohortid), '*', MUST_EXIST);
$cohortcontext = \context::instance_by_id($cohort->contextid);
external_api::validate_context($cohortcontext);
require_capability('moodle/cohort:manage', $cohortcontext);
if ($newvalue == '' || !$DB->record_exists_select('cohort', 'idnumber = ? AND id != ?', [$newvalue, $cohort->id])) {
$record = (object) ['id' => $cohort->id, 'idnumber' => $newvalue, 'contextid' => $cohort->contextid];
cohort_update_cohort($record);
$cohort->idnumber = $newvalue;
}
return new static($cohort);
}
}
+75
View File
@@ -0,0 +1,75 @@
<?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/>.
/**
* Contains class core_cohort\output\cohortname
*
* @package core_cohort
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_cohort\output;
use core_external\external_api;
use lang_string;
/**
* Class to prepare a cohort name for display.
*
* @package core_cohort
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohortname extends \core\output\inplace_editable {
/**
* Constructor.
*
* @param stdClass $cohort
*/
public function __construct($cohort) {
$cohortcontext = \context::instance_by_id($cohort->contextid);
$editable = has_capability('moodle/cohort:manage', $cohortcontext);
$displayvalue = format_string($cohort->name, true, array('context' => $cohortcontext));
parent::__construct('core_cohort', 'cohortname', $cohort->id, $editable,
$displayvalue,
$cohort->name,
new lang_string('editcohortname', 'cohort'),
new lang_string('newnamefor', 'cohort', $displayvalue));
}
/**
* Updates cohort name and returns instance of this object
*
* @param int $cohortid
* @param string $newvalue
* @return static
*/
public static function update($cohortid, $newvalue) {
global $DB;
$cohort = $DB->get_record('cohort', array('id' => $cohortid), '*', MUST_EXIST);
$cohortcontext = \context::instance_by_id($cohort->contextid);
external_api::validate_context($cohortcontext);
require_capability('moodle/cohort:manage', $cohortcontext);
$newvalue = clean_param($newvalue, PARAM_TEXT);
if (strval($newvalue) !== '') {
$record = (object)array('id' => $cohort->id, 'name' => $newvalue, 'contextid' => $cohort->contextid);
cohort_update_cohort($record);
$cohort->name = $newvalue;
}
return new static($cohort);
}
}
+230
View File
@@ -0,0 +1,230 @@
<?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/>.
/**
* Privacy class for requesting user data.
*
* @package core_cohort
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_cohort\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use core_privacy\local\request\userlist;
use core_privacy\local\request\approved_userlist;
/**
* Privacy class for requesting user data.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\plugin\provider {
/**
* Return the fields which contain personal data.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_database_table('cohort_members', [
'cohortid' => 'privacy:metadata:cohort_members:cohortid',
'userid' => 'privacy:metadata:cohort_members:userid',
'timeadded' => 'privacy:metadata:cohort_members:timeadded'
], 'privacy:metadata:cohort_members');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$sql = "SELECT ctx.id
FROM {context} ctx
INNER JOIN {cohort} c ON c.contextid = ctx.id
INNER JOIN {cohort_members} cm ON cm.cohortid = c.id
WHERE cm.userid = :userid AND (ctx.contextlevel = :contextlevel1 OR ctx.contextlevel = :contextlevel2)";
$params = [
'userid' => $userid,
'contextlevel1' => CONTEXT_SYSTEM,
'contextlevel2' => CONTEXT_COURSECAT,
];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users within a specific context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!$context instanceof \context_system && !$context instanceof \context_coursecat) {
return;
}
$params = ['contextid' => $context->id];
$sql = "SELECT cm.userid
FROM {cohort_members} cm
JOIN {cohort} c ON cm.cohortid = c.id
WHERE c.contextid = :contextid";
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
// Remove contexts different from SYSTEM or COURSECAT.
$contexts = array_reduce($contextlist->get_contexts(), function($carry, $context) {
if ($context->contextlevel == CONTEXT_SYSTEM || $context->contextlevel == CONTEXT_COURSECAT) {
$carry[] = $context->id;
}
return $carry;
}, []);
if (empty($contexts)) {
return;
}
// Get cohort data.
$userid = $contextlist->get_user()->id;
list($contextsql, $contextparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
$sql = "SELECT c.name,
c.idnumber,
c.description,
c.visible,
cm.timeadded,
ctx.id as contextid
FROM {context} ctx
INNER JOIN {cohort} c ON c.contextid = ctx.id
INNER JOIN {cohort_members} cm ON cm.cohortid = c.id
WHERE ctx.id {$contextsql}
AND cm.userid = :userid";
$params = [
'userid' => $userid
] + $contextparams;
$cohorts = $DB->get_recordset_sql($sql, $params);
foreach ($cohorts as $cohort) {
$alldata[$cohort->contextid][] = (object)[
'name' => $cohort->name,
'idnumber' => $cohort->idnumber,
'visible' => transform::yesno($cohort->visible),
'timeadded' => transform::datetime($cohort->timeadded),
];
}
$cohorts->close();
// Export cohort data.
array_walk($alldata, function($data, $contextid) {
$context = \context::instance_by_id($contextid);
writer::with_context($context)->export_related_data([], 'cohort', $data);
});
}
/**
* Delete all use data which matches the specified context.
*
* @param context $context A user context.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
if (!$context instanceof \context_system && !$context instanceof \context_coursecat) {
return;
}
static::delete_data($context);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
$context = $userlist->get_context();
if ($context instanceof \context_system || $context instanceof \context_coursecat) {
foreach ($userlist->get_userids() as $userid) {
static::delete_data($context, $userid);
}
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
if (empty($contextlist->count())) {
return;
}
$userid = $contextlist->get_user()->id;
foreach ($contextlist->get_contexts() as $context) {
if (!$context instanceof \context_system && !$context instanceof \context_coursecat) {
continue;
}
static::delete_data($context, $userid);
}
}
/**
* Delete data related to a context and user (if defined).
*
* @param context $context A context.
* @param int $userid The user ID.
*/
protected static function delete_data(\context $context, int $userid = null) {
global $DB;
$cohortids = $DB->get_fieldset_select('cohort', 'id', 'contextid = :contextid', ['contextid' => $context->id]);
foreach ($cohortids as $cohortid) {
$params = ['cohortid' => $cohortid];
if (!empty($userid)) {
$params['userid'] = $userid;
}
$DB->delete_records('cohort_members', $params);
}
}
}
@@ -0,0 +1,174 @@
<?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\audience;
use context;
use context_system;
use core_course_category;
use stdClass;
use core_reportbuilder\local\audiences\base;
use core_reportbuilder\local\helpers\database;
use MoodleQuickForm;
/**
* The backend class for Cohort member audience type
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohortmember extends base {
/**
* Adds audience's elements to the given mform
*
* @param MoodleQuickForm $mform The form to add elements to
*/
public function get_config_form(MoodleQuickForm $mform): void {
$cohorts = self::get_cohorts();
$mform->addElement('autocomplete', 'cohorts', get_string('selectfromcohort', 'cohort'),
$cohorts, ['multiple' => true]);
$mform->addRule('cohorts', null, 'required', null, 'client');
}
/**
* Helps to build SQL to retrieve users that matches the current report audience
*
* @param string $usertablealias
* @return array array of three elements [$join, $where, $params]
*/
public function get_sql(string $usertablealias): array {
global $DB;
$cohorts = $this->get_configdata()['cohorts'];
[$insql, $inparams] = $DB->get_in_or_equal($cohorts, SQL_PARAMS_NAMED, database::generate_param_name('_'));
$cm = database::generate_alias();
$join = "JOIN {cohort_members} {$cm}
ON ({$cm}.userid = {$usertablealias}.id)";
return [$join, "{$cm}.cohortid " . $insql, $inparams];
}
/**
* Return user friendly name of this audience type
*
* @return string
*/
public function get_name(): string {
return get_string('memberofcohort', 'cohort');
}
/**
* Return the description for the audience.
*
* @return string
*/
public function get_description(): string {
global $DB;
$cohortlist = [];
$cohortids = $this->get_configdata()['cohorts'];
$cohorts = $DB->get_records_list('cohort', 'id', $cohortids, 'name');
foreach ($cohorts as $cohort) {
$cohortlist[] = format_string($cohort->name, true, ['context' => $cohort->contextid, 'escape' => false]);
}
return $this->format_description_for_multiselect($cohortlist);
}
/**
* If the current user is able to add this audience.
*
* @return bool
*/
public function user_can_add(): bool {
// Check system context first.
if (has_capability('moodle/cohort:view', context_system::instance())) {
return true;
}
// If there is at least one category with given permissions, user can add.
return !empty(core_course_category::make_categories_list('moodle/cohort:view'));
}
/**
* Returns if this audience type is available for the user
*
* Check if there are available cohorts in the system for this user to use.
*
* @return bool
*/
public function is_available(): bool {
return !empty(self::get_cohorts());
}
/**
* If the current user is able to edit this audience.
*
* @return bool
*/
public function user_can_edit(): bool {
global $DB;
$canedit = true;
$cohortids = $this->get_configdata()['cohorts'];
$cohorts = $DB->get_records_list('cohort', 'id', $cohortids);
foreach ($cohorts as $cohort) {
$context = context::instance_by_id($cohort->contextid, MUST_EXIST);
$canedit = $canedit && has_capability('moodle/cohort:view', $context);
if ($canedit === false) {
break;
}
}
return $canedit;
}
/**
* Cohorts selector.
*
* @return array
*/
private static function get_cohorts(): array {
global $CFG;
require_once($CFG->dirroot.'/cohort/lib.php');
$cohortslist = [];
// Search cohorts user can view.
$usercohorts = cohort_get_all_cohorts(0, 0);
// The previous method doesn't check cohorts on system context.
$syscontext = context_system::instance();
$cohorts = array_filter($usercohorts['cohorts'], static function(stdClass $cohort) use ($syscontext): bool {
return ($cohort->contextid != $syscontext->id) || has_capability('moodle/cohort:view', $syscontext);
});
foreach ($cohorts as $cohort) {
$cohortslist[$cohort->id] = format_string($cohort->name, true, [
'context' => $cohort->contextid,
'escape' => false,
]);
}
return $cohortslist;
}
}
@@ -0,0 +1,133 @@
<?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\reportbuilder\local\entities\{cohort, cohort_member};
use core_reportbuilder\local\filters\boolean_select;
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');
$this->add_entity($cohortmemberentity
->add_join("LEFT JOIN {cohort_members} {$cohortmembertablealias}
ON {$cohortmembertablealias}.cohortid = {$cohorttablealias}.id")
);
// Join the user entity to the cohort member entity.
$userentity = new user();
$usertablealias = $userentity->get_table_alias('user');
$this->add_entity($userentity
->add_joins($cohortmemberentity->get_joins())
->add_join("LEFT JOIN {user} {$usertablealias}
ON {$usertablealias}.id = {$cohortmembertablealias}.userid")
);
// Add all columns/filters/conditions from entities to be available in custom reports.
$this->add_all_from_entities();
}
/**
* 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:name',
'cohort:context',
'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 [
'cohort:visible',
];
}
/**
* Return the condition values that will be set for the report upon creation
*
* @return array
*/
public function get_default_condition_values(): array {
return [
'cohort:visible_operator' => boolean_select::CHECKED,
];
}
/**
* Return the default sorting that will be added to the report once it is created
*
* @return array|int[]
*/
public function get_default_column_sorting(): array {
return [
'cohort:name' => SORT_ASC,
];
}
}
@@ -0,0 +1,396 @@
<?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\local\entities;
use context;
use context_helper;
use lang_string;
use stdClass;
use theme_config;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\boolean_select;
use core_reportbuilder\local\filters\cohort as cohort_filter;
use core_reportbuilder\local\filters\date;
use core_reportbuilder\local\filters\select;
use core_reportbuilder\local\filters\text;
use core_reportbuilder\local\helpers\custom_fields;
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
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'cohort',
'context',
];
}
/**
* 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 {
$tablealias = $this->get_table_alias('cohort');
$customfields = (new custom_fields(
"{$tablealias}.id",
$this->get_entity_name(),
'core_cohort',
'cohort',
))
->add_joins($this->get_joins());
$columns = array_merge($this->get_all_columns(), $customfields->get_columns());
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = array_merge($this->get_all_filters(), $customfields->get_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;
$tablealias = $this->get_table_alias('cohort');
$contextalias = $this->get_table_alias('context');
// Category/context column.
$columns[] = (new column(
'context',
new lang_string('category'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->add_join($this->get_context_join())
->set_type(column::TYPE_TEXT)
->add_fields("{$tablealias}.contextid, " . context_helper::get_preload_record_columns_sql($contextalias))
->set_is_sortable(true)
->add_callback(static function($contextid, stdClass $cohort): string {
if ($contextid === null) {
return '';
}
context_helper::preload_from_record($cohort);
return context::instance_by_id($cohort->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.
$descriptionfieldsql = "{$tablealias}.description";
if ($DB->get_dbfamily() === 'oracle') {
$descriptionfieldsql = $DB->sql_order_by_text($descriptionfieldsql, 1024);
}
$columns[] = (new column(
'description',
new lang_string('description'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->add_join($this->get_context_join())
->set_type(column::TYPE_LONGTEXT)
->add_field($descriptionfieldsql, 'description')
->add_fields("{$tablealias}.descriptionformat, {$tablealias}.id, {$tablealias}.contextid")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->add_callback(static function(?string $description, stdClass $cohort): string {
global $CFG;
require_once("{$CFG->libdir}/filelib.php");
if ($description === null) {
return '';
}
context_helper::preload_from_record($cohort);
$context = context::instance_by_id($cohort->contextid);
$description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $context->id, 'cohort',
'description', $cohort->id);
return format_text($description, $cohort->descriptionformat, ['context' => $context->id]);
});
// 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 {
if ($component === null) {
return '';
}
return $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)
->add_callback(static function (?string $theme): string {
if ((string) $theme === '') {
return '';
}
return get_string('pluginname', "theme_{$theme}");
});
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
global $DB;
$tablealias = $this->get_table_alias('cohort');
// Cohort select filter.
$filters[] = (new filter(
cohort_filter::class,
'cohortselect',
new lang_string('selectcohort', 'core_cohort'),
$this->get_entity_name(),
"{$tablealias}.id"
))
->add_joins($this->get_joins());
// 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());
// Description filter.
$filters[] = (new filter(
text::class,
'description',
new lang_string('description'),
$this->get_entity_name(),
$DB->sql_cast_to_char("{$tablealias}.description")
))
->add_joins($this->get_joins());
// Theme filter.
$filters[] = (new filter(
select::class,
'theme',
new lang_string('theme'),
$this->get_entity_name(),
"{$tablealias}.theme",
))
->set_options_callback(static function(): array {
return array_map(
fn(theme_config $theme) => $theme->get_theme_name(),
get_list_of_themes(),
);
})
->add_joins($this->get_joins());
// Visible filter.
$filters[] = (new filter(
boolean_select::class,
'visible',
new lang_string('visible', 'core_cohort'),
$this->get_entity_name(),
"{$tablealias}.visible"
))
->add_joins($this->get_joins());
return $filters;
}
/**
* Return context join used by columns
*
* @return string
*/
private function get_context_join(): string {
// If the context table is already joined, we don't need to do that again.
if ($this->has_table_join_alias('context')) {
return '';
}
$tablealias = $this->get_table_alias('cohort');
$contextalias = $this->get_table_alias('context');
return "LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$tablealias}.contextid";
}
}
@@ -0,0 +1,122 @@
<?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\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
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'cohort_members',
];
}
/**
* 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;
}
}
@@ -0,0 +1,270 @@
<?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_cohort\reportbuilder\local\systemreports;
use context;
use context_coursecat;
use context_system;
use core_cohort\reportbuilder\local\entities\cohort;
use core_reportbuilder\local\helpers\database;
use core_reportbuilder\local\report\action;
use core_reportbuilder\local\report\column;
use html_writer;
use lang_string;
use moodle_url;
use pix_icon;
use core_reportbuilder\system_report;
use stdClass;
/**
* Cohorts system report class implementation
*
* @package core_cohort
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohorts 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.
$cohortentity = new cohort();
$entitymainalias = $cohortentity->get_table_alias('cohort');
$this->set_main_table('cohort', $entitymainalias);
$this->add_entity($cohortentity);
// Any columns required by actions should be defined here to ensure they're always available.
$this->add_base_fields("{$entitymainalias}.id, {$entitymainalias}.contextid, {$entitymainalias}.visible, " .
"{$entitymainalias}.component");
// Check if report needs to show a specific category.
$contextid = $this->get_parameter('contextid', 0, PARAM_INT);
$showall = $this->get_parameter('showall', true, PARAM_BOOL);
if (!$showall) {
$paramcontextid = database::generate_param_name();
$this->add_base_condition_sql("{$entitymainalias}.contextid = :$paramcontextid", [$paramcontextid => $contextid]);
}
// Now we can call our helper methods to add the content we want to include in the report.
$this->add_columns($cohortentity);
$this->add_filters();
$this->add_actions();
// Set if report can be downloaded.
$this->set_downloadable(false);
}
/**
* Validates access to view this report
*
* @return bool
*/
protected function can_view(): bool {
$contextid = $this->get_parameter('contextid', 0, PARAM_INT);
if ($contextid) {
$context = context::instance_by_id($contextid, MUST_EXIST);
} else {
$context = context_system::instance();
}
return has_any_capability(['moodle/cohort:manage', 'moodle/cohort:view'], $context);
}
/**
* Adds the columns we want to display in the report
*
* They are provided by the entities we previously added in the {@see initialise} method, referencing each by their
* unique identifier. If custom columns are needed just for this report, they can be defined here.
*
* @param cohort $cohortentity
*/
public function add_columns(cohort $cohortentity): void {
$entitymainalias = $cohortentity->get_table_alias('cohort');
$showall = $this->get_parameter('showall', false, PARAM_BOOL);
// Category column. An extra callback is appended in order to extend the current column formatting.
if ($showall) {
$this->add_column_from_entity('cohort:context')
->add_callback(static function(string $value, stdClass $cohort): string {
$context = context::instance_by_id($cohort->contextid);
if ($context instanceof context_coursecat) {
return html_writer::link(new moodle_url('/cohort/index.php',
['contextid' => $cohort->contextid]), $value);
}
return $value;
});
}
// Name column using the inplace editable component.
$this->add_column(new column(
'editablename',
new lang_string('name', 'core_cohort'),
$cohortentity->get_entity_name()
))
->set_type(column::TYPE_TEXT)
->set_is_sortable(true)
->add_fields("{$entitymainalias}.name, {$entitymainalias}.id, {$entitymainalias}.contextid")
->add_callback(static function(string $name, stdClass $cohort): string {
global $OUTPUT, $PAGE;
$renderer = $PAGE->get_renderer('core');
$template = new \core_cohort\output\cohortname($cohort);
return $renderer->render_from_template('core/inplace_editable', $template->export_for_template($OUTPUT));
});
// ID Number column using the inplace editable component.
$this->add_column(new column(
'editableidnumber',
new lang_string('idnumber', 'core_cohort'),
$cohortentity->get_entity_name()
))
->set_type(column::TYPE_TEXT)
->set_is_sortable(true)
->add_fields("{$entitymainalias}.idnumber, {$entitymainalias}.id, {$entitymainalias}.contextid")
->add_callback(static function(?string $idnumber, stdClass $cohort): string {
global $OUTPUT, $PAGE;
$renderer = $PAGE->get_renderer('core');
$template = new \core_cohort\output\cohortidnumber($cohort);
return $renderer->render_from_template('core/inplace_editable', $template->export_for_template($OUTPUT));
});
// Description column.
$this->add_column_from_entity('cohort:description');
// Cohort size column using a custom SQL query to count cohort members.
$cm = database::generate_param_name();
$sql = "(SELECT count($cm.id) as memberscount
FROM {cohort_members} $cm
WHERE $cm.cohortid = {$entitymainalias}.id)";
$this->add_column(new column(
'memberscount',
new lang_string('memberscount', 'cohort'),
$cohortentity->get_entity_name()
))
->set_type(column::TYPE_INTEGER)
->set_is_sortable(true)
->add_field($sql, 'memberscount');
// Component column. Override the display name of a column.
$this->add_column_from_entity('cohort:component')
->set_title(new lang_string('source', 'core_plugin'));
// It's possible to set a default initial sort direction for one column.
$this->set_initial_sort_column('cohort:editablename', SORT_ASC);
}
/**
* 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 = [
'cohort:name',
'cohort:idnumber',
'cohort:description',
];
$this->add_filters_from_entities($filters);
}
/**
* Add the system report actions. An extra column will be appended to each row, containing all actions added here
*
* Note the use of ":id" placeholder which will be substituted according to actual values in the row
*/
protected function add_actions(): void {
$contextid = $this->get_parameter('contextid', 0, PARAM_INT);
$showall = $this->get_parameter('showall', true, PARAM_BOOL);
$returnurl = (new moodle_url('/cohort/index.php',
['id' => ':id', 'contextid' => $contextid, 'showall' => $showall]))->out(false);
// Hide action. It will be only shown if the property 'visible' is true and user has 'moodle/cohort:manage' capabillity.
$this->add_action((new action(
new moodle_url('/cohort/edit.php', ['id' => ':id', 'sesskey' => sesskey(), 'hide' => 1, 'returnurl' => $returnurl]),
new pix_icon('t/show', '', 'core'),
[],
false,
new lang_string('hide')
))->add_callback(function(stdClass $row): bool {
return empty($row->component) && $row->visible
&& has_capability('moodle/cohort:manage', context::instance_by_id($row->contextid));
}));
// Show action. It will be only shown if the property 'visible' is false and user has 'moodle/cohort:manage' capabillity.
$this->add_action((new action(
new moodle_url('/cohort/edit.php', ['id' => ':id', 'sesskey' => sesskey(), 'show' => 1, 'returnurl' => $returnurl]),
new pix_icon('t/hide', '', 'core'),
[],
false,
new lang_string('show')
))->add_callback(function(stdClass $row): bool {
return empty($row->component) && !$row->visible
&& has_capability('moodle/cohort:manage', context::instance_by_id($row->contextid));
}));
// Edit action. It will be only shown if user has 'moodle/cohort:manage' capabillity.
$this->add_action((new action(
new moodle_url('/cohort/edit.php', ['id' => ':id', 'returnurl' => $returnurl]),
new pix_icon('t/edit', '', 'core'),
[],
false,
new lang_string('edit')
))->add_callback(function(stdClass $row): bool {
return empty($row->component) && has_capability('moodle/cohort:manage', context::instance_by_id($row->contextid));
}));
// Delete action. It will be only shown if user has 'moodle/cohort:manage' capabillity.
$this->add_action((new action(
new moodle_url('/cohort/edit.php', ['id' => ':id', 'delete' => 1, 'returnurl' => $returnurl]),
new pix_icon('t/delete', '', 'core'),
['class' => 'text-danger'],
false,
new lang_string('delete')
))->add_callback(function(stdClass $row): bool {
return empty($row->component) && has_capability('moodle/cohort:manage', context::instance_by_id($row->contextid));
}));
// Assign members to cohort action. It will be only shown if user has 'moodle/cohort:assign' capabillity.
$this->add_action((new action(
new moodle_url('/cohort/assign.php', ['id' => ':id', 'returnurl' => $returnurl]),
new pix_icon('i/users', '', 'core'),
[],
false,
new lang_string('assign', 'core_cohort')
))->add_callback(function(stdClass $row): bool {
return empty($row->component) && has_capability('moodle/cohort:assign', context::instance_by_id($row->contextid));
}));
}
/**
* CSS class for the row
*
* @param stdClass $row
* @return string
*/
public function get_row_class(stdClass $row): string {
return (!$row->visible) ? 'text-muted' : '';
}
}