mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 20:42:22 +02:00
MDL-76851 core_courseformat: bulk section and cm delete action
This commit is contained in:
parent
12a8176926
commit
9c583f5ec5
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
define("core_courseformat/local/content/section/header",["exports","core_courseformat/local/courseeditor/dndsectionitem"],(function(_exports,_dndsectionitem){var obj;
|
||||
define("core_courseformat/local/content/section/header",["exports","core_courseformat/local/courseeditor/dndsectionitem","core/str","core/prefetch"],(function(_exports,_dndsectionitem,_str,_prefetch){var obj;
|
||||
/**
|
||||
* Course section header component.
|
||||
*
|
||||
@ -8,6 +8,6 @@ define("core_courseformat/local/content/section/header",["exports","core_coursef
|
||||
* @class core_courseformat/local/content/section/header
|
||||
* @copyright 2021 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndsectionitem=(obj=_dndsectionitem)&&obj.__esModule?obj:{default:obj};class _default extends _dndsectionitem.default{create(descriptor){this.name="content_section_header",this.selectors={ACTIONSMENU:".section_action_menu",BULKSELECT:"[data-for='sectionBulkSelect']",BULKCHECKBOX:"[data-bulkcheckbox]"},this.classes={HIDE:"d-none",SELECTED:"selected"},this.id=descriptor.id,this.section=descriptor.section,this.course=descriptor.course,this.fullregion=descriptor.fullregion}stateReady(state){this.configDragDrop(this.id,state,this.fullregion),this._refreshBulk({state:state})}getWatchers(){return[{watch:"bulk:updated",handler:this._refreshBulk}]}_refreshBulk(_ref){var _this$getElement;let{state:state}=_ref;const bulk=state.bulk;if(!this._isSectionBulkEditable())return;this.setDraggable(!bulk.enabled),null===(_this$getElement=this.getElement(this.selectors.BULKSELECT))||void 0===_this$getElement||_this$getElement.classList.toggle(this.classes.HIDE,!bulk.enabled);const disabled=!this._isSectionBulkEnabled(bulk),selected=this._isSelected(bulk);this.element.classList.toggle(this.classes.SELECTED,selected),this._setCheckboxValue(selected,disabled)}_setCheckboxValue(checked,disabled){const checkbox=this.getElement(this.selectors.BULKCHECKBOX);checkbox&&(checkbox.checked=checked,checkbox.disabled=disabled,disabled?checkbox.removeAttribute("data-is-selectable"):checkbox.dataset.isSelectable=1)}_isSectionBulkEnabled(bulk){return!!bulk.enabled&&(""===bulk.selectedType||"section"===bulk.selectedType)}_isSectionBulkEditable(){var _section$bulkeditable;const section=this.reactive.get("section",this.id);return null!==(_section$bulkeditable=null==section?void 0:section.bulkeditable)&&void 0!==_section$bulkeditable&&_section$bulkeditable}_isSelected(bulk){return"section"===bulk.selectedType&&bulk.selection.includes(this.id)}}return _exports.default=_default,_exports.default}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndsectionitem=(obj=_dndsectionitem)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("core_courseformat",["selectsection"]);class _default extends _dndsectionitem.default{create(descriptor){this.name="content_section_header",this.selectors={ACTIONSMENU:".section_action_menu",BULKSELECT:"[data-for='sectionBulkSelect']",BULKCHECKBOX:"[data-bulkcheckbox]"},this.classes={HIDE:"d-none",SELECTED:"selected"},this.id=descriptor.id,this.section=descriptor.section,this.course=descriptor.course,this.fullregion=descriptor.fullregion}stateReady(state){this.configDragDrop(this.id,state,this.fullregion),this._refreshBulk({state:state})}getWatchers(){return[{watch:"bulk:updated",handler:this._refreshBulk},{watch:"section[".concat(this.id,"].title:updated"),handler:this._refreshSectionBulkSelector}]}async _refreshSectionBulkSelector(_ref){let{element:element}=_ref;const checkbox=this.getElement(this.selectors.BULKCHECKBOX);if(!checkbox)return;const newLabel=await(0,_str.get_string)("selectsection","core_courseformat",element.title);checkbox.title=newLabel;const label=this.getElement("label[for='".concat(checkbox.id,"']"));label&&(label.innerText=newLabel)}_refreshBulk(_ref2){var _this$getElement;let{state:state}=_ref2;const bulk=state.bulk;if(!this._isSectionBulkEditable())return;this.setDraggable(!bulk.enabled),null===(_this$getElement=this.getElement(this.selectors.BULKSELECT))||void 0===_this$getElement||_this$getElement.classList.toggle(this.classes.HIDE,!bulk.enabled);const disabled=!this._isSectionBulkEnabled(bulk),selected=this._isSelected(bulk);this.element.classList.toggle(this.classes.SELECTED,selected),this._setCheckboxValue(selected,disabled)}_setCheckboxValue(checked,disabled){const checkbox=this.getElement(this.selectors.BULKCHECKBOX);checkbox&&(checkbox.checked=checked,checkbox.disabled=disabled,disabled?checkbox.removeAttribute("data-is-selectable"):checkbox.dataset.isSelectable=1)}_isSectionBulkEnabled(bulk){return!!bulk.enabled&&(""===bulk.selectedType||"section"===bulk.selectedType)}_isSectionBulkEditable(){var _section$bulkeditable;const section=this.reactive.get("section",this.id);return null!==(_section$bulkeditable=null==section?void 0:section.bulkeditable)&&void 0!==_section$bulkeditable&&_section$bulkeditable}_isSelected(bulk){return"section"===bulk.selectedType&&bulk.selection.includes(this.id)}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=header.min.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -384,42 +384,49 @@ export default class extends BaseComponent {
|
||||
* @param {Event} event the triggered event
|
||||
*/
|
||||
async _requestDeleteSection(target, event) {
|
||||
// Check we have an id.
|
||||
const sectionId = target.dataset.id;
|
||||
|
||||
if (!sectionId) {
|
||||
const sectionIds = this._getTargetIds(target);
|
||||
if (sectionIds.length == 0) {
|
||||
return;
|
||||
}
|
||||
const sectionInfo = this.reactive.get('section', sectionId);
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const cmList = sectionInfo.cmlist ?? [];
|
||||
if (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle) {
|
||||
// We need confirmation if the section has something.
|
||||
const modalParams = {
|
||||
title: getString('confirm', 'core'),
|
||||
body: getString('confirmdeletesection', 'moodle', sectionInfo.title),
|
||||
saveButtonText: getString('delete', 'core'),
|
||||
type: ModalFactory.types.SAVE_CANCEL,
|
||||
};
|
||||
|
||||
const modal = await this._modalBodyRenderedPromise(modalParams);
|
||||
|
||||
modal.getRoot().on(
|
||||
ModalEvents.save,
|
||||
e => {
|
||||
// Stop the default save button behaviour which is to close the modal.
|
||||
e.preventDefault();
|
||||
modal.destroy();
|
||||
this.reactive.dispatch('sectionDelete', [sectionId]);
|
||||
}
|
||||
);
|
||||
// We don't need confirmation to delete empty sections.
|
||||
let needsConfirmation = sectionIds.some(sectionId => {
|
||||
const sectionInfo = this.reactive.get('section', sectionId);
|
||||
const cmList = sectionInfo.cmlist ?? [];
|
||||
return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);
|
||||
});
|
||||
if (!needsConfirmation) {
|
||||
this.reactive.dispatch('sectionDelete', sectionIds);
|
||||
return;
|
||||
} else {
|
||||
// We don't need confirmation to delete empty sections.
|
||||
this.reactive.dispatch('sectionDelete', [sectionId]);
|
||||
}
|
||||
|
||||
let bodyText = null;
|
||||
if (sectionIds.length == 1) {
|
||||
const sectionInfo = this.reactive.get('section', sectionIds[0]);
|
||||
bodyText = getString('confirmdeletesection', 'moodle', sectionInfo.title);
|
||||
} else {
|
||||
bodyText = getString('sectionsdelete_confirm', 'core_courseformat');
|
||||
}
|
||||
|
||||
const modalParams = {
|
||||
title: getString('confirm', 'core'),
|
||||
body: bodyText,
|
||||
type: ModalFactory.types.DELETE_CANCEL,
|
||||
};
|
||||
|
||||
const modal = await this._modalBodyRenderedPromise(modalParams);
|
||||
|
||||
modal.getRoot().on(
|
||||
ModalEvents.delete,
|
||||
e => {
|
||||
// Stop the default save button behaviour which is to close the modal.
|
||||
e.preventDefault();
|
||||
modal.destroy();
|
||||
this.reactive.dispatch('sectionDelete', sectionIds);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -490,39 +497,43 @@ export default class extends BaseComponent {
|
||||
* @param {Event} event the triggered event
|
||||
*/
|
||||
async _requestCmDelete(target, event) {
|
||||
// Check we have an id.
|
||||
const cmId = target.dataset.id;
|
||||
|
||||
if (!cmId) {
|
||||
const cmIds = this._getTargetIds(target);
|
||||
if (cmIds.length == 0) {
|
||||
return;
|
||||
}
|
||||
const cmInfo = this.reactive.get('cm', cmId);
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const modalParams = {
|
||||
title: getString('confirm', 'core'),
|
||||
body: getString(
|
||||
let bodyText = null;
|
||||
if (cmIds.length == 1) {
|
||||
const cmInfo = this.reactive.get('cm', cmIds[0]);
|
||||
bodyText = getString(
|
||||
'deletechecktypename',
|
||||
'moodle',
|
||||
{
|
||||
type: cmInfo.modname,
|
||||
name: cmInfo.name,
|
||||
}
|
||||
),
|
||||
saveButtonText: getString('delete', 'core'),
|
||||
type: ModalFactory.types.SAVE_CANCEL,
|
||||
);
|
||||
} else {
|
||||
bodyText = getString('cmsdelete_confirm', 'core_courseformat');
|
||||
}
|
||||
|
||||
const modalParams = {
|
||||
title: getString('confirm', 'core'),
|
||||
body: bodyText,
|
||||
type: ModalFactory.types.DELETE_CANCEL,
|
||||
};
|
||||
|
||||
const modal = await this._modalBodyRenderedPromise(modalParams);
|
||||
|
||||
modal.getRoot().on(
|
||||
ModalEvents.save,
|
||||
ModalEvents.delete,
|
||||
e => {
|
||||
// Stop the default save button behaviour which is to close the modal.
|
||||
e.preventDefault();
|
||||
modal.destroy();
|
||||
this.reactive.dispatch('cmDelete', [cmId]);
|
||||
this.reactive.dispatch('cmDelete', cmIds);
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -666,6 +677,9 @@ export default class extends BaseComponent {
|
||||
if (modalParams.saveButtonText !== undefined) {
|
||||
modal.setSaveButtonText(modalParams.saveButtonText);
|
||||
}
|
||||
if (modalParams.deleteButtonText !== undefined) {
|
||||
modal.setDeleteButtonText(modalParams.saveButtonText);
|
||||
}
|
||||
modal.show();
|
||||
return;
|
||||
}).catch(() => {
|
||||
|
@ -25,6 +25,12 @@
|
||||
*/
|
||||
|
||||
import DndSectionItem from 'core_courseformat/local/courseeditor/dndsectionitem';
|
||||
import {get_string as getString} from 'core/str';
|
||||
import {prefetchStrings} from 'core/prefetch';
|
||||
|
||||
prefetchStrings('core_courseformat', [
|
||||
'selectsection',
|
||||
]);
|
||||
|
||||
export default class extends DndSectionItem {
|
||||
|
||||
@ -71,9 +77,29 @@ export default class extends DndSectionItem {
|
||||
getWatchers() {
|
||||
return [
|
||||
{watch: `bulk:updated`, handler: this._refreshBulk},
|
||||
{watch: `section[${this.id}].title:updated`, handler: this._refreshSectionBulkSelector},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the bulk checkbox when the topic name changes.
|
||||
*
|
||||
* @param {object} param
|
||||
* @param {Object} param.element the section info
|
||||
*/
|
||||
async _refreshSectionBulkSelector({element}) {
|
||||
const checkbox = this.getElement(this.selectors.BULKCHECKBOX);
|
||||
if (!checkbox) {
|
||||
return;
|
||||
}
|
||||
const newLabel = await getString('selectsection', 'core_courseformat', element.title);
|
||||
checkbox.title = newLabel;
|
||||
const label = this.getElement(`label[for='${checkbox.id}']`);
|
||||
if (label) {
|
||||
label.innerText = newLabel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a bulk options.
|
||||
*
|
||||
|
@ -273,6 +273,7 @@ export default class {
|
||||
async sectionDelete(stateManager, sectionIds) {
|
||||
const course = stateManager.get('course');
|
||||
const updates = await this._callEditWebservice('section_delete', course.id, sectionIds);
|
||||
this.bulkReset(stateManager);
|
||||
stateManager.processUpdates(updates);
|
||||
}
|
||||
|
||||
@ -285,6 +286,7 @@ export default class {
|
||||
const course = stateManager.get('course');
|
||||
this.cmLock(stateManager, cmIds, true);
|
||||
const updates = await this._callEditWebservice('cm_delete', course.id, cmIds);
|
||||
this.bulkReset(stateManager);
|
||||
this.cmLock(stateManager, cmIds, false);
|
||||
stateManager.processUpdates(updates);
|
||||
}
|
||||
|
@ -110,6 +110,18 @@ class bulkedittools implements named_templatable, renderable {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$hasmanageactivities = has_capability('moodle/course:manageactivities', $context, $user);
|
||||
if ($hasmanageactivities) {
|
||||
$controls['delete'] = [
|
||||
'icon' => 'i/delete',
|
||||
'action' => 'cmDelete',
|
||||
'name' => get_string('delete'),
|
||||
'title' => get_string('cmsdelete', 'core_courseformat'),
|
||||
'bulk' => 'cm',
|
||||
];
|
||||
}
|
||||
|
||||
return $controls;
|
||||
}
|
||||
|
||||
@ -139,6 +151,18 @@ class bulkedittools implements named_templatable, renderable {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$deletecapabilities = ['moodle/course:movesections', 'moodle/course:update'];
|
||||
if (has_all_capabilities($deletecapabilities, $context, $user)) {
|
||||
$controls['delete'] = [
|
||||
'icon' => 'i/delete',
|
||||
'action' => 'deleteSection',
|
||||
'name' => get_string('delete'),
|
||||
'title' => get_string('sectionsdelete', 'core_courseformat'),
|
||||
'bulk' => 'section',
|
||||
];
|
||||
}
|
||||
|
||||
return $controls;
|
||||
}
|
||||
}
|
||||
|
@ -238,9 +238,9 @@ class stateactions {
|
||||
require_capability('moodle/course:update', $coursecontext);
|
||||
require_capability('moodle/course:movesections', $coursecontext);
|
||||
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
|
||||
foreach ($ids as $sectionid) {
|
||||
// We need to get the latest modinfo on each iteration because the section numbers change.
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
$section = $modinfo->get_section_info_by_id($sectionid, MUST_EXIST);
|
||||
// Send all activity deletions.
|
||||
if (!empty($modinfo->sections[$section->section])) {
|
||||
|
@ -33,10 +33,20 @@ $string['bulkeditoff'] = 'Close bulk edit';
|
||||
$string['bulkcancel'] = 'Close bulk editing';
|
||||
$string['bulkselection'] = '{$a} selected';
|
||||
$string['cmavailability'] = 'Activity availability';
|
||||
$string['cmdelete_title'] = 'Delete activity?';
|
||||
$string['cmdelete_info'] = 'This will delete the {$a->type} "{$a->name}" and any user data it contains';
|
||||
$string['cmsdelete'] = 'Delete activities';
|
||||
$string['cmsdelete_info'] = 'This will delete {$a->count} activities and any user data they contain';
|
||||
$string['cmsdelete_title'] = 'Delete selected activities?';
|
||||
$string['courseindex'] = 'Course index';
|
||||
$string['nobulkaction'] = 'No bulk actions available';
|
||||
$string['preference:coursesectionspreferences'] = 'Section user preferences for course {$a}';
|
||||
$string['privacy:metadata:preference:coursesectionspreferences'] = 'Section user preferences like collapsed and expanded.';
|
||||
$string['sectionavailability'] = 'Section availability';
|
||||
$string['sectiondelete_info'] = 'This will delete "{$a->name}" and all the activities it contains.';
|
||||
$string['sectiondelete_title'] = 'Delete section?';
|
||||
$string['sectionsdelete'] = 'Delete sections';
|
||||
$string['sectionsdelete_info'] = 'This will delete {$a->count} sections and all the activities they contain.';
|
||||
$string['sectionsdelete_title'] = 'Delete selected sections?';
|
||||
$string['selectcm'] = 'Select activity {$a}';
|
||||
$string['selectsection'] = 'Select section {$a}';
|
||||
|
Loading…
x
Reference in New Issue
Block a user