This commit is contained in:
Jun Pataleta
2024-07-03 18:48:25 +08:00
14 changed files with 258 additions and 5 deletions
@@ -0,0 +1,8 @@
issueNumber: MDL-52046
notes:
core_reportbuilder:
- message: >-
The return type of the `set_checkbox_toggleall` callback, defined by
system reports, can now be null. Use if the checkbox should not be shown
for the row.
type: improved
+10
View File
@@ -0,0 +1,10 @@
define("core_cohort/actions",["exports","core/event_dispatcher","core/notification","core/pending","core/prefetch","core/str","core_cohort/repository","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core/local/inplace_editable/events"],(function(_exports,_event_dispatcher,_notification,_pending,_prefetch,_str,_repository,reportEvents,reportSelectors,_events2){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Cohorts actions.
*
* @module core_cohort/actions
* @copyright 2024 David Woloszyn <david.woloszyn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.registerEventListeners=_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);const SELECTORS_CHECKBOXES='[data-togglegroup="report-select-all"][data-toggle="slave"]:checked',SELECTORS_DELETEBUTTON='[data-action="cohort-delete-selected"]',SELECTORS_EDITNAME='[data-itemtype="cohortname"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("core_cohort",["delcohortsconfirm"]),(0,_prefetch.prefetchStrings)("core",["delete","deleteselected","selectitem"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener(_events2.eventTypes.elementUpdated,(async event=>{if(event.target.closest(SELECTORS_EDITNAME)){const newName=await(0,_str.getString)("selectitem","core",event.target.dataset.value),cohortId=event.target.dataset.itemid,checkbox=document.querySelector('input[value="'.concat(cohortId,'"]')),label=document.querySelector('label[for="'.concat(checkbox.id,'"]'));newName&&label&&(label.innerHTML=newName)}})),document.addEventListener("click",(event=>{const cohortDeleteSelected=event.target.closest(SELECTORS_DELETEBUTTON);if(cohortDeleteSelected){event.preventDefault();const reportElement=document.querySelector(reportSelectors.regions.report),cohortDeleteChecked=reportElement.querySelectorAll(SELECTORS_CHECKBOXES);if(0===cohortDeleteChecked.length)return;_notification.default.saveCancelPromise((0,_str.getString)("deleteselected","core"),(0,_str.getString)("delcohortsconfirm","core_cohort"),(0,_str.getString)("delete","core"),{triggerElement:cohortDeleteSelected}).then((()=>{const pendingPromise=new _pending.default("core_cohort/cohorts:delete"),deleteCohortIds=[...cohortDeleteChecked].map((check=>check.value));return(0,_repository.deleteCohorts)(deleteCohortIds).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}}))};_exports.registerEventListeners=registerEventListeners}));
//# sourceMappingURL=actions.min.js.map
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
define("core_cohort/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj;
/**
* Module to handle cohort AJAX requests.
*
* @module core_cohort/repository
* @copyright 2024 David Woloszyn <david.woloszyn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.deleteCohorts=_exports.deleteCohort=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.deleteCohort=cohort=>deleteCohorts([cohort]);const deleteCohorts=cohorts=>{const request={methodname:"core_cohort_delete_cohorts",args:{cohortids:cohorts}};return _ajax.default.call([request])[0]};_exports.deleteCohorts=deleteCohorts}));
//# sourceMappingURL=repository.min.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"repository.min.js","sources":["../src/repository.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/**\n * Module to handle cohort AJAX requests.\n *\n * @module core_cohort/repository\n * @copyright 2024 David Woloszyn <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Delete single cohort.\n *\n * @param {Number} cohort Cohort ID\n * @return {Promise}\n */\nexport const deleteCohort = cohort => deleteCohorts([cohort]);\n\n/**\n * Delete multiple cohorts.\n *\n * @param {Number[]} cohorts Cohort IDs\n * @return {Promise}\n */\nexport const deleteCohorts = cohorts => {\n const request = {\n methodname: 'core_cohort_delete_cohorts',\n args: {cohortids: cohorts}\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["cohort","deleteCohorts","cohorts","request","methodname","args","cohortids","Ajax","call"],"mappings":";;;;;;;2LA+B4BA,QAAUC,cAAc,CAACD,eAQxCC,cAAgBC,gBACnBC,QAAU,CACZC,WAAY,6BACZC,KAAM,CAACC,UAAWJ,iBAGfK,cAAKC,KAAK,CAACL,UAAU"}
+115
View File
@@ -0,0 +1,115 @@
// 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/>.
/**
* Cohorts actions.
*
* @module core_cohort/actions
* @copyright 2024 David Woloszyn <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {dispatchEvent} from 'core/event_dispatcher';
import Notification from 'core/notification';
import Pending from 'core/pending';
import {prefetchStrings} from 'core/prefetch';
import {getString} from 'core/str';
import {deleteCohorts} from 'core_cohort/repository';
import * as reportEvents from 'core_reportbuilder/local/events';
import * as reportSelectors from 'core_reportbuilder/local/selectors';
import {eventTypes} from 'core/local/inplace_editable/events';
const SELECTORS = {
CHECKBOXES: '[data-togglegroup="report-select-all"][data-toggle="slave"]:checked',
DELETEBUTTON: '[data-action="cohort-delete-selected"]',
EDITNAME: '[data-itemtype="cohortname"]',
};
/**
* Initialise module.
*/
export const init = () => {
prefetchStrings('core_cohort', [
'delcohortsconfirm',
]);
prefetchStrings('core', [
'delete',
'deleteselected',
'selectitem',
]);
registerEventListeners();
};
/**
* Register event listeners.
*/
export const registerEventListeners = () => {
// Edit cohort name inplace.
document.addEventListener(eventTypes.elementUpdated, async(event) => {
const editCohortName = event.target.closest(SELECTORS.EDITNAME);
if (editCohortName) {
const newName = await getString('selectitem', 'core', event.target.dataset.value);
const cohortId = event.target.dataset.itemid;
const checkbox = document.querySelector(`input[value="${cohortId}"]`);
const label = document.querySelector(`label[for="${checkbox.id}"]`);
if (newName && label) {
label.innerHTML = newName;
}
}
});
// Delete multiple cohorts.
document.addEventListener('click', event => {
const cohortDeleteSelected = event.target.closest(SELECTORS.DELETEBUTTON);
if (cohortDeleteSelected) {
event.preventDefault();
const reportElement = document.querySelector(reportSelectors.regions.report);
const cohortDeleteChecked = reportElement.querySelectorAll(SELECTORS.CHECKBOXES);
if (cohortDeleteChecked.length === 0) {
return;
}
Notification.saveCancelPromise(
getString('deleteselected', 'core'),
getString('delcohortsconfirm', 'core_cohort'),
getString('delete', 'core'),
{triggerElement: cohortDeleteSelected}
).then(() => {
const pendingPromise = new Pending('core_cohort/cohorts:delete');
const deleteCohortIds = [...cohortDeleteChecked].map(check => check.value);
// eslint-disable-next-line promise/no-nesting
return deleteCohorts(deleteCohortIds)
.then(() => {
dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);
return pendingPromise.resolve();
})
.catch(Notification.exception);
}).catch(() => {
return;
});
}
});
};
+47
View File
@@ -0,0 +1,47 @@
// 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 to handle cohort AJAX requests.
*
* @module core_cohort/repository
* @copyright 2024 David Woloszyn <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Ajax from 'core/ajax';
/**
* Delete single cohort.
*
* @param {Number} cohort Cohort ID
* @return {Promise}
*/
export const deleteCohort = cohort => deleteCohorts([cohort]);
/**
* Delete multiple cohorts.
*
* @param {Number[]} cohorts Cohort IDs
* @return {Promise}
*/
export const deleteCohorts = cohorts => {
const request = {
methodname: 'core_cohort_delete_cohorts',
args: {cohortids: cohorts}
};
return Ajax.call([request])[0];
};
@@ -59,7 +59,18 @@ class cohorts extends system_report {
// Any columns required by actions should be defined here to ensure they're always available.
$this->add_base_fields("{$entitymainalias}.id, {$entitymainalias}.contextid, {$entitymainalias}.visible, " .
"{$entitymainalias}.component");
"{$entitymainalias}.component, {$entitymainalias}.name");
$this->set_checkbox_toggleall(static function(stdClass $cohort): ?array {
if (!has_capability('moodle/cohort:manage', context::instance_by_id($cohort->contextid))) {
return null;
}
return [
$cohort->id,
get_string('selectitem', 'moodle', $cohort->name),
];
});
// Check if report needs to show a specific category.
$contextid = $this->get_parameter('contextid', 0, PARAM_INT);
+16
View File
@@ -115,5 +115,21 @@ if (!empty($searchquery)) {
]);
}
// Show the report.
echo $report->output();
// Show the delete selected button if there are records.
if ($DB->record_exists('cohort', [])) {
echo $OUTPUT->render(new single_button(
new moodle_url('#'),
get_string('deleteselected'),
'post',
single_button::BUTTON_PRIMARY,
['data-action' => 'cohort-delete-selected']
));
$PAGE->requires->js_call_amd('core_cohort/actions', 'init');
}
echo $OUTPUT->footer();
+28
View File
@@ -0,0 +1,28 @@
@core @core_cohort
Feature: Delete cohorts
In order to delete multiple cohorts
As an admin
I need to select specific cohorts and perform that action
Background:
Given the following "cohorts" exist:
| name | idnumber | contextlevel | reference | description |
| Cohort 1 | cohort1 | System | | About cohort 1 |
| Cohort 2 | cohort2 | System | | About cohort 2 |
| Cohort 3 | cohort3 | System | | About cohort 3 |
@javascript
Scenario: I can delete multiple cohorts using the checkboxes
Given I log in as "admin"
And I navigate to "Users > Accounts > Cohorts" in site administration
And I should see "Cohort 1"
And I should see "Cohort 2"
And I should see "Cohort 3"
And I click on "Select 'Cohort 1'" "checkbox"
And I click on "Select 'Cohort 2'" "checkbox"
When I click on "Delete selected" "button"
Then I should see "Do you really want to delete the selected cohorts?"
And I click on "Delete" "button" in the "Delete selected" "dialogue"
And I should not see "Cohort 1"
And I should not see "Cohort 2"
And I should see "Cohort 3"
+1
View File
@@ -47,6 +47,7 @@ $string['currentusers'] = 'Current users';
$string['currentusersmatching'] = 'Current users matching';
$string['defaultcontext'] = 'Default context';
$string['delcohort'] = 'Delete cohort';
$string['delcohortsconfirm'] = 'Do you really want to delete the selected cohorts?';
$string['delconfirm'] = 'Do you really want to delete cohort \'{$a}\'?';
$string['description'] = 'Description';
$string['displayedrows'] = '{$a->displayed} rows displayed out of {$a->total}.';
+2 -1
View File
@@ -373,7 +373,8 @@ $functions = array(
'classpath' => 'cohort/externallib.php',
'description' => 'Deletes all specified cohorts.',
'type' => 'write',
'capabilities' => 'moodle/cohort:manage'
'capabilities' => 'moodle/cohort:manage',
'ajax' => true,
),
'core_cohort_get_cohort_members' => array(
'classname' => 'core_cohort_external',
+6 -2
View File
@@ -144,7 +144,7 @@ abstract class system_report extends base {
* Define toggle all checkbox for the report, required row data should be defined by calling {@see add_base_fields}
*
* @param callable $callback Callback to return value/label for each checkbox, implementing the following signature:
* function(stdClass $row): array containing value/label pair
* function(stdClass $row): ?array containing value/label pair, or null if the checkbox should not be shown for the row
*/
final protected function set_checkbox_toggleall(callable $callback): void {
$this->checkboxcallback = $callback;
@@ -167,7 +167,11 @@ abstract class system_report extends base {
$value = '';
$label = get_string('selectall');
} else {
[$value, $label] = ($this->checkboxcallback)($row);
$checkboxdata = ($this->checkboxcallback)($row);
if ($checkboxdata === null) {
return null;
}
[$value, $label] = $checkboxdata;
}
return new checkbox_toggleall('report-select-all', $ismaster, [
+1 -1
View File
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2024062700.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2024070300.00; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.5dev (Build: 20240627)'; // Human-friendly version name