MDL-81514 groups: Add participationonly option to activity group menu
groups_print_activity_menu() and groups_get_activity_group() now include an additional $participationonly parameter, which is true by default. This can be set false when we want the user to be able to select a non-participation group within an activity, for example if a teacher wants to filter assignment submissions by non-participation groups. It should never be used when the menu is displayed to students, as this may allow them to participate using non-participation groups. groups_sort_menu_options() now has a $splitparticipation parameter, which will split non-participation groups out into their own optgroup at the end of the menu.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
issueNumber: MDL-81514
|
||||
notes:
|
||||
core_group:
|
||||
- message: >
|
||||
`groups_print_activity_menu()` and `groups_get_activity_group()` now
|
||||
include an additional `$participationonly` parameter, which is true by
|
||||
default. This can be set false when we want the user to be able to
|
||||
select a non-participation group within an activity, for example if a
|
||||
teacher wants to filter assignment submissions by non-participation
|
||||
groups. It should never be used when the menu is displayed to students,
|
||||
as this may allow them to participate using non-participation groups.
|
||||
Non-participation groups are labeled as such.
|
||||
type: improved
|
||||
@@ -35,8 +35,18 @@ class group_selector extends comboboxsearch {
|
||||
* The class constructor.
|
||||
*
|
||||
* @param stdClass $context The context object.
|
||||
* @param bool $participationonly Only include participation groups?
|
||||
*/
|
||||
public function __construct(private stdClass $context) {
|
||||
public function __construct(
|
||||
/**
|
||||
* @var stdClass The context object.
|
||||
*/
|
||||
private stdClass $context,
|
||||
/**
|
||||
* @var bool Only include participation groups?
|
||||
*/
|
||||
protected bool $participationonly = true,
|
||||
) {
|
||||
$this->activegroup = $this->get_active_group();
|
||||
$this->label = $this->get_label();
|
||||
|
||||
@@ -100,22 +110,20 @@ class group_selector extends comboboxsearch {
|
||||
if ($this->context->contextlevel === CONTEXT_MODULE) {
|
||||
$cm = get_coursemodule_from_id(false, $this->context->instanceid);
|
||||
$groupingid = $cm->groupingid;
|
||||
$participationonly = true;
|
||||
} else {
|
||||
$cm = null;
|
||||
$groupingid = $course->defaultgroupingid;
|
||||
$participationonly = false;
|
||||
}
|
||||
|
||||
$allowedgroups = groups_get_all_groups(
|
||||
courseid: $course->id,
|
||||
userid: $userid,
|
||||
groupingid: $groupingid,
|
||||
participationonly: $participationonly
|
||||
participationonly: $this->participationonly,
|
||||
);
|
||||
|
||||
if ($cm) {
|
||||
return groups_get_activity_group($cm, true, $allowedgroups);
|
||||
return groups_get_activity_group($cm, true, $allowedgroups, $this->participationonly);
|
||||
}
|
||||
return groups_get_course_group($course, true, $allowedgroups);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
define("core_group/comboboxsearch/group",["exports","core/comboboxsearch/search_combobox","core_group/comboboxsearch/repository","core/templates","core/utils","core/notification"],(function(_exports,_search_combobox,_repository,_templates,_utils,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=_interopRequireDefault(_search_combobox),_notification=_interopRequireDefault(_notification);class GroupSearch extends _search_combobox.default{constructor(){let cmid=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"cmID",void 0),_defineProperty(this,"bannedFilterFields",["id","link","groupimageurl"]),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.groupsearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.instance=component.querySelector(this.selectors.instance).dataset.instance,this.cmID=cmid;const searchValueElement=this.component.querySelector("#".concat(this.searchInput.dataset.inputElement));searchValueElement.addEventListener("change",(()=>{this.toggleDropdown();const valueElement=this.component.querySelector("#".concat(this.combobox.dataset.inputElement));valueElement.value!==searchValueElement.value&&(valueElement.value=searchValueElement.value,valueElement.dispatchEvent(new Event("change",{bubbles:!0}))),searchValueElement.value=""})),this.component.addEventListener("hide.bs.dropdown",(()=>{this.searchInput.removeAttribute("aria-activedescendant");const listbox=document.querySelector("#".concat(this.searchInput.getAttribute("aria-controls"),'[role="listbox"]'));listbox.querySelectorAll('.active[role="option"]').forEach((option=>{option.classList.remove("active")})),listbox.scrollTop=0,setTimeout((()=>{""!==this.searchInput.value&&(this.searchInput.value="",this.searchInput.dispatchEvent(new Event("input",{bubbles:!0})))}))})),this.renderDefault().catch(_notification.default.exception)}static init(){return new GroupSearch(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}componentSelector(){return".group-search"}dropdownSelector(){return".groupsearchdropdown"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_group/comboboxsearch/resultset",{groups:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,instance:this.instance,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js),this.searchInput.removeAttribute("aria-activedescendant")}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes()}async fetchDataset(){return await(0,_repository.groupFetch)(this.courseID,this.cmID).then((r=>r.groups))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((group=>Object.keys(group).some((key=>""!==group[key]&&!this.bannedFilterFields.includes(key)&&group[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((group=>({id:group.id,name:group.name,groupimageurl:group.groupimageurl}))))}async clickHandler(e){e.target.closest(this.selectors.clearSearch)&&(e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),this.searchInput.focus(),this.clearSearchButton.classList.add("d-none"),await this.filterrenderpipe())}changeHandler(e){window.location=this.selectOneLink(e.target.value)}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}selectOneLink(groupID){throw new Error("selectOneLink(".concat(groupID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GroupSearch,_exports.default}));
|
||||
define("core_group/comboboxsearch/group",["exports","core/comboboxsearch/search_combobox","core_group/comboboxsearch/repository","core/templates","core/utils","core/notification"],(function(_exports,_search_combobox,_repository,_templates,_utils,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=_interopRequireDefault(_search_combobox),_notification=_interopRequireDefault(_notification);class GroupSearch extends _search_combobox.default{constructor(){let cmid=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"cmID",void 0),_defineProperty(this,"bannedFilterFields",["id","link","groupimageurl"]),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.groupsearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.instance=component.querySelector(this.selectors.instance).dataset.instance,this.cmID=cmid;const searchValueElement=this.component.querySelector("#".concat(this.searchInput.dataset.inputElement));searchValueElement.addEventListener("change",(()=>{this.toggleDropdown();const valueElement=this.component.querySelector("#".concat(this.combobox.dataset.inputElement));valueElement.value!==searchValueElement.value&&(valueElement.value=searchValueElement.value,valueElement.dispatchEvent(new Event("change",{bubbles:!0}))),searchValueElement.value=""})),this.component.addEventListener("hide.bs.dropdown",(()=>{this.searchInput.removeAttribute("aria-activedescendant");const listbox=document.querySelector("#".concat(this.searchInput.getAttribute("aria-controls"),'[role="listbox"]'));listbox.querySelectorAll('.active[role="option"]').forEach((option=>{option.classList.remove("active")})),listbox.scrollTop=0,setTimeout((()=>{""!==this.searchInput.value&&(this.searchInput.value="",this.searchInput.dispatchEvent(new Event("input",{bubbles:!0})))}))})),this.renderDefault().catch(_notification.default.exception)}static init(){return new GroupSearch(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}componentSelector(){return".group-search"}dropdownSelector(){return".groupsearchdropdown"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_group/comboboxsearch/resultset",{groups:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,instance:this.instance,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js),this.searchInput.removeAttribute("aria-activedescendant")}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes()}async fetchDataset(){return await(0,_repository.groupFetch)(this.courseID,this.cmID).then((r=>r.groups))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((group=>Object.keys(group).some((key=>""!==group[key]&&!this.bannedFilterFields.includes(key)&&group[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((group=>({id:group.id,name:group.name,groupimageurl:group.groupimageurl,participation:group.participation}))))}async clickHandler(e){e.target.closest(this.selectors.clearSearch)&&(e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),this.searchInput.focus(),this.clearSearchButton.classList.add("d-none"),await this.filterrenderpipe())}changeHandler(e){window.location=this.selectOneLink(e.target.value)}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}selectOneLink(groupID){throw new Error("selectOneLink(".concat(groupID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GroupSearch,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=group.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -175,6 +175,7 @@ export default class GroupSearch extends search_combobox {
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
groupimageurl: group.groupimageurl,
|
||||
participation: group.participation,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
@@ -85,7 +85,7 @@ class get_groups_for_selector extends external_api {
|
||||
$cm = get_coursemodule_from_id('', $params['cmid']);
|
||||
$groupmode = groups_get_activity_groupmode($cm, $course);
|
||||
$groupingid = $cm->groupingid;
|
||||
$participationonly = true;
|
||||
$participationonly = false;
|
||||
} else {
|
||||
$context = context_course::instance($params['courseid']);
|
||||
$groupmode = $course->groupmode;
|
||||
@@ -130,19 +130,22 @@ class get_groups_for_selector extends external_api {
|
||||
]);
|
||||
}
|
||||
|
||||
$mappedgroups = array_map(function($group) use ($context, $OUTPUT) {
|
||||
$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->get_course_context()->id, 'group', 'generated', $group->id,
|
||||
'/', 'group.svg');
|
||||
$participation = $group->participation;
|
||||
} else { // All participants.
|
||||
$picture = $OUTPUT->image_url('g/g1');
|
||||
$participation = true;
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'id' => $group->id,
|
||||
'name' => format_string($group->name, true, ['context' => $context]),
|
||||
'groupimageurl' => $picture->out(false),
|
||||
'participation' => $participation,
|
||||
];
|
||||
}, $groupsmenu);
|
||||
}
|
||||
@@ -175,6 +178,7 @@ class get_groups_for_selector extends external_api {
|
||||
'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),
|
||||
'participation' => new external_value(PARAM_BOOL, 'Is this a participation group?', VALUE_REQUIRED),
|
||||
];
|
||||
return new external_single_structure($groupfields);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@
|
||||
<span class="d-block w-100 p-0 text-truncate">
|
||||
{{name}}
|
||||
</span>
|
||||
{{^participation}}
|
||||
<span class="d-block w-100 pull-left text-truncate small text-secondary-emphasis">
|
||||
{{#str}} nonparticipation, group {{/str}}
|
||||
</span>
|
||||
{{/participation}}
|
||||
</div>
|
||||
{{/content}}
|
||||
{{/core/local/comboboxsearch/resultitem}}
|
||||
|
||||
@@ -28,12 +28,20 @@
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Foo bar",
|
||||
"link": "http://foo.bar/grade/report/grader/index.php?id=42&userid=2"
|
||||
"link": "http://foo.bar/grade/report/grader/index.php?id=42&userid=2",
|
||||
"participation": true
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Bar Foo",
|
||||
"link": "http://foo.bar/grade/report/grader/index.php?id=42&userid=3"
|
||||
"link": "http://foo.bar/grade/report/grader/index.php?id=42&userid=3",
|
||||
"participation": true
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Baz Bar Foo",
|
||||
"link": "http://foo.bar/grade/report/grader/index.php?id=43&userid=2",
|
||||
"participation": false
|
||||
}
|
||||
],
|
||||
"instance": 25,
|
||||
|
||||
@@ -178,6 +178,7 @@ $string['manageactions'] = 'Manage';
|
||||
$string['messagingdisabled'] = 'Successfully disabled messaging in {$a} group(s)';
|
||||
$string['messagingenabled'] = 'Successfully enabled messaging in {$a} group(s)';
|
||||
$string['mygroups'] = 'My groups';
|
||||
$string['nonparticipation'] = 'Non-participation';
|
||||
$string['othergroups'] = 'Other groups';
|
||||
$string['overview'] = 'Overview';
|
||||
$string['participation'] = 'Show group in dropdown menu for activities in group mode';
|
||||
|
||||
+78
-19
@@ -833,11 +833,17 @@ function groups_list_to_menu($groups) {
|
||||
* Own groups are removed from allowed groups
|
||||
* @param array $allowedgroups All groups user is allowed to see
|
||||
* @param array $usergroups Groups user belongs to
|
||||
* @param bool $splitparticipation If true, split each optgroup into "Participation" and "Non-participation" optgroups.
|
||||
* @return array
|
||||
*/
|
||||
function groups_sort_menu_options($allowedgroups, $usergroups) {
|
||||
$useroptions = array();
|
||||
function groups_sort_menu_options($allowedgroups, $usergroups, bool $splitparticipation = false) {
|
||||
$nonparticipationgroups = [];
|
||||
$useroptions = [];
|
||||
if ($usergroups) {
|
||||
if ($splitparticipation) {
|
||||
[$usergroups, $usernonparticipation] = groups_split_participation_groups($usergroups);
|
||||
$nonparticipationgroups = array_merge($nonparticipationgroups, $usernonparticipation);
|
||||
}
|
||||
$useroptions = groups_list_to_menu($usergroups);
|
||||
|
||||
// Remove user groups from other groups list.
|
||||
@@ -846,21 +852,54 @@ function groups_sort_menu_options($allowedgroups, $usergroups) {
|
||||
}
|
||||
}
|
||||
|
||||
$allowedoptions = array();
|
||||
$allowedoptions = [];
|
||||
if ($allowedgroups) {
|
||||
if ($splitparticipation) {
|
||||
[$allowedgroups, $allowednonparticipation] = groups_split_participation_groups($allowedgroups);
|
||||
$nonparticipationgroups = array_merge($nonparticipationgroups, $allowednonparticipation);
|
||||
}
|
||||
$allowedoptions = groups_list_to_menu($allowedgroups);
|
||||
}
|
||||
|
||||
if ($useroptions && $allowedoptions) {
|
||||
return array(
|
||||
1 => array(get_string('mygroups', 'group') => $useroptions),
|
||||
2 => array(get_string('othergroups', 'group') => $allowedoptions)
|
||||
);
|
||||
$options = [
|
||||
1 => [
|
||||
get_string('mygroups', 'group') => $useroptions,
|
||||
],
|
||||
2 => [
|
||||
get_string('othergroups', 'group') => $allowedoptions,
|
||||
],
|
||||
];
|
||||
} else if ($useroptions) {
|
||||
return $useroptions;
|
||||
$options = $useroptions;
|
||||
} else {
|
||||
return $allowedoptions;
|
||||
$options = $allowedoptions;
|
||||
}
|
||||
if ($nonparticipationgroups) {
|
||||
$options[3] = [
|
||||
get_string('nonparticipation', 'group') => groups_list_to_menu($nonparticipationgroups),
|
||||
];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the list of groups into participation and non-participation groups.
|
||||
*
|
||||
* @param array $groups List of group records
|
||||
* @return array[] Menu options for the records, split into "Participation" and "Non-participation" optgroups.
|
||||
*/
|
||||
function groups_split_participation_groups(array $groups): array {
|
||||
$participation = [];
|
||||
$nonparticipation = [];
|
||||
foreach ($groups as $group) {
|
||||
if ($group->participation) {
|
||||
$participation[] = $group;
|
||||
} else {
|
||||
$nonparticipation[] = $group;
|
||||
}
|
||||
}
|
||||
return [$participation, $nonparticipation];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -934,9 +973,20 @@ function groups_allgroups_course_menu($course, $urlroot, $update = false, $activ
|
||||
* selecting this option does not prevent groups_get_activity_group from
|
||||
* returning 0; it will still do that if the user has chosen 'all participants'
|
||||
* in another activity, or not chosen anything.)
|
||||
* @param bool $participationonly By default, this menu will only contain groups with the "participation"
|
||||
* flag set true. Setting this argument to false will return all groups that the user is allowed to see.
|
||||
* This should only be used for cases such as a teacher wanting to filter submissions by group, not for
|
||||
* students choosing a group to submit their work under, otherwise it negates the point of the participation
|
||||
* flag.
|
||||
* @return mixed void or string depending on $return param
|
||||
*/
|
||||
function groups_print_activity_menu($cm, $urlroot, $return=false, $hideallparticipants=false) {
|
||||
function groups_print_activity_menu(
|
||||
$cm,
|
||||
$urlroot,
|
||||
$return = false,
|
||||
$hideallparticipants = false,
|
||||
bool $participationonly = true,
|
||||
) {
|
||||
global $USER, $OUTPUT;
|
||||
|
||||
if ($urlroot instanceof moodle_url) {
|
||||
@@ -966,22 +1016,23 @@ function groups_print_activity_menu($cm, $urlroot, $return=false, $hideallpartic
|
||||
|
||||
$usergroups = array();
|
||||
if ($groupmode == VISIBLEGROUPS or $aag) {
|
||||
$allowedgroups = groups_get_all_groups($cm->course, 0, $cm->groupingid, 'g.*', false, true); // Any group in grouping.
|
||||
// Any group in grouping.
|
||||
$allowedgroups = groups_get_all_groups($cm->course, 0, $cm->groupingid, 'g.*', false, $participationonly);
|
||||
// Get user's own groups and put to the top.
|
||||
$usergroups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid, 'g.*', false, true);
|
||||
$usergroups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid, 'g.*', false, $participationonly);
|
||||
} else {
|
||||
// Only assigned groups.
|
||||
$allowedgroups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid, 'g.*', false, true);
|
||||
$allowedgroups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid, 'g.*', false, $participationonly);
|
||||
}
|
||||
|
||||
$activegroup = groups_get_activity_group($cm, true, $allowedgroups);
|
||||
$activegroup = groups_get_activity_group($cm, true, $allowedgroups, $participationonly);
|
||||
|
||||
$groupsmenu = array();
|
||||
if ((!$allowedgroups or $groupmode == VISIBLEGROUPS or $aag) and !$hideallparticipants) {
|
||||
$groupsmenu[0] = get_string('allparticipants');
|
||||
}
|
||||
|
||||
$groupsmenu += groups_sort_menu_options($allowedgroups, $usergroups);
|
||||
$groupsmenu += groups_sort_menu_options($allowedgroups, $usergroups, !$participationonly);
|
||||
|
||||
if ($groupmode == VISIBLEGROUPS) {
|
||||
$grouplabel = get_string('groupsvisible');
|
||||
@@ -1068,13 +1119,21 @@ function groups_get_course_group($course, $update=false, $allowedgroups=null) {
|
||||
/**
|
||||
* Returns group active in activity, changes the group by default if 'group' page param present
|
||||
*
|
||||
* @category group
|
||||
* @param stdClass|cm_info $cm course module object
|
||||
* @param bool $update change active group if group param submitted
|
||||
* @param array $allowedgroups list of groups user may access (INTERNAL, to be used only from groups_print_activity_menu())
|
||||
* @param bool $participationonly By default, only allow groups with the "participation"
|
||||
* flag set true. Setting this argument to false will allow setting a non-participation group as the active group.
|
||||
* This should be used with care, and only for users who should be able to see non-participation groups within an activity.
|
||||
* @return mixed false if groups not used, int if groups used, 0 means all groups (access must be verified in SEPARATE mode)
|
||||
* @category group
|
||||
*/
|
||||
function groups_get_activity_group($cm, $update=false, $allowedgroups=null) {
|
||||
function groups_get_activity_group(
|
||||
$cm,
|
||||
$update = false,
|
||||
$allowedgroups = null,
|
||||
bool $participationonly = true,
|
||||
) {
|
||||
global $USER, $SESSION;
|
||||
|
||||
if (!$groupmode = groups_get_activity_groupmode($cm)) {
|
||||
@@ -1089,9 +1148,9 @@ function groups_get_activity_group($cm, $update=false, $allowedgroups=null) {
|
||||
|
||||
if (!is_array($allowedgroups)) {
|
||||
if ($groupmode == VISIBLEGROUPS or $groupmode === 'aag') {
|
||||
$allowedgroups = groups_get_all_groups($cm->course, 0, $cm->groupingid, 'g.*', false, true);
|
||||
$allowedgroups = groups_get_all_groups($cm->course, 0, $cm->groupingid, 'g.*', false, $participationonly);
|
||||
} else {
|
||||
$allowedgroups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid, 'g.*', false, true);
|
||||
$allowedgroups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid, 'g.*', false, $participationonly);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1236,14 +1236,18 @@ final class grouplib_test extends \advanced_testcase {
|
||||
/**
|
||||
* Create dummy groups array for use in menu tests
|
||||
* @param int $number
|
||||
* @param bool $alternateparticipation If true, set participation = 1 for even groups, and 0 for odd groups.
|
||||
* @return array
|
||||
*/
|
||||
protected function make_group_list($number) {
|
||||
protected function make_group_list($number, $alternateparticipation = false) {
|
||||
$testgroups = array();
|
||||
for ($a = 0; $a < $number; $a++) {
|
||||
$grp = new \stdClass();
|
||||
$grp->id = 100 + $a;
|
||||
$grp->name = 'test group ' . $grp->id;
|
||||
if ($alternateparticipation) {
|
||||
$grp->participation = ($a % 2 == 0);
|
||||
}
|
||||
$testgroups[$grp->id] = $grp;
|
||||
}
|
||||
return $testgroups;
|
||||
@@ -1302,6 +1306,82 @@ final class grouplib_test extends \advanced_testcase {
|
||||
), groups_sort_menu_options($this->make_group_list(13), $this->make_group_list(2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Splitting allowed groups by participation returns an optgroup for non-participation groups.
|
||||
*
|
||||
* @covers ::groups_sort_menu_options()
|
||||
* @return void
|
||||
*/
|
||||
public function test_groups_split_participation_allowed_groups_only(): void {
|
||||
$this->assertEquals(
|
||||
[
|
||||
100 => 'test group 100',
|
||||
3 => [
|
||||
get_string('nonparticipation', 'group') => [
|
||||
101 => 'test group 101',
|
||||
],
|
||||
],
|
||||
],
|
||||
groups_sort_menu_options($this->make_group_list(2, true), [], true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splitting user groups by participation returns an optgroup for non-participation groups.
|
||||
*
|
||||
* @covers ::groups_sort_menu_options()
|
||||
* @return void
|
||||
*/
|
||||
public function test_groups_split_participation_options_user_groups_only(): void {
|
||||
$this->assertEquals(
|
||||
[
|
||||
100 => 'test group 100',
|
||||
3 => [
|
||||
get_string('nonparticipation', 'group') => [
|
||||
101 => 'test group 101',
|
||||
],
|
||||
],
|
||||
],
|
||||
groups_sort_menu_options([], $this->make_group_list(2, true), true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splitting allowed and user groups by participation returns optgroups.
|
||||
*
|
||||
* One optgroup for user participation groups, one for other participation groups, and one for non-participation groups.
|
||||
*
|
||||
* @covers ::groups_sort_menu_options()
|
||||
* @return void
|
||||
*/
|
||||
public function test_groups_split_participation_options_user_both(): void {
|
||||
$this->assertEquals(
|
||||
[
|
||||
1 => [
|
||||
get_string('mygroups', 'group') => [
|
||||
100 => 'test group 100',
|
||||
],
|
||||
],
|
||||
2 => [
|
||||
get_string('othergroups', 'group') => [
|
||||
102 => 'test group 102',
|
||||
],
|
||||
],
|
||||
3 => [
|
||||
get_string('nonparticipation', 'group') => [
|
||||
101 => 'test group 101',
|
||||
103 => 'test group 103',
|
||||
],
|
||||
],
|
||||
],
|
||||
groups_sort_menu_options(
|
||||
$this->make_group_list(4, true),
|
||||
$this->make_group_list(2, true),
|
||||
true
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for groups_user_groups_visible.
|
||||
*/
|
||||
|
||||
@@ -44,12 +44,13 @@
|
||||
}
|
||||
|
||||
// Add dropdown menu items styles for each theme color mantainning default hover colour for contrast.
|
||||
@each $color, $value in $theme-colors {
|
||||
@each $color, $value in map-merge($theme-colors, $utilities-text-emphasis-colors) {
|
||||
.dropdown-item {
|
||||
&:hover,
|
||||
&:focus {
|
||||
&.text-#{$color},
|
||||
a.text-#{$color} {
|
||||
a.text-#{$color},
|
||||
span.text-#{$color} {
|
||||
color: $dropdown-link-hover-color !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41031,50 +41031,130 @@ div.editor_atto_toolbar button .icon {
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-primary,
|
||||
.dropdown-item:hover a.text-primary, .dropdown-item:focus.text-primary,
|
||||
.dropdown-item:focus a.text-primary {
|
||||
.dropdown-item:hover a.text-primary,
|
||||
.dropdown-item:hover span.text-primary, .dropdown-item:focus.text-primary,
|
||||
.dropdown-item:focus a.text-primary,
|
||||
.dropdown-item:focus span.text-primary {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-secondary,
|
||||
.dropdown-item:hover a.text-secondary, .dropdown-item:focus.text-secondary,
|
||||
.dropdown-item:focus a.text-secondary {
|
||||
.dropdown-item:hover a.text-secondary,
|
||||
.dropdown-item:hover span.text-secondary, .dropdown-item:focus.text-secondary,
|
||||
.dropdown-item:focus a.text-secondary,
|
||||
.dropdown-item:focus span.text-secondary {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-success,
|
||||
.dropdown-item:hover a.text-success, .dropdown-item:focus.text-success,
|
||||
.dropdown-item:focus a.text-success {
|
||||
.dropdown-item:hover a.text-success,
|
||||
.dropdown-item:hover span.text-success, .dropdown-item:focus.text-success,
|
||||
.dropdown-item:focus a.text-success,
|
||||
.dropdown-item:focus span.text-success {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-info,
|
||||
.dropdown-item:hover a.text-info, .dropdown-item:focus.text-info,
|
||||
.dropdown-item:focus a.text-info {
|
||||
.dropdown-item:hover a.text-info,
|
||||
.dropdown-item:hover span.text-info, .dropdown-item:focus.text-info,
|
||||
.dropdown-item:focus a.text-info,
|
||||
.dropdown-item:focus span.text-info {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-warning,
|
||||
.dropdown-item:hover a.text-warning, .dropdown-item:focus.text-warning,
|
||||
.dropdown-item:focus a.text-warning {
|
||||
.dropdown-item:hover a.text-warning,
|
||||
.dropdown-item:hover span.text-warning, .dropdown-item:focus.text-warning,
|
||||
.dropdown-item:focus a.text-warning,
|
||||
.dropdown-item:focus span.text-warning {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-danger,
|
||||
.dropdown-item:hover a.text-danger, .dropdown-item:focus.text-danger,
|
||||
.dropdown-item:focus a.text-danger {
|
||||
.dropdown-item:hover a.text-danger,
|
||||
.dropdown-item:hover span.text-danger, .dropdown-item:focus.text-danger,
|
||||
.dropdown-item:focus a.text-danger,
|
||||
.dropdown-item:focus span.text-danger {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-light,
|
||||
.dropdown-item:hover a.text-light, .dropdown-item:focus.text-light,
|
||||
.dropdown-item:focus a.text-light {
|
||||
.dropdown-item:hover a.text-light,
|
||||
.dropdown-item:hover span.text-light, .dropdown-item:focus.text-light,
|
||||
.dropdown-item:focus a.text-light,
|
||||
.dropdown-item:focus span.text-light {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-dark,
|
||||
.dropdown-item:hover a.text-dark, .dropdown-item:focus.text-dark,
|
||||
.dropdown-item:focus a.text-dark {
|
||||
.dropdown-item:hover a.text-dark,
|
||||
.dropdown-item:hover span.text-dark, .dropdown-item:focus.text-dark,
|
||||
.dropdown-item:focus a.text-dark,
|
||||
.dropdown-item:focus span.text-dark {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-primary-emphasis,
|
||||
.dropdown-item:hover a.text-primary-emphasis,
|
||||
.dropdown-item:hover span.text-primary-emphasis, .dropdown-item:focus.text-primary-emphasis,
|
||||
.dropdown-item:focus a.text-primary-emphasis,
|
||||
.dropdown-item:focus span.text-primary-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-secondary-emphasis,
|
||||
.dropdown-item:hover a.text-secondary-emphasis,
|
||||
.dropdown-item:hover span.text-secondary-emphasis, .dropdown-item:focus.text-secondary-emphasis,
|
||||
.dropdown-item:focus a.text-secondary-emphasis,
|
||||
.dropdown-item:focus span.text-secondary-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-success-emphasis,
|
||||
.dropdown-item:hover a.text-success-emphasis,
|
||||
.dropdown-item:hover span.text-success-emphasis, .dropdown-item:focus.text-success-emphasis,
|
||||
.dropdown-item:focus a.text-success-emphasis,
|
||||
.dropdown-item:focus span.text-success-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-info-emphasis,
|
||||
.dropdown-item:hover a.text-info-emphasis,
|
||||
.dropdown-item:hover span.text-info-emphasis, .dropdown-item:focus.text-info-emphasis,
|
||||
.dropdown-item:focus a.text-info-emphasis,
|
||||
.dropdown-item:focus span.text-info-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-warning-emphasis,
|
||||
.dropdown-item:hover a.text-warning-emphasis,
|
||||
.dropdown-item:hover span.text-warning-emphasis, .dropdown-item:focus.text-warning-emphasis,
|
||||
.dropdown-item:focus a.text-warning-emphasis,
|
||||
.dropdown-item:focus span.text-warning-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-danger-emphasis,
|
||||
.dropdown-item:hover a.text-danger-emphasis,
|
||||
.dropdown-item:hover span.text-danger-emphasis, .dropdown-item:focus.text-danger-emphasis,
|
||||
.dropdown-item:focus a.text-danger-emphasis,
|
||||
.dropdown-item:focus span.text-danger-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-light-emphasis,
|
||||
.dropdown-item:hover a.text-light-emphasis,
|
||||
.dropdown-item:hover span.text-light-emphasis, .dropdown-item:focus.text-light-emphasis,
|
||||
.dropdown-item:focus a.text-light-emphasis,
|
||||
.dropdown-item:focus span.text-light-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-dark-emphasis,
|
||||
.dropdown-item:hover a.text-dark-emphasis,
|
||||
.dropdown-item:hover span.text-dark-emphasis, .dropdown-item:focus.text-dark-emphasis,
|
||||
.dropdown-item:focus a.text-dark-emphasis,
|
||||
.dropdown-item:focus span.text-dark-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
|
||||
@@ -40965,50 +40965,130 @@ div.editor_atto_toolbar button .icon {
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-primary,
|
||||
.dropdown-item:hover a.text-primary, .dropdown-item:focus.text-primary,
|
||||
.dropdown-item:focus a.text-primary {
|
||||
.dropdown-item:hover a.text-primary,
|
||||
.dropdown-item:hover span.text-primary, .dropdown-item:focus.text-primary,
|
||||
.dropdown-item:focus a.text-primary,
|
||||
.dropdown-item:focus span.text-primary {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-secondary,
|
||||
.dropdown-item:hover a.text-secondary, .dropdown-item:focus.text-secondary,
|
||||
.dropdown-item:focus a.text-secondary {
|
||||
.dropdown-item:hover a.text-secondary,
|
||||
.dropdown-item:hover span.text-secondary, .dropdown-item:focus.text-secondary,
|
||||
.dropdown-item:focus a.text-secondary,
|
||||
.dropdown-item:focus span.text-secondary {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-success,
|
||||
.dropdown-item:hover a.text-success, .dropdown-item:focus.text-success,
|
||||
.dropdown-item:focus a.text-success {
|
||||
.dropdown-item:hover a.text-success,
|
||||
.dropdown-item:hover span.text-success, .dropdown-item:focus.text-success,
|
||||
.dropdown-item:focus a.text-success,
|
||||
.dropdown-item:focus span.text-success {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-info,
|
||||
.dropdown-item:hover a.text-info, .dropdown-item:focus.text-info,
|
||||
.dropdown-item:focus a.text-info {
|
||||
.dropdown-item:hover a.text-info,
|
||||
.dropdown-item:hover span.text-info, .dropdown-item:focus.text-info,
|
||||
.dropdown-item:focus a.text-info,
|
||||
.dropdown-item:focus span.text-info {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-warning,
|
||||
.dropdown-item:hover a.text-warning, .dropdown-item:focus.text-warning,
|
||||
.dropdown-item:focus a.text-warning {
|
||||
.dropdown-item:hover a.text-warning,
|
||||
.dropdown-item:hover span.text-warning, .dropdown-item:focus.text-warning,
|
||||
.dropdown-item:focus a.text-warning,
|
||||
.dropdown-item:focus span.text-warning {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-danger,
|
||||
.dropdown-item:hover a.text-danger, .dropdown-item:focus.text-danger,
|
||||
.dropdown-item:focus a.text-danger {
|
||||
.dropdown-item:hover a.text-danger,
|
||||
.dropdown-item:hover span.text-danger, .dropdown-item:focus.text-danger,
|
||||
.dropdown-item:focus a.text-danger,
|
||||
.dropdown-item:focus span.text-danger {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-light,
|
||||
.dropdown-item:hover a.text-light, .dropdown-item:focus.text-light,
|
||||
.dropdown-item:focus a.text-light {
|
||||
.dropdown-item:hover a.text-light,
|
||||
.dropdown-item:hover span.text-light, .dropdown-item:focus.text-light,
|
||||
.dropdown-item:focus a.text-light,
|
||||
.dropdown-item:focus span.text-light {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-dark,
|
||||
.dropdown-item:hover a.text-dark, .dropdown-item:focus.text-dark,
|
||||
.dropdown-item:focus a.text-dark {
|
||||
.dropdown-item:hover a.text-dark,
|
||||
.dropdown-item:hover span.text-dark, .dropdown-item:focus.text-dark,
|
||||
.dropdown-item:focus a.text-dark,
|
||||
.dropdown-item:focus span.text-dark {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-primary-emphasis,
|
||||
.dropdown-item:hover a.text-primary-emphasis,
|
||||
.dropdown-item:hover span.text-primary-emphasis, .dropdown-item:focus.text-primary-emphasis,
|
||||
.dropdown-item:focus a.text-primary-emphasis,
|
||||
.dropdown-item:focus span.text-primary-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-secondary-emphasis,
|
||||
.dropdown-item:hover a.text-secondary-emphasis,
|
||||
.dropdown-item:hover span.text-secondary-emphasis, .dropdown-item:focus.text-secondary-emphasis,
|
||||
.dropdown-item:focus a.text-secondary-emphasis,
|
||||
.dropdown-item:focus span.text-secondary-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-success-emphasis,
|
||||
.dropdown-item:hover a.text-success-emphasis,
|
||||
.dropdown-item:hover span.text-success-emphasis, .dropdown-item:focus.text-success-emphasis,
|
||||
.dropdown-item:focus a.text-success-emphasis,
|
||||
.dropdown-item:focus span.text-success-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-info-emphasis,
|
||||
.dropdown-item:hover a.text-info-emphasis,
|
||||
.dropdown-item:hover span.text-info-emphasis, .dropdown-item:focus.text-info-emphasis,
|
||||
.dropdown-item:focus a.text-info-emphasis,
|
||||
.dropdown-item:focus span.text-info-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-warning-emphasis,
|
||||
.dropdown-item:hover a.text-warning-emphasis,
|
||||
.dropdown-item:hover span.text-warning-emphasis, .dropdown-item:focus.text-warning-emphasis,
|
||||
.dropdown-item:focus a.text-warning-emphasis,
|
||||
.dropdown-item:focus span.text-warning-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-danger-emphasis,
|
||||
.dropdown-item:hover a.text-danger-emphasis,
|
||||
.dropdown-item:hover span.text-danger-emphasis, .dropdown-item:focus.text-danger-emphasis,
|
||||
.dropdown-item:focus a.text-danger-emphasis,
|
||||
.dropdown-item:focus span.text-danger-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-light-emphasis,
|
||||
.dropdown-item:hover a.text-light-emphasis,
|
||||
.dropdown-item:hover span.text-light-emphasis, .dropdown-item:focus.text-light-emphasis,
|
||||
.dropdown-item:focus a.text-light-emphasis,
|
||||
.dropdown-item:focus span.text-light-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.dropdown-item:hover.text-dark-emphasis,
|
||||
.dropdown-item:hover a.text-dark-emphasis,
|
||||
.dropdown-item:hover span.text-dark-emphasis, .dropdown-item:focus.text-dark-emphasis,
|
||||
.dropdown-item:focus a.text-dark-emphasis,
|
||||
.dropdown-item:focus span.text-dark-emphasis {
|
||||
color: #fff !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user