mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 21:49:15 +01:00
958da5b67e
These new settings are designed to enchance user privacy surrounding groups. They allow groups to be configured so that users outside the group cannot see the group, so that users in the group cannot see each other, or so that users cannot see the group at all, even if they are in it. This avoids issues where a group may be assigned based on sensitive personal information (such as a person requiring special arrangements due to a disability). By default, groups are visible to all and available for participation in activities, which maintains the current behaviour. For performance, a new cache has been added to track the number of groups on a course that are not visible to non-members. This allows us to revert to the existing behaviour if the new features are not being used at all on a course, and only apply the new visibility conditions if they are. Users who have the moodle/course:viewhiddengroups capability should be concious of exposing hidden groups when showing their screen to other users. The "Switch role to..." feature can be used to show a course page on screen without exposing private availability conditions, for example. The changes cover several specific areas: * grouplib functions, which most code should use to get lists of groups and members (this includes the participants page). * Activities supporting group overrides will not allow overrides for groups that are hidden from all users. * Activities supporting separate/visible groups modes will only allow groups with the new "participation" flag enabled to be selected. * Group messaging will be disabled for groups where members cannot see each other, or cannot see the group at all.
286 lines
10 KiB
PHP
286 lines
10 KiB
PHP
<?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/>.
|
|
|
|
|
|
/**
|
|
* Create and allocate users to groups
|
|
*
|
|
* @package core_group
|
|
* @copyright Matt Clarkson mattc@catalyst.net.nz
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
require_once('../config.php');
|
|
require_once('lib.php');
|
|
require_once('autogroup_form.php');
|
|
|
|
if (!defined('AUTOGROUP_MIN_RATIO')) {
|
|
define('AUTOGROUP_MIN_RATIO', 0.7); // means minimum member count is 70% in the smallest group
|
|
}
|
|
|
|
$courseid = required_param('courseid', PARAM_INT);
|
|
$PAGE->set_url('/group/autogroup.php', array('courseid' => $courseid));
|
|
|
|
if (!$course = $DB->get_record('course', array('id'=>$courseid))) {
|
|
throw new \moodle_exception('invalidcourseid');
|
|
}
|
|
|
|
// Make sure that the user has permissions to manage groups.
|
|
require_login($course);
|
|
|
|
$context = context_course::instance($courseid);
|
|
require_capability('moodle/course:managegroups', $context);
|
|
|
|
$returnurl = $CFG->wwwroot.'/group/index.php?id='.$course->id;
|
|
|
|
$strgroups = get_string('groups');
|
|
$strparticipants = get_string('participants');
|
|
$strautocreategroups = get_string('autocreategroups', 'group');
|
|
|
|
$PAGE->set_title($strgroups);
|
|
$PAGE->set_heading($course->fullname. ': '.$strgroups);
|
|
$PAGE->set_pagelayout('admin');
|
|
navigation_node::override_active_url(new moodle_url('/group/index.php', array('id' => $courseid)));
|
|
|
|
// Print the page and form
|
|
$preview = '';
|
|
$error = '';
|
|
|
|
/// Get applicable roles - used in menus etc later on
|
|
$rolenames = role_fix_names(get_profile_roles($context), $context, ROLENAME_BOTH, true);
|
|
|
|
/// Create the form
|
|
$editform = new autogroup_form(null, array('roles' => $rolenames));
|
|
$editform->set_data(array('courseid' => $courseid, 'seed' => time()));
|
|
|
|
/// Handle form submission
|
|
if ($editform->is_cancelled()) {
|
|
redirect($returnurl);
|
|
|
|
} elseif ($data = $editform->get_data()) {
|
|
|
|
/// Allocate members from the selected role to groups
|
|
switch ($data->allocateby) {
|
|
case 'no':
|
|
case 'random':
|
|
case 'lastname':
|
|
$orderby = 'lastname, firstname'; break;
|
|
case 'firstname':
|
|
$orderby = 'firstname, lastname'; break;
|
|
case 'idnumber':
|
|
$orderby = 'idnumber'; break;
|
|
default:
|
|
throw new \moodle_exception('unknoworder');
|
|
}
|
|
$source = array();
|
|
if ($data->cohortid) {
|
|
$source['cohortid'] = $data->cohortid;
|
|
}
|
|
if ($data->groupingid) {
|
|
$source['groupingid'] = $data->groupingid;
|
|
}
|
|
if ($data->groupid) {
|
|
$source['groupid'] = $data->groupid;
|
|
}
|
|
|
|
// Display only active users if the option was selected or they do not have the capability to view suspended users.
|
|
$onlyactive = !empty($data->includeonlyactiveenrol) || !has_capability('moodle/course:viewsuspendedusers', $context);
|
|
|
|
// TODO Does not support custom user profile fields (MDL-70456).
|
|
$extrafields = \core_user\fields::get_identity_fields($context, false);
|
|
$users = groups_get_potential_members($data->courseid, $data->roleid, $source, $orderby, !empty($data->notingroup),
|
|
$onlyactive, $extrafields);
|
|
$usercnt = count($users);
|
|
|
|
if ($data->allocateby == 'random') {
|
|
srand($data->seed);
|
|
shuffle($users);
|
|
}
|
|
|
|
$groups = array();
|
|
|
|
// Plan the allocation
|
|
if ($data->groupby == 'groups') {
|
|
$numgrps = $data->number;
|
|
$userpergrp = floor($usercnt/$numgrps);
|
|
|
|
} else { // members
|
|
$numgrps = ceil($usercnt/$data->number);
|
|
$userpergrp = $data->number;
|
|
|
|
if (!empty($data->nosmallgroups) and $usercnt % $data->number != 0) {
|
|
// If there would be one group with a small number of member reduce the number of groups
|
|
$missing = $userpergrp * $numgrps - $usercnt;
|
|
if ($missing > $userpergrp * (1-AUTOGROUP_MIN_RATIO)) {
|
|
// spread the users from the last small group
|
|
$numgrps--;
|
|
$userpergrp = floor($usercnt/$numgrps);
|
|
}
|
|
}
|
|
}
|
|
|
|
// allocate the users - all groups equal count first
|
|
for ($i=0; $i<$numgrps; $i++) {
|
|
$groups[$i] = array();
|
|
$groups[$i]['name'] = groups_parse_name(trim($data->namingscheme), $i);
|
|
$groups[$i]['members'] = array();
|
|
if ($data->allocateby == 'no') {
|
|
continue; // do not allocate users
|
|
}
|
|
for ($j=0; $j<$userpergrp; $j++) {
|
|
if (empty($users)) {
|
|
break 2;
|
|
}
|
|
$user = array_shift($users);
|
|
$groups[$i]['members'][$user->id] = $user;
|
|
}
|
|
}
|
|
// now distribute the rest
|
|
if ($data->allocateby != 'no') {
|
|
for ($i=0; $i<$numgrps; $i++) {
|
|
if (empty($users)) {
|
|
break 1;
|
|
}
|
|
$user = array_shift($users);
|
|
$groups[$i]['members'][$user->id] = $user;
|
|
}
|
|
}
|
|
|
|
if (isset($data->preview)) {
|
|
$table = new html_table();
|
|
if ($data->allocateby == 'no') {
|
|
$table->head = array(get_string('groupscount', 'group', $numgrps));
|
|
$table->size = array('100%');
|
|
$table->align = array('left');
|
|
$table->width = '40%';
|
|
} else {
|
|
$table->head = array(get_string('groupscount', 'group', $numgrps), get_string('groupmembers', 'group'), get_string('usercounttotal', 'group', $usercnt));
|
|
$table->size = array('20%', '70%', '10%');
|
|
$table->align = array('left', 'left', 'center');
|
|
$table->width = '90%';
|
|
}
|
|
$table->data = array();
|
|
$viewfullnames = has_capability('moodle/site:viewfullnames', $context);
|
|
foreach ($groups as $group) {
|
|
$line = array();
|
|
if (groups_get_group_by_name($courseid, $group['name'])) {
|
|
$line[] = '<span class="notifyproblem">'.get_string('groupnameexists', 'group', $group['name']).'</span>';
|
|
$error = get_string('groupnameexists', 'group', $group['name']);
|
|
} else {
|
|
$line[] = $group['name'];
|
|
}
|
|
if ($data->allocateby != 'no') {
|
|
$unames = array();
|
|
foreach ($group['members'] as $user) {
|
|
$fullname = fullname($user, $viewfullnames);
|
|
if ($extrafields) {
|
|
$extrafieldsdisplay = [];
|
|
foreach ($extrafields as $field) {
|
|
$extrafieldsdisplay[] = s($user->{$field});
|
|
}
|
|
$fullname .= ' (' . implode(', ', $extrafieldsdisplay) . ')';
|
|
}
|
|
|
|
$unames[] = $fullname;
|
|
}
|
|
$line[] = implode(', ', $unames);
|
|
$line[] = count($group['members']);
|
|
}
|
|
$table->data[] = $line;
|
|
}
|
|
|
|
$preview .= html_writer::table($table);
|
|
|
|
} else {
|
|
$grouping = null;
|
|
$createdgrouping = null;
|
|
$createdgroups = array();
|
|
$failed = false;
|
|
|
|
// prepare grouping
|
|
if (!empty($data->grouping)) {
|
|
if ($data->grouping < 0) {
|
|
$grouping = new stdClass();
|
|
$grouping->courseid = $COURSE->id;
|
|
$grouping->name = trim($data->groupingname);
|
|
$grouping->id = groups_create_grouping($grouping);
|
|
$createdgrouping = $grouping->id;
|
|
} else {
|
|
$grouping = groups_get_grouping($data->grouping);
|
|
}
|
|
}
|
|
|
|
// Save the groups data
|
|
foreach ($groups as $key=>$group) {
|
|
if (groups_get_group_by_name($courseid, $group['name'])) {
|
|
$error = get_string('groupnameexists', 'group', $group['name']);
|
|
$failed = true;
|
|
break;
|
|
}
|
|
$newgroup = new stdClass();
|
|
$newgroup->courseid = $data->courseid;
|
|
$newgroup->name = $group['name'];
|
|
$newgroup->enablemessaging = $data->enablemessaging ?? 0;
|
|
$newgroup->visibility = GROUPS_VISIBILITY_ALL;
|
|
$groupid = groups_create_group($newgroup);
|
|
$createdgroups[] = $groupid;
|
|
foreach($group['members'] as $user) {
|
|
groups_add_member($groupid, $user->id);
|
|
}
|
|
if ($grouping) {
|
|
// Ask this function not to invalidate the cache, we'll do that manually once at the end.
|
|
groups_assign_grouping($grouping->id, $groupid, null, false);
|
|
}
|
|
}
|
|
|
|
// Invalidate the course groups cache seeing as we've changed it.
|
|
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($courseid));
|
|
|
|
if ($failed) {
|
|
foreach ($createdgroups as $groupid) {
|
|
groups_delete_group($groupid);
|
|
}
|
|
if ($createdgrouping) {
|
|
groups_delete_grouping($createdgrouping);
|
|
}
|
|
} else {
|
|
redirect($returnurl);
|
|
}
|
|
}
|
|
}
|
|
|
|
$PAGE->navbar->add($strparticipants, new moodle_url('/user/index.php', array('id'=>$courseid)));
|
|
$PAGE->navbar->add($strgroups, new moodle_url('/group/index.php', array('id'=>$courseid)));
|
|
$PAGE->navbar->add($strautocreategroups);
|
|
|
|
echo $OUTPUT->header();
|
|
echo $OUTPUT->heading($strautocreategroups);
|
|
|
|
if ($error != '') {
|
|
echo $OUTPUT->notification($error);
|
|
}
|
|
|
|
/// Display the form
|
|
$editform->display();
|
|
|
|
if($preview !== '') {
|
|
echo $OUTPUT->heading(get_string('groupspreview', 'group'));
|
|
|
|
echo $preview;
|
|
}
|
|
|
|
echo $OUTPUT->footer();
|