Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Report builder audiences
18
 *
19
 * @module      core_reportbuilder/audience
20
 * @copyright   2021 David Matamoros <davidmc@moodle.com>
21
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
"use strict";
25
 
26
import 'core/inplace_editable';
27
import Templates from 'core/templates';
28
import Notification from 'core/notification';
29
import Pending from 'core/pending';
30
import {prefetchStrings} from 'core/prefetch';
31
import {getString} from 'core/str';
32
import DynamicForm from 'core_form/dynamicform';
33
import {add as addToast} from 'core/toast';
34
import {deleteAudience} from 'core_reportbuilder/local/repository/audiences';
35
import * as reportSelectors from 'core_reportbuilder/local/selectors';
36
import {loadFragment} from 'core/fragment';
37
import {markFormAsDirty} from 'core_form/changechecker';
38
 
39
let reportId = 0;
40
let contextId = 0;
41
 
42
/**
43
 * Add audience card
44
 *
45
 * @param {String} className
46
 * @param {String} title
47
 */
48
const addAudienceCard = (className, title) => {
49
    const pendingPromise = new Pending('core_reportbuilder/audience:add');
50
 
51
    const audiencesContainer = document.querySelector(reportSelectors.regions.audiencesContainer);
52
    const audienceCardLength = audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard).length;
53
 
54
    const params = {
55
        classname: className,
56
        reportid: reportId,
57
        showormessage: (audienceCardLength > 0),
58
        title: title,
59
    };
60
 
61
    // Load audience card fragment, render and then initialise the form within.
62
    loadFragment('core_reportbuilder', 'audience_form', contextId, params)
63
        .then((html, js) => {
64
            const audienceCard = Templates.appendNodeContents(audiencesContainer, html, js)[0];
65
            const audienceEmptyMessage = audiencesContainer.querySelector(reportSelectors.regions.audienceEmptyMessage);
66
 
67
            const audienceForm = initAudienceCardForm(audienceCard);
68
            // Mark as dirty new audience form created to prevent users leaving the page without saving it.
69
            markFormAsDirty(audienceForm.getFormNode());
70
            audienceEmptyMessage.classList.add('hidden');
71
 
72
            return getString('audienceadded', 'core_reportbuilder', title);
73
        })
74
        .then(addToast)
75
        .then(() => pendingPromise.resolve())
76
        .catch(Notification.exception);
77
};
78
 
79
/**
80
 * Edit audience card
81
 *
82
 * @param {Element} audienceCard
83
 */
84
const editAudienceCard = audienceCard => {
85
    const pendingPromise = new Pending('core_reportbuilder/audience:edit');
86
 
87
    // Load audience form with data for editing, then toggle visible controls in the card.
88
    const audienceForm = initAudienceCardForm(audienceCard);
89
    audienceForm.load({id: audienceCard.dataset.audienceId})
90
        .then(() => {
91
            const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);
92
            const audienceDescription = audienceCard.querySelector(reportSelectors.regions.audienceDescription);
93
            const audienceEdit = audienceCard.querySelector(reportSelectors.actions.audienceEdit);
94
 
95
            audienceFormContainer.classList.remove('hidden');
96
            audienceDescription.classList.add('hidden');
97
            audienceEdit.disabled = true;
98
 
99
            return pendingPromise.resolve();
100
        })
101
        .catch(Notification.exception);
102
};
103
 
104
/**
105
 * Initialise dynamic form within given audience card
106
 *
107
 * @param {Element} audienceCard
108
 * @return {DynamicForm}
109
 */
110
const initAudienceCardForm = audienceCard => {
111
    const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);
112
    const audienceForm = new DynamicForm(audienceFormContainer, '\\core_reportbuilder\\form\\audience');
113
 
114
    // After submitting the form, update the card instance and description properties.
115
    audienceForm.addEventListener(audienceForm.events.FORM_SUBMITTED, data => {
116
        const audienceHeading = audienceCard.querySelector(reportSelectors.regions.audienceHeading);
117
        const audienceDescription = audienceCard.querySelector(reportSelectors.regions.audienceDescription);
118
 
119
        audienceCard.dataset.audienceId = data.detail.instanceid;
120
 
121
        audienceHeading.innerHTML = data.detail.heading;
122
        audienceDescription.innerHTML = data.detail.description;
123
 
124
        closeAudienceCardForm(audienceCard);
125
 
126
        return getString('audiencesaved', 'core_reportbuilder')
127
            .then(addToast);
128
    });
129
 
130
    // If cancelling the form, close the card or remove it if it was never created.
131
    audienceForm.addEventListener(audienceForm.events.FORM_CANCELLED, () => {
132
        if (audienceCard.dataset.audienceId > 0) {
133
            closeAudienceCardForm(audienceCard);
134
        } else {
135
            removeAudienceCard(audienceCard);
136
        }
137
    });
138
 
139
    return audienceForm;
140
};
141
 
142
/**
143
 * Delete audience card
144
 *
145
 * @param {Element} audienceDelete
146
 */
147
const deleteAudienceCard = audienceDelete => {
148
    const audienceCard = audienceDelete.closest(reportSelectors.regions.audienceCard);
149
    const {audienceId, audienceTitle, audienceEditWarning = false} = audienceCard.dataset;
150
 
151
    // The edit warning indicates the audience is in use in a report schedule.
152
    const audienceDeleteConfirmation = audienceEditWarning ? 'audienceusedbyschedule' : 'deleteaudienceconfirm';
153
 
154
    Notification.saveCancelPromise(
155
        getString('deleteaudience', 'core_reportbuilder', audienceTitle),
156
        getString(audienceDeleteConfirmation, 'core_reportbuilder', audienceTitle),
157
        getString('delete', 'core'),
158
        {triggerElement: audienceDelete}
159
    ).then(() => {
160
        const pendingPromise = new Pending('core_reportbuilder/audience:delete');
161
 
162
        return deleteAudience(reportId, audienceId)
163
            .then(() => addToast(getString('audiencedeleted', 'core_reportbuilder', audienceTitle)))
164
            .then(() => {
165
                removeAudienceCard(audienceCard);
166
                return pendingPromise.resolve();
167
            })
168
            .catch(Notification.exception);
169
    }).catch(() => {
170
        return;
171
    });
172
};
173
 
174
/**
175
 * Close audience card form
176
 *
177
 * @param {Element} audienceCard
178
 */
179
const closeAudienceCardForm = audienceCard => {
180
    // Remove the [data-region="audience-form-container"] (with all the event listeners attached to it), and create it again.
181
    const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);
182
    const NewAudienceFormContainer = audienceFormContainer.cloneNode(false);
183
    audienceCard.querySelector(reportSelectors.regions.audienceForm).replaceChild(NewAudienceFormContainer, audienceFormContainer);
184
    // Show the description container and enable the action buttons.
185
    audienceCard.querySelector(reportSelectors.regions.audienceDescription).classList.remove('hidden');
186
    audienceCard.querySelector(reportSelectors.actions.audienceEdit).disabled = false;
187
    audienceCard.querySelector(reportSelectors.actions.audienceDelete).disabled = false;
188
};
189
 
190
/**
191
 * Remove audience card
192
 *
193
 * @param {Element} audienceCard
194
 */
195
const removeAudienceCard = audienceCard => {
196
    audienceCard.remove();
197
 
198
    const audiencesContainer = document.querySelector(reportSelectors.regions.audiencesContainer);
199
    const audienceCards = audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard);
200
 
201
    // Show message if there are no cards remaining, ensure first card's separator is not present.
202
    if (audienceCards.length === 0) {
203
        const audienceEmptyMessage = document.querySelector(reportSelectors.regions.audienceEmptyMessage);
204
        audienceEmptyMessage.classList.remove('hidden');
205
    } else {
206
        const audienceFirstCardSeparator = audienceCards[0].querySelector('.audience-separator');
207
        audienceFirstCardSeparator?.remove();
208
    }
209
};
210
 
211
let initialized = false;
212
 
213
/**
214
 * Initialise audiences tab.
215
 *
216
 * @param {Number} id
217
 * @param {Number} contextid
218
 */
219
export const init = (id, contextid) => {
220
    prefetchStrings('core_reportbuilder', [
221
        'audienceadded',
222
        'audiencedeleted',
223
        'audiencesaved',
224
        'audienceusedbyschedule',
225
        'deleteaudience',
226
        'deleteaudienceconfirm',
227
    ]);
228
 
229
    prefetchStrings('core', [
230
        'delete',
231
    ]);
232
 
233
    reportId = id;
234
    contextId = contextid;
235
 
236
    if (initialized) {
237
        // We already added the event listeners (can be called multiple times by mustache template).
238
        return;
239
    }
240
 
241
    document.addEventListener('click', event => {
242
 
243
        // Add instance.
244
        const audienceAdd = event.target.closest(reportSelectors.actions.audienceAdd);
245
        if (audienceAdd) {
246
            event.preventDefault();
247
            addAudienceCard(audienceAdd.dataset.uniqueIdentifier, audienceAdd.dataset.name);
248
        }
249
 
250
        // Edit instance.
251
        const audienceEdit = event.target.closest(reportSelectors.actions.audienceEdit);
252
        if (audienceEdit) {
253
            const audienceEditCard = audienceEdit.closest(reportSelectors.regions.audienceCard);
254
 
255
            event.preventDefault();
256
            editAudienceCard(audienceEditCard);
257
        }
258
 
259
        // Delete instance.
260
        const audienceDelete = event.target.closest(reportSelectors.actions.audienceDelete);
261
        if (audienceDelete) {
262
            event.preventDefault();
263
            deleteAudienceCard(audienceDelete);
264
        }
265
    });
266
 
267
    initialized = true;
268
};