[TASK] Refer to SimpleDataHandlerController instead of tce_db.php
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Private / TypeScript / AjaxDataHandler.ts
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 import {SeverityEnum} from './Enum/Severity';
15 import MessageInterface from './AjaxDataHandler/MessageInterface';
16 import ResponseInterface from './AjaxDataHandler/ResponseInterface';
17 import * as $ from 'jquery';
18 import Icons = require('./Icons');
19 import Modal = require('./Modal');
20 import Notification = require('./Notification');
21 import Viewport = require('./Viewport');
22
23 enum Identifiers {
24 hide = '.t3js-record-hide',
25 delete = '.t3js-record-delete',
26 icon = '.t3js-icon'
27 }
28
29 /**
30 * Module: TYPO3/CMS/Backend/AjaxDataHandler
31 * Javascript functions to work with AJAX and interacting with Datahandler
32 * through \TYPO3\CMS\Backend\Controller\SimpleDataHandlerController->processAjaxRequest (record_process route)
33 */
34 class AjaxDataHandler {
35 /**
36 * Refresh the page tree
37 */
38 private static refreshPageTree(): void {
39 if (Viewport.NavigationContainer && Viewport.NavigationContainer.PageTree) {
40 Viewport.NavigationContainer.PageTree.refreshTree();
41 }
42 }
43
44 constructor() {
45 $((): void => {
46 this.initialize();
47 });
48 }
49
50 /**
51 * Generic function to call from the outside the script and validate directly showing errors
52 *
53 * @param {Object} parameters
54 * @returns {JQueryPromise<any>}
55 */
56 public process(parameters: Object): JQueryPromise<any> {
57 return this._call(parameters).done((result: ResponseInterface): void => {
58 if (result.hasErrors) {
59 this.handleErrors(result);
60 }
61 });
62 }
63
64 private initialize(): void {
65 // HIDE/UNHIDE: click events for all action icons to hide/unhide
66 $(document).on('click', Identifiers.hide, (e: JQueryEventObject): void => {
67 e.preventDefault();
68 const $anchorElement = $(e.currentTarget);
69 const $iconElement = $anchorElement.find(Identifiers.icon);
70 const $rowElement = $anchorElement.closest('tr[data-uid]');
71 const params = $anchorElement.data('params');
72
73 // add a spinner
74 this._showSpinnerIcon($iconElement);
75
76 // make the AJAX call to toggle the visibility
77 this._call(params).done((result: ResponseInterface): void => {
78 // print messages on errors
79 if (result.hasErrors) {
80 this.handleErrors(result);
81 } else {
82 // adjust overlay icon
83 this.toggleRow($rowElement);
84 }
85 });
86 });
87
88 // DELETE: click events for all action icons to delete
89 $(document).on('click', Identifiers.delete, (evt: JQueryEventObject): void => {
90 evt.preventDefault();
91 const $anchorElement = $(evt.currentTarget);
92 const $modal = Modal.confirm($anchorElement.data('title'), $anchorElement.data('message'), SeverityEnum.warning, [
93 {
94 text: $anchorElement.data('button-close-text') || TYPO3.lang['button.cancel'] || 'Cancel',
95 active: true,
96 btnClass: 'btn-default',
97 name: 'cancel'
98 },
99 {
100 text: $anchorElement.data('button-ok-text') || TYPO3.lang['button.delete'] || 'Delete',
101 btnClass: 'btn-warning',
102 name: 'delete'
103 }
104 ]);
105 $modal.on('button.clicked', (e: JQueryEventObject): void => {
106 if (e.target.getAttribute('name') === 'cancel') {
107 Modal.dismiss();
108 } else if (e.target.getAttribute('name') === 'delete') {
109 Modal.dismiss();
110 this.deleteRecord($anchorElement);
111 }
112 });
113 });
114 }
115
116 /**
117 * Toggle row visibility after record has been changed
118 *
119 * @param {JQuery} $rowElement
120 */
121 private toggleRow($rowElement: JQuery): void {
122 const $anchorElement = $rowElement.find(Identifiers.hide);
123 const table = $anchorElement.closest('table[data-table]').data('table');
124 const params = $anchorElement.data('params');
125 let nextParams;
126 let nextState;
127 let iconName;
128
129 if ($anchorElement.data('state') === 'hidden') {
130 nextState = 'visible';
131 nextParams = params.replace('=0', '=1');
132 iconName = 'actions-edit-hide';
133 } else {
134 nextState = 'hidden';
135 nextParams = params.replace('=1', '=0');
136 iconName = 'actions-edit-unhide';
137 }
138 $anchorElement.data('state', nextState).data('params', nextParams);
139
140 // Update tooltip title
141 $anchorElement.tooltip('hide').one('hidden.bs.tooltip', (): void => {
142 const nextTitle = $anchorElement.data('toggleTitle');
143 // Bootstrap Tooltip internally uses only .attr('data-original-title')
144 $anchorElement
145 .data('toggleTitle', $anchorElement.attr('data-original-title'))
146 .attr('data-original-title', nextTitle)
147 .tooltip('show');
148 });
149
150 const $iconElement = $anchorElement.find(Identifiers.icon);
151 Icons.getIcon(iconName, Icons.sizes.small).done((icon: string): void => {
152 $iconElement.replaceWith(icon);
153 });
154
155 // Set overlay for the record icon
156 const $recordIcon = $rowElement.find('.col-icon ' + Identifiers.icon);
157 if (nextState === 'hidden') {
158 Icons.getIcon('miscellaneous-placeholder', Icons.sizes.small, 'overlay-hidden').done((icon: string): void => {
159 $recordIcon.append($(icon).find('.icon-overlay'));
160 });
161 } else {
162 $recordIcon.find('.icon-overlay').remove();
163 }
164
165 $rowElement.fadeTo('fast', 0.4, (): void => {
166 $rowElement.fadeTo('fast', 1);
167 });
168 if (table === 'pages') {
169 AjaxDataHandler.refreshPageTree();
170 }
171 }
172
173 /**
174 * Delete record by given element (icon in table)
175 * don't call it directly!
176 *
177 * @param {JQuery} $anchorElement
178 */
179 private deleteRecord($anchorElement: JQuery): void {
180 const params = $anchorElement.data('params');
181 let $iconElement = $anchorElement.find(Identifiers.icon);
182
183 // add a spinner
184 this._showSpinnerIcon($iconElement);
185
186 // make the AJAX call to toggle the visibility
187 this._call(params).done((result: ResponseInterface): void => {
188 // revert to the old class
189 Icons.getIcon('actions-edit-delete', Icons.sizes.small).done((icon: string): void => {
190 $iconElement = $anchorElement.find(Identifiers.icon);
191 $iconElement.replaceWith(icon);
192 });
193 // print messages on errors
194 if (result.hasErrors) {
195 this.handleErrors(result);
196 } else {
197 const $table = $anchorElement.closest('table[data-table]');
198 const $panel = $anchorElement.closest('.panel');
199 const $panelHeading = $panel.find('.panel-heading');
200 const table = $table.data('table');
201 let $rowElements = $anchorElement.closest('tr[data-uid]');
202 const uid = $rowElements.data('uid');
203 const $translatedRowElements = $table.find('[data-l10nparent=' + uid + ']').closest('tr[data-uid]');
204 $rowElements = $rowElements.add($translatedRowElements);
205
206 $rowElements.fadeTo('slow', 0.4, (): void => {
207 $rowElements.slideUp('slow', (): void => {
208 $rowElements.remove();
209 if ($table.find('tbody tr').length === 0) {
210 $panel.slideUp('slow');
211 }
212 });
213 });
214 if ($anchorElement.data('l10parent') === '0' || $anchorElement.data('l10parent') === '') {
215 const count = Number($panelHeading.find('.t3js-table-total-items').html());
216 $panelHeading.find('.t3js-table-total-items').text(count - 1);
217 }
218
219 if (table === 'pages') {
220 AjaxDataHandler.refreshPageTree();
221 }
222 }
223 });
224 }
225
226 /**
227 * Handle the errors from result object
228 *
229 * @param {Object} result
230 */
231 private handleErrors(result: ResponseInterface): void {
232 $.each(result.messages, (position: number, message: MessageInterface): void => {
233 Notification.error(message.title, message.message);
234 });
235 }
236
237 /**
238 * AJAX call to record_process route (SimpleDataHandlerController->processAjaxRequest)
239 * returns a jQuery Promise to work with
240 *
241 * @param {Object} params
242 * @returns {JQueryXHR}
243 */
244 private _call(params: Object): JQueryXHR {
245 return $.getJSON(TYPO3.settings.ajaxUrls.record_process, params);
246 }
247
248 /**
249 * Replace the given icon with a spinner icon
250 *
251 * @param {Object} $iconElement
252 * @private
253 */
254 private _showSpinnerIcon($iconElement: JQuery): void {
255 Icons.getIcon('spinner-circle-dark', Icons.sizes.small).done((icon: string): void => {
256 $iconElement.replaceWith(icon);
257 });
258 }
259 }
260
261 export default new AjaxDataHandler();