269 lines
9.8 KiB
JavaScript
269 lines
9.8 KiB
JavaScript
// 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/>.
|
|
|
|
/**
|
|
* Report builder audiences
|
|
*
|
|
* @module core_reportbuilder/audience
|
|
* @copyright 2021 David Matamoros <davidmc@moodle.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
import 'core/inplace_editable';
|
|
import Templates from 'core/templates';
|
|
import Notification from 'core/notification';
|
|
import Pending from 'core/pending';
|
|
import {prefetchStrings} from 'core/prefetch';
|
|
import {getString} from 'core/str';
|
|
import DynamicForm from 'core_form/dynamicform';
|
|
import {add as addToast} from 'core/toast';
|
|
import {deleteAudience} from 'core_reportbuilder/local/repository/audiences';
|
|
import * as reportSelectors from 'core_reportbuilder/local/selectors';
|
|
import {loadFragment} from 'core/fragment';
|
|
import {markFormAsDirty} from 'core_form/changechecker';
|
|
|
|
let reportId = 0;
|
|
let contextId = 0;
|
|
|
|
/**
|
|
* Add audience card
|
|
*
|
|
* @param {String} className
|
|
* @param {String} title
|
|
*/
|
|
const addAudienceCard = (className, title) => {
|
|
const pendingPromise = new Pending('core_reportbuilder/audience:add');
|
|
|
|
const audiencesContainer = document.querySelector(reportSelectors.regions.audiencesContainer);
|
|
const audienceCardLength = audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard).length;
|
|
|
|
const params = {
|
|
classname: className,
|
|
reportid: reportId,
|
|
showormessage: (audienceCardLength > 0),
|
|
title: title,
|
|
};
|
|
|
|
// Load audience card fragment, render and then initialise the form within.
|
|
loadFragment('core_reportbuilder', 'audience_form', contextId, params)
|
|
.then((html, js) => {
|
|
const audienceCard = Templates.appendNodeContents(audiencesContainer, html, js)[0];
|
|
const audienceEmptyMessage = audiencesContainer.querySelector(reportSelectors.regions.audienceEmptyMessage);
|
|
|
|
const audienceForm = initAudienceCardForm(audienceCard);
|
|
// Mark as dirty new audience form created to prevent users leaving the page without saving it.
|
|
markFormAsDirty(audienceForm.getFormNode());
|
|
audienceEmptyMessage.classList.add('hidden');
|
|
|
|
return getString('audienceadded', 'core_reportbuilder', title);
|
|
})
|
|
.then(addToast)
|
|
.then(() => pendingPromise.resolve())
|
|
.catch(Notification.exception);
|
|
};
|
|
|
|
/**
|
|
* Edit audience card
|
|
*
|
|
* @param {Element} audienceCard
|
|
*/
|
|
const editAudienceCard = audienceCard => {
|
|
const pendingPromise = new Pending('core_reportbuilder/audience:edit');
|
|
|
|
// Load audience form with data for editing, then toggle visible controls in the card.
|
|
const audienceForm = initAudienceCardForm(audienceCard);
|
|
audienceForm.load({id: audienceCard.dataset.audienceId})
|
|
.then(() => {
|
|
const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);
|
|
const audienceDescription = audienceCard.querySelector(reportSelectors.regions.audienceDescription);
|
|
const audienceEdit = audienceCard.querySelector(reportSelectors.actions.audienceEdit);
|
|
|
|
audienceFormContainer.classList.remove('hidden');
|
|
audienceDescription.classList.add('hidden');
|
|
audienceEdit.disabled = true;
|
|
|
|
return pendingPromise.resolve();
|
|
})
|
|
.catch(Notification.exception);
|
|
};
|
|
|
|
/**
|
|
* Initialise dynamic form within given audience card
|
|
*
|
|
* @param {Element} audienceCard
|
|
* @return {DynamicForm}
|
|
*/
|
|
const initAudienceCardForm = audienceCard => {
|
|
const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);
|
|
const audienceForm = new DynamicForm(audienceFormContainer, '\\core_reportbuilder\\form\\audience');
|
|
|
|
// After submitting the form, update the card instance and description properties.
|
|
audienceForm.addEventListener(audienceForm.events.FORM_SUBMITTED, data => {
|
|
const audienceHeading = audienceCard.querySelector(reportSelectors.regions.audienceHeading);
|
|
const audienceDescription = audienceCard.querySelector(reportSelectors.regions.audienceDescription);
|
|
|
|
audienceCard.dataset.audienceId = data.detail.instanceid;
|
|
|
|
audienceHeading.innerHTML = data.detail.heading;
|
|
audienceDescription.innerHTML = data.detail.description;
|
|
|
|
closeAudienceCardForm(audienceCard);
|
|
|
|
return getString('audiencesaved', 'core_reportbuilder')
|
|
.then(addToast);
|
|
});
|
|
|
|
// If cancelling the form, close the card or remove it if it was never created.
|
|
audienceForm.addEventListener(audienceForm.events.FORM_CANCELLED, () => {
|
|
if (audienceCard.dataset.audienceId > 0) {
|
|
closeAudienceCardForm(audienceCard);
|
|
} else {
|
|
removeAudienceCard(audienceCard);
|
|
}
|
|
});
|
|
|
|
return audienceForm;
|
|
};
|
|
|
|
/**
|
|
* Delete audience card
|
|
*
|
|
* @param {Element} audienceDelete
|
|
*/
|
|
const deleteAudienceCard = audienceDelete => {
|
|
const audienceCard = audienceDelete.closest(reportSelectors.regions.audienceCard);
|
|
const {audienceId, audienceTitle, audienceEditWarning = false} = audienceCard.dataset;
|
|
|
|
// The edit warning indicates the audience is in use in a report schedule.
|
|
const audienceDeleteConfirmation = audienceEditWarning ? 'audienceusedbyschedule' : 'deleteaudienceconfirm';
|
|
|
|
Notification.saveCancelPromise(
|
|
getString('deleteaudience', 'core_reportbuilder', audienceTitle),
|
|
getString(audienceDeleteConfirmation, 'core_reportbuilder', audienceTitle),
|
|
getString('delete', 'core'),
|
|
{triggerElement: audienceDelete}
|
|
).then(() => {
|
|
const pendingPromise = new Pending('core_reportbuilder/audience:delete');
|
|
|
|
return deleteAudience(reportId, audienceId)
|
|
.then(() => addToast(getString('audiencedeleted', 'core_reportbuilder', audienceTitle)))
|
|
.then(() => {
|
|
removeAudienceCard(audienceCard);
|
|
return pendingPromise.resolve();
|
|
})
|
|
.catch(Notification.exception);
|
|
}).catch(() => {
|
|
return;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Close audience card form
|
|
*
|
|
* @param {Element} audienceCard
|
|
*/
|
|
const closeAudienceCardForm = audienceCard => {
|
|
// Remove the [data-region="audience-form-container"] (with all the event listeners attached to it), and create it again.
|
|
const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);
|
|
const NewAudienceFormContainer = audienceFormContainer.cloneNode(false);
|
|
audienceCard.querySelector(reportSelectors.regions.audienceForm).replaceChild(NewAudienceFormContainer, audienceFormContainer);
|
|
// Show the description container and enable the action buttons.
|
|
audienceCard.querySelector(reportSelectors.regions.audienceDescription).classList.remove('hidden');
|
|
audienceCard.querySelector(reportSelectors.actions.audienceEdit).disabled = false;
|
|
audienceCard.querySelector(reportSelectors.actions.audienceDelete).disabled = false;
|
|
};
|
|
|
|
/**
|
|
* Remove audience card
|
|
*
|
|
* @param {Element} audienceCard
|
|
*/
|
|
const removeAudienceCard = audienceCard => {
|
|
audienceCard.remove();
|
|
|
|
const audiencesContainer = document.querySelector(reportSelectors.regions.audiencesContainer);
|
|
const audienceCards = audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard);
|
|
|
|
// Show message if there are no cards remaining, ensure first card's separator is not present.
|
|
if (audienceCards.length === 0) {
|
|
const audienceEmptyMessage = document.querySelector(reportSelectors.regions.audienceEmptyMessage);
|
|
audienceEmptyMessage.classList.remove('hidden');
|
|
} else {
|
|
const audienceFirstCardSeparator = audienceCards[0].querySelector('.audience-separator');
|
|
audienceFirstCardSeparator?.remove();
|
|
}
|
|
};
|
|
|
|
let initialized = false;
|
|
|
|
/**
|
|
* Initialise audiences tab.
|
|
*
|
|
* @param {Number} id
|
|
* @param {Number} contextid
|
|
*/
|
|
export const init = (id, contextid) => {
|
|
prefetchStrings('core_reportbuilder', [
|
|
'audienceadded',
|
|
'audiencedeleted',
|
|
'audiencesaved',
|
|
'audienceusedbyschedule',
|
|
'deleteaudience',
|
|
'deleteaudienceconfirm',
|
|
]);
|
|
|
|
prefetchStrings('core', [
|
|
'delete',
|
|
]);
|
|
|
|
reportId = id;
|
|
contextId = contextid;
|
|
|
|
if (initialized) {
|
|
// We already added the event listeners (can be called multiple times by mustache template).
|
|
return;
|
|
}
|
|
|
|
document.addEventListener('click', event => {
|
|
|
|
// Add instance.
|
|
const audienceAdd = event.target.closest(reportSelectors.actions.audienceAdd);
|
|
if (audienceAdd) {
|
|
event.preventDefault();
|
|
addAudienceCard(audienceAdd.dataset.uniqueIdentifier, audienceAdd.dataset.name);
|
|
}
|
|
|
|
// Edit instance.
|
|
const audienceEdit = event.target.closest(reportSelectors.actions.audienceEdit);
|
|
if (audienceEdit) {
|
|
const audienceEditCard = audienceEdit.closest(reportSelectors.regions.audienceCard);
|
|
|
|
event.preventDefault();
|
|
editAudienceCard(audienceEditCard);
|
|
}
|
|
|
|
// Delete instance.
|
|
const audienceDelete = event.target.closest(reportSelectors.actions.audienceDelete);
|
|
if (audienceDelete) {
|
|
event.preventDefault();
|
|
deleteAudienceCard(audienceDelete);
|
|
}
|
|
});
|
|
|
|
initialized = true;
|
|
};
|