05ebe24a94450fe157ace7ce8cc35b858f9ab203
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Resources / Public / JavaScript / Install.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 * Various JavaScript functions for the Install Tool
16 */
17
18 /**
19 * Handle core update
20 */
21 var TYPO3 = {};
22 TYPO3.Install = {};
23
24 TYPO3.Install.Severity = {
25 loading: -3,
26 notice: -2,
27 info: -1,
28 ok: 0,
29 warning: 1,
30 error: 2
31 };
32
33 TYPO3.Install.Severity.getCssClass = function (severity) {
34 var severityClass;
35 switch (severity) {
36 case TYPO3.Install.Severity.loading:
37 severityClass = 'notice alert-loading';
38 break;
39 case TYPO3.Install.Severity.notice:
40 severityClass = 'notice';
41 break;
42 case TYPO3.Install.Severity.ok:
43 severityClass = 'success';
44 break;
45 case TYPO3.Install.Severity.warning:
46 severityClass = 'warning';
47 break;
48 case TYPO3.Install.Severity.error:
49 severityClass = 'danger';
50 break;
51 case TYPO3.Install.Severity.info:
52 default:
53 severityClass = 'info';
54 }
55 return severityClass;
56 };
57
58 TYPO3.Install.FlashMessage = {
59 template: $('<div class="t3js-message typo3-message alert"><h4></h4><p class="messageText"></p></div>'),
60 render: function (severity, title, message) {
61 var flashMessage = this.template.clone();
62 flashMessage.addClass('alert-' + TYPO3.Install.Severity.getCssClass(severity));
63 if (title) {
64 flashMessage.find('h4').html(title);
65 }
66 if (message) {
67 flashMessage.find('.messageText').html(message);
68 } else {
69 flashMessage.find('.messageText').remove();
70 }
71 return flashMessage;
72 }
73 };
74
75 TYPO3.Install.DumpAutoload = {
76 /**
77 * output DOM Container
78 */
79 outputContainer: {},
80
81 /**
82 * Clone of the DOM object that contains the submit button
83 */
84 submitButton: {},
85
86 /**
87 * Default output messages
88 */
89 outputMessages: {
90 dumpAutoload: {
91 fatalTitle: 'Something went wrong',
92 fatalMessage: '',
93 loadingTitle: 'Loading...',
94 loadingMessage: ''
95 }
96 },
97
98 /**
99 * Fetching the templates out of the DOM
100 *
101 * @param dumpAutoloadContainer DOM element id with all needed HTML in it
102 * @return boolean DOM container could be found and initialization finished
103 */
104 initialize: function(dumpAutoloadContainer) {
105 this.outputContainer[dumpAutoloadContainer] = $('.t3js-' + dumpAutoloadContainer);
106
107 if (this.outputContainer[dumpAutoloadContainer]) {
108 // submit button: save and delete
109 if(!this.submitButton[dumpAutoloadContainer]) {
110 var submitButton = this.outputContainer[dumpAutoloadContainer].find('button[type="submit"]');
111 this.submitButton[dumpAutoloadContainer] = submitButton.clone();
112 }
113
114 // clear all messages from the run before
115 this.outputContainer[dumpAutoloadContainer].find('.typo3-message:visible ').remove();
116
117 return true;
118 }
119 return false;
120 },
121
122 /**
123 * Ajax call to dump Autoload Information
124 * @param actionName
125 */
126 dumpAutoload: function(actionName) {
127 var self = this;
128 var url = location.href + '&install[controller]=ajax&install[action]=' + actionName;
129 var isInitialized = self.initialize(actionName);
130 if (isInitialized) {
131 self.addMessage(
132 TYPO3.Install.Severity.loading,
133 self.outputMessages[actionName].loadingTitle,
134 self.outputMessages[actionName].loadingMessage,
135 actionName
136 );
137 $.ajax({
138 url: url,
139 cache: false,
140 success: function(data) {
141 if (data.success === true && Array.isArray(data.status)) {
142 if (data.status.length > 0) {
143 self.outputContainer[actionName].find('.alert-loading').hide();
144 data.status.forEach((function (element) {
145 //noinspection JSUnresolvedVariable
146 self.addMessage(
147 element.severity,
148 element.title,
149 element.message,
150 actionName
151 );
152 }));
153 } else {
154 self.outputContainer[actionName].find('.alert-loading').hide();
155 self.addMessage(
156 TYPO3.Install.Severity.ok,
157 self.outputMessages[actionName].successTitle,
158 self.outputMessages[actionName].successMessage,
159 actionName
160 );
161 }
162 } else if (data === 'unauthorized') {
163 location.reload();
164 }
165 },
166 error: function() {
167 self.outputContainer[actionName].find('.alert-loading').hide();
168 self.addMessage(
169 TYPO3.Install.Severity.error,
170 self.outputMessages[actionName].fatalTitle,
171 self.outputMessages[actionName].fatalMessage,
172 actionName
173 );
174 }
175 });
176 }
177 },
178
179 /**
180 * Move the submit button to the end of the box
181 *
182 * @param dumpAutoloadContainer DOM container name
183 */
184 moveSubmitButtonFurtherDown: function(dumpAutoloadContainer) {
185 // first remove the currently visible button
186 this.outputContainer[dumpAutoloadContainer].find('button[type="submit"]').remove();
187 // then append the cloned template to the end
188 this.outputContainer[dumpAutoloadContainer].append(this.submitButton[dumpAutoloadContainer]);
189 },
190
191 /**
192 * Show a status message
193 *
194 * @param severity
195 * @param title
196 * @param message
197 * @param dumpAutoloadContainer DOM container name
198 */
199 addMessage: function(severity, title, message, dumpAutoloadContainer) {
200 var domMessage = TYPO3.Install.FlashMessage.render(severity, title, message);
201 this.outputContainer[dumpAutoloadContainer].append(domMessage);
202 this.moveSubmitButtonFurtherDown(dumpAutoloadContainer);
203 }
204 };
205
206 TYPO3.Install.Cache = {
207 /**
208 * output DOM Container
209 */
210 outputContainer: {},
211
212 /**
213 * Clone of the DOM object that contains the submit button
214 */
215 submitButton: {},
216
217 /**
218 * Default output messages
219 */
220 outputMessages: {
221 clearAllCache: {
222 fatalTitle: 'Something went wrong',
223 fatalMessage: '',
224 loadingTitle: 'Loading...',
225 loadingMessage: ''
226 }
227 },
228
229 /**
230 * Fetching the templates out of the DOM
231 *
232 * @param cacheCheckContainer DOM element id with all needed HTML in it
233 * @return boolean DOM container could be found and initialization finished
234 */
235 initialize: function(cacheCheckContainer) {
236 this.outputContainer[cacheCheckContainer] = $('#' + cacheCheckContainer);
237
238 if (this.outputContainer[cacheCheckContainer]) {
239 // submit button: save and delete
240 if(!this.submitButton[cacheCheckContainer]) {
241 var submitButton = this.outputContainer[cacheCheckContainer].find('button[type="submit"]');
242 this.submitButton[cacheCheckContainer] = submitButton.clone();
243 }
244
245 // clear all messages from the run before
246 this.outputContainer[cacheCheckContainer].find('.typo3-message:visible ').remove();
247
248 return true;
249 }
250 return false;
251 },
252
253 /**
254 * Ajax call to clear all caches.
255 */
256 clearCache: function () {
257 $.ajax({
258 url: location.href + '&install[controller]=ajax&install[action]=clearCache',
259 cache: false
260 });
261 },
262 clearAllCache: function(actionName) {
263 var self = this;
264 var url = location.href + '&install[controller]=ajax&install[action]=' + actionName;
265 var isInitialized = self.initialize(actionName);
266 if (isInitialized) {
267 self.addMessage(
268 TYPO3.Install.Severity.loading,
269 self.outputMessages[actionName].loadingTitle,
270 self.outputMessages[actionName].loadingMessage,
271 actionName
272 );
273 $.ajax({
274 url: url,
275 cache: false,
276 success: function(data) {
277 if (data.success === true && Array.isArray(data.status)) {
278 if (data.status.length > 0) {
279 self.outputContainer[actionName].find('.alert-loading').hide();
280 data.status.forEach((function (element) {
281 //noinspection JSUnresolvedVariable
282 self.addMessage(
283 element.severity,
284 element.title,
285 element.message,
286 actionName
287 );
288 }));
289 } else {
290 self.outputContainer[actionName].find('.alert-loading').hide();
291 self.addMessage(
292 TYPO3.Install.Severity.ok,
293 self.outputMessages[actionName].successTitle,
294 self.outputMessages[actionName].successMessage,
295 actionName
296 );
297 }
298 } else if (data === 'unauthorized') {
299 location.reload();
300 }
301 }
302 });
303 }
304 },
305 /**
306 * Move the submit button to the end of the box
307 *
308 * @param cacheCheckContainer DOM container name
309 */
310 moveSubmitButtonFurtherDown: function(cacheCheckContainer) {
311 // first remove the currently visible button
312 this.outputContainer[cacheCheckContainer].find('button[type="submit"]').remove();
313 // then append the cloned template to the end
314 this.outputContainer[cacheCheckContainer].append(this.submitButton[cacheCheckContainer]);
315 },
316
317 /**
318 * Show a status message
319 *
320 * @param severity
321 * @param title
322 * @param message
323 * @param cacheCheckContainer DOM container name
324 */
325 addMessage: function(severity, title, message, cacheCheckContainer) {
326 var domMessage = TYPO3.Install.FlashMessage.render(severity, title, message);
327 this.outputContainer[cacheCheckContainer].append(domMessage);
328 this.moveSubmitButtonFurtherDown(cacheCheckContainer);
329 }
330 };
331
332 TYPO3.Install.Scrolling = {
333 isScrolledIntoView: function (elem) {
334 var $window = $(window);
335 var docViewTop = $window.scrollTop();
336 var docViewBottom = docViewTop + $window.height();
337 var $elem = $(elem);
338 var elemTop = $elem.offset().top;
339 var elemBottom = elemTop + $elem.height();
340
341 return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
342 },
343 handleButtonScrolling: function () {
344 var $fixedFooterHandler = $('#fixed-footer-handler');
345 if ($fixedFooterHandler.length > 0) {
346 var $fixedFooter = $('#fixed-footer');
347 if (!this.isScrolledIntoView($fixedFooterHandler)) {
348 $fixedFooter.addClass('fixed');
349 $fixedFooter.width($('.content-area').width());
350 } else {
351 $fixedFooter.removeClass('fixed');
352 }
353 }
354 }
355 };
356
357 TYPO3.Install.ExtensionChecker = {
358 /**
359 * Call checkExtensionsCompatibility recursively on error
360 * so we can find all incompatible extensions
361 */
362 handleCheckExtensionsError: function () {
363 this.checkExtensionsCompatibility(false);
364 },
365 /**
366 * Send an ajax request to uninstall an extension (or multiple extensions)
367 *
368 * @param extension string of extension(s) - may be comma separated
369 */
370 uninstallExtension: function (extension) {
371 var self = this;
372 var url = location.href + '&install[controller]=ajax&install[action]=uninstallExtension' +
373 '&install[uninstallExtension][extensions]=' + extension;
374 var $container = $('#checkExtensions');
375 $.ajax({
376 url: url,
377 cache: false,
378 success: function (data) {
379 if (data === 'OK') {
380 self.checkExtensionsCompatibility(true);
381 } else {
382 if (data === 'unauthorized') {
383 location.reload();
384 }
385 // workaround for xdebug returning 200 OK on fatal errors
386 if (data.substring(data.length - 2) === 'OK') {
387 self.checkExtensionsCompatibility(true);
388 } else {
389 $('.alert-loading', $container).hide();
390 var domMessage = TYPO3.Install.FlashMessage.render(
391 TYPO3.Install.Severity.error,
392 'Something went wrong. Check failed.',
393 'Message: ' + data
394 );
395 $container.append(domMessage);
396 }
397 }
398 },
399 error: function (data) {
400 self.handleCheckExtensionsError(data);
401 }
402 });
403 },
404 /**
405 * Handles result of extension compatibility check.
406 * Displays uninstall buttons for non-compatible extensions.
407 */
408 handleCheckExtensionsSuccess: function () {
409 var self = this;
410 var $checkExtensions = $('#checkExtensions');
411
412 $.ajax({
413 url: $checkExtensions.data('protocolurl'),
414 cache: false,
415 success: function (data) {
416 if (data) {
417 $('.alert-danger .messageText', $checkExtensions).html(
418 'The following extensions are not compatible. Please uninstall them and try again. '
419 );
420 var extensions = data.split(',');
421 var unloadButtonWrapper = $('<fieldset class="t3-install-form-submit"></fieldset>');
422 for (var i = 0; i < extensions.length; i++) {
423 var extension = extensions[i];
424 var unloadButton = $('<button />', {
425 text: 'Uninstall ' + $.trim(extension),
426 'class': 't3-js-uninstallSingle',
427 'data-extension': $.trim(extension)
428 });
429 var fullButton = unloadButtonWrapper.append(unloadButton);
430 $('.alert-danger .messageText', $checkExtensions).append(fullButton);
431 }
432 if (extensions.length) {
433 $(document).on('click', 't3-js-uninstallSingle', function (e) {
434 self.uninstallExtension($(this).data('extension'));
435 e.preventDefault();
436 return false;
437 });
438 }
439 var unloadAllButton = $('<button />', {
440 text: 'Uninstall all incompatible extensions: ' + data,
441 click: function (e) {
442 $('.alert-loading', $checkExtensions).show();
443 self.uninstallExtension(data);
444 e.preventDefault();
445 return false;
446 }
447 });
448 unloadButtonWrapper.append('<hr />');
449 var fullUnloadAllButton = unloadButtonWrapper.append(unloadAllButton);
450 $('.alert-danger .messageText', $checkExtensions).append(fullUnloadAllButton);
451
452 $('.alert-loading', $checkExtensions).hide();
453 $('button', $checkExtensions).show();
454 $('.alert-danger', $checkExtensions).show();
455 } else {
456 $('.t3js-message', $checkExtensions).hide();
457 $('.alert-success', $checkExtensions).show();
458 }
459 },
460 error: function () {
461 $('.t3js-message', $checkExtensions).hide();
462 $('.alert-success', $checkExtensions).show();
463 }
464 });
465 $.getJSON(
466 $checkExtensions.data('errorprotocolurl'),
467 function (data) {
468 $.each(data, function (i, error) {
469 var messageToDisplay = error.message + ' in ' + error.file + ' on line ' + error.line;
470 var domMessage = TYPO3.Install.FlashMessage.render(TYPO3.Install.Severity.warning, error.type, messageToDisplay);
471 $checkExtensions.find('.t3js-message.alert-danger').before(domMessage);
472 });
473 }
474 );
475 },
476 /**
477 * Checks extension compatibility by trying to load ext_tables and ext_localconf via ajax.
478 *
479 * @param force
480 */
481 checkExtensionsCompatibility: function (force) {
482 var self = this;
483 var url = location.href + '&install[controller]=ajax&install[action]=extensionCompatibilityTester';
484 if (force) {
485 TYPO3.Install.Cache.clearCache();
486 url += '&install[extensionCompatibilityTester][forceCheck]=1';
487 } else {
488 url += '&install[extensionCompatibilityTester][forceCheck]=0';
489 }
490 $.ajax({
491 url: url,
492 cache: false,
493 success: function (data) {
494 if (data === 'OK') {
495 self.handleCheckExtensionsSuccess();
496 } else {
497 if (data === 'unauthorized') {
498 location.reload();
499 }
500 // workaround for xdebug returning 200 OK on fatal errors
501 if (data.substring(data.length - 2) === 'OK') {
502 self.handleCheckExtensionsSuccess();
503 } else {
504 self.handleCheckExtensionsError();
505 }
506 }
507 },
508 error: function () {
509 self.handleCheckExtensionsError();
510 }
511 });
512 }
513 };
514
515 TYPO3.Install.TcaIntegrityChecker = {
516
517 /**
518 * Default output messages
519 */
520 outputMessages: {
521 tcaMigrationsCheck: {
522 fatalTitle: 'Something went wrong',
523 fatalMessage: 'Use "Check for broken extensions!"',
524 loadingTitle: 'Loading...',
525 loadingMessage: '',
526 successTitle: 'No TCA migrations need to be applied',
527 successMessage: 'Your TCA looks good.',
528 warningTitle: 'TCA migrations need to be applied',
529 warningMessage: 'Check the following list and apply needed changes.'
530 },
531 tcaExtTablesCheck: {
532 fatalTitle: 'Something went wrong',
533 fatalMessage: 'Use "Check for broken extensions!"',
534 loadingTitle: 'Loading...',
535 loadingMessage: '',
536 successTitle: 'No TCA changes in ext_tables.php files. Good job!',
537 successMessage: '',
538 warningTitle: 'Extensions change TCA in ext_tables.php',
539 warningMessage: 'Check for ExtensionManagementUtility and $GLOBALS["TCA"].'
540 }
541 },
542
543 /**
544 * output DOM Container
545 */
546 outputContainer: {},
547
548 /**
549 * Clone of the DOM object that contains the submit button
550 */
551 submitButton: {},
552
553 /**
554 * Fetching the templates out of the DOM
555 *
556 * @param tcaIntegrityCheckContainer DOM element id with all needed HTML in it
557 * @return boolean DOM container could be found and initialization finished
558 */
559 initialize: function (tcaIntegrityCheckContainer) {
560 var success = false;
561 this.outputContainer[tcaIntegrityCheckContainer] = $('#' + tcaIntegrityCheckContainer);
562
563 if (this.outputContainer[tcaIntegrityCheckContainer]) {
564 // submit button: save and delete
565 if (!this.submitButton[tcaIntegrityCheckContainer]) {
566 var submitButton = this.outputContainer[tcaIntegrityCheckContainer].find('button[type="submit"]');
567 this.submitButton[tcaIntegrityCheckContainer] = submitButton.clone();
568 // submitButton.remove();
569 }
570
571 // clear all messages from the run before
572 this.outputContainer[tcaIntegrityCheckContainer].find('.typo3-message:visible ').remove();
573
574 success = true;
575 }
576 return success;
577 },
578
579 checkTcaIntegrity: function (actionName) {
580 var self = this;
581 var url = location.href + '&install[controller]=ajax&install[action]=' + actionName;
582
583 var isInitialized = self.initialize(actionName);
584 if (isInitialized) {
585 self.addMessage(
586 TYPO3.Install.Severity.loading,
587 self.outputMessages[actionName].loadingTitle,
588 self.outputMessages[actionName].loadingMessage,
589 actionName
590 );
591
592 $.ajax({
593 url: url,
594 cache: false,
595 success: function (data) {
596
597 if (data.success === true && Array.isArray(data.status)) {
598 if (data.status.length > 0) {
599 self.outputContainer[actionName].find('.alert-loading').hide();
600 self.addMessage(
601 TYPO3.Install.Severity.warning,
602 self.outputMessages[actionName].warningTitle,
603 self.outputMessages[actionName].warningMessage,
604 actionName
605 );
606 data.status.forEach((function (element) {
607 //noinspection JSUnresolvedVariable
608 self.addMessage(
609 element.severity,
610 element.title,
611 element.message,
612 actionName
613 );
614 }));
615 } else {
616 // nothing to complain, everything fine
617 self.outputContainer[actionName].find('.alert-loading').hide();
618 self.addMessage(
619 TYPO3.Install.Severity.ok,
620 self.outputMessages[actionName].successTitle,
621 self.outputMessages[actionName].successMessage,
622 actionName
623 );
624 }
625 } else if (data === 'unauthorized') {
626 location.reload();
627 }
628 },
629 error: function () {
630 self.outputContainer[actionName].find('.alert-loading').hide();
631 self.addMessage(
632 TYPO3.Install.Severity.error,
633 self.outputMessages[actionName].fatalTitle,
634 self.outputMessages[actionName].fatalMessage,
635 actionName
636 );
637 }
638 });
639 }
640 },
641
642 /**
643 * Move the submit button to the end of the box
644 *
645 * @param tcaIntegrityCheckContainer DOM container name
646 */
647 moveSubmitButtonFurtherDown: function (tcaIntegrityCheckContainer) {
648 // first remove the currently visible button
649 this.outputContainer[tcaIntegrityCheckContainer].find('button[type="submit"]').remove();
650 // then append the cloned template to the end
651 this.outputContainer[tcaIntegrityCheckContainer].append(this.submitButton[tcaIntegrityCheckContainer]);
652 },
653
654 /**
655 * Show a status message
656 *
657 * @param severity
658 * @param title
659 * @param message
660 * @param tcaIntegrityCheckContainer DOM container name
661 */
662 addMessage: function (severity, title, message, tcaIntegrityCheckContainer) {
663 var domMessage = TYPO3.Install.FlashMessage.render(severity, title, message);
664 this.outputContainer[tcaIntegrityCheckContainer].append(domMessage);
665 this.moveSubmitButtonFurtherDown(tcaIntegrityCheckContainer);
666 }
667
668 };
669
670 TYPO3.Install.Status = {
671 getFolderStatus: function () {
672 var url = location.href + '&install[controller]=ajax&install[action]=folderStatus';
673 $.ajax({
674 url: url,
675 cache: false,
676 success: function (data) {
677 if (data > 0) {
678 $('.t3js-install-menu-folderStructure').append('<span class="badge badge-danger">' + data + '</span>');
679 }
680 }
681 });
682 },
683 getEnvironmentStatus: function () {
684 var url = location.href + '&install[controller]=ajax&install[action]=environmentStatus';
685 $.ajax({
686 url: url,
687 cache: false,
688 success: function (data) {
689 if (data > 0) {
690 $('.t3js-install-menu-systemEnvironment').append('<span class="badge badge-danger">' + data + '</span>');
691 }
692 }
693 });
694 }
695 };
696
697 TYPO3.Install.coreUpdate = {
698 /**
699 * The action queue defines what actions are called in which order
700 */
701 actionQueue: {
702 coreUpdateUpdateVersionMatrix: {
703 loadingMessage: 'Fetching list of released versions from typo3.org',
704 finishMessage: 'Fetched list of released versions',
705 nextActionName: 'coreUpdateIsUpdateAvailable'
706 },
707 coreUpdateIsUpdateAvailable: {
708 loadingMessage: 'Checking for possible regular or security update',
709 finishMessage: undefined,
710 nextActionName: undefined
711 },
712 coreUpdateCheckPreConditions: {
713 loadingMessage: 'Checking if update is possible',
714 finishMessage: 'System can be updated',
715 nextActionName: 'coreUpdateDownload'
716 },
717 coreUpdateDownload: {
718 loadingMessage: 'Downloading new core',
719 finishMessage: undefined,
720 nextActionName: 'coreUpdateVerifyChecksum'
721 },
722 coreUpdateVerifyChecksum: {
723 loadingMessage: 'Verifying checksum of downloaded core',
724 finishMessage: undefined,
725 nextActionName: 'coreUpdateUnpack'
726 },
727 coreUpdateUnpack: {
728 loadingMessage: 'Unpacking core',
729 finishMessage: undefined,
730 nextActionName: 'coreUpdateMove'
731 },
732 coreUpdateMove: {
733 loadingMessage: 'Moving core',
734 finishMessage: undefined,
735 nextActionName: 'clearCache'
736 },
737 clearCache: {
738 loadingMessage: 'Clearing caches',
739 finishMessage: 'Caches cleared',
740 nextActionName: 'coreUpdateActivate'
741 },
742 coreUpdateActivate: {
743 loadingMessage: 'Activating core',
744 finishMessage: 'Core updated - please reload your browser',
745 nextActionName: undefined
746 }
747 },
748
749 /**
750 * Clone of a DOM object acts as button template
751 */
752 buttonTemplate: null,
753
754 /**
755 * Fetching the templates out of the DOM
756 */
757 initialize: function () {
758 var buttonTemplateSection = $('#buttonTemplate');
759 this.buttonTemplate = buttonTemplateSection.children().clone();
760 },
761
762 /**
763 * Public method checkForUpdate
764 */
765 checkForUpdate: function () {
766 this.callAction('coreUpdateUpdateVersionMatrix');
767 },
768
769 /**
770 * Public method updateDevelopment
771 */
772 updateDevelopment: function () {
773 this.update('development');
774 },
775
776 /**
777 * Public method updateRegular
778 */
779 updateRegular: function () {
780 this.update('regular');
781 },
782
783 /**
784 * Execute core update.
785 *
786 * @param type Either 'development' or 'regular'
787 */
788 update: function (type) {
789 if (type !== "development") {
790 type = 'regular';
791 }
792 this.callAction('coreUpdateCheckPreConditions', type);
793 },
794
795 /**
796 * Generic method to call actions from the queue
797 *
798 * @param actionName Name of the action to be called
799 * @param type Update type (optional)
800 */
801 callAction: function (actionName, type) {
802 var self = this;
803 var data = {
804 install: {
805 controller: 'ajax',
806 action: actionName
807 }
808 };
809 if (type !== undefined) {
810 data.install["type"] = type;
811 }
812 this.addLoadingMessage(this.actionQueue[actionName].loadingMessage);
813 $.ajax({
814 url: location.href,
815 data: data,
816 cache: false,
817 success: function (result) {
818 var canContinue = self.handleResult(result, self.actionQueue[actionName].finishMessage);
819 if (canContinue === true && (self.actionQueue[actionName].nextActionName !== undefined)) {
820 self.callAction(self.actionQueue[actionName].nextActionName, type);
821 }
822 },
823 error: function (result) {
824 self.handleResult(result);
825 }
826 });
827 },
828
829 /**
830 * Handle ajax result of core update step.
831 *
832 * @param data
833 * @param successMessage Optional success message
834 */
835 handleResult: function (data, successMessage) {
836 var canContinue = false;
837 this.removeLoadingMessage();
838 if (data.success === true) {
839 canContinue = true;
840 if (data.status && typeof(data.status) === 'object') {
841 this.showStatusMessages(data.status);
842 }
843 if (data.action && typeof(data.action) === 'object') {
844 this.showActionButton(data.action);
845 }
846 if (successMessage) {
847 this.addMessage(TYPO3.Install.Severity.ok, successMessage);
848 }
849 } else {
850 // Handle clearcache until it uses the new view object
851 if (data === "OK") {
852 canContinue = true;
853 if (successMessage) {
854 this.addMessage(TYPO3.Install.Severity.ok, successMessage);
855 }
856 } else {
857 canContinue = false;
858 if (data.status && typeof(data.status) === 'object') {
859 this.showStatusMessages(data.status);
860 } else {
861 this.addMessage(TYPO3.Install.Severity.error, 'General error');
862 }
863 }
864 }
865 return canContinue;
866 },
867
868 /**
869 * Add a loading message with some text.
870 *
871 * @param messageTitle
872 */
873 addLoadingMessage: function (messageTitle) {
874 var domMessage = TYPO3.Install.FlashMessage.render(TYPO3.Install.Severity.loading, messageTitle);
875 $('#coreUpdate').append(domMessage);
876 },
877
878 /**
879 * Remove an enabled loading message
880 */
881 removeLoadingMessage: function () {
882 $('#coreUpdate').find('.alert-loading').remove();
883 },
884
885 /**
886 * Show a list of status messages
887 *
888 * @param messages
889 */
890 showStatusMessages: function (messages) {
891 var self = this;
892 $.each(messages, function (index, element) {
893 var title = false;
894 var message = false;
895 var severity = element.severity;
896 if (element.title) {
897 title = element.title;
898 }
899 if (element.message) {
900 message = element.message;
901 }
902 self.addMessage(severity, title, message);
903 });
904 },
905
906 /**
907 * Show an action button
908 *
909 * @param button
910 */
911 showActionButton: function (button) {
912 var title = false;
913 var action = false;
914 if (button.title) {
915 title = button.title;
916 }
917 if (button.action) {
918 action = button.action;
919 }
920 var domButton = this.buttonTemplate;
921 if (action) {
922 domButton.find('button').data('action', action);
923 }
924 if (title) {
925 domButton.find('button').html(title);
926 }
927 $('#coreUpdate').append(domButton);
928 },
929
930 /**
931 * Show a status message
932 *
933 * @param severity
934 * @param title
935 * @param message
936 */
937 addMessage: function (severity, title, message) {
938 var domMessage = TYPO3.Install.FlashMessage.render(severity, title, message);
939 $('#coreUpdate').append(domMessage);
940 }
941 };
942
943 $(function () {
944 // Used in database compare section to select/deselect checkboxes
945 $('.checkall').on('click', function () {
946 $(this).closest('fieldset').find(':checkbox').prop('checked', this.checked);
947 });
948
949 $('.item-description').find('a').on('click', function () {
950 var targetToggleGroupId = $(this.hash);
951 if (targetToggleGroupId) {
952 var $currentToggleGroup = $(this).closest('.toggleGroup');
953 var $targetToggleGroup = $(targetToggleGroupId).closest('.toggleGroup');
954 if ($targetToggleGroup !== $currentToggleGroup) {
955 $currentToggleGroup.removeClass('expanded');
956 $currentToggleGroup.find('.toggleData').hide();
957 $targetToggleGroup.addClass('expanded');
958 $targetToggleGroup.find('.toggleData').show();
959 TYPO3.Install.Scrolling.handleButtonScrolling();
960 }
961 }
962 });
963
964 $(document).on('click', '.t3js-all-configuration-toggle', function () {
965 var $panels = $('.panel-collapse', '#allConfiguration');
966 var action = ($panels.eq(0).hasClass('in')) ? 'hide' : 'show';
967 $panels.collapse(action);
968 });
969
970 var $configSearch = $('#configSearch');
971 if ($configSearch.length > 0) {
972 $(window).on('keydown', function (event) {
973 if (event.ctrlKey || event.metaKey) {
974 switch (String.fromCharCode(event.which).toLowerCase()) {
975 case 'f':
976 event.preventDefault();
977 $configSearch.focus();
978 break;
979 }
980 } else if (event.keyCode === 27) {
981 event.preventDefault();
982 $configSearch.val('').focus();
983 }
984 });
985 }
986
987 // Simple password strength indicator
988 $('.t3-install-form-password-strength').on('keyup', function () {
989 var value = $(this).val();
990 var strongRegex = new RegExp('^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$', 'g');
991 var mediumRegex = new RegExp('^(?=.{8,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$', 'g');
992 var enoughRegex = new RegExp('(?=.{8,}).*', 'g');
993
994 if (value.length === 0) {
995 $(this).attr('style', 'background-color:#FBB19B; border:1px solid #DC4C42');
996 } else if (!enoughRegex.test(value)) {
997 $(this).attr('style', 'background-color:#FBB19B; border:1px solid #DC4C42');
998 } else if (strongRegex.test(value)) {
999 $(this).attr('style', 'background-color:#CDEACA; border:1px solid #58B548');
1000 } else if (mediumRegex.test(value)) {
1001 $(this).attr('style', 'background-color:#FBFFB3; border:1px solid #C4B70D');
1002 } else {
1003 $(this).attr('style', 'background-color:#FBFFB3; border:1px solid #C4B70D');
1004 }
1005 });
1006
1007 // Install step database settings
1008 $('#t3js-connect-database-driver').on('change', function() {
1009 var driverSelector = '#' + $(this).val();
1010 $('.t3-install-driver-data').hide();
1011 $('.t3-install-driver-data input').attr('disabled', 'disabled');
1012 $(driverSelector + ' input').attr('disabled', false);
1013 $(driverSelector).show();
1014 }).trigger('change');
1015
1016 // Extension compatibility check
1017 var $container = $('#checkExtensions');
1018 $('.t3js-message', $container).hide();
1019 $('button', $container).click(function (e) {
1020 $('button', $container).hide();
1021 $('.t3js-message', $container).hide();
1022 $('.alert-loading', $container).show();
1023 TYPO3.Install.ExtensionChecker.checkExtensionsCompatibility(true);
1024 e.preventDefault();
1025 return false;
1026 });
1027
1028 // Handle core update
1029 var $coreUpdateSection = $('#coreUpdate');
1030 if ($coreUpdateSection) {
1031 TYPO3.Install.coreUpdate.initialize();
1032 $coreUpdateSection.on('click', 'button', (function (e) {
1033 e.preventDefault();
1034 var action = $(e.target).data('action');
1035 TYPO3.Install.coreUpdate[action]();
1036 $(e.target).closest('.t3-install-form-submit').remove();
1037 }));
1038 }
1039
1040 // Handle clearAllCache
1041 var $clearAllCacheSection = $('#clearAllCache');
1042 if ($clearAllCacheSection) {
1043 $clearAllCacheSection.on('click', 'button', (function(e) {
1044 TYPO3.Install.Cache.clearAllCache('clearAllCache');
1045 e.preventDefault();
1046 return false;
1047 }));
1048 }
1049
1050 // Handle dumpAutoload
1051 var $clearAllCacheSection = $('.t3js-dumpAutoload');
1052 if ($clearAllCacheSection) {
1053 $clearAllCacheSection.on('click', 'button', (function(e) {
1054 TYPO3.Install.DumpAutoload.dumpAutoload('dumpAutoload');
1055 e.preventDefault();
1056 return false;
1057 }));
1058 }
1059
1060 // Handle TCA ext_tables check
1061 var $tcaExtTablesCheckSection = $('#tcaExtTablesCheck');
1062 if ($tcaExtTablesCheckSection) {
1063 $tcaExtTablesCheckSection.on('click', 'button', (function (e) {
1064 TYPO3.Install.TcaIntegrityChecker.checkTcaIntegrity('tcaExtTablesCheck');
1065 e.preventDefault();
1066 return false;
1067 }));
1068 }
1069
1070 // Handle TCA Migrations check
1071 var $tcaMigrationsCheckSection = $('#tcaMigrationsCheck');
1072 if ($tcaMigrationsCheckSection) {
1073 $tcaMigrationsCheckSection.on('click', 'button', (function (e) {
1074 TYPO3.Install.TcaIntegrityChecker.checkTcaIntegrity('tcaMigrationsCheck');
1075 e.preventDefault();
1076 return false;
1077 }));
1078 }
1079
1080 var $installLeft = $('.t3js-list-group-wrapper');
1081 if ($installLeft.length > 0) {
1082 TYPO3.Install.Status.getFolderStatus();
1083 TYPO3.Install.Status.getEnvironmentStatus();
1084 }
1085 // This makes jquerys "contains" work case-insensitive
1086 jQuery.expr[':'].contains = jQuery.expr.createPseudo(function (arg) {
1087 return function (elem) {
1088 return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
1089 };
1090 });
1091 $configSearch.keyup(function () {
1092 var typedQuery = $(this).val();
1093 $('div.item').each(function () {
1094 var $item = $(this);
1095 if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
1096 $item.removeClass('hidden').addClass('searchhit');
1097 } else {
1098 $item.removeClass('searchhit').addClass('hidden');
1099 }
1100 });
1101 $('.searchhit').parent().collapse('show');
1102 });
1103 var $searchFields = $configSearch;
1104 var searchResultShown = ('' !== $searchFields.first().val());
1105
1106 // make search field clearable
1107 $searchFields.clearable({
1108 onClear: function () {
1109 if (searchResultShown) {
1110 $(this).closest('form').submit();
1111 }
1112 }
1113 });
1114
1115 // Define width of fixed menu
1116 var $menuWrapper = $('#menuWrapper');
1117 var $menuListGroup = $menuWrapper.children('.t3js-list-group-wrapper');
1118 $menuWrapper.on('affixed.bs.affix', function () {
1119 $menuListGroup.width($(this).parent().width());
1120 });
1121 $menuListGroup.width($menuWrapper.parent().width());
1122 $(window).resize(function () {
1123 $menuListGroup.width($('#menuWrapper').parent().width());
1124 });
1125 var $collapse = $('.collapse');
1126 $collapse.on('shown.bs.collapse', function () {
1127 TYPO3.Install.Scrolling.handleButtonScrolling();
1128 });
1129 $collapse.on('hidden.bs.collapse', function () {
1130 TYPO3.Install.Scrolling.handleButtonScrolling();
1131 });
1132
1133 // trigger 'handleButtonScrolling' on page scroll
1134 // if the user scroll until page bottom, we need to remove 'position: fixed'
1135 // so that the copyright info (footer) is not overlaid by the 'fixed button'
1136 var scrollTimeout;
1137 $(window).on('scroll', function () {
1138 clearTimeout(scrollTimeout);
1139 scrollTimeout = setTimeout(function () {
1140 TYPO3.Install.Scrolling.handleButtonScrolling();
1141 }, 50);
1142 });
1143
1144 // automatically select the custom preset if a value in one of its input fields is changed
1145 $('.t3js-custom-preset').on('input', function () {
1146 $('#' + $(this).data('radio')).prop('checked', true);
1147 });
1148
1149 TYPO3.Install.upgradeAnalysis.initialize();
1150 TYPO3.Install.upgradeAnalysis.hideDoumentationFile();
1151 TYPO3.Install.upgradeAnalysis.restoreDocumentationFile();
1152 });
1153
1154 TYPO3.Install.upgradeAnalysis = {
1155 chosenField: null,
1156 fulltextSearchField: null,
1157
1158 provideTags: function () {
1159
1160 var tagString = '';
1161 $('.upgrade_analysis_item_to_filter').each(function () {
1162 tagString += $(this).data('item-tags') + ',';
1163 });
1164
1165 var tagArray = TYPO3.Install.upgradeAnalysis.trimExplodeAndUnique(',', tagString);
1166 $.each(tagArray, function (i, tag) {
1167 $('#tagsort_tags_container').append('<option>' + tag + '</option>');
1168 });
1169 this.chosenField.trigger('chosen:updated');
1170
1171 var config = {
1172 '.chosen-select': {},
1173 '.chosen-select-deselect': {allow_single_deselect: true},
1174 '.chosen-select-no-single': {disable_search_threshold: 10},
1175 '.chosen-select-no-results': {no_results_text: 'Oops, nothing found!'},
1176 '.chosen-select-width': {width: "100%"}
1177 };
1178 for (var selector in config) {
1179 $(selector).chosen(config[selector]);
1180 }
1181 this.chosenField.on('change', function () {
1182 TYPO3.Install.upgradeAnalysis.combinedFilterSearch();
1183 });
1184 this.fulltextSearchField.keyup(function () {
1185 TYPO3.Install.upgradeAnalysis.combinedFilterSearch();
1186 });
1187 },
1188
1189 combinedFilterSearch: function () {
1190 var $items = $('div.item');
1191 if (this.chosenField.val().length < 1 && this.fulltextSearchField.val().length < 1) {
1192 $('.panel-version:not(:first) > .panel-collapse').collapse('hide');
1193 $items.removeClass('hidden searchhit filterhit');
1194 return false;
1195 }
1196 $items.addClass('hidden').removeClass('searchhit filterhit');
1197
1198 // apply tags
1199 if (this.chosenField.val().length > 0) {
1200 $items
1201 .addClass('hidden')
1202 .removeClass('filterhit');
1203 var orTags = [];
1204 var andTags = [];
1205 $.each(this.chosenField.val(), function (index, item) {
1206 var tagFilter = '[data-item-tags*="' + item + '"]';
1207 if (item.indexOf(':') > 0) {
1208 orTags.push(tagFilter);
1209 } else {
1210 andTags.push(tagFilter);
1211 }
1212 });
1213 var andString = andTags.join('');
1214 var tags = [];
1215 if (orTags.length) {
1216 for (var i=0; i<orTags.length; i++) {
1217 tags.push(andString + orTags[i]);
1218 }
1219 } else {
1220 tags.push(andString);
1221 }
1222 var tagSelection = tags.join(',');
1223 $(tagSelection)
1224 .removeClass('hidden')
1225 .addClass('searchhit filterhit');
1226 } else {
1227 $items
1228 .addClass('filterhit')
1229 .removeClass('hidden');
1230 }
1231 // apply fulltext search
1232 var typedQuery = this.fulltextSearchField.val();
1233 $('div.item.filterhit').each(function () {
1234 var $item = $(this);
1235 if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
1236 $item.removeClass('hidden').addClass('searchhit');
1237 } else {
1238 $item.removeClass('searchhit').addClass('hidden');
1239 }
1240 });
1241
1242 $('.searchhit').closest('.panel-collapse').collapse('show');
1243
1244 //check for empty panels
1245 $('.panel-version').each(function () {
1246 if ($(this).find('.searchhit', '.filterhit').length < 1) {
1247 $(this).find(' > .panel-collapse').collapse('hide');
1248 }
1249 });
1250
1251
1252 },
1253
1254 initialize: function () {
1255 this.chosenField = $('.t3js-chosen-select');
1256 this.fulltextSearchField = $('.t3js-fulltext-search');
1257 TYPO3.Install.upgradeAnalysis.provideTags();
1258 },
1259
1260 hideDoumentationFile: function () {
1261 $(document).on('click', '.t3js-upgradeanalysis-ignore', function () {
1262 var $button = $(this);
1263 var filepath = $button.data('filepath');
1264 var token = $('#saveIgnoredItemsToken').html();
1265 $button
1266 .toggleClass('t3js-upgradeanalysis-restore t3js-upgradeanalysis-ignore')
1267 .find('i')
1268 .toggleClass('fa-eye fa-eye-slash');
1269 $button
1270 .closest('.panel')
1271 .appendTo('.panel-body-read');
1272 var postData = {
1273 'install': {
1274 'ignoreFile': filepath,
1275 'token': token,
1276 'action': 'saveIgnoredItems'
1277 }
1278 };
1279 $.ajax({
1280 method: 'POST',
1281 data: postData,
1282 url: location.href + '&install[controller]=ajax'
1283 });
1284
1285 return false;
1286 });
1287 },
1288
1289 restoreDocumentationFile: function () {
1290 $(document).on('click', '.t3js-upgradeanalysis-restore', function () {
1291 var $button = $(this);
1292 var filepath = $button.data('filepath');
1293 var version = $button.closest('.panel').data('item-version');
1294 var token = $('#removeIgnoredItemsToken').html();
1295 $button
1296 .toggleClass('t3js-upgradeanalysis-restore t3js-upgradeanalysis-ignore')
1297 .find('i')
1298 .toggleClass('fa-eye fa-eye-slash');
1299 $button
1300 .closest('.panel')
1301 .appendTo('*[data-group-version="' + version + '"] .panel-body');
1302 var postData = {
1303 'install': {
1304 'ignoreFile': filepath,
1305 'token': token,
1306 'action': 'removeIgnoredItems'
1307 }
1308 };
1309 $.ajax({
1310 method: 'POST',
1311 data: postData,
1312 url: location.href + '&install[controller]=ajax'
1313 });
1314 });
1315 },
1316
1317 trimExplodeAndUnique: function (delimiter, string) {
1318 var result = [];
1319 var items = string.split(delimiter);
1320 for (var i = 0; i < items.length; i++) {
1321 var item = items[i].trim();
1322 if (item.length > 0) {
1323 if ($.inArray(item, result) === -1) {
1324 result.push(item);
1325 }
1326 }
1327 }
1328 return result;
1329 }
1330 };