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
+189
View File
@@ -0,0 +1,189 @@
<?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_group\customfield;
use context;
use context_course;
use context_system;
use core_customfield\api;
use core_customfield\handler;
use core_customfield\field_controller;
use moodle_url;
use restore_task;
/**
* Group handler for custom fields.
*
* @package core_group
* @author Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
* @copyright 2023 Catalyst IT Pty Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_handler extends handler {
/**
* @var group_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/group:configurecustomfields', $this->get_configuration_context());
}
/**
* The current user can edit custom fields on the given group.
*
* @param field_controller $field
* @param int $instanceid id of the group 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/course:managegroups', $this->get_instance_context($instanceid));
}
/**
* The current user can view custom fields on the given group.
*
* @param field_controller $field
* @param int $instanceid id of the group 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/course:managegroups', 'moodle/course: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('/group/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 $COURSE, $DB;
if ($instanceid > 0) {
$group = $DB->get_record('groups', ['id' => $instanceid], '*', MUST_EXIST);
return context_course::instance($group->courseid);
} else if (!empty($COURSE->id)) {
return context_course::instance($COURSE->id);
} else {
return context_system::instance();
}
}
/**
* Get raw data associated with all fields current user can view or edit
*
* @param int $instanceid
* @return array
*/
public function get_instance_data_for_backup(int $instanceid): array {
$finalfields = [];
$instancedata = $this->get_instance_data($instanceid, true);
foreach ($instancedata as $data) {
if ($data->get('id') && $this->can_backup($data->get_field(), $instanceid)) {
$finalfields[] = [
'id' => $data->get('id'),
'shortname' => $data->get_field()->get('shortname'),
'type' => $data->get_field()->get('type'),
'value' => $data->get_value(),
'valueformat' => $data->get('valueformat'),
'valuetrust' => $data->get('valuetrust'),
'groupid' => $data->get('instanceid'),
];
}
}
return $finalfields;
}
/**
* Creates or updates custom field data.
*
* @param restore_task $task
* @param array $data
*
* @return int|void Conditionally returns the ID of the created or updated record.
*/
public function restore_instance_data_from_backup(restore_task $task, array $data) {
$instanceid = $data['groupid'];
$context = $this->get_instance_context($instanceid);
$editablefields = $this->get_editable_fields($instanceid);
$records = api::get_instance_fields_data($editablefields, $instanceid);
foreach ($records as $d) {
$field = $d->get_field();
if ($field->get('shortname') === $data['shortname'] && $field->get('type') === $data['type']) {
if (!$d->get('id')) {
$d->set($d->datafield(), $data['value']);
$d->set('value', $data['value']);
$d->set('valueformat', $data['valueformat']);
$d->set('valuetrust', !empty($data['valuetrust']));
$d->set('contextid', $context->id);
$d->save();
}
return $d->get('id');
}
}
}
}
@@ -0,0 +1,190 @@
<?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_group\customfield;
use context;
use context_course;
use context_system;
use core_customfield\api;
use core_customfield\handler;
use core_customfield\field_controller;
use moodle_url;
use restore_task;
/**
* Grouping handler for custom fields.
*
* @package core_group
* @author Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
* @copyright 2023 Catalyst IT Pty Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grouping_handler extends handler {
/**
* @var grouping_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/group:configurecustomfields', $this->get_configuration_context());
}
/**
* The current user can edit custom fields on the given group.
*
* @param field_controller $field
* @param int $instanceid id of the group 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/course:managegroups', $this->get_instance_context($instanceid));
}
/**
* The current user can view custom fields on the given group.
*
* @param field_controller $field
* @param int $instanceid id of the group 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/course:managegroups', 'moodle/course: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('/group/grouping_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 $COURSE, $DB;
if ($instanceid > 0) {
$grouping = $DB->get_record('groupings', ['id' => $instanceid], '*', MUST_EXIST);
return context_course::instance($grouping->courseid);
} else if (!empty($COURSE->id)) {
return context_course::instance($COURSE->id);
} else {
return context_system::instance();
}
}
/**
* Get raw data associated with all fields current user can view or edit
*
* @param int $instanceid
* @return array
*/
public function get_instance_data_for_backup(int $instanceid): array {
$finalfields = [];
$instancedata = $this->get_instance_data($instanceid, true);
foreach ($instancedata as $data) {
if ($data->get('id') && $this->can_backup($data->get_field(), $instanceid)) {
$finalfields[] = [
'id' => $data->get('id'),
'shortname' => $data->get_field()->get('shortname'),
'type' => $data->get_field()->get('type'),
'value' => $data->get_value(),
'valueformat' => $data->get('valueformat'),
'valuetrust' => $data->get('valuetrust'),
'groupingid' => $data->get('instanceid'),
];
}
}
return $finalfields;
}
/**
* Creates or updates custom field data for a instanceid from backup data.
*
* The handlers have to override it if they support backup
*
* @param restore_task $task
* @param array $data
*
* @return int|void Conditionally returns the ID of the created or updated record.
*/
public function restore_instance_data_from_backup(restore_task $task, array $data) {
$instanceid = $data['groupingid'];
$context = $this->get_instance_context($instanceid);
$editablefields = $this->get_editable_fields($instanceid);
$records = api::get_instance_fields_data($editablefields, $instanceid);
foreach ($records as $d) {
$field = $d->get_field();
if ($field->get('shortname') === $data['shortname'] && $field->get('type') === $data['type']) {
if (!$d->get('id')) {
$d->set($d->datafield(), $data['value']);
$d->set('value', $data['value']);
$d->set('valueformat', $data['valueformat']);
$d->set('valuetrust', !empty($data['valuetrust']));
$d->set('contextid', $context->id);
$d->save();
}
return $d->get('id');
}
}
}
}
+150
View File
@@ -0,0 +1,150 @@
<?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_group\external;
use context_course;
use core_external\external_api;
use core_external\external_description;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_grades\external\coding_exception;
use core_grades\external\invalid_parameter_exception;
use core_grades\external\moodle_exception;
use core_grades\external\restricted_context_exception;
use moodle_url;
/**
* External group name and image API implementation
*
* @package core_group
* @copyright 2022 Mathew May <mathew.solutions>
* @category external
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class get_groups_for_selector extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'courseid' => new external_value(PARAM_INT, 'Course Id', VALUE_REQUIRED),
]
);
}
/**
* Given a course ID find the existing user groups and map some fields to the returned array of group objects.
*
* @param int $courseid
* @return array Groups and warnings to pass back to the calling widget.
*/
public static function execute(int $courseid): array {
global $DB, $USER, $OUTPUT;
$params = self::validate_parameters(
self::execute_parameters(),
[
'courseid' => $courseid,
]
);
$warnings = [];
$context = context_course::instance($params['courseid']);
parent::validate_context($context);
$mappedgroups = [];
$course = $DB->get_record('course', ['id' => $params['courseid']]);
// Initialise the grade tracking object.
if ($groupmode = $course->groupmode) {
$aag = has_capability('moodle/site:accessallgroups', $context);
$usergroups = [];
$groupuserid = 0;
if ($groupmode == VISIBLEGROUPS || $aag) {
// Get user's own groups and put to the top.
$usergroups = groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid);
} else {
$groupuserid = $USER->id;
}
$allowedgroups = groups_get_all_groups($course->id, $groupuserid, $course->defaultgroupingid);
$allgroups = array_merge($allowedgroups, $usergroups);
// Filter out any duplicate groups.
$groupsmenu = array_intersect_key($allgroups, array_unique(array_column($allgroups, 'name')));
if (!$allowedgroups || $groupmode == VISIBLEGROUPS || $aag) {
array_unshift($groupsmenu, (object) [
'id' => 0,
'name' => get_string('allparticipants'),
]);
}
$mappedgroups = array_map(function($group) use ($context, $OUTPUT) {
if ($group->id) { // Particular group. Get the group picture if it exists, otherwise return a generic image.
$picture = get_group_picture_url($group, $group->courseid, true) ??
moodle_url::make_pluginfile_url($context->id, 'group', 'generated', $group->id, '/', 'group.svg');
} else { // All participants.
$picture = $OUTPUT->image_url('g/g1');
}
return (object) [
'id' => $group->id,
'name' => format_string($group->name, true, ['context' => $context]),
'groupimageurl' => $picture->out(false),
];
}, $groupsmenu);
}
return [
'groups' => $mappedgroups,
'warnings' => $warnings,
];
}
/**
* Returns description of what the group search for the widget should return.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'groups' => new external_multiple_structure(self::group_description()),
'warnings' => new external_warnings(),
]);
}
/**
* Create group return value description.
*
* @return external_description
*/
public static function group_description(): external_description {
$groupfields = [
'id' => new external_value(PARAM_ALPHANUM, 'An ID for the group', VALUE_REQUIRED),
'name' => new external_value(PARAM_TEXT, 'The full name of the group', VALUE_REQUIRED),
'groupimageurl' => new external_value(PARAM_URL, 'Group image URL', VALUE_OPTIONAL),
];
return new external_single_structure($groupfields);
}
}
@@ -0,0 +1,41 @@
<?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_group\hook;
use stdClass;
/**
* Hook after group creation.
*
* @package core_group
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions after a group is created.')]
#[\core\attribute\tags('group')]
class after_group_created {
/**
* Constructor for the hook.
*
* @param stdClass $groupinstance The group instance.
*/
public function __construct(
/** @var stdClass The group instance */
public readonly stdClass $groupinstance,
) {
}
}
@@ -0,0 +1,41 @@
<?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_group\hook;
use stdClass;
/**
* Hook after group deletion.
*
* @package core_group
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions after a group is deleted.')]
#[\core\attribute\tags('group')]
class after_group_deleted {
/**
* Constructor for the hook.
*
* @param stdClass $groupinstance The group instance.
*/
public function __construct(
/** @var stdClass The group instance */
public readonly stdClass $groupinstance,
) {
}
}
@@ -0,0 +1,44 @@
<?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_group\hook;
use stdClass;
/**
* Hook after a member added to the group.
*
* @package core_group
* @copyright 2023 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions after members added to the group.')]
#[\core\attribute\tags('group', 'user')]
class after_group_membership_added {
/**
* Constructor for the hook.
*
* @param stdClass $groupinstance The group instance.
* @param array $userids The user ids.
*/
public function __construct(
/** @var stdClass The group instance */
public readonly stdClass $groupinstance,
/** @var array The user ids */
public readonly array $userids,
) {
}
}
@@ -0,0 +1,44 @@
<?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_group\hook;
use stdClass;
/**
* Hook after a member removed from the group.
*
* @package core_group
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions after members removed from the group.')]
#[\core\attribute\tags('group', 'user')]
class after_group_membership_removed {
/**
* Constructor for the hook.
*
* @param stdClass $groupinstance The group instance.
* @param array $userids The user ids.
*/
public function __construct(
/** @var stdClass The group instance */
public readonly stdClass $groupinstance,
/** @var array The user ids */
public readonly array $userids,
) {
}
}
@@ -0,0 +1,41 @@
<?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_group\hook;
use stdClass;
/**
* Hook after group updates.
*
* @package core_group
* @copyright 2023 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions after a group is updated.')]
#[\core\attribute\tags('group')]
class after_group_updated {
/**
* Constructor for the hook.
*
* @param stdClass $groupinstance The group instance.
*/
public function __construct(
/** @var stdClass The group instance */
public readonly stdClass $groupinstance,
) {
}
}
+93
View File
@@ -0,0 +1,93 @@
<?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/>.
/**
* Group details page.
*
* @package core_group
* @copyright 2017 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_group\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use renderer_base;
use stdClass;
use templatable;
use context_course;
use moodle_url;
/**
* Group details page class.
*
* @package core_group
* @copyright 2017 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_details implements renderable, templatable {
/** @var stdClass $group An object with the group information. */
protected $group;
/**
* group_details constructor.
*
* @param int $groupid Group ID to show details of.
*/
public function __construct($groupid) {
$this->group = groups_get_group($groupid, '*', MUST_EXIST);
}
/**
* Export the data.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
if (!empty($this->group->description) || (!empty($this->group->picture))) {
$context = context_course::instance($this->group->courseid);
$description = file_rewrite_pluginfile_urls($this->group->description,
'pluginfile.php',
$context->id,
'group',
'description',
$this->group->id);
$descriptionformat = $this->group->descriptionformat ?? FORMAT_MOODLE;
$options = [
'overflowdiv' => true,
'context' => $context
];
$data = new stdClass();
$data->name = format_string($this->group->name, true, ['context' => $context]);
$data->pictureurl = get_group_picture_url($this->group, $this->group->courseid, true);
$data->description = format_text($description, $descriptionformat, $options);
if (has_capability('moodle/course:managegroups', $context)) {
$url = new moodle_url('/group/group.php', ['id' => $this->group->id, 'courseid' => $this->group->courseid]);
$data->editurl = $url->out(false);
}
return $data;
} else {
return;
}
}
}
+117
View File
@@ -0,0 +1,117 @@
<?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/>.
/**
* Group index page.
*
* @package core_group
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_group\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use renderer_base;
use stdClass;
use templatable;
/**
* Group index page class.
*
* @package core_group
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class index_page implements renderable, templatable {
/** @var int $courseid The course ID. */
public $courseid;
/** @var array The array of groups to be rendered. */
public $groups;
/** @var string The name of the currently selected group. */
public $selectedgroupname;
/** @var array The array of group members to be rendered, if a group is selected. */
public $selectedgroupmembers;
/** @var bool Whether to disable the add members/edit group buttons. */
public $disableaddedit;
/** @var bool Whether to disable the delete group button. */
public $disabledelete;
/** @var array Groups that can't be deleted by the user. */
public $undeletablegroups;
/** @var bool Whether to show/hide the messaging setting buttons. */
public $messagingsettingsvisible;
/**
* index_page constructor.
*
* @param int $courseid The course ID.
* @param array $groups The array of groups to be rendered.
* @param string $selectedgroupname The name of the currently selected group.
* @param array $selectedgroupmembers The array of group members to be rendered, if a group is selected.
* @param bool $disableaddedit Whether to disable the add members/edit group buttons.
* @param bool $disabledelete Whether to disable the delete group button.
* @param array $undeletablegroups Groups that can't be deleted by the user.
* @param bool $messagingsettingsvisible If the messaging settings buttons should be visible.
*/
public function __construct($courseid, $groups, $selectedgroupname, $selectedgroupmembers, $disableaddedit, $disabledelete,
$undeletablegroups, $messagingsettingsvisible) {
$this->courseid = $courseid;
$this->groups = $groups;
$this->selectedgroupname = $selectedgroupname;
$this->selectedgroupmembers = $selectedgroupmembers;
$this->disableaddedit = $disableaddedit;
$this->disabledelete = $disabledelete;
$this->undeletablegroups = $undeletablegroups;
$this->messagingsettingsvisible = $messagingsettingsvisible;
}
/**
* Export the data.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
global $CFG;
$data = new stdClass();
// Variables that will be passed to the JS helper.
$data->courseid = $this->courseid;
$data->wwwroot = $CFG->wwwroot;
// To be passed to the JS init script in the template. Encode as a JSON string.
$data->undeletablegroups = json_encode($this->undeletablegroups);
// Some buttons are enabled if single group selected.
$data->addmembersdisabled = $this->disableaddedit;
$data->editgroupsettingsdisabled = $this->disableaddedit;
$data->deletegroupdisabled = $this->disabledelete;
$data->groups = $this->groups;
$data->members = $this->selectedgroupmembers;
$data->selectedgroup = $this->selectedgroupname;
$data->messagingsettingsvisible = $this->messagingsettingsvisible;
return $data;
}
}
+61
View File
@@ -0,0 +1,61 @@
<?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/>.
/**
* Renderers.
*
* @package core_group
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_group\output;
defined('MOODLE_INTERNAL') || die();
use plugin_renderer_base;
/**
* Renderer class.
*
* @package core_group
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends plugin_renderer_base {
/**
* Defer to template.
*
* @param index_page $page
* @return string
*/
public function render_index_page(index_page $page) {
$data = $page->export_for_template($this);
return parent::render_from_template('core_group/index', $data);
}
/**
* Defer to template.
*
* @param group_details $page Group details page object.
* @return string HTML to render the group details.
*/
public function group_details(group_details $page) {
$data = $page->export_for_template($this);
return parent::render_from_template('core_group/group_details', $data);
}
}
@@ -0,0 +1,180 @@
<?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_group\output\user_groups_editable
*
* @package core_group
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_group\output;
use context_course;
use core_user;
use core_external;
use coding_exception;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/group/lib.php');
/**
* Class to display list of user groups.
*
* @package core_group
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_groups_editable extends \core\output\inplace_editable {
/** @var $coursegroups */
private $coursegroups = null;
/** @var $context */
private $context = null;
/**
* Constructor.
*
* @param \stdClass $course The current course
* @param \context $context The course context
* @param \stdClass $user The current user
* @param \stdClass[] $coursegroups The list of course groups from groups_get_all_groups with membership.
* @param array $value Array of groupids.
*/
public function __construct($course, $context, $user, $coursegroups, $value) {
// Check capabilities to get editable value.
$editable = has_capability('moodle/course:managegroups', $context) && !empty($coursegroups);
// Invent an itemid.
$itemid = $course->id . ':' . $user->id;
$value = json_encode($value);
// Remember these for the display value.
$this->coursegroups = $coursegroups;
$this->context = $context;
parent::__construct('core_group', 'user_groups', $itemid, $editable, $value, $value);
// Assignable groups.
$options = [];
foreach ($coursegroups as $group) {
$options[$group->id] = format_string($group->name, true, ['context' => $this->context]);
}
$fullname = fullname($user, has_capability('moodle/site:viewfullnames', $this->context));
$fullname = htmlspecialchars($fullname, ENT_QUOTES, 'utf-8');
$this->edithint = get_string('editusersgroupsa', 'group', $fullname);
$this->editlabel = get_string('editusersgroupsa', 'group', $fullname);
$attributes = ['multiple' => true];
$this->set_type_autocomplete($options, $attributes);
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param \renderer_base $output
* @return array
*/
public function export_for_template(\renderer_base $output) {
$listofgroups = [];
$groupids = json_decode($this->value);
foreach ($groupids as $id) {
$listofgroups[] = format_string($this->coursegroups[$id]->name, true, ['context' => $this->context]);
}
if (!empty($listofgroups)) {
$this->displayvalue = implode(', ', $listofgroups);
} else {
$this->displayvalue = get_string('groupsnone');
}
return parent::export_for_template($output);
}
/**
* Updates the value in database and returns itself, called from inplace_editable callback
*
* @param int $itemid
* @param mixed $newvalue
* @return \self
*/
public static function update($itemid, $newvalue) {
// Check caps.
// Do the thing.
// Return one of me.
// Validate the inputs.
list($courseid, $userid) = explode(':', $itemid, 2);
$courseid = clean_param($courseid, PARAM_INT);
$userid = clean_param($userid, PARAM_INT);
$groupids = json_decode($newvalue);
foreach ($groupids as $index => $groupid) {
$groupids[$index] = clean_param($groupid, PARAM_INT);
}
// Check user is enrolled in the course.
$context = context_course::instance($courseid);
core_external::validate_context($context);
if (!is_enrolled($context, $userid)) {
throw new coding_exception('User does not belong to the course');
}
// Check that all the groups belong to the course.
$coursegroups = groups_get_all_groups($courseid, 0, 0, 'g.*', true);
$byid = [];
foreach ($groupids as $groupid) {
if (!isset($coursegroups[$groupid])) {
throw new coding_exception('Group does not belong to the course');
}
$byid[$groupid] = $groupid;
}
$groupids = $byid;
// Check permissions.
require_capability('moodle/course:managegroups', $context);
// Process adds.
foreach ($groupids as $groupid) {
if (!isset($coursegroups[$groupid]->members[$userid])) {
// Add them.
groups_add_member($groupid, $userid);
// Keep this variable in sync.
$coursegroups[$groupid]->members[$userid] = $userid;
}
}
// Process removals.
foreach ($coursegroups as $groupid => $group) {
if (isset($group->members[$userid]) && !isset($groupids[$groupid])) {
if (groups_remove_member_allowed($groupid, $userid)) {
groups_remove_member($groupid, $userid);
unset($coursegroups[$groupid]->members[$userid]);
} else {
$groupids[$groupid] = $groupid;
}
}
}
$course = get_course($courseid);
$user = core_user::get_user($userid);
return new self($course, $context, $user, $coursegroups, array_values($groupids));
}
}
+400
View File
@@ -0,0 +1,400 @@
<?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 Subsystem implementation for core_group.
*
* @package core_group
* @category privacy
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_group\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\userlist;
/**
* Privacy Subsystem implementation for core_group.
*
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
// Groups store user data.
\core_privacy\local\metadata\provider,
// The group subsystem contains user's group memberships.
\core_privacy\local\request\subsystem\provider,
// The group subsystem can provide information to other plugins.
\core_privacy\local\request\subsystem\plugin_provider,
// This plugin is capable of determining which users have data within it.
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\shared_userlist_provider
{
/**
* Returns meta data about this system.
*
* @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('groups_members', [
'groupid' => 'privacy:metadata:groups:groupid',
'userid' => 'privacy:metadata:groups:userid',
'timeadded' => 'privacy:metadata:groups:timeadded',
], 'privacy:metadata:groups');
$collection->link_subsystem('core_message', 'privacy:metadata:core_message');
return $collection;
}
/**
* Writes user data to the writer for the user to download.
*
* @param \context $context The context to export data for.
* @param string $component The component that is calling this function. Empty string means no component.
* @param array $subcontext The sub-context in which to export this data.
* @param int $itemid Optional itemid associated with component.
*/
public static function export_groups(\context $context, string $component, array $subcontext = [], int $itemid = 0) {
global $DB, $USER;
if (!$context instanceof \context_course) {
return;
}
$subcontext[] = get_string('groups', 'core_group');
$sql = "SELECT gm.id, gm.timeadded, gm.userid, g.name, gm.groupid
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = :courseid
AND gm.component = :component
AND gm.userid = :userid";
$params = [
'courseid' => $context->instanceid,
'component' => $component,
'userid' => $USER->id
];
if ($itemid) {
$sql .= ' AND gm.itemid = :itemid';
$params['itemid'] = $itemid;
}
$groups = $DB->get_records_sql($sql, $params);
$groupstoexport = array_map(function($group) {
return (object) [
'name' => format_string($group->name),
'timeadded' => transform::datetime($group->timeadded),
];
}, $groups);
if (!empty($groups)) {
\core_privacy\local\request\writer::with_context($context)
->export_data($subcontext, (object) [
'groups' => $groupstoexport,
]);
foreach ($groups as $group) {
// Export associated conversations to this group.
\core_message\privacy\provider::export_conversations($USER->id, 'core_group', 'groups',
$context, [], $group->groupid);
}
}
}
/**
* Deletes all group memberships for a specified context and component.
*
* @param \context $context Details about which context to delete group memberships for.
* @param string $component Component to delete. Empty string means no component (manual group memberships).
* @param int $itemid Optional itemid associated with component.
*/
public static function delete_groups_for_all_users(\context $context, string $component, int $itemid = 0) {
global $DB;
if (!$context instanceof \context_course) {
return;
}
if (!$DB->record_exists('groups', ['courseid' => $context->instanceid])) {
return;
}
$select = "component = :component AND groupid IN (SELECT g.id FROM {groups} g WHERE courseid = :courseid)";
$params = ['component' => $component, 'courseid' => $context->instanceid];
if ($itemid) {
$select .= ' AND itemid = :itemid';
$params['itemid'] = $itemid;
}
// Delete the group conversations.
$groups = $DB->get_records_select('groups_members', $select, $params);
foreach ($groups as $group) {
\core_message\privacy\provider::delete_conversations_for_all_users($context, 'core_group', 'groups', $group->groupid);
}
// Remove members from the group.
$DB->delete_records_select('groups_members', $select, $params);
// Purge the group and grouping cache for users.
\cache_helper::purge_by_definition('core', 'user_group_groupings');
}
/**
* Deletes all records for a user from a list of approved contexts.
*
* @param approved_contextlist $contextlist Contains the user ID and a list of contexts to be deleted from.
* @param string $component Component to delete from. Empty string means no component (manual memberships).
* @param int $itemid Optional itemid associated with component.
*/
public static function delete_groups_for_user(approved_contextlist $contextlist, string $component, int $itemid = 0) {
global $DB;
$userid = $contextlist->get_user()->id;
$contextids = $contextlist->get_contextids();
if (!$contextids) {
return;
}
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED);
$contextparams += ['contextcourse' => CONTEXT_COURSE];
$groupselect = "SELECT g.id
FROM {groups} g
JOIN {context} ctx ON g.courseid = ctx.instanceid AND ctx.contextlevel = :contextcourse
WHERE ctx.id $contextsql";
if (!$DB->record_exists_sql($groupselect, $contextparams)) {
return;
}
$select = "userid = :userid AND component = :component AND groupid IN ({$groupselect})";
$params = ['userid' => $userid, 'component' => $component] + $contextparams;
if ($itemid) {
$select .= ' AND itemid = :itemid';
$params['itemid'] = $itemid;
}
// Delete the group conversations.
$groups = $DB->get_records_select('groups_members', $select, $params);
foreach ($groups as $group) {
\core_message\privacy\provider::delete_conversations_for_user($contextlist, 'core_group', 'groups', $group->groupid);
}
// Remove members from the group.
$DB->delete_records_select('groups_members', $select, $params);
// Invalidate the group and grouping cache for the user.
\cache_helper::invalidate_by_definition('core', 'user_group_groupings', array(), array($userid));
}
/**
* Add the list of users who are members of some groups in the specified constraints.
*
* @param userlist $userlist The userlist to add the users to.
* @param string $component The component to check.
* @param int $itemid Optional itemid associated with component.
*/
public static function get_group_members_in_context(userlist $userlist, string $component, int $itemid = 0) {
$context = $userlist->get_context();
if (!$context instanceof \context_course) {
return;
}
// Group members in the given context.
$sql = "SELECT gm.userid
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = :courseid AND gm.component = :component";
$params = [
'courseid' => $context->instanceid,
'component' => $component
];
if ($itemid) {
$sql .= ' AND gm.itemid = :itemid';
$params['itemid'] = $itemid;
}
$userlist->add_from_sql('userid', $sql, $params);
// Get the users with some group conversation in this context.
\core_message\privacy\provider::add_conversations_in_context($userlist, 'core_group', 'groups', $itemid);
}
/**
* Deletes all records for multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
* @param string $component Component to delete from. Empty string means no component (manual memberships).
* @param int $itemid Optional itemid associated with component.
*/
public static function delete_groups_for_users(approved_userlist $userlist, string $component, int $itemid = 0) {
global $DB;
$context = $userlist->get_context();
$userids = $userlist->get_userids();
if (!$context instanceof \context_course) {
return;
}
list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$groupselect = "SELECT id FROM {groups} WHERE courseid = :courseid";
$groupparams = ['courseid' => $context->instanceid];
$select = "component = :component AND userid {$usersql} AND groupid IN ({$groupselect})";
$params = ['component' => $component] + $groupparams + $userparams;
if ($itemid) {
$select .= ' AND itemid = :itemid';
$params['itemid'] = $itemid;
}
// Delete the group conversations for these users.
$groups = $DB->get_records_select('groups_members', $select, $params);
foreach ($groups as $group) {
\core_message\privacy\provider::delete_conversations_for_users($userlist, 'core_group', 'groups', $group->groupid);
}
$DB->delete_records_select('groups_members', $select, $params);
// Invalidate the group and grouping cache for the user.
\cache_helper::invalidate_by_definition('core', 'user_group_groupings', array(), $userids);
}
/**
* Get the list of contexts that contain group membership for the specified user.
*
* @param int $userid The user to search.
* @param string $component The component to check.
* @param int $itemid Optional itemid associated with component.
* @return contextlist The contextlist containing the list of contexts.
*/
public static function get_contexts_for_group_member(int $userid, string $component, int $itemid = 0) {
$contextlist = new contextlist();
$sql = "SELECT ctx.id
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
JOIN {context} ctx ON g.courseid = ctx.instanceid AND ctx.contextlevel = :contextcourse
WHERE gm.userid = :userid AND gm.component = :component";
$params = [
'contextcourse' => CONTEXT_COURSE,
'userid' => $userid,
'component' => $component
];
if ($itemid) {
$sql .= ' AND gm.itemid = :itemid';
$params['itemid'] = $itemid;
}
$contextlist->add_from_sql($sql, $params);
// Get the contexts where the userid has group conversations.
\core_message\privacy\provider::add_contexts_for_conversations($contextlist, $userid, 'core_group', 'groups', $itemid);
return $contextlist;
}
/**
* Get the list of users who have data within a context.
*
* @param int $userid The user to search.
* @return contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
return static::get_contexts_for_group_member($userid, '');
}
/**
* Get the list of users who have data within a 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_course) {
return;
}
static::get_group_members_in_context($userlist, '');
}
/**
* 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) {
$contexts = $contextlist->get_contexts();
foreach ($contexts as $context) {
static::export_groups($context, '');
}
}
/**
* Delete all data for all users in the specified context.
*
* @param context $context The specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
static::delete_groups_for_all_users($context, '');
}
/**
* 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) {
static::delete_groups_for_user($contextlist, '');
}
/**
* 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) {
static::delete_groups_for_users($userlist, '');
}
}
@@ -0,0 +1,151 @@
<?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_group\reportbuilder\datasource;
use core_group\reportbuilder\local\entities\{grouping, group, group_member};
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\{course, user};
use core_reportbuilder\local\helpers\database;
/**
* Groups datasource
*
* @package core_group
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class groups extends datasource {
/**
* Return user friendly name of the datasource
*
* @return string
*/
public static function get_name(): string {
return get_string('groups', 'core_group');
}
/**
* Initialise report
*/
protected function initialise(): void {
$courseentity = new course();
$coursealias = $courseentity->get_table_alias('course');
$this->set_main_table('course', $coursealias);
$this->add_entity($courseentity);
$paramsiteid = database::generate_param_name();
$this->add_base_condition_sql("{$coursealias}.id != :{$paramsiteid}", [$paramsiteid => SITEID]);
// Re-use the context table alias/join from the course entity in subsequent entities.
$contextalias = $courseentity->get_table_alias('context');
$this->add_join($courseentity->get_context_join());
// Group entity.
$groupentity = (new group())
->set_table_alias('context', $contextalias);
$groupsalias = $groupentity->get_table_alias('groups');
$this->add_entity($groupentity
->add_join("LEFT JOIN {groups} {$groupsalias} ON {$groupsalias}.courseid = {$coursealias}.id"));
// Grouping entity.
$groupingentity = (new grouping())
->set_table_alias('context', $contextalias);
$groupingsalias = $groupingentity->get_table_alias('groupings');
// Sub-select for all groupings groups.
$groupinginnerselect = "
SELECT gr.*, grg.groupid
FROM {groupings} gr
JOIN {groupings_groups} grg ON grg.groupingid = gr.id";
$this->add_entity($groupingentity
->add_joins($groupentity->get_joins())
->add_join("LEFT JOIN ({$groupinginnerselect}) {$groupingsalias}
ON {$groupingsalias}.courseid = {$coursealias}.id AND {$groupingsalias}.groupid = {$groupsalias}.id"));
// Group member entity.
$groupmemberentity = new group_member();
$groupsmembersalias = $groupmemberentity->get_table_alias('groups_members');
$this->add_entity($groupmemberentity
->add_joins($groupentity->get_joins())
->add_join("LEFT JOIN {groups_members} {$groupsmembersalias} ON {$groupsmembersalias}.groupid = {$groupsalias}.id"));
// User entity.
$userentity = new user();
$useralias = $userentity->get_table_alias('user');
$this->add_entity($userentity
->add_joins($groupmemberentity->get_joins())
->add_join("LEFT JOIN {user} {$useralias} ON {$useralias}.id = {$groupsmembersalias}.userid"));
// Add all elements 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 [
'course:coursefullnamewithlink',
'group:name',
'user:fullname',
];
}
/**
* Return the column sorting that will be added to the report upon creation
*
* @return int[]
*/
public function get_default_column_sorting(): array {
return [
'course:coursefullnamewithlink' => SORT_ASC,
'group:name' => SORT_ASC,
'user:fullname' => SORT_ASC,
];
}
/**
* Return the filters that will be added to the report as part of default setup
*
* @return string[]
*/
public function get_default_filters(): array {
return [
'course:fullname',
'group:name',
];
}
/**
* Return the conditions that will be added to the report as part of default setup
*
* @return string[]
*/
public function get_default_conditions(): array {
return [
'course:fullname',
'group:name',
];
}
}
@@ -0,0 +1,343 @@
<?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_group\reportbuilder\local\entities;
use context_course;
use context_helper;
use html_writer;
use lang_string;
use moodle_url;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{boolean_select, date, select, text};
use core_reportbuilder\local\helpers\{custom_fields, format};
use core_reportbuilder\local\report\{column, filter};
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->libdir}/grouplib.php");
/**
* Group entity
*
* @package core_group
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'context',
'groups',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('group', 'core_group');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$groupsalias = $this->get_table_alias('groups');
$customfields = (new custom_fields(
"{$groupsalias}.id",
$this->get_entity_name(),
'core_group',
'group',
))
->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;
$contextalias = $this->get_table_alias('context');
$groupsalias = $this->get_table_alias('groups');
// Name column.
$columns[] = (new column(
'name',
new lang_string('name'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$groupsalias}.name, {$groupsalias}.courseid")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->set_is_sortable(true)
->set_callback(static function($name, stdClass $group): string {
if ($name === null) {
return '';
}
context_helper::preload_from_record($group);
$context = context_course::instance($group->courseid);
return format_string($group->name, true, ['context' => $context]);
});
// ID number column.
$columns[] = (new column(
'idnumber',
new lang_string('idnumber'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$groupsalias}.idnumber")
->set_is_sortable(true);
// Description column.
$descriptionfieldsql = "{$groupsalias}.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())
->set_type(column::TYPE_LONGTEXT)
->add_field($descriptionfieldsql, 'description')
->add_fields("{$groupsalias}.descriptionformat, {$groupsalias}.id, {$groupsalias}.courseid")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->set_is_sortable(false)
->set_callback(static function(?string $description, stdClass $group): string {
global $CFG;
if ($description === null) {
return '';
}
require_once("{$CFG->libdir}/filelib.php");
context_helper::preload_from_record($group);
$context = context_course::instance($group->courseid);
$description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $context->id, 'group',
'description', $group->id);
return format_text($description, $group->descriptionformat, ['context' => $context]);
});
// Enrolment key column.
$columns[] = (new column(
'enrolmentkey',
new lang_string('enrolmentkey', 'core_group'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$groupsalias}.enrolmentkey")
->set_is_sortable(true);
// Visibility column.
$columns[] = (new column(
'visibility',
new lang_string('visibilityshort', 'core_group'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_fields("{$groupsalias}.visibility")
->set_is_sortable(true)
// It doesn't make sense to offer integer aggregation methods for this column.
->set_disabled_aggregation(['avg', 'max', 'min', 'sum'])
->set_callback(static function(?int $visibility): string {
if ($visibility === null) {
return '';
}
$options = [
GROUPS_VISIBILITY_ALL => new lang_string('visibilityall', 'core_group'),
GROUPS_VISIBILITY_MEMBERS => new lang_string('visibilitymembers', 'core_group'),
GROUPS_VISIBILITY_OWN => new lang_string('visibilityown', 'core_group'),
GROUPS_VISIBILITY_NONE => new lang_string('visibilitynone', 'core_group'),
];
return (string) ($options[$visibility] ?? $visibility);
});
// Participation column.
$columns[] = (new column(
'participation',
new lang_string('participationshort', 'core_group'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_BOOLEAN)
->add_fields("{$groupsalias}.participation")
->set_is_sortable(true)
->set_callback([format::class, 'boolean_as_text']);
// Picture column.
$columns[] = (new column(
'picture',
new lang_string('picture'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_fields("{$groupsalias}.picture, {$groupsalias}.id, {$contextalias}.id AS contextid")
->set_is_sortable(false)
// It doesn't make sense to offer integer aggregation methods for this column.
->set_disabled_aggregation(['avg', 'max', 'min', 'sum'])
->set_callback(static function ($picture, stdClass $group): string {
if (empty($group->picture)) {
return '';
}
$pictureurl = moodle_url::make_pluginfile_url($group->contextid, 'group', 'icon', $group->id, '/', 'f2');
$pictureurl->param('rev', $group->picture);
return html_writer::img($pictureurl, '');
});
// 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("{$groupsalias}.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("{$groupsalias}.timemodified")
->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 {
$groupsalias = $this->get_table_alias('groups');
// Name filter.
$filters[] = (new filter(
text::class,
'name',
new lang_string('name'),
$this->get_entity_name(),
"{$groupsalias}.name"
))
->add_joins($this->get_joins());
// ID number filter.
$filters[] = (new filter(
text::class,
'idnumber',
new lang_string('idnumber'),
$this->get_entity_name(),
"{$groupsalias}.idnumber"
))
->add_joins($this->get_joins());
// Visibility filter.
$filters[] = (new filter(
select::class,
'visibility',
new lang_string('visibilityshort', 'core_group'),
$this->get_entity_name(),
"{$groupsalias}.visibility"
))
->add_joins($this->get_joins())
->set_options([
GROUPS_VISIBILITY_ALL => new lang_string('visibilityall', 'core_group'),
GROUPS_VISIBILITY_MEMBERS => new lang_string('visibilitymembers', 'core_group'),
GROUPS_VISIBILITY_OWN => new lang_string('visibilityown', 'core_group'),
GROUPS_VISIBILITY_NONE => new lang_string('visibilitynone', 'core_group'),
]);
// Participation filter.
$filters[] = (new filter(
boolean_select::class,
'participation',
new lang_string('participationshort', 'core_group'),
$this->get_entity_name(),
"{$groupsalias}.participation"
))
->add_joins($this->get_joins());
// Time created filter.
$filters[] = (new filter(
date::class,
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
"{$groupsalias}.timecreated"
))
->add_joins($this->get_joins());
return $filters;
}
}
@@ -0,0 +1,132 @@
<?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_group\reportbuilder\local\entities;
use core_reportbuilder\local\filters\date;
use lang_string;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\{column, filter};
/**
* Group member entity
*
* @package core_group
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_member extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'groups_members',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('groupmember', 'core_group');
}
/**
* 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 {
$groupsmembersalias = $this->get_table_alias('groups_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("{$groupsmembersalias}.timeadded")
->set_is_sortable(true)
->set_callback([format::class, 'userdate']);
// Component column.
$columns[] = (new column(
'component',
new lang_string('plugin', 'core'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$groupsmembersalias}.component")
->set_is_sortable(true);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$groupsmembersalias = $this->get_table_alias('groups_members');
// Time added filter.
$filters[] = (new filter(
date::class,
'timeadded',
new lang_string('timeadded', 'core_reportbuilder'),
$this->get_entity_name(),
"{$groupsmembersalias}.timeadded"
))
->add_joins($this->get_joins());
return $filters;
}
}
@@ -0,0 +1,237 @@
<?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_group\reportbuilder\local\entities;
use context_course;
use context_helper;
use lang_string;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{date, text};
use core_reportbuilder\local\helpers\{custom_fields, format};
use core_reportbuilder\local\report\{column, filter};
/**
* Grouping entity
*
* @package core_group
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grouping extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'context',
'groupings',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('grouping', 'core_group');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$groupingsalias = $this->get_table_alias('groupings');
$customfields = (new custom_fields(
"{$groupingsalias}.id",
$this->get_entity_name(),
'core_group',
'grouping',
))
->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;
$contextalias = $this->get_table_alias('context');
$groupingsalias = $this->get_table_alias('groupings');
// Name column.
$columns[] = (new column(
'name',
new lang_string('name'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$groupingsalias}.name, {$groupingsalias}.courseid")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->set_is_sortable(true)
->set_callback(static function($name, stdClass $grouping): string {
if ($name === null) {
return '';
}
context_helper::preload_from_record($grouping);
$context = context_course::instance($grouping->courseid);
return format_string($grouping->name, true, ['context' => $context]);
});
// ID number column.
$columns[] = (new column(
'idnumber',
new lang_string('idnumber'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$groupingsalias}.idnumber")
->set_is_sortable(true);
// Description column.
$descriptionfieldsql = "{$groupingsalias}.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())
->set_type(column::TYPE_LONGTEXT)
->add_field($descriptionfieldsql, 'description')
->add_fields("{$groupingsalias}.descriptionformat, {$groupingsalias}.id, {$groupingsalias}.courseid")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->set_is_sortable(false)
->set_callback(static function(?string $description, stdClass $grouping): string {
global $CFG;
if ($description === null) {
return '';
}
require_once("{$CFG->libdir}/filelib.php");
context_helper::preload_from_record($grouping);
$context = context_course::instance($grouping->courseid);
$description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $context->id, 'grouping',
'description', $grouping->id);
return format_text($description, $grouping->descriptionformat, ['context' => $context]);
});
// 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("{$groupingsalias}.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("{$groupingsalias}.timemodified")
->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 {
$groupingsalias = $this->get_table_alias('groupings');
// Name filter.
$filters[] = (new filter(
text::class,
'name',
new lang_string('name'),
$this->get_entity_name(),
"{$groupingsalias}.name"
))
->add_joins($this->get_joins());
// ID number filter.
$filters[] = (new filter(
text::class,
'idnumber',
new lang_string('idnumber'),
$this->get_entity_name(),
"{$groupingsalias}.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(),
"{$groupingsalias}.timecreated"
))
->add_joins($this->get_joins());
return $filters;
}
}
+181
View File
@@ -0,0 +1,181 @@
<?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/>.
/**
* Group visibility methods
*
* @package core_group
* @copyright 2022 onwards Catalyst IT EU {@link https://catalyst-eu.net}
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_group;
/**
* Group visibility methods.
*/
class visibility {
/**
* Store the number groups with visibility other than ALL on the course.
*
* @param int $courseid Course ID to update the cache for.
* @param \cache|null $cache Existing cache instance. If null, once will be created.
* @return void
* @throws \dml_exception
*/
public static function update_hiddengroups_cache(int $courseid, ?\cache $cache = null): void {
global $DB;
if (!$cache) {
$cache = \cache::make('core', 'coursehiddengroups');
}
$hiddengroups = $DB->count_records_select('groups', 'courseid = ? AND visibility != ?',
[$courseid, GROUPS_VISIBILITY_ALL]);
$cache->set($courseid, $hiddengroups);
}
/**
* Return whether a course currently had hidden groups.
*
* This can be used as a shortcut to decide whether visibility restrictions need to be applied. If this returns false,
* we may be able to use cached data, or do a much simpler query.
*
* @param int $courseid
* @return bool
* @throws \coding_exception
* @throws \dml_exception
*/
public static function course_has_hidden_groups(int $courseid): bool {
$cache = \cache::make('core', 'coursehiddengroups');
$hiddengroups = $cache->get($courseid);
if ($hiddengroups === false) {
self::update_hiddengroups_cache($courseid, $cache);
$cache->get($courseid);
}
return $hiddengroups > 0;
}
/**
* Can the current user view all the groups on the course?
*
* Returns true if there are no groups on the course with visibility != ALL,
* or if the user has viewhiddengroups.
*
* This is useful for deciding whether we need to perform additional visibility checkes
* such as the sql_* methods of this class.
*
* @param int $courseid
* @return bool
*/
public static function can_view_all_groups(int $courseid): bool {
$viewhidden = has_capability('moodle/course:viewhiddengroups', \context_course::instance($courseid));
$hashidden = self::course_has_hidden_groups($courseid);
return $viewhidden || !$hashidden;
}
/**
* Return SQL conditions for determining whether a user can see a group and its memberships.
*
* @param int $userid
* @param string $groupsalias The SQL alias being used for the groups table.
* @param string $groupsmembersalias The SQL alias being used for the groups_members table.
* @return array [$where, $params]
*/
public static function sql_group_visibility_where(int $userid,
string $groupsalias = 'g', string $groupsmembersalias = 'gm'): array {
global $USER;
// Apply visibility restrictions.
// Everyone can see who is in groups with ALL visibility.
$where = "({$groupsalias}.visibility = :all";
$params['all'] = GROUPS_VISIBILITY_ALL;
if ($userid == $USER->id) {
// If the user is looking at their own groups, they can see those with MEMBERS or OWN visibility.
$where .= " OR {$groupsalias}.visibility IN (:members, :own)";
$params['members'] = GROUPS_VISIBILITY_MEMBERS;
$params['own'] = GROUPS_VISIBILITY_OWN;
} else {
list($memberssql, $membersparams) = self::sql_members_visibility_condition($groupsalias, $groupsmembersalias);
// If someone else's groups, they can see those with MEMBERS visibilty, only if they are a member too.
$where .= " OR ($memberssql)";
$params = array_merge($params, $membersparams);
}
$where .= ")";
return [$where, $params];
}
/**
* Return SQL conditions for determining whether a user can see a group's members.
*
* @param string $groupsalias The SQL alias being used for the groups table.
* @param string $groupsmembersalias The SQL alias being used for the groups_members table.
* @param string $useralias The SQL alias being used for the user table.
* @param string $paramprefix Prefix for the parameter names.
* @return array [$where, $params]
*/
public static function sql_member_visibility_where(
string $groupsalias = 'g',
string $groupsmembersalias = 'gm',
string $useralias = 'u',
string $paramprefix = '',
): array {
global $USER;
list($memberssql, $membersparams) = self::sql_members_visibility_condition($groupsalias, $groupsmembersalias, $paramprefix);
$where = "(
{$groupsalias}.visibility = :{$paramprefix}all
OR ($memberssql)
OR ({$groupsalias}.visibility = :{$paramprefix}own AND {$useralias}.id = :{$paramprefix}currentuser2)
)";
$params = [
"{$paramprefix}all" => GROUPS_VISIBILITY_ALL,
"{$paramprefix}own" => GROUPS_VISIBILITY_OWN,
"{$paramprefix}currentuser2" => $USER->id,
];
$params = array_merge($params, $membersparams);
return [$where, $params];
}
/**
* Return a condition to check if a user can view a group because it has MEMBERS visibility and they are a member.
*
* @param string $groupsalias The SQL alias being used for the groups table.
* @param string $groupsmembersalias The SQL alias being used for the groups_members table.
* @param string $paramprefix Prefix for the parameter names.
* @return array [$sql, $params]
*/
protected static function sql_members_visibility_condition(
string $groupsalias = 'g',
string $groupsmembersalias = 'gm',
string $paramprefix = '',
): array {
global $USER;
$sql = "{$groupsalias}.visibility = :{$paramprefix}members
AND (
SELECT gm2.id
FROM {groups_members} gm2
WHERE gm2.groupid = {$groupsmembersalias}.groupid
AND gm2.userid = :{$paramprefix}currentuser
) IS NOT NULL";
$params = [
"{$paramprefix}members" => GROUPS_VISIBILITY_MEMBERS,
"{$paramprefix}currentuser" => $USER->id
];
return [$sql, $params];
}
}