9b5139549357538e7d5e5553150356243e3a8765
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Private / TypeScript / Localization.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 * as $ from 'jquery';
16 import Icons = require('./Icons');
17 import Wizard = require('TYPO3/CMS/Backend/Wizard');
18
19 type LanguageRecord = {
20   uid: number;
21   title: string;
22   flagIcon: string;
23 };
24
25 type SummaryColumns = {
26   columns: { [key: number]: string };
27   columnList: Array<number>;
28 };
29
30 type SummaryColPosRecord = {
31   uid: number;
32   title: string;
33   icon: string;
34 };
35
36 type SummaryRecord = {
37   columns: SummaryColumns;
38   records: Array<Array<SummaryColPosRecord>>;
39 };
40
41 class Localization {
42   private triggerButton: string = '.t3js-localize';
43   private localizationMode: string = null;
44   private sourceLanguage: number = null;
45   private records: Array<any> = [];
46
47   constructor() {
48     $((): void => {
49       this.initialize();
50     });
51   }
52
53   private initialize(): void {
54     const me = this;
55     Icons.getIcon('actions-localize', Icons.sizes.large).done((localizeIconMarkup: string): void => {
56       Icons.getIcon('actions-edit-copy', Icons.sizes.large).done((copyIconMarkup: string): void => {
57         $(me.triggerButton).removeClass('disabled');
58
59         $(document).on('click', me.triggerButton, (e: JQueryEventObject): void => {
60           e.preventDefault();
61
62           const $triggerButton = $(e.currentTarget);
63           const actions: Array<string> = [];
64           let slideStep1: string = '';
65
66           if ($triggerButton.data('allowTranslate')) {
67             actions.push(
68               '<div class="row">'
69               + '<div class="btn-group col-sm-3">'
70               + '<label class="btn btn-block btn-default t3js-localization-option" data-helptext=".t3js-helptext-translate">'
71               + localizeIconMarkup
72               + '<input type="radio" name="mode" id="mode_translate" value="localize" style="display: none">'
73               + '<br>Translate</label>'
74               + '</div>'
75               + '<div class="col-sm-9">'
76               + '<p class="t3js-helptext t3js-helptext-translate text-muted">' + TYPO3.lang['localize.educate.translate'] + '</p>'
77               + '</div>'
78               + '</div>',
79             );
80           }
81
82           if ($triggerButton.data('allowCopy')) {
83             actions.push(
84               '<div class="row">'
85               + '<div class="col-sm-3 btn-group">'
86               + '<label class="btn btn-block btn-default t3js-localization-option" data-helptext=".t3js-helptext-copy">'
87               + copyIconMarkup
88               + '<input type="radio" name="mode" id="mode_copy" value="copyFromLanguage" style="display: none">'
89               + '<br>Copy</label>'
90               + '</div>'
91               + '<div class="col-sm-9">'
92               + '<p class="t3js-helptext t3js-helptext-copy text-muted">' + TYPO3.lang['localize.educate.copy'] + '</p>'
93               + '</div>'
94               + '</div>',
95             );
96           }
97
98           slideStep1 += '<div data-toggle="buttons">' + actions.join('<hr>') + '</div>';
99           Wizard.addSlide(
100             'localize-choose-action',
101             TYPO3.lang['localize.wizard.header_page']
102               .replace('{0}', $triggerButton.data('page'))
103               .replace('{1}', $triggerButton.data('languageName')),
104             slideStep1,
105             SeverityEnum.info,
106           );
107           Wizard.addSlide(
108             'localize-choose-language',
109             TYPO3.lang['localize.view.chooseLanguage'],
110             '',
111             SeverityEnum.info,
112             ($slide: JQuery): void => {
113               Icons.getIcon('spinner-circle-dark', Icons.sizes.large).done((markup: string): void => {
114                 $slide.html('<div class="text-center">' + markup + '</div>');
115
116                 this.loadAvailableLanguages(
117                   parseInt($triggerButton.data('pageId'), 10),
118                   parseInt($triggerButton.data('languageId'), 10),
119                 ).done((result: Array<LanguageRecord>): void => {
120                   if (result.length === 1) {
121                     // We only have one result, auto select the record and continue
122                     this.sourceLanguage = result[0].uid;
123                     Wizard.unlockNextStep().trigger('click');
124                     return;
125                   }
126
127                   Wizard.getComponent().on('click', '.t3js-language-option', (optionEvt: JQueryEventObject): void => {
128                     const $me = $(optionEvt.currentTarget);
129                     const $radio = $me.find('input[type="radio"]');
130
131                     this.sourceLanguage = $radio.val();
132                     console.log('Localization.ts@132', this.sourceLanguage);
133                     Wizard.unlockNextStep();
134                   });
135
136                   const $languageButtons = $('<div />', {class: 'row', 'data-toggle': 'buttons'});
137
138                   for (const languageObject of result) {
139                     $languageButtons.append(
140                       $('<div />', {class: 'col-sm-4'}).append(
141                         $('<label />', {class: 'btn btn-default btn-block t3js-language-option option'})
142                           .text(' ' + languageObject.title)
143                           .prepend(languageObject.flagIcon)
144                           .prepend(
145                             $('<input />', {
146                               type: 'radio',
147                               name: 'language',
148                               id: 'language' + languageObject.uid,
149                               value: languageObject.uid,
150                               style: 'display: none;',
151                             },
152                           ),
153                         ),
154                       ),
155                     );
156                   }
157                   $slide.empty().append($languageButtons);
158                 });
159               });
160             },
161           );
162           Wizard.addSlide(
163             'localize-summary',
164             TYPO3.lang['localize.view.summary'],
165             '',
166             SeverityEnum.info, ($slide: JQuery): void => {
167               Icons.getIcon('spinner-circle-dark', Icons.sizes.large).done((markup: string): void => {
168                 $slide.html('<div class="text-center">' + markup + '</div>');
169               });
170               this.getSummary(
171                 parseInt($triggerButton.data('pageId'), 10),
172                 parseInt($triggerButton.data('languageId'), 10),
173               ).done((result: SummaryRecord): void => {
174                 $slide.empty();
175                 this.records = [];
176
177                 const columns = result.columns.columns;
178                 const columnList = result.columns.columnList;
179
180                 columnList.forEach((colPos: number): void => {
181                   if (typeof result.records[colPos] === 'undefined') {
182                     return;
183                   }
184
185                   const column = columns[colPos];
186                   const $row = $('<div />', {class: 'row'});
187
188                   result.records[colPos].forEach((record: SummaryColPosRecord): void => {
189                     const label = ' (' + record.uid + ') ' + record.title;
190                     this.records.push(record.uid);
191
192                     $row.append(
193                       $('<div />', {'class': 'col-sm-6'}).append(
194                         $('<div />', {'class': 'input-group'}).append(
195                           $('<span />', {'class': 'input-group-addon'}).append(
196                             $('<input />', {
197                               type: 'checkbox',
198                               'class': 't3js-localization-toggle-record',
199                               id: 'record-uid-' + record.uid,
200                               checked: 'checked',
201                               'data-uid': record.uid,
202                               'aria-label': label,
203                             }),
204                           ),
205                           $('<label />', {
206                             'class': 'form-control',
207                             for: 'record-uid-' + record.uid,
208                           }).text(label).prepend(record.icon),
209                         ),
210                       ),
211                     );
212                   });
213
214                   $slide.append(
215                     $('<fieldset />', {
216                       'class': 'localization-fieldset',
217                     }).append(
218                       $('<label />').text(column).prepend(
219                         $('<input />', {
220                           'class': 't3js-localization-toggle-column',
221                           type: 'checkbox',
222                           checked: 'checked',
223                         }),
224                       ),
225                       $row,
226                     ),
227                   );
228                 });
229
230                 Wizard.unlockNextStep();
231
232                 Wizard.getComponent().on('change', '.t3js-localization-toggle-record', (cmpEvt: JQueryEventObject): void => {
233                   const $me = $(cmpEvt.currentTarget);
234                   const uid = $me.data('uid');
235                   const $parent = $me.closest('fieldset');
236                   const $columnCheckbox = $parent.find('.t3js-localization-toggle-column');
237
238                   if ($me.is(':checked')) {
239                     this.records.push(uid);
240                   } else {
241                     const index = this.records.indexOf(uid);
242                     if (index > -1) {
243                       this.records.splice(index, 1);
244                     }
245                   }
246
247                   const $allChildren = $parent.find('.t3js-localization-toggle-record');
248                   const $checkedChildren = $parent.find('.t3js-localization-toggle-record:checked');
249
250                   $columnCheckbox.prop('checked', $checkedChildren.length > 0);
251                   $columnCheckbox.prop('indeterminate', $checkedChildren.length > 0 && $checkedChildren.length < $allChildren.length);
252
253                   if (this.records.length > 0) {
254                     Wizard.unlockNextStep();
255                   } else {
256                     Wizard.lockNextStep();
257                   }
258                 }).on('change', '.t3js-localization-toggle-column', (toggleEvt): void => {
259                   const $me = $(toggleEvt.currentTarget);
260                   const $children = $me.closest('fieldset').find('.t3js-localization-toggle-record');
261
262                   $children.prop('checked', $me.is(':checked'));
263                   $children.trigger('change');
264                 });
265               });
266             },
267           );
268
269           Wizard.addFinalProcessingSlide((): void => {
270             this.localizeRecords(
271               parseInt($triggerButton.data('pageId'), 10),
272               parseInt($triggerButton.data('languageId'), 10),
273               this.records,
274             ).done((): void => {
275               Wizard.dismiss();
276               document.location.reload();
277             });
278           }).done((): void => {
279             Wizard.show();
280
281             Wizard.getComponent().on('click', '.t3js-localization-option', (optionEvt: JQueryEventObject): void => {
282               const $me = $(optionEvt.currentTarget);
283               const $radio = $me.find('input[type="radio"]');
284
285               if ($me.data('helptext')) {
286                 const $container = $(optionEvt.delegateTarget);
287                 $container.find('.t3js-helptext').addClass('text-muted');
288                 $container.find($me.data('helptext')).removeClass('text-muted');
289               }
290               this.localizationMode = $radio.val();
291               Wizard.unlockNextStep();
292             });
293           });
294         });
295       });
296     });
297   }
298
299   /**
300    * Load available languages from page
301    *
302    * @param {number} pageId
303    * @param {number} languageId
304    * @returns {JQueryXHR}
305    */
306   private loadAvailableLanguages(pageId: number, languageId: number): JQueryXHR {
307     return $.ajax({
308       url: TYPO3.settings.ajaxUrls.page_languages,
309       data: {
310         pageId: pageId,
311         languageId: languageId,
312       },
313     });
314   }
315
316   /**
317    * Get summary for record processing
318    *
319    * @param {number} pageId
320    * @param {number} languageId
321    * @returns {JQueryXHR}
322    */
323   private getSummary(pageId: number, languageId: number): JQueryXHR {
324     return $.ajax({
325       url: TYPO3.settings.ajaxUrls.records_localize_summary,
326       data: {
327         pageId: pageId,
328         destLanguageId: languageId,
329         languageId: this.sourceLanguage,
330       },
331     });
332   }
333
334   /**
335    * Localize records
336    *
337    * @param {number} pageId
338    * @param {number} languageId
339    * @param {Array<number>} uidList
340    * @returns {JQueryXHR}
341    */
342   private localizeRecords(pageId: number, languageId: number, uidList: Array<number>): JQueryXHR {
343     return $.ajax({
344       url: TYPO3.settings.ajaxUrls.records_localize,
345       data: {
346         pageId: pageId,
347         srcLanguageId: this.sourceLanguage,
348         destLanguageId: languageId,
349         action: this.localizationMode,
350         uidList: uidList,
351       },
352     });
353   }
354 }
355
356 export = new Localization();