Merge branch 'mdl-76270-master' of https://github.com/matthewhilton/moodle

This commit is contained in:
Jake Dallimore 2023-05-16 13:27:48 +08:00
commit c5ac5bcfd5
No known key found for this signature in database
15 changed files with 402 additions and 71 deletions

9
group/amd/build/grouppicker.min.js vendored Normal file
View File

@ -0,0 +1,9 @@
define("core_group/grouppicker",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default=
/**
* @module core_group/groupPicker
* @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class{constructor(){let elementId=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"groups";const pickerDomElement=document.getElementById(elementId);if(!pickerDomElement)throw new Error("Groups picker was not found.");this.element=pickerDomElement}getDomElement(){return this.element}getSelectedValues(){return Array.from(this.element.querySelectorAll("option:checked")).map((el=>parseInt(el.value)))}},_exports.default}));
//# sourceMappingURL=grouppicker.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"grouppicker.min.js","sources":["../src/grouppicker.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n/**\n * @module core_group/groupPicker\n * @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Class used for interfacing with the group select picker.\n *\n * @class core_group/GroupPicker\n */\nexport default class GroupPicker {\n /**\n * Creates the group picker class and finds the corresponding DOM element.\n *\n * @param {String} elementId The DOM element id of the <select> input\n * @throws Error if the element was not found.\n */\n constructor(elementId = \"groups\") {\n const pickerDomElement = document.getElementById(elementId);\n\n if (!pickerDomElement) {\n throw new Error(\"Groups picker was not found.\");\n }\n\n this.element = pickerDomElement;\n }\n\n /**\n * Returns the DOM element this class is linked to.\n *\n * @returns {HTMLElement} The DOM element\n */\n getDomElement() {\n return this.element;\n }\n\n /**\n * Returns the selected group values.\n *\n * @returns {Number[]} The group IDs that are currently selected.\n */\n getSelectedValues() {\n const selectedOptionElements = Array.from(this.element.querySelectorAll(\"option:checked\"));\n const selectedGroups = selectedOptionElements.map(el => parseInt(el.value));\n\n return selectedGroups;\n }\n}\n"],"names":["constructor","elementId","pickerDomElement","document","getElementById","Error","element","getDomElement","this","getSelectedValues","Array","from","querySelectorAll","map","el","parseInt","value"],"mappings":";;;;;;MAgCIA,kBAAYC,iEAAY,eACdC,iBAAmBC,SAASC,eAAeH,eAE5CC,uBACK,IAAIG,MAAM,qCAGfC,QAAUJ,iBAQnBK,uBACWC,KAAKF,QAQhBG,2BACmCC,MAAMC,KAAKH,KAAKF,QAAQM,iBAAiB,mBAC1BC,KAAIC,IAAMC,SAASD,GAAGE"}

8
group/amd/build/index.min.js vendored Normal file
View File

@ -0,0 +1,8 @@
define("core_group/index",["exports","./grouppicker"],(function(_exports,_grouppicker){var obj;
/**
* @module core_group/index
* @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateBulkActionButtons=_exports.setElementEnabled=_exports.init=void 0;const groupPicker=new(_grouppicker=(obj=_grouppicker)&&obj.__esModule?obj:{default:obj}).default;_exports.init=()=>{groupPicker.getDomElement().addEventListener("change",updateBulkActionButtons),updateBulkActionButtons()};const updateBulkActionButtons=()=>{const aGroupIsSelected=0!==groupPicker.getSelectedValues().length,bulkActionsEnabledStatuses={enablemessaging:aGroupIsSelected,disablemessaging:aGroupIsSelected};Object.entries(bulkActionsEnabledStatuses).map((_ref=>{let[buttonId,enabled]=_ref;return setElementEnabled(buttonId,enabled)}))};_exports.updateBulkActionButtons=updateBulkActionButtons;const setElementEnabled=(domElementId,enabled)=>{const element=document.getElementById(domElementId);element&&(enabled?element.removeAttribute("disabled"):element.setAttribute("disabled","disabled"))};_exports.setElementEnabled=setElementEnabled}));
//# sourceMappingURL=index.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.min.js","sources":["../src/index.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n/**\n * @module core_group/index\n * @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport GroupPicker from \"./grouppicker\";\n\nconst groupPicker = new GroupPicker();\n\n/**\n * Initialise page.\n */\nexport const init = () => {\n // Init event listeners.\n groupPicker.getDomElement().addEventListener(\"change\", updateBulkActionButtons);\n\n // Call initially to set initial button state.\n updateBulkActionButtons();\n};\n\n/**\n * Updates the bulk action buttons depending on specific conditions.\n */\nexport const updateBulkActionButtons = () => {\n const groupsSelected = groupPicker.getSelectedValues();\n const aGroupIsSelected = groupsSelected.length !== 0;\n\n // Collate the conditions where each button is enabled/disabled.\n const bulkActionsEnabledStatuses = {\n 'enablemessaging': aGroupIsSelected,\n 'disablemessaging': aGroupIsSelected\n };\n\n // Update the status of each button.\n Object.entries(bulkActionsEnabledStatuses).map(([buttonId, enabled]) => setElementEnabled(buttonId, enabled));\n};\n\n/**\n * Adds or removes the given element's disabled attribute.\n * @param {string} domElementId ID of the dom element (without the #)\n * @param {bool} enabled If false, the disable attribute is applied, else it is removed.\n */\nexport const setElementEnabled = (domElementId, enabled) => {\n const element = document.getElementById(domElementId);\n\n if (!element) {\n // If there is no element, we do nothing.\n // The element could be purposefully hidden or removed.\n return;\n }\n\n if (!enabled) {\n element.setAttribute('disabled', 'disabled');\n } else {\n element.removeAttribute('disabled');\n }\n};\n"],"names":["groupPicker","getDomElement","addEventListener","updateBulkActionButtons","aGroupIsSelected","getSelectedValues","length","bulkActionsEnabledStatuses","Object","entries","map","_ref","buttonId","enabled","setElementEnabled","domElementId","element","document","getElementById","removeAttribute","setAttribute"],"mappings":";;;;;oJAsBMA,YAAc,6FAKA,KAEhBA,YAAYC,gBAAgBC,iBAAiB,SAAUC,yBAGvDA,iCAMSA,wBAA0B,WAE7BC,iBAA6C,IAD5BJ,YAAYK,oBACKC,OAGlCC,2BAA6B,iBACZH,kCACCA,kBAIxBI,OAAOC,QAAQF,4BAA4BG,KAAIC,WAAEC,SAAUC,qBAAaC,kBAAkBF,SAAUC,4EAQ3FC,kBAAoB,CAACC,aAAcF,iBACtCG,QAAUC,SAASC,eAAeH,cAEnCC,UAMAH,QAGDG,QAAQG,gBAAgB,YAFxBH,QAAQI,aAAa,WAAY"}

View File

@ -0,0 +1,63 @@
// 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/>.
/**
* @module core_group/groupPicker
* @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Class used for interfacing with the group select picker.
*
* @class core_group/GroupPicker
*/
export default class GroupPicker {
/**
* Creates the group picker class and finds the corresponding DOM element.
*
* @param {String} elementId The DOM element id of the <select> input
* @throws Error if the element was not found.
*/
constructor(elementId = "groups") {
const pickerDomElement = document.getElementById(elementId);
if (!pickerDomElement) {
throw new Error("Groups picker was not found.");
}
this.element = pickerDomElement;
}
/**
* Returns the DOM element this class is linked to.
*
* @returns {HTMLElement} The DOM element
*/
getDomElement() {
return this.element;
}
/**
* Returns the selected group values.
*
* @returns {Number[]} The group IDs that are currently selected.
*/
getSelectedValues() {
const selectedOptionElements = Array.from(this.element.querySelectorAll("option:checked"));
const selectedGroups = selectedOptionElements.map(el => parseInt(el.value));
return selectedGroups;
}
}

72
group/amd/src/index.js Normal file
View File

@ -0,0 +1,72 @@
// 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/>.
/**
* @module core_group/index
* @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import GroupPicker from "./grouppicker";
const groupPicker = new GroupPicker();
/**
* Initialise page.
*/
export const init = () => {
// Init event listeners.
groupPicker.getDomElement().addEventListener("change", updateBulkActionButtons);
// Call initially to set initial button state.
updateBulkActionButtons();
};
/**
* Updates the bulk action buttons depending on specific conditions.
*/
export const updateBulkActionButtons = () => {
const groupsSelected = groupPicker.getSelectedValues();
const aGroupIsSelected = groupsSelected.length !== 0;
// Collate the conditions where each button is enabled/disabled.
const bulkActionsEnabledStatuses = {
'enablemessaging': aGroupIsSelected,
'disablemessaging': aGroupIsSelected
};
// Update the status of each button.
Object.entries(bulkActionsEnabledStatuses).map(([buttonId, enabled]) => setElementEnabled(buttonId, enabled));
};
/**
* Adds or removes the given element's disabled attribute.
* @param {string} domElementId ID of the dom element (without the #)
* @param {bool} enabled If false, the disable attribute is applied, else it is removed.
*/
export const setElementEnabled = (domElementId, enabled) => {
const element = document.getElementById(domElementId);
if (!element) {
// If there is no element, we do nothing.
// The element could be purposefully hidden or removed.
return;
}
if (!enabled) {
element.setAttribute('disabled', 'disabled');
} else {
element.removeAttribute('disabled');
}
};

View File

@ -59,6 +59,9 @@ class index_page implements renderable, templatable {
/** @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.
*
@ -69,9 +72,10 @@ class index_page implements renderable, templatable {
* @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) {
$undeletablegroups, $messagingsettingsvisible) {
$this->courseid = $courseid;
$this->groups = $groups;
$this->selectedgroupname = $selectedgroupname;
@ -79,6 +83,7 @@ class index_page implements renderable, templatable {
$this->disableaddedit = $disableaddedit;
$this->disabledelete = $disabledelete;
$this->undeletablegroups = $undeletablegroups;
$this->messagingsettingsvisible = $messagingsettingsvisible;
}
/**
@ -105,6 +110,7 @@ class index_page implements renderable, templatable {
$data->groups = $this->groups;
$data->members = $this->selectedgroupmembers;
$data->selectedgroup = $this->selectedgroupname;
$data->messagingsettingsvisible = $this->messagingsettingsvisible;
return $data;
}

View File

@ -169,7 +169,7 @@ UpdatableMembersCombo.prototype.refreshMembers = function () {
document.getElementById("deletegroup").disabled = selectionCount == 0;
if(singleSelection) {
var sUrl = this.wwwRoot+"/group/index.php?id="+this.courseId+"&group="+groupId+"&act_ajax_getmembersingroup";
var sUrl = this.wwwRoot + "/group/index.php?id=" + this.courseId + "&group=" + groupId + "&action=ajax_getmembersingroup";
var self = this;
YUI().use('io', function (Y) {
Y.io(sUrl, {

View File

@ -28,8 +28,9 @@ require_once('lib.php');
$courseid = required_param('id', PARAM_INT);
$groupid = optional_param('group', false, PARAM_INT);
$userid = optional_param('user', false, PARAM_INT);
$action = groups_param_action();
// Support either single group= parameter, or array groups[]
$action = optional_param('action', false, PARAM_TEXT);
// Support either single group= parameter, or array groups[].
if ($groupid) {
$groupids = array($groupid);
} else {
@ -40,11 +41,10 @@ $singlegroup = (count($groupids) == 1);
$returnurl = $CFG->wwwroot.'/group/index.php?id='.$courseid;
// Get the course information so we can print the header and
// check the course id is valid
// check the course id is valid.
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
$url = new moodle_url('/group/index.php', array('id'=>$courseid));
$url = new moodle_url('/group/index.php', array('id' => $courseid));
navigation_node::override_active_url($url);
if ($userid) {
$url->param('user', $userid);
@ -63,7 +63,7 @@ require_capability('moodle/course:managegroups', $context);
$PAGE->requires->js('/group/clientlib.js', true);
$PAGE->requires->js('/group/module.js', true);
// Check for multiple/no group errors
// Check for multiple/no group errors.
if (!$singlegroup) {
switch($action) {
case 'ajax_getmembersingroup':
@ -75,7 +75,7 @@ if (!$singlegroup) {
}
switch ($action) {
case false: //OK, display form.
case false: // OK, display form.
break;
case 'ajax_getmembersingroup':
@ -93,11 +93,11 @@ switch ($action) {
$viewfullnames = has_capability('moodle/site:viewfullnames', $context);
foreach($groupmemberroles as $roleid=>$roledata) {
foreach ($groupmemberroles as $roleid => $roledata) {
$shortroledata = new stdClass();
$shortroledata->name = $roledata->name;
$shortroledata->users = array();
foreach($roledata->users as $member) {
foreach ($roledata->users as $member) {
$shortmember = new stdClass();
$shortmember->id = $member->id;
$shortmember->name = fullname($member, $viewfullnames);
@ -123,48 +123,60 @@ switch ($action) {
throw new \moodle_exception('errorselectsome', 'group', $returnurl);
}
$groupidlist = implode(',', $groupids);
redirect(new moodle_url('/group/delete.php', array('courseid'=>$courseid, 'groups'=>$groupidlist)));
redirect(new moodle_url('/group/delete.php', array('courseid' => $courseid, 'groups' => $groupidlist)));
break;
case 'showcreateorphangroupform':
redirect(new moodle_url('/group/group.php', array('courseid'=>$courseid)));
redirect(new moodle_url('/group/group.php', array('courseid' => $courseid)));
break;
case 'showautocreategroupsform':
redirect(new moodle_url('/group/autogroup.php', array('courseid'=>$courseid)));
redirect(new moodle_url('/group/autogroup.php', array('courseid' => $courseid)));
break;
case 'showimportgroups':
redirect(new moodle_url('/group/import.php', array('id'=>$courseid)));
redirect(new moodle_url('/group/import.php', array('id' => $courseid)));
break;
case 'showgroupsettingsform':
redirect(new moodle_url('/group/group.php', array('courseid'=>$courseid, 'id'=>$groupids[0])));
redirect(new moodle_url('/group/group.php', array('courseid' => $courseid, 'id' => $groupids[0])));
break;
case 'updategroups': //Currently reloading.
case 'updategroups': // Currently reloading.
break;
case 'removemembers':
break;
case 'showaddmembersform':
redirect(new moodle_url('/group/members.php', array('group'=>$groupids[0])));
redirect(new moodle_url('/group/members.php', array('group' => $groupids[0])));
break;
case 'updatemembers': //Currently reloading.
case 'updatemembers': // Currently reloading.
break;
default: //ERROR.
case 'enablemessaging':
set_groups_messaging($groupids, true);
redirect($returnurl, get_string('messagingenabled', 'group', count($groupids)), null,
\core\output\notification::NOTIFY_SUCCESS);
break;
case 'disablemessaging':
set_groups_messaging($groupids, false);
redirect($returnurl, get_string('messagingdisabled', 'group', count($groupids)), null,
\core\output\notification::NOTIFY_SUCCESS);
break;
default: // ERROR.
throw new \moodle_exception('unknowaction', '', $returnurl);
break;
}
// Print the page and form
// Print the page and form.
$strgroups = get_string('groups');
$strparticipants = get_string('participants');
/// Print header
// Print header.
$PAGE->set_title($strgroups);
$PAGE->set_heading($course->fullname);
$PAGE->set_pagelayout('standard');
@ -244,42 +256,11 @@ if ($singlegroup) {
$disableaddedit = !$singlegroup;
$disabledelete = !empty($groupids);
$caneditmessaging = \core_message\api::can_create_group_conversation($USER->id, $context);
$renderable = new \core_group\output\index_page($courseid, $groupoptions, $selectedname, $members, $disableaddedit, $disabledelete,
$preventgroupremoval);
$preventgroupremoval, $caneditmessaging);
$output = $PAGE->get_renderer('core_group');
echo $output->render($renderable);
echo $OUTPUT->footer();
/**
* Returns the first button action with the given prefix, taken from
* POST or GET, otherwise returns false.
* @see /lib/moodlelib.php function optional_param().
* @param string $prefix 'act_' as in 'action'.
* @return string The action without the prefix, or false if no action found.
*/
function groups_param_action($prefix = 'act_') {
$action = false;
//($_SERVER['QUERY_STRING'] && preg_match("/$prefix(.+?)=(.+)/", $_SERVER['QUERY_STRING'], $matches)) { //b_(.*?)[&;]{0,1}/
if ($_POST) {
$form_vars = $_POST;
}
elseif ($_GET) {
$form_vars = $_GET;
}
if ($form_vars) {
foreach ($form_vars as $key => $value) {
if (preg_match("/$prefix(.+)/", $key, $matches)) {
$action = $matches[1];
break;
}
}
}
if ($action && !preg_match('/^\w+$/', $action)) {
$action = false;
throw new \moodle_exception('unknowaction');
}
///if (debugging()) echo 'Debug: '.$action;
return $action;
}

View File

@ -1208,3 +1208,17 @@ function core_group_inplace_editable($itemtype, $itemid, $newvalue) {
return \core_group\output\user_groups_editable::update($itemid, $newvalue);
}
}
/**
* Updates group messaging to enable/disable in bulk.
*
* @param array $groupids array of group id numbers.
* @param bool $enabled if true, enables messaging else disables messaging
*/
function set_groups_messaging(array $groupids, bool $enabled): void {
foreach ($groupids as $groupid) {
$data = groups_get_group($groupid, '*', MUST_EXIST);
$data->enablemessaging = $enabled;
groups_update_group($data);
}
}

View File

@ -34,6 +34,7 @@
* groups array The list of groups.
* members array The list of members, grouped based on roles.
* undeletablegroups string A JSON string containing an array of group IDs that a user cannot delete.
* messagingsettingsvisible bool Wether the messaging settings buttons should be visible.
Example context (json):
{
@ -42,6 +43,7 @@
"editgroupsettingsdisabled": false,
"deletegroupdisabled": false,
"addmembersdisabled": false,
"messagingenabled": true,
"groups": [
{
"value": "1",
@ -91,23 +93,33 @@
{{/groups}}
</select>
</div>
<h3> {{#str}} withselected, group {{/str}} </h3>
<div class="form-group">
<input type="submit" name="act_updatemembers" id="updatemembers" value="{{#str}}showmembersforgroup, group{{/str}}" class="btn btn-secondary" />
<button type="submit" name="action" id="updatemembers" value="updatemembers" class="btn btn-secondary">{{#str}}showmembersforgroup, group{{/str}}</button>
</div>
<div class="form-group">
<input type="submit" name="act_showgroupsettingsform" id="showeditgroupsettingsform" value="{{#str}}editgroupsettings, group{{/str}}" {{#editgroupsettingsdisabled}}disabled="disabled"{{/editgroupsettingsdisabled}} class="btn btn-secondary" />
<button type="submit" name="action" id="showeditgroupsettingsform" value="showgroupsettingsform" {{#editgroupsettingsdisabled}}disabled="disabled"{{/editgroupsettingsdisabled}} class="btn btn-secondary">{{#str}}editgroupsettings, group{{/str}}</button>
</div>
<div class="form-group">
<input type="submit" name="act_deletegroup" id="deletegroup" value="{{#str}}deleteselectedgroup, group{{/str}}" {{#deletegroupdisabled}}disabled="disabled"{{/deletegroupdisabled}} class="btn btn-secondary" />
<button type="submit" name="action" id="deletegroup" value="deletegroup" {{#deletegroupdisabled}}disabled="disabled"{{/deletegroupdisabled}} class="btn btn-secondary">{{#str}}deleteselectedgroup, group{{/str}}</button>
</div>
{{#messagingsettingsvisible}}
<div class="form-group">
<button type="submit" name="action" id="disablemessaging" value="disablemessaging" class="btn btn-secondary" disabled="disabled">{{#str}}disablemessagingaction, group{{/str}}</button>
</div>
<div class="form-group">
<button type="submit" name="action" id="enablemessaging" value="enablemessaging" class="btn btn-secondary" disabled="disabled">{{#str}}enablemessagingaction, group{{/str}}</button>
</div>
{{/messagingsettingsvisible}}
<h3> {{#str}} manageactions, group {{/str}} </h3>
<div class="form-group">
<button type="submit" name="action" id="showcreateorphangroupform" value="showcreateorphangroupform" class="btn btn-secondary">{{#str}}creategroup, group{{/str}}</button>
</div>
<div class="form-group">
<input type="submit" name="act_showcreateorphangroupform" id="showcreateorphangroupform" value="{{#str}}creategroup, group{{/str}}" class="btn btn-secondary" />
<button type="submit" name="action" id="showautocreategroupsform" value="showautocreategroupsform" class="btn btn-secondary">{{#str}}autocreategroups, group{{/str}}</button>
</div>
<div class="form-group">
<input type="submit" name="act_showautocreategroupsform" id="showautocreategroupsform" value="{{#str}}autocreategroups, group{{/str}}" class="btn btn-secondary" />
</div>
<div class="form-group">
<input type="submit" name="act_showimportgroups" id="showimportgroups" value="{{#str}}importgroups, group{{/str}}" class="btn btn-secondary" />
<button type="submit" name="action" id="showimportgroups" value="showimportgroups" class="btn btn-secondary">{{#str}}importgroups, group{{/str}}</button>
</div>
</div>
<div class="col-md-6 mb-1">
@ -127,7 +139,7 @@
</select>
</div>
<div class="form-group">
<input type="submit" value="{{#str}}adduserstogroup, group{{/str}}" class="btn btn-secondary" {{#addmembersdisabled}}disabled="disabled"{{/addmembersdisabled}} name="act_showaddmembersform" id="showaddmembersform"/>
<button type="submit" value="showaddmembersform" class="btn btn-secondary" {{#addmembersdisabled}}disabled="disabled"{{/addmembersdisabled}} name="action" id="showaddmembersform">{{#str}}adduserstogroup, group{{/str}}</button>
</div>
</div>
</div>
@ -143,3 +155,6 @@
M.core_group.groupslist(Y, undeletableGroups);
});
{{/js}}
{{#js}}
require(['core_group/index'], (module) => module.init());
{{/js}}

View File

@ -0,0 +1,66 @@
@core @core_group
Feature: Bulk update group messaging status
In order to update group messaging settings in bulk
As a teacher
I need to be able to select the groups and update their messaging settings using the buttons provided.
Background:
Given the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And I log in as "teacher1"
And I am on the "Course 1" "groups" page
And I press "Create group"
And I set the following fields to these values:
| Group name | Group-A-Test |
| Group ID number | Group-A-Test |
And I press "Save changes"
And I press "Create group"
And I set the following fields to these values:
| Group name | Group-B-Test |
| Group ID number | Group-B-Test |
And I press "Save changes"
And I select "Groups" from the "jump" singleselect
@javascript
Scenario: Bulk enable messaging in groups
Given I set the field "groups" to "Group-A-Test (0)"
And I press "Edit group settings"
And I set the field "id_enablemessaging" to "0"
And I press "Save changes"
And I wait until the page is ready
And the field "groups" matches value "Group-A-Test (0)"
And I press "Enable messaging"
And I wait until the page is ready
And I should see "Successfully enabled messaging in 1 group(s)"
And I set the field "groups" to "Group-A-Test (0)"
And I press "Edit group settings"
Then the field "id_enablemessaging" matches value "1"
@javascript
Scenario: Bulk disable messaging in groups
Given I set the field "groups" to "Group-A-Test (0)"
And I press "Edit group settings"
And I set the field "id_enablemessaging" to "1"
And I press "Save changes"
And I wait until the page is ready
And the field "groups" matches value "Group-A-Test (0)"
And I press "Disable messaging"
And I wait until the page is ready
And I should see "Successfully disabled messaging in 1 group(s)"
And I set the field "groups" to "Group-A-Test (0)"
And I press "Edit group settings"
Then the field "id_enablemessaging" matches value "0"
@javascript
Scenario: Messaging buttons are enabled when a group is selected
Given I set the field "groups" to "Group-A-Test (0)"
Then the field "groups" matches value "Group-A-Test (0)"
And the "Enable messaging" "button" should be enabled
And the "Disable messaging" "button" should be enabled

View File

@ -40,11 +40,11 @@ Feature: Automatic deletion of groups and groupings
@javascript
Scenario: Delete groups and groupings with and without ID numbers
Given I set the field "groups" to "Group (without ID) (0)"
And I press "Delete selected group"
And I press "Delete"
And I press "Yes"
Then the "groups" select box should not contain "Group (without ID) (0)"
And I set the field "groups" to "Group (with ID) (0)"
And I press "Delete selected group"
And I press "Delete"
And I press "Yes"
And the "groups" select box should not contain "Group (with ID) (0)"
And I select "Groupings" from the "jump" singleselect
@ -65,9 +65,9 @@ Feature: Automatic deletion of groups and groupings
And I log in as "teacher1"
And I am on the "Course 1" "groups" page
When I set the field "groups" to "Group (with ID) (0)"
Then the "Delete selected group" "button" should be disabled
Then the "Delete" "button" should be disabled
And I set the field "groups" to "Group (without ID) (0)"
And I press "Delete selected group"
And I press "Delete"
And I press "Yes"
And I should not see "Group (without ID)"
And I select "Groupings" from the "jump" singleselect

View File

@ -789,4 +789,93 @@ class lib_test extends \advanced_testcase {
['prefname' => 'reptile'], 'JOIN {user_preferences} up ON up.userid = u.id');
$this->assertEquals('snake', reset($result[0]->users)->value);
}
/**
* Tests set_groups_messaging
*
* @covers \core_group::set_groups_messaging
*/
public function test_set_groups_messaging() {
$this->resetAfterTest();
$this->setAdminUser();
$dg = $this->getDataGenerator();
$course = $dg->create_course();
// Create some groups in the course.
$groupids = [];
for ($i = 0; $i < 5; $i++) {
$group = new \stdClass();
$group->courseid = $course->id;
$group->name = 'group-'.$i;
$group->enablemessaging = 0;
$groupids[] = groups_create_group($group);
}
// They should all initially be disabled.
$alldisabledinitially = $this->check_groups_messaging_status_is($groupids, $course->id, false);
$this->assertTrue($alldisabledinitially);
// Enable messaging for all the groups.
set_groups_messaging($groupids, true);
// Check they were all enabled.
$allenabled = $this->check_groups_messaging_status_is($groupids, $course->id, true);
$this->assertTrue($allenabled);
// Disable messaging for all the groups.
set_groups_messaging($groupids, false);
// Check they were all disabled.
$alldisabled = $this->check_groups_messaging_status_is($groupids, $course->id, false);
$this->assertTrue($alldisabled);
}
/**
* Tests set group messaging where it doesn't exist
*
* @covers \core_group::set_groups_messaging
*/
public function test_set_groups_messaging_doesnt_exist() {
$this->resetAfterTest();
$this->setAdminUser();
$groupids = [-1];
$this->expectException('dml_exception');
set_groups_messaging($groupids, false);
}
/**
* Checks the given list of groups to verify their messaging settings.
*
* @param array $groupids array of group ids
* @param int $courseid the course the groups are in
* @param bool $desired the desired setting value
* @return bool true if all groups $enablemessaging setting matches the given $desired value, else false
*/
private function check_groups_messaging_status_is(array $groupids, int $courseid, bool $desired) {
$context = \context_course::instance($courseid);
foreach ($groupids as $groupid) {
$conversation = \core_message\api::get_conversation_by_area(
'core_group',
'groups',
$groupid,
$context->id
);
// An empty conversation means it has not been enabled yet.
if (empty($conversation)) {
$conversation = (object) [
'enabled' => 0
];
}
if ($desired !== boolval($conversation->enabled)) {
return false;
}
}
return true;
}
}

View File

@ -54,11 +54,13 @@ $string['deletegroupconfirm'] = 'Are you sure you want to delete group \'{$a}\'?
$string['deletegrouping'] = 'Delete grouping';
$string['deletegroupingconfirm'] = 'Are you sure you want to delete grouping \'{$a}\'? (Groups in the grouping are not deleted.)';
$string['deletegroupsconfirm'] = 'Are you sure you want to delete the following groups?';
$string['deleteselectedgroup'] = 'Delete selected group';
$string['deleteselectedgroup'] = 'Delete';
$string['disablemessagingaction'] = 'Disable messaging';
$string['editgroupingsettings'] = 'Edit grouping settings';
$string['editgroupsettings'] = 'Edit group settings';
$string['editusersgroupsa'] = 'Edit groups for "{$a}"';
$string['enablemessaging'] = 'Group messaging';
$string['enablemessagingaction'] = 'Enable messaging';
$string['enablemessaging_help'] = 'If enabled, group members can send messages to the others in their group via the messaging drawer.';
$string['encoding'] = 'Encoding';
$string['enrolmentkey'] = 'Enrolment key';
@ -171,6 +173,9 @@ $string['nousersinrole'] = 'There are no suitable users in the selected role';
$string['number'] = 'Group/member count';
$string['numgroups'] = 'Number of groups';
$string['nummembers'] = 'Members per group';
$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['othergroups'] = 'Other groups';
$string['overview'] = 'Overview';
@ -216,3 +221,4 @@ $string['visibilitymembers'] = 'Only visible to members';
$string['visibilityown'] = 'Only see own membership';
$string['visibilitynone'] = 'Hidden';
$string['memberofgroup'] = 'Group member of: {$a}';
$string['withselected'] = 'With selected';