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