MDL-74987 core_courseformat: alt to duplicate modules
If a user drags an activity and press the ALT key, the activity will be duplicated in the new place instead of simply moved. This behaviour is quite normal in many softwares and it is a non intrusive usability improvement.
This commit is contained in:
@@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/dndcmitem",["exports","core/reactiv
|
||||
* @copyright 2021 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class _default extends _reactive.BaseComponent{configDragDrop(cmid){this.id=cmid,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}dragStart(dropdata){this.reactive.dispatch("cmDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("cmDrag",[dropdata.id],!1)}getDraggableData(){return this.reactive.getExporter().cmDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){return"cm"===(null==dropdata?void 0:dropdata.type)}showDropZone(dropdata){dropdata.nextcmid!=this.id&&dropdata.id!=this.id&&this.element.classList.add(this.classes.DROPUP)}hideDropZone(){this.element.classList.remove(this.classes.DROPUP)}drop(dropdata){dropdata.id!=this.id&&dropdata.nextcmid!=this.id&&this.reactive.dispatch("cmMove",[dropdata.id],null,this.id)}}return _exports.default=_default,_exports.default}));
|
||||
class _default extends _reactive.BaseComponent{configDragDrop(cmid){this.id=cmid,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}dragStart(dropdata){this.reactive.dispatch("cmDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("cmDrag",[dropdata.id],!1)}getDraggableData(){return this.reactive.getExporter().cmDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){return"cm"===(null==dropdata?void 0:dropdata.type)}showDropZone(dropdata){dropdata.nextcmid!=this.id&&dropdata.id!=this.id&&this.element.classList.add(this.classes.DROPUP)}hideDropZone(){this.element.classList.remove(this.classes.DROPUP)}drop(dropdata,event){if(dropdata.id!=this.id&&dropdata.nextcmid!=this.id){const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],null,this.id)}}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=dndcmitem.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/dndsection",["exports","core/reacti
|
||||
* @copyright 2021 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class _default extends _reactive.BaseComponent{configState(state){this.id=this.element.dataset.id,this.section=state.section.get(this.id),this.course=state.course}configDragDrop(sectionitem){this.reactive.isEditing&&this.reactive.supportComponents&&(this.sectionitem=sectionitem,this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.sectionitem&&this.sectionitem.unregister(),void 0!==this.dragdrop&&this.dragdrop.unregister()}getLastCm(){return null}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type))return!0;if("section"===(null==dropdata?void 0:dropdata.type)){const sectionzeroid=this.course.sectionlist[0];return(null==dropdata?void 0:dropdata.id)!=this.id&&(null==dropdata?void 0:dropdata.id)!=sectionzeroid&&this.id!=sectionzeroid}return!1}showDropZone(dropdata){var _this$getLastCm;"cm"==dropdata.type&&(null===(_this$getLastCm=this.getLastCm())||void 0===_this$getLastCm||_this$getLastCm.classList.add(this.classes.DROPDOWN));"section"==dropdata.type&&(this.section.number>dropdata.number?(this.element.classList.remove(this.classes.DROPUP),this.element.classList.add(this.classes.DROPDOWN)):(this.element.classList.add(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)))}hideDropZone(){var _this$getLastCm2;null===(_this$getLastCm2=this.getLastCm())||void 0===_this$getLastCm2||_this$getLastCm2.classList.remove(this.classes.DROPDOWN),this.element.classList.remove(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)}drop(dropdata){"cm"==dropdata.type&&this.reactive.dispatch("cmMove",[dropdata.id],this.id),"section"==dropdata.type&&this.reactive.dispatch("sectionMove",[dropdata.id],this.id)}}return _exports.default=_default,_exports.default}));
|
||||
class _default extends _reactive.BaseComponent{configState(state){this.id=this.element.dataset.id,this.section=state.section.get(this.id),this.course=state.course}configDragDrop(sectionitem){this.reactive.isEditing&&this.reactive.supportComponents&&(this.sectionitem=sectionitem,this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.sectionitem&&this.sectionitem.unregister(),void 0!==this.dragdrop&&this.dragdrop.unregister()}getLastCm(){return null}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type))return!0;if("section"===(null==dropdata?void 0:dropdata.type)){const sectionzeroid=this.course.sectionlist[0];return(null==dropdata?void 0:dropdata.id)!=this.id&&(null==dropdata?void 0:dropdata.id)!=sectionzeroid&&this.id!=sectionzeroid}return!1}showDropZone(dropdata){var _this$getLastCm;"cm"==dropdata.type&&(null===(_this$getLastCm=this.getLastCm())||void 0===_this$getLastCm||_this$getLastCm.classList.add(this.classes.DROPDOWN));"section"==dropdata.type&&(this.section.number>dropdata.number?(this.element.classList.remove(this.classes.DROPUP),this.element.classList.add(this.classes.DROPDOWN)):(this.element.classList.add(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)))}hideDropZone(){var _this$getLastCm2;null===(_this$getLastCm2=this.getLastCm())||void 0===_this$getLastCm2||_this$getLastCm2.classList.remove(this.classes.DROPDOWN),this.element.classList.remove(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)}drop(dropdata,event){if("cm"==dropdata.type){const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id)}"section"==dropdata.type&&this.reactive.dispatch("sectionMove",[dropdata.id],this.id)}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=dndsection.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/dndsectionitem",["exports","core/re
|
||||
* @copyright 2021 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class _default extends _reactive.BaseComponent{configDragDrop(sectionid,state,fullregion){this.id=sectionid,void 0===this.section&&(this.section=state.section.get(this.id)),void 0===this.course&&(this.course=state.course),this.section.number>0&&(this.getDraggableData=this._getDraggableData),this.fullregion=fullregion,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}_getDraggableData(){return this.reactive.getExporter().sectionDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type)){var _this$section;const firstcmid=null===(_this$section=this.section)||void 0===_this$section?void 0:_this$section.cmlist[0];return dropdata.id!==firstcmid}return!1}showDropZone(){this.element.classList.add(this.classes.DROPZONE)}hideDropZone(){this.element.classList.remove(this.classes.DROPZONE)}drop(dropdata){var _this$section2;"cm"==dropdata.type&&this.reactive.dispatch("cmMove",[dropdata.id],this.id,null===(_this$section2=this.section)||void 0===_this$section2?void 0:_this$section2.cmlist[0])}}return _exports.default=_default,_exports.default}));
|
||||
class _default extends _reactive.BaseComponent{configDragDrop(sectionid,state,fullregion){this.id=sectionid,void 0===this.section&&(this.section=state.section.get(this.id)),void 0===this.course&&(this.course=state.course),this.section.number>0&&(this.getDraggableData=this._getDraggableData),this.fullregion=fullregion,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}_getDraggableData(){return this.reactive.getExporter().sectionDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type)){var _this$section;const firstcmid=null===(_this$section=this.section)||void 0===_this$section?void 0:_this$section.cmlist[0];return dropdata.id!==firstcmid}return!1}showDropZone(){this.element.classList.add(this.classes.DROPZONE)}hideDropZone(){this.element.classList.remove(this.classes.DROPZONE)}drop(dropdata,event){if("cm"==dropdata.type){var _this$section2;const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id,null===(_this$section2=this.section)||void 0===_this$section2?void 0:_this$section2.cmlist[0])}}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=dndsectionitem.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
@@ -120,11 +120,13 @@ export default class extends BaseComponent {
|
||||
* Drop event handler.
|
||||
*
|
||||
* @param {Object} dropdata the accepted drop data
|
||||
* @param {Event} event the drop event
|
||||
*/
|
||||
drop(dropdata) {
|
||||
drop(dropdata, event) {
|
||||
// Call the move mutation if necessary.
|
||||
if (dropdata.id != this.id && dropdata.nextcmid != this.id) {
|
||||
this.reactive.dispatch('cmMove', [dropdata.id], null, this.id);
|
||||
const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';
|
||||
this.reactive.dispatch(mutation, [dropdata.id], null, this.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,11 +151,13 @@ export default class extends BaseComponent {
|
||||
* Drop event handler.
|
||||
*
|
||||
* @param {Object} dropdata the accepted drop data
|
||||
* @param {Event} event the drop event
|
||||
*/
|
||||
drop(dropdata) {
|
||||
drop(dropdata, event) {
|
||||
// Call the move mutation.
|
||||
if (dropdata.type == 'cm') {
|
||||
this.reactive.dispatch('cmMove', [dropdata.id], this.id);
|
||||
const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';
|
||||
this.reactive.dispatch(mutation, [dropdata.id], this.id);
|
||||
}
|
||||
if (dropdata.type == 'section') {
|
||||
this.reactive.dispatch('sectionMove', [dropdata.id], this.id);
|
||||
|
||||
@@ -135,11 +135,13 @@ export default class extends BaseComponent {
|
||||
* Drop event handler.
|
||||
*
|
||||
* @param {Object} dropdata the accepted drop data
|
||||
* @param {Event} event the drop event
|
||||
*/
|
||||
drop(dropdata) {
|
||||
drop(dropdata, event) {
|
||||
// Call the move mutation.
|
||||
if (dropdata.type == 'cm') {
|
||||
this.reactive.dispatch('cmMove', [dropdata.id], this.id, this.section?.cmlist[0]);
|
||||
const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';
|
||||
this.reactive.dispatch(mutation, [dropdata.id], this.id, this.section?.cmlist[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,8 +180,9 @@ export default class {
|
||||
* @param {StateManager} stateManager the current state manager
|
||||
* @param {array} cmIds the list of course modules ids
|
||||
* @param {number|undefined} targetSectionId the optional target sectionId
|
||||
* @param {number|undefined} targetCmId the target course module id
|
||||
*/
|
||||
async cmDuplicate(stateManager, cmIds, targetSectionId) {
|
||||
async cmDuplicate(stateManager, cmIds, targetSectionId, targetCmId) {
|
||||
const course = stateManager.get('course');
|
||||
// Lock all target sections.
|
||||
const sectionIds = new Set();
|
||||
@@ -195,7 +196,7 @@ export default class {
|
||||
}
|
||||
this.sectionLock(stateManager, Array.from(sectionIds), true);
|
||||
|
||||
const updates = await this._callEditWebservice('cm_duplicate', course.id, cmIds, targetSectionId);
|
||||
const updates = await this._callEditWebservice('cm_duplicate', course.id, cmIds, targetSectionId, targetCmId);
|
||||
stateManager.processUpdates(updates);
|
||||
|
||||
this.sectionLock(stateManager, Array.from(sectionIds), false);
|
||||
|
||||
@@ -428,8 +428,8 @@ class stateactions {
|
||||
* @param stateupdates $updates the affected course elements track
|
||||
* @param stdClass $course the course object
|
||||
* @param int[] $ids course modules ids to duplicate
|
||||
* @param int $targetsectionid optional target section id destination
|
||||
* @param int $targetcmid not used
|
||||
* @param int|null $targetsectionid optional target section id destination
|
||||
* @param int|null $targetcmid optional target before cm id destination
|
||||
*/
|
||||
public function cm_duplicate(
|
||||
stateupdates $updates,
|
||||
@@ -461,12 +461,19 @@ class stateactions {
|
||||
$targetsection = $modinfo->get_section_info_by_id($targetsectionid, MUST_EXIST);
|
||||
}
|
||||
|
||||
$beforecm = null;
|
||||
if (!empty($targetcmid)) {
|
||||
$this->validate_cms($course, [$targetcmid], __FUNCTION__);
|
||||
$beforecm = $modinfo->get_cm($targetcmid);
|
||||
$targetsection = $modinfo->get_section_info_by_id($beforecm->section, MUST_EXIST);
|
||||
}
|
||||
|
||||
// Duplicate course modules.
|
||||
$affectedcmids = [];
|
||||
foreach ($cms as $cm) {
|
||||
if ($newcm = duplicate_module($course, $cm)) {
|
||||
if ($targetsection) {
|
||||
moveto_module($newcm, $targetsection);
|
||||
moveto_module($newcm, $targetsection, $beforecm);
|
||||
} else {
|
||||
$affectedcmids[] = $newcm->id;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user