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 filters editor
18
 *
19
 * @module      core_reportbuilder/local/editor/filters
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 $ from 'jquery';
27
import AutoComplete from 'core/form-autocomplete';
28
import 'core/inplace_editable';
29
import Notification from 'core/notification';
30
import Pending from 'core/pending';
31
import {prefetchStrings} from 'core/prefetch';
32
import SortableList from 'core/sortable_list';
33
import {getString} from 'core/str';
34
import Templates from 'core/templates';
35
import {add as addToast} from 'core/toast';
36
import * as reportSelectors from 'core_reportbuilder/local/selectors';
37
import {addFilter, deleteFilter, reorderFilter} from 'core_reportbuilder/local/repository/filters';
38
 
39
/**
40
 * Reload filters settings region
41
 *
42
 * @param {Element} reportElement
43
 * @param {Object} templateContext
44
 * @return {Promise}
45
 */
46
const reloadSettingsFiltersRegion = (reportElement, templateContext) => {
47
    const pendingPromise = new Pending('core_reportbuilder/filters:reload');
48
    const settingsFiltersRegion = reportElement.querySelector(reportSelectors.regions.settingsFilters);
49
 
50
    return Templates.renderForPromise('core_reportbuilder/local/settings/filters', {filters: templateContext})
51
        .then(({html, js}) => {
52
            Templates.replaceNode(settingsFiltersRegion, html, js);
53
 
54
            initFiltersForm();
55
 
56
            // Re-focus the add filter element after reloading the region.
57
            const reportAddFilter = reportElement.querySelector(reportSelectors.actions.reportAddFilter);
58
            reportAddFilter?.focus();
59
 
60
            return pendingPromise.resolve();
61
        });
62
};
63
 
64
/**
65
 * Initialise filters form, must be called on each init because the form container is re-created when switching editor modes
66
 */
67
const initFiltersForm = () => {
68
    const reportElement = document.querySelector(reportSelectors.regions.report);
69
 
70
    // Enhance filter selector.
71
    const reportAddFilter = reportElement.querySelector(reportSelectors.actions.reportAddFilter);
72
    AutoComplete.enhanceField(reportAddFilter, false, '', getString('selectafilter', 'core_reportbuilder'))
73
        .catch(Notification.exception);
74
};
75
 
76
/**
77
 * Initialise module, prefetch all required strings
78
 *
79
 * @param {Boolean} initialized Ensure we only add our listeners once
80
 */
81
export const init = initialized => {
82
    prefetchStrings('core_reportbuilder', [
83
        'deletefilter',
84
        'deletefilterconfirm',
85
        'filteradded',
86
        'filterdeleted',
87
        'filtermoved',
88
        'selectafilter',
89
    ]);
90
 
91
    prefetchStrings('core', [
92
        'delete',
93
    ]);
94
 
95
    initFiltersForm();
96
    if (initialized) {
97
        return;
98
    }
99
 
100
    // Add filter to report.
101
    document.addEventListener('change', event => {
102
        const reportAddFilter = event.target.closest(reportSelectors.actions.reportAddFilter);
103
        if (reportAddFilter) {
104
            event.preventDefault();
105
 
106
            // Check if dropdown is closed with no filter selected.
107
            if (reportAddFilter.value === "" || reportAddFilter.value === "0") {
108
                return;
109
            }
110
 
111
            const reportElement = reportAddFilter.closest(reportSelectors.regions.report);
112
            const pendingPromise = new Pending('core_reportbuilder/filters:add');
113
 
114
            addFilter(reportElement.dataset.reportId, reportAddFilter.value)
115
                .then(data => reloadSettingsFiltersRegion(reportElement, data))
116
                .then(() => getString('filteradded', 'core_reportbuilder',
117
                    reportAddFilter.options[reportAddFilter.selectedIndex].text))
118
                .then(addToast)
119
                .then(() => pendingPromise.resolve())
120
                .catch(Notification.exception);
121
        }
122
    });
123
 
124
    document.addEventListener('click', event => {
125
 
126
        // Remove filter from report.
127
        const reportRemoveFilter = event.target.closest(reportSelectors.actions.reportRemoveFilter);
128
        if (reportRemoveFilter) {
129
            event.preventDefault();
130
 
131
            const reportElement = reportRemoveFilter.closest(reportSelectors.regions.report);
132
            const filterContainer = reportRemoveFilter.closest(reportSelectors.regions.activeFilter);
133
            const filterName = filterContainer.dataset.filterName;
134
 
135
            Notification.saveCancelPromise(
136
                getString('deletefilter', 'core_reportbuilder', filterName),
137
                getString('deletefilterconfirm', 'core_reportbuilder', filterName),
138
                getString('delete', 'core'),
139
                {triggerElement: reportRemoveFilter}
140
            ).then(() => {
141
                const pendingPromise = new Pending('core_reportbuilder/filters:remove');
142
 
143
                return deleteFilter(reportElement.dataset.reportId, filterContainer.dataset.filterId)
144
                    .then(data => reloadSettingsFiltersRegion(reportElement, data))
145
                    .then(() => addToast(getString('filterdeleted', 'core_reportbuilder', filterName)))
146
                    .then(() => pendingPromise.resolve())
147
                    .catch(Notification.exception);
148
            }).catch(() => {
149
                return;
150
            });
151
        }
152
    });
153
 
154
    // Initialize sortable list to handle active filters moving (note JQuery dependency, see MDL-72293 for resolution).
155
    var activeFiltersSortableList = new SortableList(`${reportSelectors.regions.activeFilters} ul`, {isHorizontal: false});
156
    activeFiltersSortableList.getElementName = element => Promise.resolve(element.data('filterName'));
157
 
158
    $(document).on(SortableList.EVENTS.DROP, `${reportSelectors.regions.report} li[data-filter-id]`, (event, info) => {
159
        if (info.positionChanged) {
160
            const pendingPromise = new Pending('core_reportbuilder/filters:reorder');
161
            const reportElement = event.target.closest(reportSelectors.regions.report);
162
            const filterId = info.element.data('filterId');
163
            const filterPosition = info.element.data('filterPosition');
164
 
165
            // Select target position, if moving to the end then count number of element siblings.
166
            let targetFilterPosition = info.targetNextElement.data('filterPosition') || info.element.siblings().length + 2;
167
            if (targetFilterPosition > filterPosition) {
168
                targetFilterPosition--;
169
            }
170
 
171
            // Re-order filter, giving drop event transition time to finish.
172
            const reorderPromise = reorderFilter(reportElement.dataset.reportId, filterId, targetFilterPosition);
173
            Promise.all([reorderPromise, new Promise(resolve => setTimeout(resolve, 1000))])
174
                .then(([data]) => reloadSettingsFiltersRegion(reportElement, data))
175
                .then(() => getString('filtermoved', 'core_reportbuilder', info.element.data('filterName')))
176
                .then(addToast)
177
                .then(() => pendingPromise.resolve())
178
                .catch(Notification.exception);
179
        }
180
    });
181
};