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 conditions editor
18
 *
19
 * @module      core_reportbuilder/local/editor/conditions
20
 * @copyright   2021 Paul Holden <paulh@moodle.com>
21
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
"use strict";
25
 
26
import $ from 'jquery';
27
import {dispatchEvent} from 'core/event_dispatcher';
28
import AutoComplete from 'core/form-autocomplete';
29
import 'core/inplace_editable';
30
import Notification from 'core/notification';
31
import Pending from 'core/pending';
32
import {prefetchStrings} from 'core/prefetch';
33
import SortableList from 'core/sortable_list';
34
import {getString} from 'core/str';
35
import Templates from 'core/templates';
36
import {add as addToast} from 'core/toast';
37
import DynamicForm from 'core_form/dynamicform';
38
import * as reportEvents from 'core_reportbuilder/local/events';
39
import * as reportSelectors from 'core_reportbuilder/local/selectors';
40
import {addCondition, deleteCondition, reorderCondition, resetConditions} from 'core_reportbuilder/local/repository/conditions';
41
 
42
/**
43
 * Reload conditions settings region
44
 *
45
 * @param {Element} reportElement
46
 * @param {Object} templateContext
47
 * @return {Promise}
48
 */
49
const reloadSettingsConditionsRegion = (reportElement, templateContext) => {
50
    const pendingPromise = new Pending('core_reportbuilder/conditions:reload');
51
    const settingsConditionsRegion = reportElement.querySelector(reportSelectors.regions.settingsConditions);
52
 
53
    return Templates.renderForPromise('core_reportbuilder/local/settings/conditions', {conditions: templateContext})
54
        .then(({html, js}) => {
55
            const conditionsjs = $.parseHTML(templateContext.javascript, null, true).map(node => node.innerHTML).join("\n");
56
            Templates.replaceNode(settingsConditionsRegion, html, js + conditionsjs);
57
 
58
            initConditionsForm();
59
 
60
            // Re-focus the add condition element after reloading the region.
61
            const reportAddCondition = reportElement.querySelector(reportSelectors.actions.reportAddCondition);
62
            reportAddCondition?.focus();
63
 
64
            return pendingPromise.resolve();
65
        });
66
};
67
 
68
/**
69
 * Initialise conditions form, must be called on each init because the form container is re-created when switching editor modes
70
 */
71
const initConditionsForm = () => {
72
    const reportElement = document.querySelector(reportSelectors.regions.report);
73
 
74
    // Enhance condition selector.
75
    const reportAddCondition = reportElement.querySelector(reportSelectors.actions.reportAddCondition);
76
    AutoComplete.enhanceField(reportAddCondition, false, '', getString('selectacondition', 'core_reportbuilder'))
77
        .catch(Notification.exception);
78
 
79
    // Handle dynamic conditions form.
80
    const conditionFormContainer = reportElement.querySelector(reportSelectors.regions.settingsConditions);
81
    if (!conditionFormContainer) {
82
        return;
83
    }
84
    const conditionForm = new DynamicForm(conditionFormContainer, '\\core_reportbuilder\\form\\condition');
85
 
86
    // Submit report conditions.
87
    conditionForm.addEventListener(conditionForm.events.FORM_SUBMITTED, event => {
88
        event.preventDefault();
89
 
90
        getString('conditionsapplied', 'core_reportbuilder')
91
            .then(addToast)
92
            .catch(Notification.exception);
93
 
94
        // After the form has been submitted, we should trigger report table reload.
95
        dispatchEvent(reportEvents.tableReload, {}, reportElement);
96
    });
97
 
98
    // Reset report conditions.
99
    conditionForm.addEventListener(conditionForm.events.NOSUBMIT_BUTTON_PRESSED, event => {
100
        event.preventDefault();
101
 
102
        Notification.saveCancelPromise(
103
            getString('resetconditions', 'core_reportbuilder'),
104
            getString('resetconditionsconfirm', 'core_reportbuilder'),
105
            getString('resetall', 'core_reportbuilder'),
106
            {triggerElement: event.detail}
107
        ).then(() => {
108
            const pendingPromise = new Pending('core_reportbuilder/conditions:reset');
109
 
110
            return resetConditions(reportElement.dataset.reportId)
111
                .then(data => reloadSettingsConditionsRegion(reportElement, data))
112
                .then(() => addToast(getString('conditionsreset', 'core_reportbuilder')))
113
                .then(() => {
114
                    dispatchEvent(reportEvents.tableReload, {}, reportElement);
115
                    return pendingPromise.resolve();
116
                })
117
                .catch(Notification.exception);
118
        }).catch(() => {
119
            return;
120
        });
121
    });
122
};
123
 
124
/**
125
 * Initialise module, prefetch all required strings
126
 *
127
 * @param {Boolean} initialized Ensure we only add our listeners once
128
 */
129
export const init = initialized => {
130
    prefetchStrings('core_reportbuilder', [
131
        'conditionadded',
132
        'conditiondeleted',
133
        'conditionmoved',
134
        'conditionsapplied',
135
        'conditionsreset',
136
        'deletecondition',
137
        'deleteconditionconfirm',
138
        'resetall',
139
        'resetconditions',
140
        'resetconditionsconfirm',
141
        'selectacondition',
142
    ]);
143
 
144
    prefetchStrings('core', [
145
        'delete',
146
    ]);
147
 
148
    initConditionsForm();
149
    if (initialized) {
150
        return;
151
    }
152
 
153
    // Add condition to report.
154
    document.addEventListener('change', event => {
155
        const reportAddCondition = event.target.closest(reportSelectors.actions.reportAddCondition);
156
        if (reportAddCondition) {
157
            event.preventDefault();
158
 
159
            // Check if dropdown is closed with no condition selected.
160
            if (reportAddCondition.value === "" || reportAddCondition.value === "0") {
161
                return;
162
            }
163
 
164
            const reportElement = reportAddCondition.closest(reportSelectors.regions.report);
165
            const pendingPromise = new Pending('core_reportbuilder/conditions:add');
166
 
167
            addCondition(reportElement.dataset.reportId, reportAddCondition.value)
168
                .then(data => reloadSettingsConditionsRegion(reportElement, data))
169
                .then(() => getString('conditionadded', 'core_reportbuilder',
170
                    reportAddCondition.options[reportAddCondition.selectedIndex].text))
171
                .then(addToast)
172
                .then(() => {
173
                    dispatchEvent(reportEvents.tableReload, {}, reportElement);
174
                    return pendingPromise.resolve();
175
                })
176
                .catch(Notification.exception);
177
        }
178
    });
179
 
180
    document.addEventListener('click', event => {
181
 
182
        // Remove condition from report.
183
        const reportRemoveCondition = event.target.closest(reportSelectors.actions.reportRemoveCondition);
184
        if (reportRemoveCondition) {
185
            event.preventDefault();
186
 
187
            const reportElement = reportRemoveCondition.closest(reportSelectors.regions.report);
188
            const conditionContainer = reportRemoveCondition.closest(reportSelectors.regions.activeCondition);
189
            const conditionName = conditionContainer.dataset.conditionName;
190
 
191
            Notification.saveCancelPromise(
192
                getString('deletecondition', 'core_reportbuilder', conditionName),
193
                getString('deleteconditionconfirm', 'core_reportbuilder', conditionName),
194
                getString('delete', 'core'),
195
                {triggerElement: reportRemoveCondition}
196
            ).then(() => {
197
                const pendingPromise = new Pending('core_reportbuilder/conditions:remove');
198
 
199
                return deleteCondition(reportElement.dataset.reportId, conditionContainer.dataset.conditionId)
200
                    .then(data => reloadSettingsConditionsRegion(reportElement, data))
201
                    .then(() => addToast(getString('conditiondeleted', 'core_reportbuilder', conditionName)))
202
                    .then(() => {
203
                        dispatchEvent(reportEvents.tableReload, {}, reportElement);
204
                        return pendingPromise.resolve();
205
                    })
206
                    .catch(Notification.exception);
207
            }).catch(() => {
208
                return;
209
            });
210
        }
211
    });
212
 
213
    // Initialize sortable list to handle active conditions moving (note JQuery dependency, see MDL-72293 for resolution).
214
    var activeConditionsSortableList = new SortableList(`${reportSelectors.regions.activeConditions}`,
215
        {isHorizontal: false});
216
    activeConditionsSortableList.getElementName = element => Promise.resolve(element.data('conditionName'));
217
 
218
    $(document).on(SortableList.EVENTS.DROP, reportSelectors.regions.activeCondition, (event, info) => {
219
        if (info.positionChanged) {
220
            const pendingPromise = new Pending('core_reportbuilder/conditions:reorder');
221
            const reportElement = event.target.closest(reportSelectors.regions.report);
222
            const conditionId = info.element.data('conditionId');
223
            const conditionPosition = info.element.data('conditionPosition');
224
 
225
            // Select target position, if moving to the end then count number of element siblings.
226
            let targetConditionPosition = info.targetNextElement.data('conditionPosition') || info.element.siblings().length + 2;
227
            if (targetConditionPosition > conditionPosition) {
228
                targetConditionPosition--;
229
            }
230
 
231
            // Re-order condition, giving drop event transition time to finish.
232
            const reorderPromise = reorderCondition(reportElement.dataset.reportId, conditionId, targetConditionPosition);
233
            Promise.all([reorderPromise, new Promise(resolve => setTimeout(resolve, 1000))])
234
                .then(([data]) => reloadSettingsConditionsRegion(reportElement, data))
235
                .then(() => getString('conditionmoved', 'core_reportbuilder', info.element.data('conditionName')))
236
                .then(addToast)
237
                .then(() => {
238
                    dispatchEvent(reportEvents.tableReload, {}, reportElement);
239
                    return pendingPromise.resolve();
240
                })
241
                .catch(Notification.exception);
242
        }
243
    });
244
};