MDL-78324 core: Convert core/tag to esm

This module is converted to ESM in preparation for simplification of
Modal instantiation.
This commit is contained in:
Andrew Nicols 2023-08-18 00:01:24 +08:00
parent 98f0aebde6
commit a797020524
No known key found for this signature in database
GPG Key ID: 6D1E3157C8CFBF14
3 changed files with 405 additions and 423 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -21,445 +21,427 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.0
*/
define([
'jquery',
'core/ajax',
'core/templates',
'core/notification',
'core/str',
'core/modal_save_cancel',
'core/modal_events',
'core/pending',
], function(
$,
ajax,
templates,
notification,
str,
SaveCancelModal,
ModalEvents,
Pending
) {
return /** @alias module:core/tag */ {
/**
* Initialises tag index page.
*
* @method initTagindexPage
*/
initTagindexPage: function() {
// Click handler for changing tag type.
$('body').delegate('.tagarea[data-ta] a[data-quickload=1]', 'click', function(e) {
var pendingPromise = new Pending('core/tag:initTagindexPage');
import $ from 'jquery';
import {call as fetchMany} from 'core/ajax';
import * as Notification from 'core/notification';
import * as Templates from 'core/templates';
import {
get_string as getString,
get_strings as getStrings,
} from 'core/str';
import * as ModalEvents from 'core/modal_events';
import Pending from 'core/pending';
import SaveCancelModal from 'core/modal_save_cancel';
import Config from 'core/config';
e.preventDefault();
var target = $(this);
var query = target[0].search.replace(/^\?/, '');
var tagarea = target.closest('.tagarea[data-ta]');
var args = query.split('&').reduce(function(s, c) {
var t = c.split('=');
s[t[0]] = decodeURIComponent(t[1]);
return s;
}, {});
const getTagIndex = (tagindex) => fetchMany([{
methodname: 'core_tag_get_tagindex',
args: {tagindex}
}])[0];
ajax.call([{
methodname: 'core_tag_get_tagindex',
args: {tagindex: args}
}])[0]
.then(function(data) {
return templates.render('core_tag/index', data);
})
.then(function(html, js) {
templates.replaceNode(tagarea, html, js);
return;
})
.always(pendingPromise.resolve)
.catch(notification.exception);
});
const getCheckedTags = (form) => form.querySelectorAll('input[data-togglegroup="tags-manage"][data-toggle="slave"]:checked');
const handleCombineRequest = async(tagManagementCombine) => {
const pendingPromise = new Pending('core/tag:tag-management-combine');
const form = tagManagementCombine.closest('form');
const checkedTags = getCheckedTags(form);
if (checkedTags.length <= 1) {
// We need at least 2 tags to combine them.
Notification.alert(
getString('combineselected', 'tag'),
getString('selectmultipletags', 'tag'),
getString('ok'),
);
return;
}
const tags = Array.from(checkedTags.values()).map((tag) => {
const namedElement = document.querySelector(`.inplaceeditable[data-itemtype=tagname][data-itemid="${tag.value}"]`);
return {
id: tag.value,
name: namedElement.dataset.value,
};
});
const modal = await SaveCancelModal.create({
title: getString('combineselected', 'tag'),
buttons: {
save: getString('continue', 'core'),
},
body: Templates.render('core_tag/combine_tags', {tags}),
show: true,
removeOnClose: true,
});
/**
* Initialises tag management page.
*
* @method initManagePage
*/
initManagePage: function() {
// Set cell 'time modified' to 'now' when any of the element is updated in this row.
$('body').on('updated', '[data-inplaceeditable]', function(e) {
var pendingPromise = new Pending('core/tag:initManagePage');
// Handle save event.
modal.getRoot().on(ModalEvents.save, (e) => {
e.preventDefault();
str.get_strings([
{
key: 'selecttag',
component: 'core_tag',
},
{
key: 'now',
component: 'core',
},
])
.then(function(result) {
$('label[for="tagselect' + e.ajaxreturn.itemid + '"]').html(result[0]);
$(e.target).closest('tr').find('td.col-timemodified').html(result[1]);
// Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?
const tempElement = document.createElement('input');
tempElement.hidden = true;
tempElement.name = tagManagementCombine.name;
form.append(tempElement);
return;
})
.always(pendingPromise.resolve)
.catch(notification.exception);
// Get the selected tag from the modal.
var maintag = $('input[name=maintag]:checked', '#combinetags_form').val();
// Append this in the tags list form.
$("<input type='hidden'/>").attr('name', 'maintag').attr('value', maintag).appendTo(form);
// Submit the tags list form.
form.submit();
});
if (e.ajaxreturn.itemtype === 'tagflag') {
var row = $(e.target).closest('tr');
if (e.ajaxreturn.value === '0') {
row.removeClass('table-warning');
} else {
row.addClass('table-warning');
}
}
});
await modal.getBodyPromise();
// Tick the first option.
const firstOption = document.querySelector('#combinetags_form input[type=radio]');
firstOption.focus();
firstOption.checked = true;
// Confirmation for single tag delete link.
$('.tag-management-table').delegate('a.tagdelete', 'click', function(e) {
var pendingPromise = new Pending('core/tag:tagdelete');
pendingPromise.resolve();
e.preventDefault();
var href = $(this).attr('href');
str.get_strings([
{key: 'delete', component: 'core'},
{key: 'confirmdeletetag', component: 'tag'},
{key: 'yes', component: 'core'},
{key: 'no', component: 'core'},
])
.then(function(s) {
return notification.confirm(s[0], s[1], s[2], s[3], function() {
window.location.href = href;
});
})
.always(pendingPromise.resolve)
.catch(notification.exception);
});
return;
};
// Confirmation for bulk tag delete button.
$("#tag-management-delete").click(function(e) {
var form = $(this).closest('form').get(0);
const addStandardTags = async() => {
var pendingPromise = new Pending('core/tag:addstandardtag');
var cnt = $(form).find("input[data-togglegroup='tags-manage'][data-toggle='slave']:checked").length;
if (!cnt) {
return;
}
var pendingPromise = new Pending('core/tag:tag-management-delete');
var tempElement = $("<input type='hidden'/>").attr('name', this.name);
e.preventDefault();
str.get_strings([
{key: 'delete', component: 'core'},
{key: 'confirmdeletetags', component: 'tag'},
{key: 'yes', component: 'core'},
{key: 'no', component: 'core'},
])
.then(function(s) {
return notification.confirm(s[0], s[1], s[2], s[3], function() {
tempElement.appendTo(form);
form.submit();
});
})
.always(pendingPromise.resolve)
.catch(notification.exception);
});
// Confirmation for bulk tag combine button.
$("#tag-management-combine").click(function(e) {
var pendingPromise = new Pending('core/tag:tag-management-combine');
e.preventDefault();
var form = $(this).closest('form').get(0);
var tags = $(form).find("input[data-togglegroup='tags-manage'][data-toggle='slave']:checked");
if (tags.length <= 1) {
str.get_strings([
{key: 'combineselected', component: 'tag'},
{key: 'selectmultipletags', component: 'tag'},
{key: 'ok'},
])
.then(function(s) {
return notification.alert(s[0], s[1], s[2]);
})
.always(pendingPromise.resolve)
.catch(notification.exception);
return;
}
var tempElement = $("<input type='hidden'/>").attr('name', this.name);
var saveButtonText = '';
var tagOptions = [];
tags.each(function() {
var tagid = $(this).val(),
tagname = $('.inplaceeditable[data-itemtype=tagname][data-itemid=' + tagid + ']').attr('data-value');
tagOptions.push({
id: tagid,
name: tagname
});
});
str.get_strings([
{key: 'combineselected', component: 'tag'},
{key: 'continue', component: 'core'}
])
.then(function(langStrings) {
var modalTitle = langStrings[0];
saveButtonText = langStrings[1];
var templateContext = {
tags: tagOptions
};
return SaveCancelModal.create({
title: modalTitle,
body: templates.render('core_tag/combine_tags', templateContext),
buttons: {
save: saveButtonText,
},
show: true,
removeOnClose: true,
});
})
.then(function(modal) {
// Handle save event.
modal.getRoot().on(ModalEvents.save, function(e) {
e.preventDefault();
// Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?
tempElement.appendTo(form);
// Get the selected tag from the modal.
var maintag = $('input[name=maintag]:checked', '#combinetags_form').val();
// Append this in the tags list form.
$("<input type='hidden'/>").attr('name', 'maintag').attr('value', maintag).appendTo(form);
// Submit the tags list form.
form.submit();
});
// Tick the first option.
$('#combinetags_form input[type=radio]').first().focus().prop('checked', true);
return;
})
.always(pendingPromise.resolve)
.catch(notification.exception);
});
// When user changes tag name to some name that already exists suggest to combine the tags.
$('body').on('updatefailed', '[data-inplaceeditable][data-itemtype=tagname]', function(e) {
var exception = e.exception; // The exception object returned by the callback.
var newvalue = e.newvalue; // The value that user tried to udpated the element to.
var tagid = $(e.target).attr('data-itemid');
if (exception.errorcode === 'namesalreadybeeingused') {
var pendingPromise = new Pending('core/tag:updatefailed');
e.preventDefault(); // This will prevent default error dialogue.
str.get_strings([
{key: 'confirm', component: 'core'},
{key: 'nameuseddocombine', component: 'tag'},
{key: 'yes', component: 'core'},
{key: 'cancel', component: 'core'},
])
.then(function(s) {
return notification.confirm(s[0], s[1], s[2], s[3], function() {
window.location.href = window.location.href + "&newname=" + encodeURIComponent(newvalue) +
"&tagid=" + encodeURIComponent(tagid) +
'&action=renamecombine&sesskey=' + M.cfg.sesskey;
});
})
.always(pendingPromise.resolve)
.catch(notification.exception);
}
});
// Form for adding standard tags.
$('body').on('click', 'a[data-action=addstandardtag]', function(e) {
var pendingPromise = new Pending('core/tag:addstandardtag');
e.preventDefault();
return SaveCancelModal.create({
title: str.get_string('addotags', 'tag'),
body: templates.render('core_tag/add_tags', {
actionurl: window.location.href,
sesskey: M.cfg.sesskey
}),
buttons: {
save: str.get_string('continue', 'core'),
},
show: true,
removeOnClose: true,
})
.then(function(modal) {
// Handle save event.
modal.getRoot().on(ModalEvents.save, function(e) {
var tagsInput = $(e.currentTarget).find('#id_tagslist');
var name = tagsInput.val().trim();
// Set the text field's value to the trimmed value.
tagsInput.val(name);
// Add submit event listener to the form.
var tagsForm = $('#addtags_form');
tagsForm.on('submit', function(e) {
// Validate the form.
var form = $('#addtags_form');
if (form[0].checkValidity() === false) {
e.preventDefault();
e.stopPropagation();
}
form.addClass('was-validated');
// BS2 compatibility.
$('[data-region="tagslistinput"]').addClass('error');
var errorMessage = $('#id_tagslist_error_message');
errorMessage.removeAttr('hidden');
errorMessage.addClass('help-block');
});
// Try to submit the form.
tagsForm.submit();
return false;
});
return;
})
.always(pendingPromise.resolve)
.catch(notification.exception);
});
const modal = await SaveCancelModal.create({
title: getString('addotags', 'tag'),
body: Templates.render('core_tag/add_tags', {
actionurl: window.location.href,
sesskey: M.cfg.sesskey,
}),
buttons: {
save: getString('continue', 'core'),
},
removeOnClose: true,
show: true,
});
/**
* Initialises tag collection management page.
*
* @method initManageCollectionsPage
*/
initManageCollectionsPage: function() {
$('body').on('updated', '[data-inplaceeditable]', function(e) {
var pendingPromise = new Pending('core/tag:initManageCollectionsPage-updated');
// Handle save event.
modal.getRoot().on(ModalEvents.save, (e) => {
var tagsInput = $(e.currentTarget).find('#id_tagslist');
var name = tagsInput.val().trim();
var ajaxreturn = e.ajaxreturn,
areaid, collid, isenabled;
if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareaenable') {
areaid = $(this).attr('data-itemid');
$(".tag-collections-table ul[data-collectionid] li[data-areaid=" + areaid + "]").hide();
isenabled = ajaxreturn.value;
if (isenabled === '1') {
$(this).closest('tr').removeClass('dimmed_text');
collid = $(this).closest('tr').find('[data-itemtype="tagareacollection"]').attr("data-value");
$(".tag-collections-table ul[data-collectionid=" + collid + "] li[data-areaid=" + areaid + "]").show();
} else {
$(this).closest('tr').addClass('dimmed_text');
}
}
if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareacollection') {
areaid = $(this).attr('data-itemid');
$(".tag-collections-table ul[data-collectionid] li[data-areaid=" + areaid + "]").hide();
collid = $(this).attr('data-value');
isenabled = $(this).closest('tr').find('[data-itemtype="tagareaenable"]').attr("data-value");
if (isenabled === "1") {
$(".tag-collections-table ul[data-collectionid=" + collid + "] li[data-areaid=" + areaid + "]").show();
}
}
pendingPromise.resolve();
});
$('body').on('click', '.addtagcoll > a', function(e) {
var pendingPromise = new Pending('core/tag:initManageCollectionsPage-addtagcoll');
// Set the text field's value to the trimmed value.
tagsInput.val(name);
// Add submit event listener to the form.
var tagsForm = $('#addtags_form');
tagsForm.on('submit', function(e) {
// Validate the form.
var form = $('#addtags_form');
if (form[0].checkValidity() === false) {
e.preventDefault();
var keys = [
{
key: 'addtagcoll',
component: 'tag'
},
{
key: 'create',
component: 'core'
}
];
e.stopPropagation();
}
form.addClass('was-validated');
var href = $(this).attr('data-url');
var saveButtonText = '';
str.get_strings(keys)
.then(function(langStrings) {
var modalTitle = langStrings[0];
saveButtonText = langStrings[1];
var templateContext = {
actionurl: href,
sesskey: M.cfg.sesskey
};
return SaveCancelModal.create({
title: modalTitle,
body: templates.render('core_tag/add_tag_collection', templateContext),
buttons: {
save: saveButtonText,
},
show: true,
removeOnClose: true,
});
})
.then(function(modal) {
// Handle save event.
modal.getRoot().on(ModalEvents.save, function(e) {
var collectionInput = $(e.currentTarget).find('#addtagcoll_name');
var name = collectionInput.val().trim();
// Set the text field's value to the trimmed value.
collectionInput.val(name);
// BS2 compatibility.
$('[data-region="tagslistinput"]').addClass('error');
var errorMessage = $('#id_tagslist_error_message');
errorMessage.removeAttr('hidden');
errorMessage.addClass('help-block');
});
// Add submit event listener to the form.
var form = $('#addtagcoll_form');
form.on('submit', function(e) {
// Validate the form.
if (form[0].checkValidity() === false) {
e.preventDefault();
e.stopPropagation();
}
form.addClass('was-validated');
// Try to submit the form.
tagsForm.submit();
// BS2 compatibility.
$('[data-region="addtagcoll_nameinput"]').addClass('error');
var errorMessage = $('#id_addtagcoll_name_error_message');
errorMessage.removeAttr('hidden');
errorMessage.addClass('help-block');
});
return false;
});
// Try to submit the form.
form.submit();
await modal.getBodyPromise();
pendingPromise.resolve();
};
return false;
});
const deleteSelectedTags = async(form) => {
const checkedTags = getCheckedTags(form);
if (!checkedTags.length) {
return;
}
return modal;
try {
await Notification.saveCancelPromise(
getString('delete'),
getString('confirmdeletetags', 'tag'),
getString('yes'),
getString('no'),
);
})
.always(pendingPromise.resolve)
.catch(notification.exception);
});
// Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?
const tempElement = document.createElement('input');
tempElement.hidden = true;
tempElement.name = 'bulkdelete';
form.append(tempElement);
form.submit();
} catch {
return;
}
};
$('body').on('click', '.tag-collections-table .action_delete', function(e) {
var pendingPromise = new Pending('core/tag:initManageCollectionsPage-action_delete');
const deleteSelectedTag = async(button) => {
try {
await Notification.saveCancelPromise(
getString('delete'),
getString('confirmdeletetag', 'tag'),
getString('yes'),
getString('no'),
);
window.location.href = button.href;
} catch {
return;
}
};
const deleteSelectedCollection = async(button) => {
try {
await Notification.saveCancelPromise(
getString('delete'),
getString('suredeletecoll', 'tag', button.dataset.collname),
getString('yes'),
getString('no'),
);
const redirectTarget = new URL(button.dataset.url);
redirectTarget.searchParams.set('sesskey', Config.sesskey);
window.location.href = redirectTarget;
} catch {
return;
}
};
const addTagCollection = async(link) => {
const pendingPromise = new Pending('core/tag:initManageCollectionsPage-addtagcoll');
const href = link.dataset.url;
const modal = await SaveCancelModal.create({
title: getString('addtagcoll', 'tag'),
buttons: {
save: getString('create', 'core'),
},
body: Templates.render('core_tag/add_tag_collection', {
actionurl: href,
sesskey: M.cfg.sesskey,
}),
removeOnClose: true,
show: true,
});
// Handle save event.
modal.getRoot().on(ModalEvents.save, (e) => {
const collectionInput = $(e.currentTarget).find('#addtagcoll_name');
const name = collectionInput.val().trim();
// Set the text field's value to the trimmed value.
collectionInput.val(name);
// Add submit event listener to the form.
const form = $('#addtagcoll_form');
form.on('submit', function(e) {
// Validate the form.
if (form[0].checkValidity() === false) {
e.preventDefault();
var href = $(this).attr('data-url') + '&sesskey=' + M.cfg.sesskey;
str.get_strings([
{key: 'delete'},
{key: 'suredeletecoll', component: 'tag', param: $(this).attr('data-collname')},
{key: 'yes'},
{key: 'no'},
])
.then(function(s) {
return notification.confirm(s[0], s[1], s[2], s[3], function() {
window.location.href = href;
});
})
.always(pendingPromise.resolve)
.catch(notification.exception);
});
e.stopPropagation();
}
form.addClass('was-validated');
// BS2 compatibility.
$('[data-region="addtagcoll_nameinput"]').addClass('error');
const errorMessage = $('#id_addtagcoll_name_error_message');
errorMessage.removeAttr('hidden');
errorMessage.addClass('help-block');
});
// Try to submit the form.
form.submit();
return false;
});
pendingPromise.resolve();
};
/**
* Initialises tag index page.
*
* @method initTagindexPage
*/
export const initTagindexPage = async() => {
document.addEventListener('click', async(e) => {
const targetArea = e.target.closest('a[data-quickload="1"]');
if (!targetArea) {
return;
}
};
});
const tagArea = targetArea.closest('.tagarea[data-ta]');
if (!tagArea) {
return;
}
e.preventDefault();
const pendingPromise = new Pending('core/tag:initTagindexPage');
const query = targetArea.search.replace(/^\?/, '');
const params = Object.fromEntries((new URLSearchParams(query)).entries());
try {
const data = await getTagIndex(params);
const {html, js} = await Templates.renderForPromise('core_tag/index', data);
Templates.replaceNode(tagArea, html, js);
} catch (error) {
Notification.exception(error);
}
pendingPromise.resolve();
});
};
/**
* Initialises tag management page.
*
* @method initManagePage
*/
export const initManagePage = () => {
// Set cell 'time modified' to 'now' when any of the element is updated in this row.
$('body').on('updated', '[data-inplaceeditable]', function(e) {
var pendingPromise = new Pending('core/tag:initManagePage');
getStrings([
{
key: 'selecttag',
component: 'core_tag',
},
{
key: 'now',
component: 'core',
},
])
.then(function(result) {
$('label[for="tagselect' + e.ajaxreturn.itemid + '"]').html(result[0]);
$(e.target).closest('tr').find('td.col-timemodified').html(result[1]);
return;
})
.always(pendingPromise.resolve)
.catch(Notification.exception);
if (e.ajaxreturn.itemtype === 'tagflag') {
var row = $(e.target).closest('tr');
if (e.ajaxreturn.value === '0') {
row.removeClass('table-warning');
} else {
row.addClass('table-warning');
}
}
});
// Confirmation for bulk tag combine button.
document.addEventListener('click', async(e) => {
const tagManagementCombine = e.target.closest('#tag-management-combine');
if (tagManagementCombine) {
e.preventDefault();
handleCombineRequest(tagManagementCombine);
}
if (e.target.closest('a[data-action="addstandardtag"]')) {
e.preventDefault();
addStandardTags();
}
const bulkActionDeleteButton = e.target.closest('#tag-management-delete');
if (bulkActionDeleteButton) {
e.preventDefault();
deleteSelectedTags(bulkActionDeleteButton.closest('form'));
}
const rowDeleteButton = e.target.closest('.tagdelete');
if (rowDeleteButton) {
e.preventDefault();
deleteSelectedTag(rowDeleteButton);
}
});
// When user changes tag name to some name that already exists suggest to combine the tags.
$('body').on('updatefailed', '[data-inplaceeditable][data-itemtype=tagname]', async(e) => {
var exception = e.exception; // The exception object returned by the callback.
var newvalue = e.newvalue; // The value that user tried to udpated the element to.
var tagid = $(e.target).attr('data-itemid');
if (exception.errorcode !== 'namesalreadybeeingused') {
return;
}
e.preventDefault(); // This will prevent default error dialogue.
try {
await Notification.saveCancelPromise(
getString('confirm'),
getString('nameuseddocombine', 'tag'),
getString('yes'),
getString('cancel'),
);
// The Promise will resolve on 'Yes' button, and reject on 'Cancel' button.
const redirectTarget = new URL(window.location);
redirectTarget.searchParams.set('newname', newvalue);
redirectTarget.searchParams.set('tagid', tagid);
redirectTarget.searchParams.set('action', 'renamecombine');
redirectTarget.searchParams.set('sesskey', Config.sesskey);
window.location.href = redirectTarget;
} catch {
return;
}
});
};
/**
* Initialises tag collection management page.
*
* @method initManageCollectionsPage
*/
export const initManageCollectionsPage = () => {
$('body').on('updated', '[data-inplaceeditable]', function(e) {
var pendingPromise = new Pending('core/tag:initManageCollectionsPage-updated');
var ajaxreturn = e.ajaxreturn,
areaid, collid, isenabled;
if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareaenable') {
areaid = $(this).attr('data-itemid');
$(".tag-collections-table ul[data-collectionid] li[data-areaid=" + areaid + "]").hide();
isenabled = ajaxreturn.value;
if (isenabled === '1') {
$(this).closest('tr').removeClass('dimmed_text');
collid = $(this).closest('tr').find('[data-itemtype="tagareacollection"]').attr("data-value");
$(".tag-collections-table ul[data-collectionid=" + collid + "] li[data-areaid=" + areaid + "]").show();
} else {
$(this).closest('tr').addClass('dimmed_text');
}
}
if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareacollection') {
areaid = $(this).attr('data-itemid');
$(".tag-collections-table ul[data-collectionid] li[data-areaid=" + areaid + "]").hide();
collid = $(this).attr('data-value');
isenabled = $(this).closest('tr').find('[data-itemtype="tagareaenable"]').attr("data-value");
if (isenabled === "1") {
$(".tag-collections-table ul[data-collectionid=" + collid + "] li[data-areaid=" + areaid + "]").show();
}
}
pendingPromise.resolve();
});
document.addEventListener('click', async(e) => {
const addTagCollectionNode = e.target.closest('.addtagcoll > a');
if (addTagCollectionNode) {
e.preventDefault();
addTagCollection(addTagCollectionNode);
return;
}
const deleteCollectionButton = e.target.closest('.tag-collections-table .action_delete');
if (deleteCollectionButton) {
e.preventDefault();
deleteSelectedCollection(deleteCollectionButton);
}
});
};