19edf1e163447ae64fd054b807e9be4095ef1a1c
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Resources / Public / JavaScript / Backend / FormManager / ViewModel.js
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 /**
15 * Module: TYPO3/CMS/Form/Backend/FormManager/ViewModel
16 */
17 define(['jquery',
18 'TYPO3/CMS/Backend/Modal',
19 'TYPO3/CMS/Backend/Severity',
20 'TYPO3/CMS/Backend/Wizard',
21 'TYPO3/CMS/Backend/Icons',
22 'TYPO3/CMS/Backend/Notification'
23 ], function($, Modal, Severity, Wizard, Icons, Notification) {
24 'use strict';
25
26 return (function($, Modal, Severity, Wizard, Icons, Notification) {
27
28 /**
29 * @private
30 *
31 * @var object
32 */
33 var _formManagerApp = null;
34
35 /**
36 * @private
37 *
38 * @var object
39 */
40 var _domElementIdentifierCache = {};
41
42 /**
43 * @private
44 *
45 * @return void
46 */
47 function _domElementIdentifierCacheSetup() {
48 _domElementIdentifierCache = {
49 newFormModalTrigger: {identifier: '[data-identifier="newForm"]'},
50 duplicateFormModalTrigger: {identifier: '[data-identifier="duplicateForm"]'},
51 removeFormModalTrigger: {identifier: '[data-identifier="removeForm"]'},
52
53 newFormName: {identifier: '[data-identifier="newFormName"]'},
54 newFormSavePath: {identifier: '[data-identifier="newFormSavePath"]'},
55 advancedWizard: {identifier: '[data-identifier="advancedWizard"]'},
56 newFormPrototypeName: {identifier: '[data-identifier="newFormPrototypeName"]'},
57 newFormTemplate: {identifier: '[data-identifier="newFormTemplate"]'},
58
59 duplicateFormName: {identifier: '[data-identifier="duplicateFormName"]'},
60 duplicateFormSavePath: {identifier: '[data-identifier="duplicateFormSavePath"]'},
61
62 showReferences: {identifier: '[data-identifier="showReferences"]'},
63 referenceLink: {identifier: '[data-identifier="referenceLink"]'},
64
65 tooltip: {identifier: '[data-toggle="tooltip"]'},
66
67 moduleBody: {class: '.module-body.t3js-module-body'},
68 t3Logo: {class: '.t3-message-page-logo'},
69 t3Footer: {id: '#t3-footer'}
70 }
71 };
72
73 /**
74 * @private
75 *
76 * @return void
77 * @throws 1477506500
78 * @throws 1477506501
79 * @throws 1477506502
80 */
81 function _newFormSetup() {
82 $(getDomElementIdentifier('newFormModalTrigger')).on('click', function(e) {
83 e.preventDefault();
84
85 /**
86 * Wizard step 1
87 */
88 Wizard.addSlide('new-form-step-1', TYPO3.lang['formManager.newFormWizard.step1.title'], '', Severity.info, function(slide) {
89 var advandecWizardHasOptions, folders, html, modal, nextButton, prototypes,
90 savePathSelect, templates;
91
92 modal = Wizard.setup.$carousel.closest('.modal');
93 nextButton = modal.find('.modal-footer').find('button[name="next"]');
94
95 folders = _formManagerApp.getAccessibleFormStorageFolders();
96 if (folders.length === 0) {
97 html = '<div class="new-form-modal">'
98 + '<div class="form-horizontal">'
99 + '<div>'
100 + '<label class="control-label">' + TYPO3.lang['formManager.newFormWizard.step1.noStorages'] + '</label>'
101 + '</div>'
102 + '</div>'
103 + '</div>';
104
105 slide.html(html);
106 _formManagerApp.assert(false, 'No accessible form storage folders', 1477506500);
107 }
108
109 Wizard.set('savePath', folders[0]['value']);
110 if (folders.length > 1) {
111 savePathSelect = $('<select class="new-form-save-path form-control" data-identifier="newFormSavePath" />');
112 for (var i = 0, len = folders.length; i < len; ++i) {
113 var option = new Option(folders[i]['label'], folders[i]['value']);
114 $(savePathSelect).append(option);
115 }
116 }
117
118 prototypes = _formManagerApp.getPrototypes();
119
120 _formManagerApp.assert(prototypes.length > 0, 'No prototypes available', 1477506501);
121 Wizard.set('prototypeName', prototypes[0]['value']);
122
123 templates = _formManagerApp.getTemplatesForPrototype(prototypes[0]['value']);
124 _formManagerApp.assert(templates.length > 0, 'No templates available', 1477506502);
125 Wizard.set('templatePath', templates[0]['value']);
126
127 html = '<div class="new-form-modal">'
128 + '<div class="form-horizontal">'
129 + '<div>'
130 + '<label class="control-label">' + TYPO3.lang['formManager.form_name'] + '</label>'
131 + '<input class="new-form-name form-control has-error" data-identifier="newFormName" />';
132
133 if (savePathSelect) {
134 html += '<label class="control-label">' + TYPO3.lang['formManager.form_save_path'] + '</label>' + $(savePathSelect)[0].outerHTML;
135 }
136
137 if (prototypes.length > 1 || templates.length > 1) {
138 html += '<label class="control-label">' + TYPO3.lang['formManager.newFormWizard.step1.advanced'] + '</label>'
139 + '<div class="t3-form-controls"><input type="checkbox" class="new-form-advance-wizard" data-identifier="advancedWizard" /></div>';
140 }
141
142 html += '</div>'
143 + '</div>'
144 + '</div>';
145
146 slide.html(html);
147 $(getDomElementIdentifier('newFormName'), modal).focus();
148
149 $(getDomElementIdentifier('newFormName'), modal).on('keyup paste', function(e) {
150 if ($(this).val().length > 0) {
151 $(this).removeClass('has-error');
152 Wizard.unlockNextStep();
153 Wizard.set('formName', $(this).val());
154 } else {
155 $(this).addClass('has-error');
156 Wizard.lockNextStep();
157 }
158 });
159
160 $(getDomElementIdentifier('newFormSavePath'), modal).on('change', function(e) {
161 Wizard.set('savePath', $(getDomElementIdentifier('newFormSavePath') + ' option:selected', modal).val());
162 });
163
164 $(getDomElementIdentifier('advancedWizard'), modal).on('change', function(e) {
165 if ($(this).is(':checked')) {
166 Wizard.set('advancedWizard', true);
167 } else {
168 Wizard.set('advancedWizard', false);
169 }
170 });
171
172 nextButton.on('click', function() {
173 Wizard.setup.forceSelection = false;
174 Icons.getIcon('spinner-circle-dark', Icons.sizes.large, null, null).done(function(markup) {
175 slide.html($('<div />', {class: 'text-center'}).append(markup));
176 });
177 });
178 });
179
180 /**
181 * Wizard step 2
182 */
183 Wizard.addSlide('new-form-step-2', TYPO3.lang['formManager.newFormWizard.step2.title'], '', Severity.info, function(slide, settings) {
184 var addOnTemplateChangeEvents, html, modal, nextButton, prototypes, prototypeNameSelect,
185 templates, templateSelect;
186
187 if (settings['advancedWizard'] !== true) {
188 Wizard.unlockNextStep().trigger('click');
189 return;
190 }
191
192 modal = Wizard.setup.$carousel.closest('.modal');
193 nextButton = modal.find('.modal-footer').find('button[name="next"]');
194
195 prototypeNameSelect = $('<select class="new-form-prototype-name form-control" data-identifier="newFormPrototypeName" />');
196 templateSelect = $('<select class="new-form-template form-control" data-identifier="newFormTemplate" />');
197
198 prototypes = _formManagerApp.getPrototypes();
199 templates = {};
200 if (prototypes.length > 0) {
201 for (var i = 0, len = prototypes.length; i < len; ++i) {
202 var option = new Option(prototypes[i]['label'], prototypes[i]['value']);
203 $(prototypeNameSelect).append(option);
204 }
205
206 templates = _formManagerApp.getTemplatesForPrototype(prototypes[0]['value']);
207 for (var i = 0, len = templates.length; i < len; ++i) {
208 var option = new Option(templates[i]['label'], templates[i]['value']);
209 $(templateSelect).append(option);
210 }
211 }
212
213 html = '<div class="new-form-modal">'
214 + '<div class="form-horizontal">'
215 + '<div>';
216
217 if (prototypes.length > 1) {
218 html += '<label class="control-label">' + TYPO3.lang['formManager.form_prototype'] + '</label>' + $(prototypeNameSelect)[0].outerHTML;
219 }
220 if (templates.length > 1) {
221 html += '<label class="control-label">' + TYPO3.lang['formManager.form_template'] + '</label>' + $(templateSelect)[0].outerHTML;
222 }
223
224 html += '</div>'
225 + '</div>'
226 + '</div>';
227
228 slide.html(html);
229 if (prototypes.length > 1) {
230 $(getDomElementIdentifier('newFormPrototypeName'), modal).focus();
231 } else if (templates.length > 1) {
232 $(getDomElementIdentifier('newFormTemplate'), modal).focus();
233 }
234
235 addOnTemplateChangeEvents = function() {
236 $(getDomElementIdentifier('newFormTemplate'), modal).on('change', function(e) {
237 Wizard.set('templatePath', $(getDomElementIdentifier('newFormTemplate') + ' option:selected', modal).val());
238 });
239 };
240
241 $(getDomElementIdentifier('newFormPrototypeName'), modal).on('change', function(e) {
242 Wizard.set('prototypeName', $(this).val());
243 templates = _formManagerApp.getTemplatesForPrototype($(this).val());
244 $(getDomElementIdentifier('newFormTemplate'), modal).off().empty();
245 for (var i = 0, len = templates.length; i < len; ++i) {
246 var option = new Option(templates[i]['label'], templates[i]['value']);
247 $(getDomElementIdentifier('newFormTemplate'), modal).append(option);
248 Wizard.set('templatePath', templates[0]['value']);
249 }
250 addOnTemplateChangeEvents();
251 });
252
253 addOnTemplateChangeEvents();
254
255 nextButton.on('click', function() {
256 Icons.getIcon('spinner-circle-dark', Icons.sizes.large, null, null).done(function(markup) {
257 slide.html($('<div />', {class: 'text-center'}).append(markup));
258 });
259 });
260 });
261
262 /**
263 * Wizard step 3
264 */
265 Wizard.addSlide('new-form-step-3', TYPO3.lang['formManager.newFormWizard.step3.title'], TYPO3.lang['formManager.newFormWizard.step3.message'], Severity.info);
266
267 /**
268 * Wizard step 4
269 */
270 Wizard.addFinalProcessingSlide(function() {
271 $.post(_formManagerApp.getAjaxEndpoint('create'), {
272 tx_form_web_formformbuilder: {
273 formName: Wizard.setup.settings['formName'],
274 templatePath: Wizard.setup.settings['templatePath'],
275 prototypeName: Wizard.setup.settings['prototypeName'],
276 savePath: Wizard.setup.settings['savePath']
277 }
278 }, function(data, textStatus, jqXHR) {
279 if (data['status'] === 'success') {
280 document.location = data.url;
281 } else {
282 Notification.error(TYPO3.lang['formManager.newFormWizard.step4.errorTitle'], TYPO3.lang['formManager.newFormWizard.step4.errorMessage'] + " " + data['message']);
283 }
284 Wizard.dismiss();
285 }).fail(function(jqXHR, textStatus, errorThrown) {
286 var parser = new DOMParser(),
287 responseDocument = parser.parseFromString(jqXHR.responseText, "text/html"),
288 responseBody = $(responseDocument.body);
289
290 Notification.error(textStatus, errorThrown, 2);
291 Wizard.dismiss();
292
293 $(getDomElementIdentifier('t3Logo', 'class'), responseBody).remove();
294 $(getDomElementIdentifier('t3Footer', 'id'), responseBody).remove();
295 $(getDomElementIdentifier('moduleBody', 'class')).html(responseBody.html());
296 });
297 }).done(function() {
298 Wizard.show();
299 });
300 });
301 };
302
303 /**
304 * @private
305 *
306 * @return void
307 */
308 function _removeFormSetup() {
309 $(getDomElementIdentifier('removeFormModalTrigger')).on('click', function(e) {
310 var modalButtons = [], that;
311
312 e.preventDefault();
313 that = $(this)
314
315 modalButtons.push({
316 text: TYPO3.lang['formManager.cancel'],
317 active: true,
318 btnClass: 'btn-default',
319 name: 'cancel',
320 trigger: function() {
321 Modal.currentModal.trigger('modal-dismiss');
322 }
323 });
324
325 modalButtons.push({
326 text: TYPO3.lang['formManager.remove_form'],
327 active: true,
328 btnClass: 'btn-warning',
329 name: 'createform',
330 trigger: function() {
331 document.location = _formManagerApp.getAjaxEndpoint('delete') + '&tx_form_web_formformbuilder[formPersistenceIdentifier]=' + that.data('formPersistenceIdentifier');
332 Modal.currentModal.trigger('modal-dismiss');
333 }
334 });
335
336 Modal.show(
337 TYPO3.lang['formManager.remove_form_title'],
338 TYPO3.lang['formManager.remove_form_message'],
339 Severity.warning,
340 modalButtons
341 );
342 });
343 };
344
345 /**
346 * @private
347 *
348 * @return void
349 * @throws 1477649539
350 */
351 function _duplicateFormSetup() {
352 $(getDomElementIdentifier('duplicateFormModalTrigger')).on('click', function(e) {
353 var that;
354
355 e.preventDefault();
356 that = $(this);
357
358 /**
359 * Wizard step 1
360 */
361 Wizard.addSlide('duplicate-form-step-1', TYPO3.lang['formManager.duplicateFormWizard.step1.title'].replace('{0}', that.data('formName')), '', Severity.info, function(slide) {
362 var folders, html, modal, nextButton, savePathSelect;
363
364 modal = Wizard.setup.$carousel.closest('.modal');
365 nextButton = modal.find('.modal-footer').find('button[name="next"]');
366
367 folders = _formManagerApp.getAccessibleFormStorageFolders();
368 _formManagerApp.assert(folders.length > 0, 'No accessible form storage folders', 1477649539);
369
370 Wizard.set('formPersistenceIdentifier', that.data('formPersistenceIdentifier'));
371 Wizard.set('savePath', folders[0]['value']);
372 if (folders.length > 1) {
373 savePathSelect = $('<select class="duplicate-form-save-path form-control" data-identifier="duplicateFormSavePath" />');
374 for (var i = 0, len = folders.length; i < len; ++i) {
375 var option = new Option(folders[i]['label'], folders[i]['value']);
376 $(savePathSelect).append(option);
377 }
378 }
379
380 html = '<div class="duplicate-form-modal">'
381 + '<div class="form-horizontal">'
382 + '<div>'
383 + '<label class="control-label">' + TYPO3.lang['formManager.new_form_name'] + '</label>'
384 + '<input class="duplicate-form-name form-control has-error" data-identifier="duplicateFormName" />';
385
386 if (savePathSelect) {
387 html += '<label class="control-label">' + TYPO3.lang['formManager.form_save_path'] + '</label>' + $(savePathSelect)[0].outerHTML;
388 }
389
390 html += '</div>'
391 + '</div>'
392 + '</div>';
393
394 slide.html(html);
395 $(getDomElementIdentifier('duplicateFormName'), modal).focus();
396
397 $(getDomElementIdentifier('duplicateFormName'), modal).on('keyup paste', function(e) {
398 if ($(this).val().length > 0) {
399 $(this).removeClass('has-error');
400 Wizard.unlockNextStep();
401 Wizard.set('formName', $(this).val());
402 } else {
403 $(this).addClass('has-error');
404 Wizard.lockNextStep();
405 }
406 });
407
408 $(getDomElementIdentifier('duplicateFormSavePath'), modal).on('change', function(e) {
409 Wizard.set('savePath', $(getDomElementIdentifier('duplicateFormSavePath') + ' option:selected', modal).val());
410 });
411 });
412
413 /**
414 * Wizard step 2
415 */
416 Wizard.addFinalProcessingSlide(function() {
417 $.post(_formManagerApp.getAjaxEndpoint('duplicate'), {
418 tx_form_web_formformbuilder: {
419 formName: Wizard.setup.settings['formName'],
420 formPersistenceIdentifier: Wizard.setup.settings['formPersistenceIdentifier'],
421 savePath: Wizard.setup.settings['savePath']
422 }
423 }, function(data, textStatus, jqXHR) {
424 if (data['status'] === 'success') {
425 document.location = data.url;
426 } else {
427 Notification.error(TYPO3.lang['formManager.duplicateFormWizard.step2.errorTitle'], TYPO3.lang['formManager.duplicateFormWizard.step2.errorMessage'] + " " + data['message']);
428 }
429 Wizard.dismiss();
430 }).fail(function(jqXHR, textStatus, errorThrown) {
431 var parser = new DOMParser(),
432 responseDocument = parser.parseFromString(jqXHR.responseText, "text/html"),
433 responseBody = $(responseDocument.body);
434
435 Notification.error(textStatus, errorThrown, 2);
436 Wizard.dismiss();
437
438 $(getDomElementIdentifier('t3Logo', 'class'), responseBody).remove();
439 $(getDomElementIdentifier('t3Footer', 'id'), responseBody).remove();
440 $(getDomElementIdentifier('moduleBody', 'class')).html(responseBody.html());
441 });
442 }).done(function() {
443 Wizard.show();
444 });
445 });
446 };
447
448 /**
449 * @private
450 *
451 * @return void
452 */
453 function _showReferencesSetup() {
454 $(getDomElementIdentifier('showReferences')).on('click', function(e) {
455 var that, url;
456
457 e.preventDefault();
458 that = this;
459 url = _formManagerApp.getAjaxEndpoint('references') + '&tx_form_web_formformbuilder[formPersistenceIdentifier]=' + $(this).data('formPersistenceIdentifier');
460
461 $.get(url, function(data, textStatus, jqXHR) {
462 var html, modalButtons = [], referencesLength;
463
464 modalButtons.push({
465 text: TYPO3.lang['formManager.cancel'],
466 active: true,
467 btnClass: 'btn-default',
468 name: 'cancel',
469 trigger: function() {
470 Modal.currentModal.trigger('modal-dismiss');
471 }
472 });
473
474 referencesLength = data['references'].length;
475 if (referencesLength > 0) {
476 html = '<div>'
477 + '<h3>' + TYPO3.lang['formManager.references.headline'].replace('{0}', $(that).data('formName')) + '</h3>'
478 + '</div>'
479 + '<div class="table-fit">'
480 + '<table id="forms" class="table table-striped table-condensed">'
481 + '<thead>'
482 + '<tr>'
483 + '<th>' + TYPO3.lang['formManager.page'] + '</th>'
484 + '<th>' + TYPO3.lang['formManager.record'] + '</th>'
485 + '</tr>'
486 + '</thead>'
487 + '<tbody>';
488
489 for (var i = 0, len = data['references'].length; i < len; ++i) {
490 html += '<tr>'
491 + '<td>' + data['references'][i]['recordPageTitle'] + '</td>'
492 + '<td>'
493 + data['references'][i]['recordIcon']
494 + '<a href="' + data['references'][i]['recordEditUrl'] + '" data-identifier="referenceLink">'
495 + data['references'][i]['recordTitle'] + ' (uid: ' + data['references'][i]['recordUid'] + ')'
496 + '</a>'
497 + '</td>'
498 + '</tr>';
499 }
500
501 html += '</tbody>'
502 + '</table>'
503 + '</div>';
504 } else {
505 html = '<div>'
506 + '<h1>' + TYPO3.lang['formManager.references.title'].replace('{0}', data['formPersistenceIdentifier']) + '</h1>'
507 + '</div>'
508 + '<div>' + TYPO3.lang['formManager.no_references'] + '</div>';
509 }
510
511 html = $(html);
512 $(getDomElementIdentifier('referenceLink'), html).on('click', function(e) {
513 e.preventDefault();
514 Modal.currentModal.trigger('modal-dismiss');
515 document.location = $(this).prop('href');
516 });
517
518 Modal.show(
519 TYPO3.lang['formManager.references.title'],
520 html,
521 Severity.info,
522 modalButtons
523 );
524 }).fail(function(jqXHR, textStatus, errorThrown) {
525 if (jqXHR.status !== 0) {
526 Notification.error(textStatus, errorThrown, 2);
527 }
528 });
529 });
530 };
531
532 /**
533 * @public
534 *
535 * @param string elementIdentifier
536 * @param string type
537 * @return mixed|undefined
538 * @throws 1477506413
539 * @throws 1477506414
540 */
541 function getDomElementIdentifier(elementIdentifier, type) {
542 _formManagerApp.assert(elementIdentifier.length > 0, 'Invalid parameter "elementIdentifier"', 1477506413);
543 _formManagerApp.assert(typeof _domElementIdentifierCache[elementIdentifier] !== "undefined", 'elementIdentifier "' + elementIdentifier + '" does not exist', 1477506414);
544 if (typeof type === "undefined") {
545 type = 'identifier';
546 }
547
548 return _domElementIdentifierCache[elementIdentifier][type] || undefined;
549 };
550
551 /**
552 * @public
553 *
554 * @param object formManagerApp
555 * @return void
556 */
557 function bootstrap(formManagerApp) {
558 _formManagerApp = formManagerApp;
559 _domElementIdentifierCacheSetup();
560 _removeFormSetup();
561 _newFormSetup();
562 _duplicateFormSetup();
563 _showReferencesSetup();
564 $(getDomElementIdentifier('tooltip')).tooltip();
565 };
566
567 /**
568 * Publish the public methods.
569 * Implements the "Revealing Module Pattern".
570 */
571 return {
572 bootstrap: bootstrap
573 };
574 })($, Modal, Severity, Wizard, Icons, Notification);
575 });