forked from moodle/moodle
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MDL-68093 groups: Add visibility and participation settings
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.
- Loading branch information
1 parent
5e1df25
commit 958da5b
Showing
30 changed files
with
1,470 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
<?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 <[email protected]> | ||
* @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. | ||
* @return array [$where, $params] | ||
*/ | ||
public static function sql_member_visibility_where(string $groupsalias = 'g', | ||
string $groupsmembersalias = 'gm', string $useralias = 'u'): array { | ||
global $USER; | ||
|
||
list($memberssql, $membersparams) = self::sql_members_visibility_condition($groupsalias, $groupsmembersalias); | ||
|
||
$where = " AND ( | ||
{$groupsalias}.visibility = :all | ||
OR ($memberssql) | ||
OR ({$groupsalias}.visibility = :own AND {$useralias}.id = :currentuser2) | ||
)"; | ||
$params = [ | ||
'all' => GROUPS_VISIBILITY_ALL, | ||
'own' => GROUPS_VISIBILITY_OWN, | ||
'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. | ||
* @return array [$sql, $params] | ||
*/ | ||
protected static function sql_members_visibility_condition(string $groupsalias = 'g', | ||
string $groupsmembersalias = 'gm'): array { | ||
global $USER; | ||
$sql = "{$groupsalias}.visibility = :members | ||
AND ( | ||
SELECT gm2.id | ||
FROM {groups_members} gm2 | ||
WHERE gm2.groupid = {$groupsmembersalias}.groupid | ||
AND gm2.userid = :currentuser | ||
) IS NOT NULL"; | ||
$params = [ | ||
'members' => GROUPS_VISIBILITY_MEMBERS, | ||
'currentuser' => $USER->id | ||
]; | ||
|
||
return [$sql, $params]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.