first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,33 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
require([
'jquery',
], function($) {
'use strict';
$(function() {
BackendUserConfirmation.addFocusToFormInput();
});
var BackendUserConfirmation = {
confirmationPassword: '#confirmationPassword',
addFocusToFormInput: function () {
var $confirmationPassword = $(this.confirmationPassword);
if ($confirmationPassword.length) {
$confirmationPassword.focus();
}
}
};
});
@@ -0,0 +1,23 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
require([
'jquery',
'TYPO3/CMS/Install/Router'
], function($, Router) {
'use strict';
$(function() {
Router.initialize();
});
});
@@ -0,0 +1,475 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Walk through the installation process of TYPO3
*/
require([
'jquery',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/PasswordStrength'
], function($, InfoBox, Severity, ProgressBar, PasswordStrength) {
'use strict';
$(function() {
Installer.initialize();
});
var Installer = {
selectorBody: '.t3js-body',
selectorModuleContent: '.t3js-module-content',
selectorMainContent: '.t3js-installer-content',
selectorProgressBar: '.t3js-installer-progress',
selectorDatabaseConnectOutput: '.t3js-installer-databaseConnect-output',
selectorDatabaseSelectOutput: '.t3js-installer-databaseSelect-output',
selectorDatabaseDataOutput: '.t3js-installer-databaseData-output',
selectorDefaultConfigurationOutput: '.t3js-installer-defaultConfiguration-output',
initialize: function() {
var self = this;
$(document).on('click', '.t3js-installer-environmentFolders-retry', function(e) {
e.preventDefault();
self.showEnvironmentAndFolders();
});
$(document).on('click', '.t3js-installer-environmentFolders-execute', function(e) {
e.preventDefault();
self.executeEnvironmentAndFolders();
});
$(document).on('click', '.t3js-installer-databaseConnect-execute', function(e) {
e.preventDefault();
self.executeDatabaseConnect();
});
$(document).on('click', '.t3js-installer-databaseSelect-execute', function(e) {
e.preventDefault();
self.executeDatabaseSelect();
});
$(document).on('click', '.t3js-installer-databaseData-execute', function(e) {
e.preventDefault();
self.executeDatabaseData();
});
$(document).on('click', '.t3js-installer-defaultConfiguration-execute', function(e) {
e.preventDefault();
self.executeDefaultConfiguration();
});
$(document).on('keyup', '.t3-install-form-password-strength', function() {
PasswordStrength.initialize('.t3-install-form-password-strength');
});
// Database connect db driver selection
$(document).on('change', '#t3js-connect-database-driver', function() {
var driver = $(this).val();
$('.t3-install-driver-data').hide();
$('.t3-install-driver-data input').attr('disabled', 'disabled');
$('#' + driver + ' input').attr('disabled', false);
$('#' + driver).show();
});
this.setProgress(0);
this.getMainLayout();
},
getUrl: function(action) {
var url = location.href;
url = url.replace(location.search, '');
if (action !== undefined) {
url = url + '?install[action]=' + action;
}
return url;
},
setProgress: function(done) {
var $progressBar = $(this.selectorProgressBar);
var percent = 0;
if (done !== 0) {
percent = (done / 5) * 100;
$progressBar.find('.progress-bar').empty().text(done + ' / 5 - ' + percent + '% Complete');
}
$progressBar
.find('.progress-bar')
.css('width', percent + '%')
.attr('aria-valuenow', percent);
},
getMainLayout: function() {
var self = this;
$.ajax({
url: this.getUrl('mainLayout'),
cache: false,
success: function(data) {
$(self.selectorBody).empty().append(data.html);
self.checkInstallerAvailable();
}
})
},
checkInstallerAvailable: function() {
var self = this;
$.ajax({
url: this.getUrl('checkInstallerAvailable'),
cache: false,
success: function(data) {
if (data.success === true) {
self.checkEnvironmentAndFolders();
} else {
self.showInstallerNotAvailable();
}
}
});
},
showInstallerNotAvailable: function() {
var $outputContainer = $(this.selectorMainContent);
$.ajax({
url: this.getUrl('showInstallerNotAvailable'),
cache: false,
success: function(data) {
if (data.success === true) {
$outputContainer.empty().append(data.html);
}
}
});
},
checkEnvironmentAndFolders: function() {
var self = this;
this.setProgress(1);
$.ajax({
url: this.getUrl('checkEnvironmentAndFolders'),
cache: false,
success: function(data) {
if (data.success === true) {
self.checkTrustedHostsPattern();
} else {
self.showEnvironmentAndFolders();
}
}
});
},
showEnvironmentAndFolders: function() {
var $outputContainer = $(this.selectorMainContent);
$.ajax({
url: this.getUrl('showEnvironmentAndFolders'),
cache: false,
success: function(data) {
if (data.success === true) {
$outputContainer.empty().html(data.html);
var $detailContainer = $('.t3js-installer-environment-details');
var hasMessage = false;
if (Array.isArray(data.environmentStatusErrors)) {
data.environmentStatusErrors.forEach(function(element) {
hasMessage = true;
var message = InfoBox.render(element.severity, element.title, element.message);
$detailContainer.append(message);
});
}
if (Array.isArray(data.environmentStatusWarnings)) {
data.environmentStatusWarnings.forEach(function(element) {
hasMessage = true;
var message = InfoBox.render(element.severity, element.title, element.message);
$detailContainer.append(message);
});
}
if (Array.isArray(data.structureErrors)) {
data.structureErrors.forEach(function(element) {
hasMessage = true;
var message = InfoBox.render(element.severity, element.title, element.message);
$detailContainer.append(message);
});
}
if (hasMessage === true) {
$detailContainer.show();
$('.t3js-installer-environmentFolders-bad').show();
} else {
$('.t3js-installer-environmentFolders-good').show();
}
}
}
});
},
executeEnvironmentAndFolders: function() {
var self = this;
$.ajax({
url: this.getUrl('executeEnvironmentAndFolders'),
cache: false,
success: function(data) {
if (data.success === true) {
self.checkTrustedHostsPattern();
} else {
// @todo message output handling
}
}
});
},
checkTrustedHostsPattern: function() {
var self = this;
$.ajax({
url: this.getUrl('checkTrustedHostsPattern'),
cache: false,
success: function(data) {
if (data.success === true) {
self.executeSilentConfigurationUpdate();
} else {
self.executeAdjustTrustedHostsPattern();
}
}
});
},
executeAdjustTrustedHostsPattern: function() {
var self = this;
$.ajax({
url: this.getUrl('executeAdjustTrustedHostsPattern'),
cache: false,
success: function(data) {
self.executeSilentConfigurationUpdate();
}
});
},
executeSilentConfigurationUpdate: function() {
var self = this;
$.ajax({
url: this.getUrl('executeSilentConfigurationUpdate'),
cache: false,
success: function(data) {
if (data.success === true) {
self.checkDatabaseConnect();
} else {
self.executeSilentConfigurationUpdate();
}
}
});
},
checkDatabaseConnect: function() {
this.setProgress(2);
var self = this;
$.ajax({
url: this.getUrl('checkDatabaseConnect'),
cache: false,
success: function(data) {
if (data.success === true) {
self.checkDatabaseSelect();
} else {
self.showDatabaseConnect();
}
}
});
},
showDatabaseConnect: function() {
var $outputContainer = $(this.selectorMainContent);
$.ajax({
url: this.getUrl('showDatabaseConnect'),
cache: false,
success: function(data) {
if (data.success === true) {
$outputContainer.empty().html(data.html);
$('#t3js-connect-database-driver').trigger('change');
}
}
});
},
executeDatabaseConnect: function() {
var self = this;
var $outputContainer = $(this.selectorDatabaseConnectOutput);
var postData = {
'install[action]': 'executeDatabaseConnect',
'install[token]': $(self.selectorModuleContent).data('installer-database-connect-execute-token')
};
$($(this.selectorBody + ' form').serializeArray()).each(function() {
postData[this.name] = this.value;
});
$.ajax({
url: this.getUrl(),
cache: false,
method: 'POST',
data: postData,
success: function(data) {
if (data.success === true) {
self.checkDatabaseSelect();
} else {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.empty().append(message);
});
}
}
}
});
},
checkDatabaseSelect: function() {
var self = this;
this.setProgress(3);
$.ajax({
url: this.getUrl('checkDatabaseSelect'),
cache: false,
success: function(data) {
if (data.success === true) {
self.checkDatabaseData();
} else {
self.showDatabaseSelect();
}
}
});
},
showDatabaseSelect: function() {
var $outputContainer = $(this.selectorMainContent);
$.ajax({
url: this.getUrl('showDatabaseSelect'),
cache: false,
success: function(data) {
if (data.success === true) {
$outputContainer.empty().html(data.html);
}
}
});
},
executeDatabaseSelect: function() {
var self = this;
var $outputContainer = $(this.selectorDatabaseSelectOutput);
var postData = {
'install[action]': 'executeDatabaseSelect',
'install[token]': $(self.selectorModuleContent).data('installer-database-select-execute-token')
};
$($(this.selectorBody + ' form').serializeArray()).each(function() {
postData[this.name] = this.value;
});
$.ajax({
url: this.getUrl(),
cache: false,
method: 'POST',
data: postData,
success: function(data) {
if (data.success === true) {
self.checkDatabaseData();
} else {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.empty().append(message);
});
}
}
}
});
},
checkDatabaseData: function() {
var self = this;
this.setProgress(4);
$.ajax({
url: this.getUrl('checkDatabaseData'),
cache: false,
success: function(data) {
if (data.success === true) {
self.showDefaultConfiguration();
} else {
self.showDatabaseData();
}
}
});
},
showDatabaseData: function() {
var $outputContainer = $(this.selectorMainContent);
$.ajax({
url: this.getUrl('showDatabaseData'),
cache: false,
success: function(data) {
if (data.success === true) {
$outputContainer.empty().html(data.html);
}
}
});
},
executeDatabaseData: function() {
var self = this;
var $outputContainer = $(this.selectorDatabaseDataOutput);
var postData = {
'install[action]': 'executeDatabaseData',
'install[token]': $(self.selectorModuleContent).data('installer-database-data-execute-token')
};
$($(this.selectorBody + ' form').serializeArray()).each(function() {
postData[this.name] = this.value;
});
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.empty().html(message);
$.ajax({
url: this.getUrl(),
cache: false,
method: 'POST',
data: postData,
success: function(data) {
if (data.success === true) {
self.showDefaultConfiguration();
} else {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.empty().append(message);
});
}
}
}
});
},
showDefaultConfiguration: function() {
var $outputContainer = $(this.selectorMainContent);
this.setProgress(5);
$.ajax({
url: this.getUrl('showDefaultConfiguration'),
cache: false,
success: function(data) {
if (data.success === true) {
$outputContainer.empty().html(data.html);
}
}
});
},
executeDefaultConfiguration: function() {
var self = this;
var postData = {
'install[action]': 'executeDefaultConfiguration',
'install[token]': $(self.selectorModuleContent).data('installer-default-configuration-execute-token')
};
$($(this.selectorBody + ' form').serializeArray()).each(function() {
postData[this.name] = this.value;
});
$.ajax({
url: this.getUrl(),
cache: false,
method: 'POST',
data: postData,
success: function(data) {
top.location.href = data.redirect;
}
});
}
};
});
@@ -0,0 +1,72 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/AjaxQueue
*/
define(['jquery'], function($) {
'use strict';
return {
requests: [],
requestCount: 0,
threshold: 5,
queue: [],
add: function(payload) {
this.queue.push(payload);
this.handleNext();
},
flush: function() {
this.queue = [];
this.requests.map(function(request) {
request.abort();
});
this.requests = [];
},
handleNext: function() {
if (this.queue.length > 0 && this.requestCount < this.threshold) {
var that = this;
this.incrementRequestCount();
this.sendRequest(that.queue.shift()).always(function() {
that.decrementRequestCount();
that.handleNext();
});
}
},
sendRequest: function(payload) {
var that = this;
var xhr = $.ajax(payload);
this.requests.push(xhr);
xhr.always(function() {
const idx = that.requests.indexOf(xhr);
delete that.requests[idx];
});
return xhr;
},
incrementRequestCount: function() {
this.requestCount++;
},
decrementRequestCount: function() {
if (this.requestCount > 0) {
this.requestCount--;
}
},
};
});
@@ -0,0 +1,57 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/Cache
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Backend/Icons',
'TYPO3/CMS/Backend/Notification'
], function($, Router, Icons, Notification) {
'use strict';
return {
initialize: function($trigger) {
$.ajax({
url: Router.getUrl('cacheClearAll', 'maintenance'),
cache: false,
beforeSend: function() {
$trigger.addClass('disabled');
},
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
if (data.status.length > 0) {
data.status.forEach((function(element) {
Notification.success(element.title, element.message);
}));
}
} else {
Notification.error('Something went wrong clearing caches');
}
},
error: function(xhr) {
// In case the clear cache action fails (typically 500 from server), do not kill the entire
// install tool, instead show a notification that something went wrong.
Notification.error(
'Clearing caches went wrong on the server side. Check the system for broken extensions or missing database tables and try again'
);
},
complete: function() {
$trigger.removeClass('disabled');
}
});
}
};
});
@@ -0,0 +1,99 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/CreateAdmin
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Install/PasswordStrength',
'TYPO3/CMS/Backend/Notification'
], function($, Router, Severity, PasswordStrength, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorChangeForm: '#t3js-changeInstallToolPassword-form',
selectorOutputContainer: '.t3js-changeInstallToolPassword-output',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getData();
currentModal.on('submit', this.selectorChangeForm, function(e) {
e.preventDefault();
self.change();
});
currentModal.on('click', '.t3-install-form-password-strength', function(e) {
PasswordStrength.initialize('.t3-install-form-password-strength');
});
},
getData: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('changeInstallToolPasswordGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
change: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('install-tool-token');
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'changeInstallToolPassword',
'token': executeToken,
'password': self.currentModal.find('.t3js-changeInstallToolPassword-password').val(),
'passwordCheck': self.currentModal.find('.t3js-changeInstallToolPassword-password-check').val()
}
},
cache: false,
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.showMessage('', element.message, element.severity);
});
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
},
complete: function() {
self.currentModal.find('.t3js-changeInstallToolPassword-password,.t3js-changeInstallToolPassword-password-check').val('')
}
});
}
};
});
@@ -0,0 +1,122 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/ClearTable
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorClearTrigger: '.t3js-clearTables-clear',
selectorStatsTrigger: '.t3js-clearTables-stats',
selectorOutputContainer: '.t3js-clearTables-output',
selectorStatContainer: '.t3js-clearTables-stat-container',
selectorStatTemplate: '.t3js-clearTables-stat-template',
selectorStatDescription: '.t3js-clearTables-stat-description',
selectorStatRows: '.t3js-clearTables-stat-rows',
selectorStatName: '.t3js-clearTables-stat-name',
selectorStatLastRuler: '.t3js-clearTables-stat-lastRuler',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getStats();
currentModal.on('click', this.selectorStatsTrigger, function(e) {
e.preventDefault();
$(self.selectorOutputContainer).empty();
self.getStats();
});
currentModal.on('click', this.selectorClearTrigger, function(e) {
var table = $(e.target).closest(self.selectorClearTrigger).data('table');
e.preventDefault();
self.clear(table);
});
},
getStats: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('clearTablesStats'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
if (Array.isArray(data.stats) && data.stats.length > 0) {
data.stats.forEach(function(element) {
if (element.rowCount > 0) {
var aStat = modalContent.find(self.selectorStatTemplate).clone();
aStat.find(self.selectorStatDescription).text(element.description);
aStat.find(self.selectorStatName).text(element.name);
aStat.find(self.selectorStatRows).text(element.rowCount);
aStat.find(self.selectorClearTrigger).attr('data-table', element.name);
modalContent.find(self.selectorStatContainer).append(aStat.html());
}
});
}
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
clear: function(table) {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('clear-tables-clear-token');
$.ajax({
url: Router.getUrl(),
method: 'POST',
context: this,
data: {
'install': {
'action': 'clearTablesClear',
'token': executeToken,
'table': table
}
},
cache: false,
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.success(element.message);
});
} else {
Notification.error('Something went wrong');
}
this.getStats();
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,124 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/ClearTypo3tempFiles
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorDeleteTrigger: '.t3js-clearTypo3temp-delete',
selectorOutputContainer: '.t3js-clearTypo3temp-output',
selectorStatContainer: '.t3js-clearTypo3temp-stat-container',
selectorStatsTrigger: '.t3js-clearTypo3temp-stats',
selectorStatTemplate: '.t3js-clearTypo3temp-stat-template',
selectorStatDescription: '.t3js-clearTypo3temp-stat-description',
selectorStatNumberOfFiles: '.t3js-clearTypo3temp-stat-numberOfFiles',
selectorStatDirectory: '.t3js-clearTypo3temp-stat-directory',
selectorStatName: '.t3js-clearTypo3temp-stat-name',
selectorStatLastRuler: '.t3js-clearTypo3temp-stat-lastRuler',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getStats();
currentModal.on('click', this.selectorStatsTrigger, function(e) {
e.preventDefault();
$(self.selectorOutputContainer).empty();
self.getStats();
});
currentModal.on('click', this.selectorDeleteTrigger, function(e) {
var folder = $(this).data('folder');
var storageUid = $(this).data('storage-uid');
e.preventDefault();
self.delete(folder, storageUid);
});
},
getStats: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('clearTypo3tempFilesStats'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
if (Array.isArray(data.stats) && data.stats.length > 0) {
data.stats.forEach(function(element) {
if (element.numberOfFiles > 0) {
var aStat = modalContent.find(self.selectorStatTemplate).clone();
aStat.find(self.selectorStatNumberOfFiles).text(element.numberOfFiles);
aStat.find(self.selectorStatDirectory).text(element.directory);
aStat.find(self.selectorDeleteTrigger).attr('data-folder', element.directory);
aStat.find(self.selectorDeleteTrigger).attr('data-storage-uid', element.storageUid);
modalContent.find(self.selectorStatContainer).append(aStat.html());
}
});
}
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
delete: function(folder, storageUid) {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('clear-typo3temp-delete-token');
$.ajax({
method: 'POST',
url: Router.getUrl(),
context: this,
data: {
'install': {
'action': 'clearTypo3tempFiles',
'token': executeToken,
'folder': folder,
'storageUid': storageUid
}
},
cache: false,
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.success(element.message);
});
this.getStats();
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,275 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/CoreUpdate
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, Severity, Notification) {
'use strict';
return {
/**
* The action queue defines what actions are called in which order
*/
actionQueue: {
coreUpdateIsUpdateAvailable: {
loadingMessage: 'Checking for possible regular or security update',
finishMessage: undefined,
nextActionName: undefined
},
coreUpdateCheckPreConditions: {
loadingMessage: 'Checking if update is possible',
finishMessage: 'System can be updated',
nextActionName: 'coreUpdateDownload'
},
coreUpdateDownload: {
loadingMessage: 'Downloading new core',
finishMessage: undefined,
nextActionName: 'coreUpdateVerifyChecksum'
},
coreUpdateVerifyChecksum: {
loadingMessage: 'Verifying checksum of downloaded core',
finishMessage: undefined,
nextActionName: 'coreUpdateUnpack'
},
coreUpdateUnpack: {
loadingMessage: 'Unpacking core',
finishMessage: undefined,
nextActionName: 'coreUpdateMove'
},
coreUpdateMove: {
loadingMessage: 'Moving core',
finishMessage: undefined,
nextActionName: 'coreUpdateActivate'
},
coreUpdateActivate: {
loadingMessage: 'Activating core',
finishMessage: 'Core updated - please reload your browser',
nextActionName: undefined
}
},
selectorModalBody: '.t3js-modal-body',
selectorOutput: '.t3js-coreUpdate-output',
selectorTemplate: '.t3js-coreUpdate-buttonTemplate',
/**
* Clone of a DOM object acts as button template
*/
buttonTemplate: null,
/**
* Fetching the templates out of the DOM
*/
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getData().done(function() {
var buttonTemplateSection = currentModal.find(self.selectorTemplate);
self.buttonTemplate = buttonTemplateSection.children().clone();
});
currentModal.on('click', '.t3js-coreUpdate-init', function(e) {
e.preventDefault();
// Don't use jQuery's data() function, as the DOM is re-rendered and any set data attribute gets lost.
// See showActionButton()
var action = $(e.target).attr('data-action');
currentModal.find(self.selectorOutput).empty();
self[action]();
});
},
getData: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
return $.ajax({
url: Router.getUrl('coreUpdateGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
/**
* Public method checkForUpdate
*/
checkForUpdate: function() {
this.callAction('coreUpdateIsUpdateAvailable');
},
/**
* Public method updateDevelopment
*/
updateDevelopment: function() {
this.update('development');
},
updateRegular: function() {
this.update('regular');
},
/**
* Execute core update.
*
* @param type Either 'development' or 'regular'
*/
update: function(type) {
if (type !== 'development') {
type = 'regular';
}
this.callAction('coreUpdateCheckPreConditions', type);
},
/**
* Generic method to call actions from the queue
*
* @param actionName Name of the action to be called
* @param type Update type (optional)
*/
callAction: function(actionName, type) {
var self = this;
var data = {
install: {
action: actionName
}
};
if (type !== undefined) {
data.install["type"] = type;
}
this.addLoadingMessage(this.actionQueue[actionName].loadingMessage);
$.ajax({
url: Router.getUrl(),
data: data,
cache: false,
success: function(result) {
var canContinue = self.handleResult(result, self.actionQueue[actionName].finishMessage);
if (canContinue === true && (self.actionQueue[actionName].nextActionName !== undefined)) {
self.callAction(self.actionQueue[actionName].nextActionName, type);
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, self.currentModal.find(self.selectorModalBody));
}
});
},
/**
* Handle ajax result of core update step.
*
* @param data
* @param successMessage Optional success message
*/
handleResult: function(data, successMessage) {
var canContinue = data.success;
this.removeLoadingMessage();
if (data.status && typeof(data.status) === 'object') {
this.showStatusMessages(data.status);
}
if (data.action && typeof(data.action) === 'object') {
this.showActionButton(data.action);
}
if (canContinue && successMessage) {
this.addMessage(Severity.ok, successMessage);
}
return canContinue;
},
/**
* Add a loading message with some text.
*
* @param messageTitle
*/
addLoadingMessage: function(messageTitle) {
var domMessage = FlashMessage.render(Severity.loading, messageTitle);
this.currentModal.find(this.selectorOutput).append(domMessage);
},
/**
* Remove an enabled loading message
*/
removeLoadingMessage: function() {
this.currentModal.find(this.selectorOutput).find('.alert-loading').remove();
},
/**
* Show a list of status messages
*
* @param messages
*/
showStatusMessages: function(messages) {
var self = this;
$.each(messages, function(index, element) {
var title = false;
var message = false;
var severity = element.severity;
if (element.title) {
title = element.title;
}
if (element.message) {
message = element.message;
}
self.addMessage(severity, title, message);
});
},
/**
* Show an action button
*
* @param button
*/
showActionButton: function(button) {
var title = false;
var action = false;
if (button.title) {
title = button.title;
}
if (button.action) {
action = button.action;
}
var domButton = this.buttonTemplate;
if (action) {
domButton.attr('data-action', action);
}
if (title) {
domButton.text(title);
}
this.currentModal.find(this.selectorOutput).append(domButton);
},
/**
* Show a status message
*
* @param severity
* @param title
* @param message
*/
addMessage: function(severity, title, message) {
var domMessage = FlashMessage.render(severity, title, message);
this.currentModal.find(this.selectorOutput).append(domMessage);
}
};
});
@@ -0,0 +1,111 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/CreateAdmin
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Install/PasswordStrength',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, PasswordStrength, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorCreateForm: '#t3js-createAdmin-form',
selectorOutputContainer: '.t3js-createAdmin-output',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getData();
currentModal.on('submit', this.selectorCreateForm, function(e) {
e.preventDefault();
self.create();
});
currentModal.on('click', '.t3-install-form-password-strength', function(e) {
PasswordStrength.initialize('.t3-install-form-password-strength');
});
},
getData: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('createAdminGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
create: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('create-admin-token');
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'createAdmin',
'token': executeToken,
'userName': self.currentModal.find('.t3js-createAdmin-user').val(),
'userPassword': self.currentModal.find('.t3js-createAdmin-password').val(),
'userPasswordCheck': self.currentModal.find('.t3js-createAdmin-password-check').val(),
'userSystemMaintainer': (self.currentModal.find('.t3js-createAdmin-system-maintainer').is(':checked')) ? 1 : 0
}
},
cache: false,
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
if (element.severity == 2) {
Notification.error(element.message);
}
else {
Notification.success(element.title);
}
});
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
this.currentModal.find('.t3js-createAdmin-user').val('');
this.currentModal.find('.t3js-createAdmin-password').val('');
this.currentModal.find('.t3js-createAdmin-password-check').val('');
this.currentModal.find('.t3js-createAdmin-system-maintainer').prop('checked', false);
}
};
});
@@ -0,0 +1,200 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/DatabaseAnalyzer
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorAnalyzeTrigger: '.t3js-databaseAnalyzer-analyze',
selectorExecuteTrigger: '.t3js-databaseAnalyzer-execute',
selectorOutputContainer: '.t3js-databaseAnalyzer-output',
selectorSuggestionBlock: '.t3js-databaseAnalyzer-suggestion-block',
selectorSuggestionList: '.t3js-databaseAnalyzer-suggestion-list',
selectorSuggestionLine: '.t3js-databaseAnalyzer-suggestion-line',
selectorSuggestionLineTemplate: '.t3js-databaseAnalyzer-suggestion-line-template',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getData();
// Select / deselect all checkboxes
currentModal.on('click', '.t3js-databaseAnalyzer-suggestion-block-checkbox', function(e) {
$(this).closest('fieldset').find(':checkbox').prop('checked', this.checked);
});
currentModal.on('click', this.selectorAnalyzeTrigger, function(e) {
e.preventDefault();
self.analyze();
});
currentModal.on('click', this.selectorExecuteTrigger, function(e) {
e.preventDefault();
self.execute();
});
},
getData: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('databaseAnalyzer'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
self.analyze();
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
analyze: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var outputContainer = modalContent.find(this.selectorOutputContainer);
var executeTrigger = modalContent.find(this.selectorExecuteTrigger);
var analyzeTrigger = modalContent.find(this.selectorAnalyzeTrigger);
outputContainer.empty().append(ProgressBar.render(Severity.loading, 'Analyzing current database schema...', ''));
analyzeTrigger.prop('disabled', true);
executeTrigger.prop('disabled', true);
outputContainer.on('change', 'input[type="checkbox"]', function() {
var hasCheckedCheckboxes = outputContainer.find(':checked').length > 0;
executeTrigger.prop('disabled', !hasCheckedCheckboxes);
});
$.ajax({
url: Router.getUrl('databaseAnalyzerAnalyze'),
cache: false,
success: function(data) {
if (data.success === true) {
if (Array.isArray(data.status)) {
outputContainer.find('.alert-loading').remove();
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
outputContainer.append(message);
});
}
if (Array.isArray(data.suggestions)) {
data.suggestions.forEach(function(element) {
var aBlock = modalContent.find(self.selectorSuggestionBlock).clone();
aBlock.removeClass(self.selectorSuggestionBlock.substr(1));
var key = element.key;
aBlock.find('.t3js-databaseAnalyzer-suggestion-block-legend').text(element.label);
aBlock.find('.t3js-databaseAnalyzer-suggestion-block-checkbox').attr('id', 't3-install-' + key + '-checkbox');
if (element.enabled) {
aBlock.find('.t3js-databaseAnalyzer-suggestion-block-checkbox').attr('checked', 'checked');
}
aBlock.find('.t3js-databaseAnalyzer-suggestion-block-label').attr('for', 't3-install-' + key + '-checkbox');
element.children.forEach(function(line) {
var aLine = modalContent.find(self.selectorSuggestionLineTemplate).children().clone();
var hash = line.hash;
var $checkbox = aLine.find('.t3js-databaseAnalyzer-suggestion-line-checkbox');
$checkbox.attr('id', 't3-install-db-' + hash).attr('data-hash', hash);
if (element.enabled) {
$checkbox.attr('checked', 'checked');
}
aLine.find('.t3js-databaseAnalyzer-suggestion-line-label').attr('for', 't3-install-db-' + hash);
aLine.find('.t3js-databaseAnalyzer-suggestion-line-statement').text(line.statement);
if (typeof line.current !== 'undefined') {
aLine.find('.t3js-databaseAnalyzer-suggestion-line-current-value').text(line.current);
aLine.find('.t3js-databaseAnalyzer-suggestion-line-current').show();
}
if (typeof line.rowCount !== 'undefined') {
aLine.find('.t3js-databaseAnalyzer-suggestion-line-count-value').text(line.rowCount);
aLine.find('.t3js-databaseAnalyzer-suggestion-line-count').show();
}
aBlock.find(self.selectorSuggestionList).append(aLine);
});
outputContainer.append(aBlock.html());
});
var isInitiallyDisabled = outputContainer.find(':checked').length === 0;
analyzeTrigger.prop('disabled', false);
executeTrigger.prop('disabled', isInitiallyDisabled);
}
if (data.suggestions.length === 0 && data.status.length === 0) {
outputContainer.append(InfoBox.render(Severity.ok, 'Database schema is up to date. Good job!', ''));
}
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
execute: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('database-analyzer-execute-token');
var outputContainer = modalContent.find(this.selectorOutputContainer);
var selectedHashes = [];
outputContainer.find('.t3js-databaseAnalyzer-suggestion-line input:checked').each(function() {
selectedHashes.push($(this).data('hash'));
});
outputContainer.empty().append(ProgressBar.render(Severity.loading, 'Executing database updates...', ''));
modalContent.find(this.selectorExecuteTrigger).prop('disabled', true);
modalContent.find(this.selectorAnalyzeTrigger).prop('disabled', true);
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'databaseAnalyzerExecute',
'token': executeToken,
'hashes': selectedHashes
}
},
cache: false,
success: function(data) {
if (data.success === true) {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.showMessage(element.title, element.message, element.severity);
});
}
}
self.analyze();
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,61 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/DumpAutoload
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorDumpTrigger: '.t3js-dumpAutoload-dump',
selectorOutputContainer: '.t3js-dumpAutoload-output',
initialize: function($trigger) {
$.ajax({
url: Router.getUrl('dumpAutoload'),
cache: false,
beforeSend: function() {
$trigger.addClass('disabled');
},
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
if (data.status.length > 0) {
data.status.forEach((function(element) {
Notification.success(element.message);
}));
}
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
// In case the dump action fails (typically 500 from server), do not kill the entire
// install tool, instead show a notification that something went wrong.
Notification.error('Dumping autoload files went wrong on the server side. Check the system for broken extensions and try again')
},
complete: function() {
$trigger.removeClass('disabled');
}
});
}
};
});
@@ -0,0 +1,93 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/EnvironmentCheck
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorGridderBadge: '.t3js-environmentCheck-badge',
selectorExecuteTrigger: '.t3js-environmentCheck-execute',
selectorOutputContainer: '.t3js-environmentCheck-output',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
// Get status on initialize to have the badge and content ready
this.runTests();
currentModal.on('click', this.selectorExecuteTrigger, function(e) {
e.preventDefault();
self.runTests();
});
},
runTests: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $errorBadge = $(this.selectorGridderBadge);
$errorBadge.text('').hide();
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
modalContent.find(this.selectorOutputContainer).empty().append(message);
$.ajax({
url: Router.getUrl('environmentCheckGetStatus'),
cache: false,
success: function(data) {
modalContent.empty().append(data.html);
var warningCount = 0;
var errorCount = 0;
if (data.success === true && typeof(data.status) === 'object') {
$.each(data.status, function(i, element) {
if (Array.isArray(element) && element.length > 0) {
element.forEach(function(aStatus) {
if (aStatus.severity === 1) {
warningCount += 1;
}
if (aStatus.severity === 2) {
errorCount += 1;
}
var message = InfoBox.render(aStatus.severity, aStatus.title, aStatus.message);
modalContent.find(self.selectorOutputContainer).append(message);
});
}
});
if (errorCount > 0) {
$errorBadge.removeClass('label-warning').addClass('label-danger').text(errorCount).show();
} else if (warningCount > 0) {
$errorBadge.removeClass('label-error').addClass('label-warning').text(warningCount).show();
}
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,204 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/ExtensionCompatTester
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Install/Cache',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Cache, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorCheckTrigger: '.t3js-extensionCompatTester-check',
selectorUninstallTrigger: '.t3js-extensionCompatTester-uninstall',
selectorOutputContainer: '.t3js-extensionCompatTester-output',
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getLoadedExtensionList();
currentModal.on('click', this.selectorCheckTrigger, function(e) {
currentModal.find(self.selectorUninstallTrigger).hide();
currentModal.find(self.selectorOutputContainer).empty();
self.getLoadedExtensionList();
});
currentModal.on('click', this.selectorUninstallTrigger, function(e) {
self.uninstallExtension($(e.target).data('extension'));
});
},
getLoadedExtensionList: function () {
const self = this;
this.currentModal.find(this.selectorCheckTrigger).prop('disabled', true);
this.currentModal.find('.modal-loading').hide();
const modalContent = this.currentModal.find(this.selectorModalBody);
const $outputContainer = this.currentModal.find(this.selectorOutputContainer);
const message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.append(message);
$.ajax({
url: Router.getUrl('extensionCompatTesterLoadedExtensionList'),
cache: false,
success: function (data) {
modalContent.empty().append(data.html);
const $outputContainer = self.currentModal.find(self.selectorOutputContainer);
const progressBar = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.append(progressBar);
if (data.success === true) {
self.loadExtLocalconf().done(function() {
$outputContainer.append(
InfoBox.render(Severity.ok, 'ext_localconf.php of all loaded extensions successfully loaded', '')
);
self.loadExtTables().done(function() {
$outputContainer.append(
InfoBox.render(Severity.ok, 'ext_tables.php of all loaded extensions successfully loaded', '')
);
}).fail(function(xhr) {
self.renderFailureMessages('ext_tables.php', xhr.responseJSON.brokenExtensions, $outputContainer);
}).always(function() {
self.unlockModal();
})
}).fail(function(xhr) {
self.renderFailureMessages('ext_localconf.php', xhr.responseJSON.brokenExtensions, $outputContainer);
$outputContainer.append(
InfoBox.render(Severity.notice, 'Skipped scanning ext_tables.php files due to previous errors', '')
);
self.unlockModal();
});
} else {
Notification.error('Something went wrong');
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
unlockModal: function() {
this.currentModal.find(this.selectorOutputContainer).find('.alert-loading').remove();
this.currentModal.find(this.selectorCheckTrigger).prop('disabled', false);
},
renderFailureMessages: function(scope, brokenExtensions, $outputContainer) {
for (let i = 0; i < brokenExtensions.length; ++i) {
let extension = brokenExtensions[i];
let uninstallAction;
if (!extension.isProtected) {
uninstallAction = $('<button />', {'class': 'btn btn-danger t3js-extensionCompatTester-uninstall'})
.attr('data-extension', extension.name)
.text('Uninstall extension "' + extension.name + '"');
}
$outputContainer.append(
InfoBox.render(
Severity.error,
'Loading ' + scope + ' of extension "' + extension.name + '" failed',
(extension.isProtected ? 'Extension is mandatory and cannot be uninstalled.' : '')
),
uninstallAction
);
}
this.unlockModal();
},
loadExtLocalconf: function() {
var executeToken = this.currentModal.find(this.selectorModuleContent).data('extension-compat-tester-load-ext_localconf-token');
return $.ajax({
url: Router.getUrl(),
method: 'POST',
cache: false,
data: {
'install': {
'action': 'extensionCompatTesterLoadExtLocalconf',
'token': executeToken
}
}
});
},
loadExtTables: function() {
var executeToken = this.currentModal.find(this.selectorModuleContent).data('extension-compat-tester-load-ext_tables-token');
return $.ajax({
url: Router.getUrl(),
method: 'POST',
cache: false,
data: {
'install': {
'action': 'extensionCompatTesterLoadExtTables',
'token': executeToken
}
}
});
},
/**
* Send an ajax request to uninstall an extension (or multiple extensions)
*
* @param extension string of extension(s) - may be comma separated
*/
uninstallExtension: function(extension) {
var self = this;
var executeToken = this.currentModal.find(this.selectorModuleContent).data('extension-compat-tester-uninstall-extension-token');
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = $(this.selectorOutputContainer);
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.append(message);
$.ajax({
url: Router.getUrl(),
cache: false,
method: 'POST',
data: {
'install': {
'action': 'extensionCompatTesterUninstallExtension',
'token': executeToken,
'extension': extension
}
},
success: function(data) {
if (data.success) {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
modalContent.find(self.selectorOutputContainer).empty().append(message);
});
}
$(self.selectorUninstallTrigger).hide();
//Cache.clearAll();
self.getLoadedExtensionList();
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,236 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/ExtensionConfiguration
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'TYPO3/CMS/Backend/ModuleMenu',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification, ModuleMenu) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorFormListener: '.t3js-extensionConfiguration-form',
selectorOutputContainer: '.t3js-extensionConfiguration-output',
selectorSearchInput: '.t3js-extensionConfiguration-search',
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getContent();
// Focus search field on certain user interactions
currentModal.on('keydown', function(e) {
var $searchInput = currentModal.find(self.selectorSearchInput);
if (e.ctrlKey || e.metaKey) {
// Focus search field on ctrl-f
switch (String.fromCharCode(e.which).toLowerCase()) {
case 'f':
e.preventDefault();
$searchInput.focus();
break;
}
} else if (e.keyCode === 27) {
// Clear search on ESC key
e.preventDefault();
$searchInput.val('').focus();
}
});
// Perform expand collapse on search matches
currentModal.on('keyup', this.selectorSearchInput, function(e) {
var typedQuery = $(e.target).val();
var $searchInput = currentModal.find(self.selectorSearchInput);
currentModal.find('.search-item').each(function() {
var $item = $(this);
if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
$item.removeClass('hidden').addClass('searchhit');
} else {
$item.removeClass('searchhit').addClass('hidden');
}
});
currentModal.find('.searchhit').collapse('show');
// Make search field clearable
require(['jquery.clearable'], function() {
$searchInput.clearable().focus();
});
});
currentModal.on('submit', this.selectorFormListener, function(e) {
e.preventDefault();
self.write($(this));
});
},
getContent: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('extensionConfigurationGetContent'),
cache: false,
success: function(data) {
if (data.success === true) {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.success(element.title, element.message);
});
}
modalContent.html(data.html);
self.initializeWrap();
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
/**
* Submit the form and show the result message
*
* @param {jQuery} $form The form of the current extension
*/
write: function($form) {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('extension-configuration-write-token');
var extensionConfiguration = {};
$.each($form.serializeArray(), function() {
extensionConfiguration[this.name] = this.value;
});
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'token': executeToken,
'action': 'extensionConfigurationWrite',
'extensionKey': $form.attr('data-extensionKey'),
'extensionConfiguration': extensionConfiguration
}
},
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.showMessage(element.title, element.message, element.severity);
});
if ($('body').data('context') === 'backend') {
ModuleMenu.App.refreshMenu();
}
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
}).always(function() {
});
},
/**
* configuration properties
*/
initializeWrap: function() {
this.currentModal.find('.t3js-emconf-offset').each(function() {
var $me = $(this);
var $parent = $me.parent();
var id = $me.attr('id');
var val = $me.attr('value');
var valArr = val.split(',');
$me
.attr('data-offsetfield-x', '#' + id + '_offset_x')
.attr('data-offsetfield-y', '#' + id + '_offset_y')
.wrap('<div class="hidden"></div>');
var elementX = $('<div>', {'class': 'form-multigroup-item'}).append(
$('<div>', {'class': 'input-group'}).append(
$('<div>', {'class': 'input-group-addon'}).text('x'),
$('<input>', {
'id': id + '_offset_x',
'class': 'form-control t3js-emconf-offsetfield',
'data-target': '#' + id,
'value': $.trim(valArr[0])
})
)
);
var elementY = $('<div>', {'class': 'form-multigroup-item'}).append(
$('<div>', {'class': 'input-group'}).append(
$('<div>', {'class': 'input-group-addon'}).text('y'),
$('<input>', {
'id': id + '_offset_y',
'class': 'form-control t3js-emconf-offsetfield',
'data-target': '#' + id,
'value': $.trim(valArr[1])
})
)
);
var offsetGroup = $('<div>', {'class': 'form-multigroup-wrap'}).append(elementX, elementY);
$parent.append(offsetGroup);
$parent.find('.t3js-emconf-offsetfield').keyup(function() {
var $target = $parent.find($(this).data('target'));
$target.val($parent.find($target.data('offsetfield-x')).val() + ',' + $parent.find($target.data('offsetfield-y')).val());
});
});
this.currentModal.find('.t3js-emconf-wrap').each(function() {
var $me = $(this),
$parent = $me.parent(),
id = $me.attr('id'),
val = $me.attr('value'),
valArr = val.split('|');
$me.attr('data-wrapfield-start', '#' + id + '_wrap_start')
.attr('data-wrapfield-end', '#' + id + '_wrap_end')
.wrap('<div class="hidden"></div>');
var wrapGroup = $('<div>', {'class': 'form-multigroup-wrap'}).append(
$('<div>', {'class': 'form-multigroup-item'}).append(
$('<input>', {
'id': id + '_wrap_start',
'class': 'form-control t3js-emconf-wrapfield',
'data-target': '#' + id,
'value': $.trim(valArr[0])
})
),
$('<div>', {'class': 'form-multigroup-item'}).append(
$('<input>', {
'id': id + '_wrap_end',
'class': 'form-control t3js-emconf-wrapfield',
'data-target': '#' + id,
'value': $.trim(valArr[1])
})
)
);
$parent.append(wrapGroup);
$parent.find('.t3js-emconf-wrapfield').keyup(function() {
var $target = $parent.find($(this).data('target'));
$target.val($parent.find($target.data('wrapfield-start')).val() + '|' + $parent.find($target.data('wrapfield-end')).val());
});
});
}
};
});
@@ -0,0 +1,339 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/ExtensionScanner
*/
define(['jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Backend/Notification',
'TYPO3/CMS/Install/AjaxQueue'
], function($, Router, Notification, AjaxQueue) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
listOfAffectedRestFileHashes: [],
selectorExtensionContainer: '.t3js-extensionScanner-extension',
selectorNumberOfFiles: '.t3js-extensionScanner-number-of-files',
selectorScanSingleTrigger: '.t3js-extensionScanner-scan-single',
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getData();
currentModal.on('show.bs.collapse', self.selectorExtensionContainer, function(e) {
// Scan a single extension by opening the panel
var $me = $(e.currentTarget);
if (typeof $me.data('scanned') === 'undefined') {
var extension = $me.data('extension');
self.scanSingleExtension(extension);
$me.data('scanned', true);
}
}).on('hide.bs.modal', function() {
AjaxQueue.flush();
}).on('click', self.selectorScanSingleTrigger, function(e) {
// Scan a single extension by clicking "Rescan"
e.preventDefault();
var extension = $(e.currentTarget).closest(self.selectorExtensionContainer).data('extension');
self.scanSingleExtension(extension);
}).on('click', '.t3js-extensionScanner-scan-all', function(e) {
// Scan all button
e.preventDefault();
var $extensions = currentModal.find(self.selectorExtensionContainer);
self.scanAll($extensions);
});
},
getData: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('extensionScannerGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
/**
* @param {string} extension
* @returns {string}
*/
getExtensionSelector: function(extension) {
return this.selectorExtensionContainer + '-' + extension;
},
/**
* @param {JQuery} $extensions
*/
scanAll: function($extensions) {
var self = this;
this.currentModal.find(this.selectorExtensionContainer)
.removeClass('panel-danger panel-warning panel-success')
.find('.panel-progress-bar')
.css('width', 0)
.attr('aria-valuenow', 0)
.find('span')
.text('0%');
this.setProgressForAll();
$extensions.each(function() {
var $me = $(this);
var extension = $me.data('extension');
self.scanSingleExtension(extension);
$me.data('scanned', true);
});
},
/**
* @param {string} extension
* @param {number} doneFiles
* @param {number} numberOfFiles
*/
setStatusMessageForScan: function(extension, doneFiles, numberOfFiles) {
this.currentModal.find(this.getExtensionSelector(extension))
.find(this.selectorNumberOfFiles)
.text('Checked ' + doneFiles + ' of ' + numberOfFiles + ' files');
},
/**
* @param {string} extension
* @param {number} doneFiles
* @param {number} numberOfFiles
*/
setProgressForScan: function(extension, doneFiles, numberOfFiles) {
var percent = (doneFiles / numberOfFiles) * 100;
this.currentModal.find(this.getExtensionSelector(extension))
.find('.panel-progress-bar')
.css('width', percent + '%')
.attr('aria-valuenow', percent)
.find('span')
.text(percent + '%');
},
/**
* Update main progress bar
*/
setProgressForAll: function() {
var self = this;
// var numberOfExtensions = $(this.selectorExtensionContainer).length;
var numberOfExtensions = this.currentModal.find(this.selectorExtensionContainer).length;
var numberOfSuccess = this.currentModal.find(this.selectorExtensionContainer + '.t3js-extensionscan-finished.panel-success').length;
var numberOfWarning = this.currentModal.find(this.selectorExtensionContainer + '.t3js-extensionscan-finished.panel-warning').length;
var numberOfError = this.currentModal.find(this.selectorExtensionContainer + '.t3js-extensionscan-finished.panel-danger').length;
var numberOfScannedExtensions = numberOfSuccess + numberOfWarning + numberOfError;
var percent = (numberOfScannedExtensions / numberOfExtensions) * 100;
var modalContent = this.currentModal.find(this.selectorModalBody);
this.currentModal.find('.t3js-extensionScanner-progress-all-extension .progress-bar')
.css('width', percent + '%')
.attr('aria-valuenow', percent)
.find('span')
.text(numberOfScannedExtensions + ' of ' + numberOfExtensions + ' scanned');
if (numberOfScannedExtensions === numberOfExtensions) {
Notification.success('Scan finished', 'All extensions have been scanned');
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'extensionScannerMarkFullyScannedRestFiles',
'token': self.currentModal.find(self.selectorModuleContent).data('extension-scanner-mark-fully-scanned-rest-files-token'),
'hashes': self.uniqueArray(this.listOfAffectedRestFileHashes)
}
},
cache: false,
success: function(data) {
if (data.success === true) {
Notification.success('Marked not affected files', 'Marked ' + data.markedAsNotAffected + ' ReST files as not affected.');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
},
/**
* Helper method removing duplicate entries from an array
*
* @param {Array} anArray
* @returns {Array}
*/
uniqueArray: function(anArray) {
return anArray.filter(function(value, index, self) {
return self.indexOf(value) === index;
});
},
/**
* Handle a single extension scan
*
* @param {string} extension
*/
scanSingleExtension: function(extension) {
var self = this;
var executeToken = this.currentModal.find(this.selectorModuleContent).data('extension-scanner-files-token');
var modalContent = this.currentModal.find(this.selectorModalBody);
var $extensionContainer = this.currentModal.find(this.getExtensionSelector(extension));
var hitTemplate = '#t3js-extensionScanner-file-hit-template';
var restTemplate = '#t3js-extensionScanner-file-hit-rest-template';
var hitFound = false;
$extensionContainer.removeClass('panel-danger panel-warning panel-success t3js-extensionscan-finished');
$extensionContainer.data('hasRun', 'true');
$extensionContainer.find('.t3js-extensionScanner-scan-single').text('Scanning...').attr('disabled', 'disabled');
$extensionContainer.find('.t3js-extensionScanner-extension-body-loc').empty().text('0');
$extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text('0');
$extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty().text('0');
this.setProgressForAll();
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'extensionScannerFiles',
'token': executeToken,
'extension': extension
}
},
cache: false,
success: function(data) {
if (data.success === true && Array.isArray(data.files)) {
var numberOfFiles = data.files.length;
if (numberOfFiles > 0) {
self.setStatusMessageForScan(extension, 0, numberOfFiles);
$extensionContainer.find('.t3js-extensionScanner-extension-body').text('');
var doneFiles = 0;
data.files.forEach(function(file) {
AjaxQueue.add(
{
method: 'POST',
data: {
'install': {
'action': 'extensionScannerScanFile',
'token': self.currentModal.find(self.selectorModuleContent).data('extension-scanner-scan-file-token'),
'extension': extension,
'file': file
}
},
url: Router.getUrl(),
cache: false,
success: function(fileData) {
doneFiles = doneFiles + 1;
self.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
self.setProgressForScan(extension, doneFiles, numberOfFiles);
if (fileData.success && $.isArray(fileData.matches)) {
$(fileData.matches).each(function() {
hitFound = true;
var match = this;
var aMatch = modalContent.find(hitTemplate).clone();
aMatch.find('.t3js-extensionScanner-hit-file-panel-head').attr('href', '#collapse' + match.uniqueId);
aMatch.find('.t3js-extensionScanner-hit-file-panel-body').attr('id', 'collapse' + match.uniqueId);
aMatch.find('.t3js-extensionScanner-hit-filename').text(file);
aMatch.find('.t3js-extensionScanner-hit-message').text(match.message);
if (match.indicator === 'strong') {
aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
.append('<span class="badge" title="Reliable match, false positive unlikely">strong</span>');
} else {
aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
.append('<span class="badge" title="Probable match, but can be a false positive">weak</span>');
}
if (match.silenced === true) {
aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
.append('<span class="badge" title="Match has been annotated by extension author as false positive match">silenced</span>');
}
aMatch.find('.t3js-extensionScanner-hit-file-lineContent').empty().text(match.lineContent);
aMatch.find('.t3js-extensionScanner-hit-file-line').empty().text(match.line + ': ');
if ($.isArray(match.restFiles)) {
$(match.restFiles).each(function() {
var restFile = this;
var aRest = modalContent.find(restTemplate).clone();
aRest.find('.t3js-extensionScanner-hit-rest-panel-head').attr('href', '#collapse' + restFile.uniqueId);
aRest.find('.t3js-extensionScanner-hit-rest-panel-head .badge').empty().text(restFile.version);
aRest.find('.t3js-extensionScanner-hit-rest-panel-body').attr('id', 'collapse' + restFile.uniqueId);
aRest.find('.t3js-extensionScanner-hit-rest-headline').text(restFile.headline);
aRest.find('.t3js-extensionScanner-hit-rest-body').text(restFile.content);
aRest.addClass('panel-' + restFile.class);
aMatch.find('.t3js-extensionScanner-hit-file-rest-container').append(aRest);
self.listOfAffectedRestFileHashes.push(restFile.file_hash);
});
}
var panelClass =
aMatch.find('.panel-breaking', '.t3js-extensionScanner-hit-file-rest-container').length > 0
? 'panel-danger'
: 'panel-warning';
aMatch.addClass(panelClass);
$extensionContainer.find('.t3js-extensionScanner-extension-body').removeClass('hide').append(aMatch);
if (panelClass === 'panel-danger') {
$extensionContainer.removeClass('panel-warning').addClass(panelClass);
}
if (panelClass === 'panel-warning' && !$extensionContainer.hasClass('panel-danger')) {
$extensionContainer.addClass(panelClass);
}
});
}
if (fileData.success) {
var currentLinesOfCode = parseInt($extensionContainer.find('.t3js-extensionScanner-extension-body-loc').text());
$extensionContainer.find('.t3js-extensionScanner-extension-body-loc').empty().text(currentLinesOfCode + parseInt(fileData.effectiveCodeLines));
if (fileData.isFileIgnored) {
var currentIgnoredFiles = parseInt($extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').text());
$extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text(currentIgnoredFiles + 1);
}
var currentIgnoredLines = parseInt($extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').text());
$extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty().text(currentIgnoredLines + parseInt(fileData.ignoredLines));
}
if (doneFiles === numberOfFiles) {
if (!hitFound) {
$extensionContainer.addClass('panel-success');
}
$extensionContainer.addClass('t3js-extensionscan-finished');
self.setProgressForAll();
$extensionContainer.find('.t3js-extensionScanner-scan-single').text('Rescan').attr('disabled', null);
}
},
error: function(data) {
doneFiles = doneFiles + 1;
self.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
self.setProgressForScan(extension, doneFiles, numberOfFiles);
self.setProgressForAll();
console.error(data);
}
}
);
});
} else {
Notification.warning('No files found', 'The extension EXT:' + extension + ' contains no files we can scan');
}
} else {
Notification.error('Oops, an error occurred', 'Please look at the browser console output for details');
console.error(data);
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,88 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/Features
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-features-content',
selectorSaveTrigger: '.t3js-features-save',
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getContent();
currentModal.on('click', this.selectorSaveTrigger, function(e) {
e.preventDefault();
self.save();
});
},
getContent: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('featuresGetContent'),
cache: false,
success: function(data) {
if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
save: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('features-save-token');
var postData = {};
$(this.currentModal.find(this.selectorModuleContent + ' form').serializeArray()).each(function() {
postData[this.name] = this.value;
});
postData['install[action]'] = 'featuresSave';
postData['install[token]'] = executeToken;
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: postData,
cache: false,
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.showMessage(element.title, element.message, element.severity);
});
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,49 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/FlashMessage
*/
define(['jquery', 'TYPO3/CMS/Install/Severity'], function($, Severity) {
'use strict';
/**
* @type {{template: (*)}}
*/
var FlashMessage = {
template: $('<div class="t3js-message typo3-message alert"><h4></h4><p class="messageText"></p></div>')
};
/**
* render a FlashMessage
* @param {Number} severity
* @param {String} title
* @param {String} message
* @returns {jQuery}
*/
FlashMessage.render = function(severity, title, message) {
var flashMessage = this.template.clone();
flashMessage.addClass('alert-' + Severity.getCssClass(severity));
if (title) {
flashMessage.find('h4').text(title);
}
if (message) {
flashMessage.find('.messageText').text(message);
} else {
flashMessage.find('.messageText').remove();
}
return flashMessage;
};
return FlashMessage;
});
@@ -0,0 +1,142 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/FolderStructure
*/
define(['jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorGridderBadge: '.t3js-folderStructure-badge',
selectorFixTrigger: '.t3js-folderStructure-errors-fix',
selectorOutputContainer: '.t3js-folderStructure-output',
selectorErrorContainer: '.t3js-folderStructure-errors',
selectorErrorList: '.t3js-folderStructure-errors-list',
selectorErrorFixTrigger: '.t3js-folderStructure-errors-fix',
selectorOkContainer: '.t3js-folderStructure-ok',
selectorOkList: '.t3js-folderStructure-ok-list',
selectorPermissionContainer: '.t3js-folderStructure-permissions',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
// Get status on initialize to have the badge and content ready
this.getStatus();
currentModal.on('click', this.selectorErrorFixTrigger, function(e) {
e.preventDefault();
self.fix();
});
},
getStatus: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $errorBadge = $(this.selectorGridderBadge);
$errorBadge.text('').hide();
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
modalContent.find(self.selectorOutputContainer).empty().append(message);
$.ajax({
url: Router.getUrl('folderStructureGetStatus'),
cache: false,
success: function(data) {
modalContent.empty().append(data.html);
if (data.success === true && Array.isArray(data.errorStatus)) {
var errorCount = 0;
if (data.errorStatus.length > 0) {
modalContent.find(self.selectorErrorContainer).show();
modalContent.find(self.selectorErrorList).empty();
data.errorStatus.forEach((function(element) {
errorCount += 1;
$errorBadge.text(errorCount).show();
var message = InfoBox.render(element.severity, element.title, element.message);
modalContent.find(self.selectorErrorList).append(message);
}));
} else {
modalContent.find(self.selectorErrorContainer).hide();
}
}
if (data.success === true && Array.isArray(data.okStatus)) {
if (data.okStatus.length > 0) {
modalContent.find(self.selectorOkContainer).show();
modalContent.find(self.selectorOkList).empty();
data.okStatus.forEach((function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
modalContent.find(self.selectorOkList).append(message);
}));
} else {
modalContent.find(self.selectorOkContainer).hide();
}
}
var element = data.folderStructureFilePermissionStatus;
message = InfoBox.render(element.severity, element.title, element.message);
modalContent.find(self.selectorPermissionContainer).empty().append(message);
element = data.folderStructureDirectoryPermissionStatus;
message = InfoBox.render(element.severity, element.title, element.message);
modalContent.find(self.selectorPermissionContainer).append(message);
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
fix: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputContainer);
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.empty().html(message);
$.ajax({
url: Router.getUrl('folderStructureFix'),
cache: false,
success: function(data) {
self.removeLoadingMessage($outputContainer);
if (data.success === true && Array.isArray(data.fixedStatus)) {
if (data.fixedStatus.length > 0) {
data.fixedStatus.forEach(function(element) {
message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.append(message);
});
} else {
message = InfoBox.render(Severity.warning, 'Nothing fixed', '');
$outputContainer.append(message);
}
self.getStatus();
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
removeLoadingMessage: function($container) {
$container.find('.alert-loading').remove();
}
};
});
@@ -0,0 +1,118 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/ImageProcessing
*/
define(['jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorExecuteTrigger: '.t3js-imageProcessing-execute',
selectorTestContainer: '.t3js-imageProcessing-twinContainer',
selectorTwinImageTemplate: '.t3js-imageProcessing-twinImage-template',
selectorCommandContainer: '.t3js-imageProcessing-command',
selectorCommandText: '.t3js-imageProcessing-command-text',
selectorTwinImages: '.t3js-imageProcessing-images',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getData();
currentModal.on('click', this.selectorExecuteTrigger, function(e) {
e.preventDefault();
self.runTests();
});
},
getData: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('imageProcessingGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
self.runTests();
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
runTests: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $twinImageTemplate = this.currentModal.find(this.selectorTwinImageTemplate);
modalContent.find(this.selectorTestContainer).each(function() {
var $container = $(this);
var testType = $container.data('test');
var message = InfoBox.render(Severity.loading, 'Loading...', '');
$container.empty().html(message);
$.ajax({
url: Router.getUrl(testType),
cache: false,
success: function(data) {
if (data.success === true) {
$container.empty();
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$container.append(message);
});
}
var $aTwin = $twinImageTemplate.clone();
$aTwin.removeClass('t3js-imageProcessing-twinImage-template');
if (data.fileExists === true) {
$aTwin.find('img.reference').attr('src', data.referenceFile);
$aTwin.find('img.result').attr('src', data.outputFile);
$aTwin.find(self.selectorTwinImages).show();
}
if (Array.isArray(data.command) && data.command.length > 0) {
$aTwin.find(self.selectorCommandContainer).show();
var commandText = [];
data.command.forEach(function(element) {
commandText.push('<strong>Command:</strong>\n' + element[1]);
if (element.length === 3) {
commandText.push('<strong>Result:</strong>\n' + element[2]);
}
});
$aTwin.find(self.selectorCommandText).html(commandText.join('\n'));
}
$container.append($aTwin);
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
});
}
};
});
@@ -0,0 +1,49 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/InfoBox
*/
define(['jquery', 'TYPO3/CMS/Install/Severity'], function($, Severity) {
'use strict';
/**
* @type {{template: (*)}}
*/
var InfoBox = {
template: $('<div class="t3js-infobox callout callout-sm"><h4 class="callout-title"></h4><div class="callout-body"></div></div>')
};
/**
* render a InfoBox
* @param {Number} severity
* @param {String} title
* @param {String} message
* @returns {jQuery}
*/
InfoBox.render = function(severity, title, message) {
var infoBox = this.template.clone();
infoBox.addClass('callout-' + Severity.getCssClass(severity));
if (title) {
infoBox.find('h4').text(title);
}
if (message) {
infoBox.find('.callout-body').text(message);
} else {
infoBox.find('.callout-body').remove();
}
return infoBox;
};
return InfoBox;
});
@@ -0,0 +1,528 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/LanguagePacks
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Core/SecurityUtility',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, SecurityUtility) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorOutputContainer: '.t3js-languagePacks-output',
selectorContentContainer: '.t3js-languagePacks-mainContent',
selectorActivateLanguage: '.t3js-languagePacks-activateLanguage',
selectorActivateLanguageIcon: '#t3js-languagePacks-activate-icon',
selectorAddLanguageToggle: '.t3js-languagePacks-addLanguage-toggle',
selectorLanguageInactive: '.t3js-languagePacks-inactive',
selectorDeactivateLanguage: '.t3js-languagePacks-deactivateLanguage',
selectorDeactivateLanguageIcon: '#t3js-languagePacks-deactivate-icon',
selectorUpdate: '.t3js-languagePacks-update',
selectorLanguageUpdateIcon: '#t3js-languagePacks-languageUpdate-icon',
selectorNotifications: '.t3js-languagePacks-notifications',
currentModal: {},
activeLanguages: [],
activeExtensions: [],
packsUpdateDetails: {
toHandle: 0,
handled: 0,
updated: 0,
new: 0,
failed: 0
},
notifications: [],
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
// Get configuration list on modal open
this.getData();
currentModal.on('click', this.selectorAddLanguageToggle, function(e) {
currentModal.find(self.selectorContentContainer + ' ' + self.selectorLanguageInactive).toggle();
});
currentModal.on('click', this.selectorActivateLanguage, function(e) {
var iso = $(e.target).closest(self.selectorActivateLanguage).data('iso');
e.preventDefault();
self.activateLanguage(iso);
});
currentModal.on('click', this.selectorDeactivateLanguage, function(e) {
var iso = $(e.target).closest(self.selectorDeactivateLanguage).data('iso');
e.preventDefault();
self.deactivateLanguage(iso);
});
currentModal.on('click', this.selectorUpdate, function(e) {
var iso = $(e.target).closest(self.selectorUpdate).data('iso');
var extension = $(e.target).closest(self.selectorUpdate).data('extension');
e.preventDefault();
self.updatePacks(iso, extension);
});
},
getData: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('languagePacksGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
self.activeLanguages = data.activeLanguages;
self.activeExtensions = data.activeExtensions;
modalContent.empty().append(data.html);
var contentContainer = modalContent.parent().find(self.selectorContentContainer);
contentContainer.empty();
contentContainer.append(self.languageMatrixHtml(data));
contentContainer.append(self.extensionMatrixHtml(data));
$('[data-toggle="tooltip"]').tooltip({container: contentContainer});
} else {
var message = InfoBox.render(Severity.error, 'Something went wrong', '');
self.addNotification(message);
}
self.renderNotifications();
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
activateLanguage: function(iso) {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputContainer);
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.empty().append(message);
$.ajax({
url: Router.getUrl(),
method: 'POST',
context: this,
data: {
'install': {
'action': 'languagePacksActivateLanguage',
'token': this.currentModal.find(this.selectorModuleContent).data('language-packs-activate-language-token'),
'iso': iso
}
},
cache: false,
beforeSend: function() {
self.getNotificationBox().empty();
},
success: function(data) {
$outputContainer.empty();
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
self.addNotification(message);
});
} else {
var message = FlashMessage.render(Severity.error, 'Something went wrong', '');
self.addNotification(message);
}
this.getData();
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
deactivateLanguage: function(iso) {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputContainer);
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.empty().append(message);
$.ajax({
url: Router.getUrl(),
method: 'POST',
context: this,
data: {
'install': {
'action': 'languagePacksDeactivateLanguage',
'token': self.currentModal.find(self.selectorModuleContent).data('language-packs-deactivate-language-token'),
'iso': iso
}
},
cache: false,
beforeSend: function() {
self.getNotificationBox().empty();
},
success: function(data) {
$outputContainer.empty();
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
self.addNotification(message);
});
} else {
var message = FlashMessage.render(Severity.error, 'Something went wrong', '');
self.addNotification(message);
}
this.getData();
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
updatePacks: function(iso, extension) {
var self = this;
var $outputContainer = this.currentModal.find(this.selectorOutputContainer);
var $contentContainer = this.currentModal.find(this.selectorContentContainer);
var updateIsoTimes = true;
var isos = iso === undefined ? this.activeLanguages : [ iso ];
var extensions = this.activeExtensions;
if (extension !== undefined) {
extensions = [ extension ];
updateIsoTimes = false;
}
this.packsUpdateDetails = {
toHandle: isos.length * extensions.length,
handled: 0,
updated: 0,
new: 0,
failed: 0
};
$outputContainer.empty().append(
$('<div>', {'class': 'progress'}).append(
$('<div>', {
'class': 'progress-bar progress-bar-info',
'role': 'progressbar',
'aria-valuenow': 0,
'aria-valuemin': 0,
'aria-valuemax': 100,
'style': 'width: 0;'
}).append(
$('<span>', {'class': 'text-nowrap'}).text('0 of ' + this.packsUpdateDetails.toHandle + ' language ' +
this.pluralize(this.packsUpdateDetails.toHandle) + ' updated')
)
));
$contentContainer.empty();
isos.forEach(function(iso) {
extensions.forEach(function(extension) {
$.ajax({
url: Router.getUrl(),
method: 'POST',
context: this,
data: {
'install': {
'action': 'languagePacksUpdatePack',
'token': self.currentModal.find(self.selectorModuleContent).data('language-packs-update-pack-token'),
'iso': iso,
'extension': extension
}
},
cache: false,
beforeSend: function() {
self.getNotificationBox().empty();
},
success: function(data) {
if (data.success === true) {
self.packsUpdateDetails.handled++;
if (data.packResult === 'new') {
self.packsUpdateDetails.new++;
} else if (data.packResult === 'update') {
self.packsUpdateDetails.updated++;
} else {
self.packsUpdateDetails.failed++;
}
self.packUpdateDone(updateIsoTimes, isos);
} else {
self.packsUpdateDetails.handled++;
self.packsUpdateDetails.failed++;
self.packUpdateDone(updateIsoTimes, isos);
}
},
error: function(xhr) {
self.packsUpdateDetails.handled++;
self.packsUpdateDetails.failed++;
self.packUpdateDone(updateIsoTimes, isos);
}
});
});
});
},
pluralize: function(count, word, suffix, additionalCount) {
word = word || 'pack';
suffix = suffix || 's';
additionalCount = additionalCount || 0;
return count !== 1 && additionalCount !== 1 ? word + suffix : word;
},
packUpdateDone: function(updateIsoTimes, isos) {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputContainer);
if (this.packsUpdateDetails.handled === this.packsUpdateDetails.toHandle) {
// All done - create summary, update 'last update' of iso list, render main view
var message = InfoBox.render(
Severity.ok,
'Language packs updated',
this.packsUpdateDetails.new + ' new language ' + this.pluralize(this.packsUpdateDetails.new) + ' downloaded, ' +
this.packsUpdateDetails.updated + ' language ' + this.pluralize(this.packsUpdateDetails.updated) + ' updated, ' +
this.packsUpdateDetails.failed + ' language ' + this.pluralize(this.packsUpdateDetails.failed) + ' not available'
);
this.addNotification(message);
if (updateIsoTimes === true) {
$.ajax({
url: Router.getUrl(),
method: 'POST',
context: this,
data: {
'install': {
'action': 'languagePacksUpdateIsoTimes',
'token': this.currentModal.find(this.selectorModuleContent).data('language-packs-update-iso-times-token'),
'isos': isos
}
},
cache: false,
success: function(data) {
if (data.success === true) {
self.getData();
} else {
var message = FlashMessage.render(Severity.error, 'Something went wrong', '');
self.addNotification(message);
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
} else {
this.getData();
}
} else {
// Update progress bar
var percent = (this.packsUpdateDetails.handled / this.packsUpdateDetails.toHandle) * 100;
$outputContainer.find('.progress-bar')
.css('width', percent + '%')
.attr('aria-valuenow', percent)
.find('span')
.text(this.packsUpdateDetails.handled + ' of ' + this.packsUpdateDetails.toHandle + ' language ' +
this.pluralize(this.packsUpdateDetails.handled, 'pack', 's', this.packsUpdateDetails.toHandle) + ' updated');
}
},
languageMatrixHtml: function(data) {
var activateIcon = this.currentModal.find(this.selectorActivateLanguageIcon).html();
var deactivateIcon = this.currentModal.find(this.selectorDeactivateLanguageIcon).html();
var updateIcon = this.currentModal.find(this.selectorLanguageUpdateIcon).html();
var $markupContainer = $('<div>');
var $tbody = $('<tbody>');
data.languages.forEach(function(language) {
var active = language.active;
var $tr = $('<tr>');
if (active) {
$tbody.append(
$tr.append(
$('<td>').append(
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-deactivateLanguage',
'data-iso': language.iso,
'data-toggle': 'tooltip',
'title': 'Deactivate'
}).append(deactivateIcon),
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-update',
'data-iso': language.iso,
'data-toggle': 'tooltip',
'title': 'Download language packs'
}).append(updateIcon)
)
)
);
} else {
$tbody.append(
$tr.addClass('t3-languagePacks-inactive t3js-languagePacks-inactive').css({'display': 'none'}).append(
$('<td>').append(
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-activateLanguage',
'data-iso': language.iso,
'data-toggle': 'tooltip',
'title': 'Activate'
}).append(activateIcon)
)
)
);
}
$tr.append(
$('<td>').text(language.name),
$('<td>').text(language.iso),
$('<td>').text(language.dependencies.join(', ')),
$('<td>').text(language.lastUpdate === null ? '' : language.lastUpdate)
);
$tbody.append($tr);
});
$markupContainer.append(
$('<h3>').text('Active languages'),
$('<table>', {'class': 'table table-striped table-bordered'}).append(
$('<thead>').append(
$('<tr>').append(
$('<th>').append(
$('<button>', {'class': 'btn btn-default t3js-languagePacks-addLanguage-toggle', 'type': 'button'}).append(
$('<span>').append(activateIcon),
' Add language'
),
$('<button>', {'class': 'btn btn-default disabled update-all t3js-languagePacks-update', 'type': 'button', 'disabled': 'disabled'}).append(
$('<span>').append(updateIcon),
' Update all'
)
),
$('<th>').text('Language'),
$('<th>').text('Locale'),
$('<th>').text('Dependencies'),
$('<th>').text('Last update')
)
),
$tbody
)
);
if (Array.isArray(this.activeLanguages) && this.activeLanguages.length) {
$markupContainer.find('.update-all').removeClass('disabled').removeAttr('disabled');
}
return $markupContainer.html();
},
extensionMatrixHtml: function(data) {
var securityUtility = new SecurityUtility();
var updateIcon = this.currentModal.find(this.selectorLanguageUpdateIcon).html();
var tooltip = '';
var extensionTitle = '';
var rowCount = 0;
var $markupContainer = $('<div>');
var $headerRow = $('<tr>');
$headerRow.append(
$('<th>').text('Extension'),
$('<th>').text('Key')
);
data.activeLanguages.forEach(function(language) {
$headerRow.append(
$('<th>').append(
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-update',
'data-iso': language,
'data-toggle': 'tooltip',
'title': 'Download and update all language packs'
}).append(
$('<span>').append(updateIcon),
' ' + language
)
)
)
});
var $tbody = $('<tbody>');
data.extensions.forEach(function(extension) {
rowCount++;
if (typeof extension.icon !== 'undefined') {
extensionTitle = $('<span>').append(
$('<img>', {
'style': 'max-height: 16px; max-width: 16px;',
'src': '../' + extension.icon,
'alt': extension.title
}),
$('<span>').text(' ' + extension.title)
);
} else {
extensionTitle = $('<span>').text(extension.title)
}
var $tr = $('<tr>');
$tr.append(
$('<td>').html(extensionTitle),
$('<td>').text(extension.key)
);
extension.packs.forEach(function(pack) {
var $column = $('<td>');
$tr.append($column);
if (pack.exists !== true) {
if (pack.lastUpdate !== null) {
tooltip = 'No language pack available for ' + pack.iso + ' when tried at ' + pack.lastUpdate + '. Click to re-try.';
} else {
tooltip = 'Language pack not downloaded. Click to download';
}
} else {
if (pack.lastUpdate === null) {
tooltip = 'Downloaded. Click to renew';
} else {
tooltip = 'Language pack downloaded at ' + pack.lastUpdate + '. Click to renew';
}
}
$column.append(
$('<a>', {
'class': 'btn btn-default t3js-languagePacks-update',
'data-extension': extension.key,
'data-iso': pack.iso,
'data-toggle': 'tooltip',
'title': securityUtility.encodeHtml(tooltip),
}).append(updateIcon)
);
});
$tbody.append($tr);
});
$markupContainer.append(
$('<h3>').text('Translation status'),
$('<table>', {'class': 'table table-striped table-bordered'}).append(
$('<thead>').append($headerRow),
$tbody
)
);
if (rowCount === 0) {
return InfoBox.render(Severity.ok, 'Language packs have been found for every installed extension.', 'To download the latest changes, use the refresh button in the list above.');
}
return $markupContainer.html();
},
getNotificationBox: function() {
return this.currentModal.find(this.selectorNotifications);
},
addNotification: function(notification) {
this.notifications.push(notification);
},
renderNotifications: function() {
var $notificationBox = this.getNotificationBox();
for (var i = 0; i < this.notifications.length; ++i) {
$notificationBox.append(this.notifications[i]);
}
this.notifications = [];
}
};
});
@@ -0,0 +1,164 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/LocalConfiguration
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorToggleAllTrigger: '.t3js-localConfiguration-toggleAll',
selectorWriteTrigger: '.t3js-localConfiguration-write',
selectorSearchTrigger: '.t3js-localConfiguration-search',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getContent();
// Write out new settings
currentModal.on('click', this.selectorWriteTrigger, function() {
self.write();
});
// Expand / collapse "Toggle all" button
currentModal.on('click', this.selectorToggleAllTrigger, function() {
var modalContent = self.currentModal.find(self.selectorModalBody);
var panels = modalContent.find('.panel-collapse');
var action = (panels.eq(0).hasClass('in')) ? 'hide' : 'show';
panels.collapse(action);
});
// Make jquerys "contains" work case-insensitive
jQuery.expr[':'].contains = jQuery.expr.createPseudo(function(arg) {
return function(elem) {
return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
// Focus search field on certain user interactions
currentModal.on('keydown', function(e) {
var $searchInput = currentModal.find(self.selectorSearchTrigger);
if (e.ctrlKey || e.metaKey) {
// Focus search field on ctrl-f
switch (String.fromCharCode(e.which).toLowerCase()) {
case 'f':
e.preventDefault();
$searchInput.focus();
break;
}
} else if (e.keyCode === 27) {
// Clear search on ESC key
e.preventDefault();
$searchInput.val('').focus();
}
});
// Perform expand collapse on search matches
currentModal.on('keyup', this.selectorSearchTrigger, function(e) {
var typedQuery = $(e.target).val();
var $searchInput = currentModal.find((self.selectorSearchTrigger));
currentModal.find('div.item').each(function() {
var $item = $(this);
if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
$item.removeClass('hidden').addClass('searchhit');
} else {
$item.removeClass('searchhit').addClass('hidden');
}
});
currentModal.find('.searchhit').parent().collapse('show');
// Make search field clearable
require(['jquery.clearable'], function() {
var searchResultShown = ('' !== $searchInput.first().val());
$searchInput.clearable().focus();
});
});
},
getContent: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('localConfigurationGetContent'),
cache: false,
success: function(data) {
if (data.success === true) {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.success(element.title, element.message);
});
}
modalContent.html(data.html);
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
write: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('local-configuration-write-token');
var configurationValues = {};
this.currentModal.find('.t3js-localConfiguration-pathValue').each(function(i, element) {
var $element = $(element);
if ($element.attr('type') === 'checkbox') {
if (element.checked) {
configurationValues[$element.data('path')] = '1';
} else {
configurationValues[$element.data('path')] = '0';
}
} else {
configurationValues[$element.data('path')] = $element.val();
}
});
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'localConfigurationWrite',
'token': executeToken,
'configurationValues': configurationValues
}
},
cache: false,
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.showMessage(element.title, element.message, element.severity);
});
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,99 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/CreateAdmin
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorForm: '#t3js-mailTest-form',
selectorEmail: '.t3js-mailTest-email',
selectorOutputContainer: '.t3js-mailTest-output',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getData();
currentModal.on('submit', this.selectorForm, function(e) {
e.preventDefault();
self.send();
});
},
getData: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('mailTestGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
send: function() {
var executeToken = this.currentModal.find(this.selectorModuleContent).data('mail-test-token');
var $outputContainer = this.currentModal.find(this.selectorOutputContainer);
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.empty().html(message);
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'mailTest',
'token': executeToken,
'email': this.currentModal.find('.t3js-mailTest-email').val()
}
},
cache: false,
success: function(data) {
$outputContainer.empty();
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.html(message);
});
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
// 500 can happen here if the mail configuration is broken
Notification.error('Something went wrong');
}
});
}
};
});
@@ -0,0 +1,45 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/Cache
*/
define([
'jquery'
], function($) {
'use strict';
return {
initialize: function(field) {
// Simple password strength indicator
$(document).on('keyup', field, function() {
var value = $(this).val();
var strongRegex = new RegExp('^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$', 'g');
var mediumRegex = new RegExp('^(?=.{8,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$', 'g');
var enoughRegex = new RegExp('(?=.{8,}).*', 'g');
if (value.length === 0) {
$(this).attr('style', 'background-color:#FBB19B; border:1px solid #DC4C42');
} else if (!enoughRegex.test(value)) {
$(this).attr('style', 'background-color:#FBB19B; border:1px solid #DC4C42');
} else if (strongRegex.test(value)) {
$(this).attr('style', 'background-color:#CDEACA; border:1px solid #58B548');
} else if (mediumRegex.test(value)) {
$(this).attr('style', 'background-color:#FBFFB3; border:1px solid #C4B70D');
} else {
$(this).attr('style', 'background-color:#FBFFB3; border:1px solid #C4B70D');
}
});
}
};
});
@@ -0,0 +1,57 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/LanguagePacks
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
currentModal: {},
initialize: function(currentModal) {
this.currentModal = currentModal;
this.getData();
},
getData: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('phpInfoGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,137 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/Presets
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-presets-content',
selectorActivateTrigger: '.t3js-presets-activate',
selectorOutputContainer: '.t3js-presets-output',
selectorImageExecutable: '.t3js-presets-image-executable',
selectorImageExecutableTrigger: '.t3js-presets-image-executable-trigger',
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.getContent();
// Load content with post data on click 'custom image executable path'
currentModal.on('click', this.selectorImageExecutableTrigger, function(e) {
e.preventDefault();
self.getCustomImagePathContent();
});
// Write out selected preset
currentModal.on('click', this.selectorActivateTrigger, function(e) {
e.preventDefault();
self.activate();
});
// Automatically select the custom preset if a value in one of its input fields is changed
currentModal.find('.t3js-custom-preset').on('input', '.t3js-custom-preset', function(e) {
$('#' + $(this).data('radio')).prop('checked', true);
});
},
getContent: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('presetsGetContent'),
cache: false,
success: function(data) {
if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
getCustomImagePathContent: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
var presetsContentToken = this.currentModal.find(this.selectorModuleContent).data('presets-content-token');
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'token': presetsContentToken,
'action': 'presetsGetContent',
'values': {
'Image': {
'additionalSearchPath': this.currentModal.find(this.selectorImageExecutable).val()
}
}
}
},
cache: false,
success: function(data) {
if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
activate: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('presets-activate-token');
var postData = {};
$(this.currentModal.find(this.selectorModuleContent + ' form').serializeArray()).each(function() {
postData[this.name] = this.value;
});
postData['install[action]'] = 'presetsActivate';
postData['install[token]'] = executeToken;
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: postData,
cache: false,
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.showMessage(element.title, element.message, element.severity);
});
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,48 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/ProgressBar
*/
define(['jquery', 'TYPO3/CMS/Install/Severity'], function($, Severity) {
'use strict';
/**
* @type {{template: (*)}}
*/
var ProgressBar = {
template: $('<div class="progress"><div class="t3js-progressbar progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <span></span></div></div>')
};
/**
* render a ProgressBar
* @param {Number} severity
* @param {String} title
* @param {Number} progress
* @returns {jQuery}
*/
ProgressBar.render = function(severity, title, progress) {
var progressBar = this.template.clone();
progressBar.addClass('progress-bar-' + Severity.getCssClass(severity));
if (progress) {
progressBar.css('width', progress + '%');
progressBar.attr('aria-valuenow', progresss);
}
if (title) {
progressBar.find('span').text(title);
}
return progressBar;
};
return ProgressBar;
});
@@ -0,0 +1,60 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/DumpAutoload
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorOutputContainer: '.t3js-resetBackendUserUc-output',
initialize: function($trigger) {
$.ajax({
url: Router.getUrl('resetBackendUserUc'),
cache: false,
beforeSend: function() {
$trigger.addClass('disabled');
},
success: function(data) {
if (data.success === true && Array.isArray(data.status)) {
if (data.status.length > 0) {
data.status.forEach(function(element) {
Notification.success(element.message);
});
}
} else {
Notification.error('Something went wrong ...');
}
},
error: function(xhr) {
// If reset fails on server side (typically a 500), do not crash entire install tool
// but render an error notification instead.
Notification.error('Resetting backend user uc failed. Please check the system for missing database fields and try again.');
},
complete: function() {
$trigger.removeClass('disabled');
}
});
}
};
});
@@ -0,0 +1,439 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/Router
*/
define([
'jquery',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Backend/Modal',
'TYPO3/CMS/Backend/Icons'
], function($, InfoBox, Severity, ProgressBar, Modal, Icons) {
'use strict';
return {
selectorBody: '.t3js-body',
selectorMainContent: '.t3js-module-body',
initialize: function() {
var self = this;
this.registerInstallToolRoutes();
$(document).on('click', '.t3js-login-lockInstallTool', function(e) {
e.preventDefault();
self.logout();
});
$(document).on('click', '.t3js-login-login', function(e) {
e.preventDefault();
self.login();
});
$(document).on('keydown', '#t3-install-form-password', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
$('.t3js-login-login').click();
}
});
$(document).on('click', '.card .btn', function(e) {
e.preventDefault();
var $me = $(e.currentTarget);
var requireModule = $me.data('require');
var inlineState = $me.data('inline');
var isInline = typeof inlineState !== 'undefined' && parseInt(inlineState) === 1;
if (isInline) {
require([requireModule], function(aModule) {
if (typeof aModule.initialize !== 'undefined') {
aModule.initialize($me);
}
});
} else {
var modalTitle = $me.closest('.card').find('.card-title').html();
var modalSize = $me.data('modalSize') || Modal.sizes.large;
var $modal = Modal.advanced({
type: Modal.types.default,
title: modalTitle,
size: modalSize,
content: $('<div class="modal-loading">'),
additionalCssClasses: ['install-tool-modal'],
callback: function (currentModal) {
require([requireModule], function (aModule) {
if (typeof aModule.initialize !== 'undefined') {
aModule.initialize(currentModal);
}
});
}
});
Icons.getIcon('spinner-circle', Icons.sizes.default, null, null, Icons.markupIdentifiers.inline).done(function(icon) {
$modal.find('.modal-loading').append(icon);
});
}
});
if ($(this.selectorBody).data('context') === 'backend') {
this.executeSilentConfigurationUpdate();
} else {
this.preAccessCheck();
}
},
registerInstallToolRoutes: function() {
if (typeof TYPO3.settings === 'undefined') {
TYPO3.settings = {
ajaxUrls: {
icons: '?install[controller]=icon&install[action]=getIcon',
icons_cache: '?install[controller]=icon&install[action]=getCacheIdentifier'
}
}
}
},
getUrl: function(action, controller) {
var url = location.href;
var context = $(this.selectorBody).data('context');
url = url.replace(location.search, '');
if (controller === undefined) {
controller = $(this.selectorBody).data('controller');
}
url = url + '?install[controller]=' + controller;
if (context !== undefined && context !== '') {
url = url + '&install[context]=' + context;
}
if (action !== undefined) {
url = url + '&install[action]=' + action;
}
return url;
},
preAccessCheck: function() {
this.updateLoadingInfo("Execute pre access check");
var self = this;
$.ajax({
url: this.getUrl("preAccessCheck", "layout"),
cache: false,
success: function(data) {
if (data.installToolLocked) {
self.checkEnableInstallToolFile();
} else if (!data.isAuthorized) {
self.showLogin();
} else {
self.executeSilentConfigurationUpdate();
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
})
},
executeSilentConfigurationUpdate: function() {
var self = this;
this.updateLoadingInfo('Checking session and executing silent configuration update');
$.ajax({
url: this.getUrl('executeSilentConfigurationUpdate', 'layout'),
cache: false,
success: function(data) {
if (data.success === true) {
self.executeSilentLegacyExtConfExtensionConfigurationUpdate();
} else {
self.executeSilentConfigurationUpdate();
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
/**
* Legacy layer to upmerge LocalConfiguration EXT/extConf serialized array keys
* to EXTENSIONS array in LocalConfiguration for initial update from v8 to v9.
*
* @deprecated since TYPO3 v9, will be removed with v10 - re-route executeSilentConfigurationUpdate()
* to executeSilentExtensionConfigurationUpdate() on removal of this function.
*/
executeSilentLegacyExtConfExtensionConfigurationUpdate: function() {
var self = this;
this.updateLoadingInfo('Executing silent extension configuration update');
$.ajax({
url: this.getUrl('executeSilentLegacyExtConfExtensionConfigurationUpdate', 'layout'),
cache: false,
success: function(data) {
if (data.success === true) {
self.executeSilentExtensionConfigurationSynchronization();
} else {
var message = InfoBox.render(Severity.error, 'Something went wrong', '');
$outputContainer.empty().append(message);
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
/**
* Extensions which come with new default settings in ext_conf_template.txt extension
* configuration files get their new defaults written to LocalConfiguration.
*/
executeSilentExtensionConfigurationSynchronization: function() {
var self = this;
this.updateLoadingInfo('Executing silent extension configuration synchronization');
$.ajax({
url: this.getUrl('executeSilentExtensionConfigurationSynchronization', 'layout'),
cache: false,
success: function(data) {
if (data.success === true) {
self.loadMainLayout();
} else {
var message = InfoBox.render(Severity.error, 'Something went wrong', '');
$outputContainer.empty().append(message);
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
loadMainLayout: function() {
var self = this;
var $outputContainer = $(this.selectorBody);
this.updateLoadingInfo('Loading main layout');
$.ajax({
url: this.getUrl('mainLayout', 'layout'),
cache: false,
success: function(data) {
if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
$outputContainer.empty().append(data.html);
// Mark main module as active in standalone
if ($(self.selectorBody).data('context') !== 'backend') {
var controller = $outputContainer.data('controller');
$outputContainer.find('.t3js-mainmodule[data-controller="' + controller + '"]').addClass('active');
}
self.loadCards();
} else {
var message = InfoBox.render(Severity.error, 'Something went wrong', '');
$outputContainer.empty().append(message);
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
handleAjaxError: function(xhr, $outputContainer) {
var message = '';
if (xhr.status === 403) {
// Install tool session expired - depending on context render error message or login
var context = $(this.selectorBody).data('context');
if (context === 'backend') {
message = InfoBox.render(
Severity.error,
'The install tool session expired. Please reload the backend and try again.'
);
$(this.selectorBody).empty().append(message);
} else {
this.checkEnableInstallToolFile();
}
} else {
// @todo Recovery tests should be started here
var url = this.getUrl(undefined, 'upgrade');
message =
'<div class="t3js-infobox callout callout-sm callout-danger">'
+ '<div class="callout-body">'
+ '<p>Something went wrong. Please use <b><a href="' + url + '">Check for broken'
+ ' extensions</a></b> to see if a loaded extension breaks this part of the install tool'
+ ' and unload it.</p>'
+ '<p>The box below may additionally reveal further details on what went wrong depending on your debug settings.'
+ ' It may help to temporarily switch to debug mode using <b>Settings > Configuration Presets > Debug settings.</b></p>'
+ '<p>If this error happens at an early state and no full exception back trace is shown, it may also help'
+ ' to manually increase debugging output in <code>typo3conf/LocalConfiguration.php</code>:'
+ '<code>[\'BE\'][\'debug\'] => true</code>, <code>[\'SYS\'][\'devIPmask\'] => \'*\'</code>, <code>[\'SYS\'][\'displayErrors\'] => 1</code>,'
+ '<code>[\'SYS\'][\'systemLogLevel\'] => 0</code>, <code>[\'SYS\'][\'exceptionalErrors\'] => 12290</code></p>'
+ '</div>'
+ '</div>'
+ '<div class="panel-group" role="tablist" aria-multiselectable="true">'
+ '<div class="panel panel-default panel-flat searchhit">'
+ '<div class="panel-heading" role="tab" id="heading-error">'
+ '<h3 class="panel-title">'
+ '<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse-error" aria-expanded="true" aria-controls="collapse-error" class="collapsed">'
+ '<span class="caret"></span>'
+ '<strong>Ajax error</strong>'
+ '</a>'
+'</h3>'
+ '</div>'
+ '<div id="collapse-error" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-error">'
+ '<div class="panel-body">'
+ xhr.responseText
+ '</div>'
+ '</div>'
+ '</div>';
if (typeof $outputContainer !== 'undefined') {
// Write to given output container. This is typically a modal if given
$outputContainer.empty().html(message);
} else {
// Else write to main frame
$(this.selectorBody).empty().html(message);
}
}
},
checkEnableInstallToolFile: function() {
var self = this;
$.ajax({
url: this.getUrl('checkEnableInstallToolFile'),
cache: false,
success: function(data) {
if (data.success === true) {
self.checkLogin();
} else {
self.showEnableInstallTool();
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
showEnableInstallTool: function() {
var self = this;
$.ajax({
url: this.getUrl('showEnableInstallToolFile'),
cache: false,
success: function(data) {
if (data.success === true) {
$(self.selectorBody).empty().append(data.html);
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
checkLogin: function() {
var self = this;
$.ajax({
url: this.getUrl('checkLogin'),
cache: false,
success: function(data) {
if (data.success === true) {
self.loadMainLayout();
} else {
self.showLogin();
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
showLogin: function() {
var self = this;
$.ajax({
url: this.getUrl('showLogin'),
cache: false,
success: function(data) {
if (data.success === true) {
$(self.selectorBody).empty().append(data.html);
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
login: function() {
var self = this;
var $outputContainer = $('.t3js-login-output');
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.empty().html(message);
$.ajax({
url: this.getUrl(),
cache: false,
method: 'POST',
data: {
'install': {
'action': 'login',
'token': $('[data-login-token]').data('login-token'),
'password': $('.t3-install-form-input-text').val()
}
},
success: function(data) {
if (data.success === true) {
self.executeSilentConfigurationUpdate();
} else {
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.empty().html(message);
});
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
logout: function() {
var self = this;
$.ajax({
url: self.getUrl('logout'),
cache: false,
success: function(data) {
if (data.success === true) {
self.showEnableInstallTool();
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
loadCards: function() {
var self = this;
var outputContainer = $(this.selectorMainContent);
$.ajax({
url: this.getUrl('cards'),
cache: false,
success: function(data) {
if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
outputContainer.empty().append(data.html);
} else {
var message = InfoBox.render(Severity.error, 'Something went wrong', '');
outputContainer.empty().append(message);
}
},
error: function(xhr) {
self.handleAjaxError(xhr);
}
});
},
updateLoadingInfo: function(info) {
var $outputContainer = $(this.selectorBody);
$outputContainer.find('#t3js-ui-block-detail').text(info);
}
};
});
@@ -0,0 +1,55 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/Severity
*/
define([], function() {
'use strict';
var Severity = {
loading: -3,
notice: -2,
info: -1,
ok: 0,
warning: 1,
error: 2
};
Severity.getCssClass = function(severity) {
var severityClass;
switch (severity) {
case Severity.loading:
severityClass = 'notice alert-loading';
break;
case Severity.notice:
severityClass = 'notice';
break;
case Severity.ok:
severityClass = 'success';
break;
case Severity.warning:
severityClass = 'warning';
break;
case Severity.error:
severityClass = 'danger';
break;
case Severity.info:
default:
severityClass = 'info';
}
return severityClass;
};
return Severity;
});
@@ -0,0 +1,57 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/LanguagePacks
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
currentModal: {},
initialize: function(currentModal) {
this.currentModal = currentModal;
this.getData();
},
getData: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('systemInformationGetData'),
cache: false,
success: function(data) {
if (data.success === true) {
modalContent.empty().append(data.html);
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,136 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/SystemMaintainer
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap',
'chosen'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorWriteTrigger: '.t3js-systemMaintainer-write',
selectorOutputContainer: '.t3js-systemMaintainer-output',
selectorChosenContainer: '.t3js-systemMaintainer-chosen',
selectorChosenField: '.t3js-systemMaintainer-chosen-select',
currentModal: {},
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
var isInIframe = window.location !== window.parent.location;
if (isInIframe) {
top.require(['TYPO3/CMS/Install/chosen.jquery.min'], function () {
self.getList();
});
}
else {
this.getList();
}
currentModal.on('click', this.selectorWriteTrigger, function(e) {
e.preventDefault();
self.write();
});
},
getList: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
$.ajax({
url: Router.getUrl('systemMaintainerGetList'),
cache: false,
success: function(data) {
if (data.success === true) {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.success(element.title, element.message);
});
}
modalContent.html((data.html));
if (Array.isArray(data.users)) {
data.users.forEach(function(element) {
var name = element.username;
if (element.disable) {
name = '[DISABLED] ' + name;
}
var $option = $('<option>', {'value': element.uid}).text(name);
if (element.isSystemMaintainer) {
$option.attr('selected', 'selected');
}
modalContent.find(self.selectorChosenField).append($option);
});
}
var config = {
'.t3js-systemMaintainer-chosen-select': {width: "100%", placeholder_text_multiple: "users"}
};
for (var selector in config) {
modalContent.find(selector).chosen(config[selector]);
}
modalContent.find(self.selectorChosenContainer).show();
modalContent.find(self.selectorChosenField).trigger('chosen:updated');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
write: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('system-maintainer-write-token');
var selectedUsers = this.currentModal.find(this.selectorChosenField).val();
$.ajax({
method: 'POST',
url: Router.getUrl(),
data: {
'install': {
'users': selectedUsers,
'token': executeToken,
'action': 'systemMaintainerWrite'
}
},
success: function(data) {
if (data.success === true) {
if (Array.isArray(data.status)) {
data.status.forEach(function(element) {
Notification.success(element.title, element.message);
});
}
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,81 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/TcaExtTablesCheck
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorCheckTrigger: '.t3js-tcaExtTablesCheck-check',
selectorOutputContainer: '.t3js-tcaExtTablesCheck-output',
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.check();
currentModal.on('click', this.selectorCheckTrigger, function(e) {
e.preventDefault();
self.check();
});
},
check: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = $(this.selectorOutputContainer);
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.empty().html(message);
$.ajax({
url: Router.getUrl('tcaExtTablesCheck'),
cache: false,
success: function(data) {
modalContent.empty().append(data.html);
if (data.success === true && Array.isArray(data.status)) {
if (data.status.length > 0) {
var message = InfoBox.render(
Severity.warning,
'Following extensions change TCA in ext_tables.php',
'Check ext_tables.php files, look for ExtensionManagementUtility calls and $GLOBALS[\'TCA\'] modifications'
);
modalContent.find(self.selectorOutputContainer).append(message);
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.append(message);
modalContent.append(message);
});
} else {
var message = InfoBox.render(Severity.ok, 'No TCA changes in ext_tables.php files. Good job!', '');
modalContent.find(self.selectorOutputContainer).append(message);
}
} else {
Notification.error('Something went wrong', 'Use "Check for broken extensions"');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,82 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/TcaIntegrityChecker
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification'
], function($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorCheckTrigger: '.t3js-tcaMigrationsCheck-check',
selectorOutputContainer: '.t3js-tcaMigrationsCheck-output',
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
this.check();
currentModal.on('click', this.selectorCheckTrigger, function(e) {
e.preventDefault();
self.check();
});
},
check: function() {
var self = this;
var $outputContainer = $(this.selectorOutputContainer);
var modalContent = this.currentModal.find(this.selectorModalBody);
var message = ProgressBar.render(Severity.loading, 'Loading...', '');
$outputContainer.empty().html(message);
$.ajax({
url: Router.getUrl('tcaMigrationsCheck'),
cache: false,
success: function(data) {
modalContent.empty().append(data.html);
if (data.success === true && Array.isArray(data.status)) {
if (data.status.length > 0) {
var message = InfoBox.render(
Severity.warning,
'TCA migrations need to be applied',
'Check the following list and apply needed changes.'
);
modalContent.find(self.selectorOutputContainer).empty();
modalContent.find(self.selectorOutputContainer).append(message);
data.status.forEach(function(element) {
var message = InfoBox.render(element.severity, element.title, element.message);
modalContent.find(self.selectorOutputContainer).append(message);
});
} else {
var message = InfoBox.render(Severity.ok, 'No TCA migrations need to be applied', 'Your TCA looks good.');
modalContent.find(self.selectorOutputContainer).append(message);
}
} else {
var message = FlashMessage.render(Severity.error, 'Something went wrong', 'Use "Check for broken extensions"');
modalContent.find(self.selectorOutputContainer).append(message);
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
}
};
});
@@ -0,0 +1,330 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/UpgradeDocs
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'bootstrap',
'chosen',
'jquery.clearable'
], function($, Router, ProgressBar, InfoBox, Severity, Notification) {
'use strict';
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorFulltextSearch: '.t3js-upgradeDocs-fulltext-search',
selectorChosenField: '.t3js-upgradeDocs-chosen-select',
selectorChangeLogsForVersionContainer: '.t3js-version-changes',
selectorChangeLogsForVersion: '.t3js-changelog-list',
selectorUpgradeDoc: '.t3js-upgrade-doc',
chosenField: null,
fulltextSearchField: null,
initialize: function(currentModal) {
var self = this;
this.currentModal = currentModal;
var isInIframe = (window.location !== window.parent.location);
if (isInIframe) {
top.require(['TYPO3/CMS/Install/chosen.jquery.min'], function () {
self.getContent();
});
}
else {
this.getContent();
}
// Mark a file as read
currentModal.on('click', '.t3js-upgradeDocs-markRead', function(e) {
self.markRead(e.target);
});
currentModal.on('click', '.t3js-upgradeDocs-unmarkRead', function(e) {
self.unmarkRead(e.target);
});
// Make jquerys "contains" work case-insensitive
jQuery.expr[':'].contains = jQuery.expr.createPseudo(function(arg) {
return function(elem) {
return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
},
getContent: function() {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
modalContent.on('show.bs.collapse', this.selectorUpgradeDoc, function (e) {
self.renderTags($(e.currentTarget));
});
$.ajax({
url: Router.getUrl('upgradeDocsGetContent'),
cache: false,
success: function(data) {
if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
modalContent.empty().append(data.html);
self.initializeFullTextSearch();
self.initializeChosenSelector();
self.loadChangelogs();
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
loadChangelogs: function() {
var self = this;
var promises = [];
var modalContent = this.currentModal.find(this.selectorModalBody);
this.currentModal.find(this.selectorChangeLogsForVersionContainer).each(function(index, el) {
var $request = $.ajax({
url: Router.getUrl('upgradeDocsGetChangelogForVersion'),
cache: false,
data: {
install: {
version: el.dataset.version
}
},
success: function(data) {
if (data.success === true) {
var $panelGroup = $(el);
var $container = $panelGroup.find(self.selectorChangeLogsForVersion);
$container.html(data.html);
self.moveNotRelevantDocuments($container);
// Remove loading spinner form panel
$panelGroup.find('.t3js-panel-loading').remove();
} else {
Notification.error('Something went wrong');
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
promises.push($request);
});
$.when.apply($, promises).done(function () {
self.fulltextSearchField.prop('disabled', false);
self.appendItemsToChosenSelector();
});
},
initializeFullTextSearch: function() {
var self = this;
this.fulltextSearchField = this.currentModal.find(this.selectorFulltextSearch);
this.fulltextSearchField.clearable().focus();
this.initializeChosenSelector();
this.fulltextSearchField.on('keyup', function() {
self.combinedFilterSearch();
});
},
initializeChosenSelector: function() {
var self = this;
this.chosenField = this.currentModal.find(this.selectorModalBody).find(this.selectorChosenField);
var config = {
'.chosen-select': {width: "100%", placeholder_text_multiple: "tags"},
'.chosen-select-deselect': {allow_single_deselect: true},
'.chosen-select-no-single': {disable_search_threshold: 10},
'.chosen-select-no-results': {no_results_text: 'Oops, nothing found!'},
'.chosen-select-width': {width: "100%"}
};
for (var selector in config) {
this.currentModal.find(selector).chosen(config[selector]);
}
this.chosenField.on('change', function() {
self.combinedFilterSearch();
});
},
/**
* Appends tags to the chosen selector
*/
appendItemsToChosenSelector: function() {
var self = this;
var tagString = '';
$(this.currentModal.find(this.selectorUpgradeDoc)).each(function() {
tagString += $(this).data('item-tags') + ',';
});
var tagArray = this.trimExplodeAndUnique(',', tagString).sort(function(a, b) {
// Sort case-insensitive by name
return a.toLowerCase().localeCompare(b.toLowerCase());
});
this.chosenField.prop('disabled', false);
$.each(tagArray, function(i, tag) {
self.chosenField.append($('<option>').text(tag));
});
this.chosenField.trigger('chosen:updated');
},
combinedFilterSearch: function() {
var modalContent = this.currentModal.find(this.selectorModalBody);
var $items = modalContent.find('div.item');
if (this.chosenField.val().length < 1 && this.fulltextSearchField.val().length < 1) {
this.currentModal.find('.panel-version .panel-collapse.in').collapse('hide');
$items.removeClass('hidden searchhit filterhit');
return false;
}
$items.addClass('hidden').removeClass('searchhit filterhit');
// apply tags
if (this.chosenField.val().length > 0) {
$items
.addClass('hidden')
.removeClass('filterhit');
var orTags = [];
var andTags = [];
$.each(this.chosenField.val(), function(index, item) {
var tagFilter = '[data-item-tags*="' + item + '"]';
if (item.indexOf(':') > 0) {
orTags.push(tagFilter);
} else {
andTags.push(tagFilter);
}
});
var andString = andTags.join('');
var tags = [];
if (orTags.length) {
for (var i = 0; i < orTags.length; i++) {
tags.push(andString + orTags[i]);
}
} else {
tags.push(andString);
}
var tagSelection = tags.join(',');
modalContent.find(tagSelection)
.removeClass('hidden')
.addClass('searchhit filterhit');
} else {
$items
.addClass('filterhit')
.removeClass('hidden');
}
// apply fulltext search
var typedQuery = this.fulltextSearchField.val();
modalContent.find('div.item.filterhit').each(function() {
var $item = $(this);
if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
$item.removeClass('hidden').addClass('searchhit');
} else {
$item.removeClass('searchhit').addClass('hidden');
}
});
modalContent.find('.searchhit').closest('.panel-collapse').collapse('show');
//check for empty panels
modalContent.find('.panel-version').each(function() {
if ($(this).find('.searchhit', '.filterhit').length < 1) {
$(this).find(' > .panel-collapse').collapse('hide');
}
});
},
renderTags: function($upgradeDocumentContainer) {
var $tagContainer = $upgradeDocumentContainer.find('.t3js-tags');
if ($tagContainer.children().length === 0) {
var tags = $upgradeDocumentContainer.data('item-tags').split(',');
tags.forEach(function (value) {
$tagContainer.append($('<span />', {'class': 'label'}).text(value));
});
}
},
/**
* Moves all documents that are either read or not affected
*
* @param {JQuery} $container
*/
moveNotRelevantDocuments: function($container) {
$container.find('[data-item-state="read"]').appendTo(this.currentModal.find('.panel-body-read'));
$container.find('[data-item-state="notAffected"]').appendTo(this.currentModal.find('.panel-body-not-affected'));
},
markRead: function(element) {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('upgrade-docs-mark-read-token');
var $button = $(element).closest('a');
$button.toggleClass('t3js-upgradeDocs-unmarkRead t3js-upgradeDocs-markRead');
$button.find('i').toggleClass('fa-check fa-ban');
$button.closest('.panel').appendTo(this.currentModal.find('.panel-body-read'));
$.ajax({
method: 'POST',
url: Router.getUrl(),
data: {
'install': {
'ignoreFile': $button.data('filepath'),
'token': executeToken,
'action': 'upgradeDocsMarkRead'
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
unmarkRead: function(element) {
var modalContent = this.currentModal.find(this.selectorModalBody);
var executeToken = this.currentModal.find(this.selectorModuleContent).data('upgrade-docs-unmark-read-token');
var $button = $(element).closest('a');
var version = $button.closest('.panel').data('item-version');
$button.toggleClass('t3js-upgradeDocs-markRead t3js-upgradeDocs-unmarkRead');
$button.find('i').toggleClass('fa-check fa-ban');
$button.closest('.panel').appendTo(this.currentModal.find('*[data-group-version="' + version + '"] .panel-body'));
$.ajax({
method: 'POST',
url: Router.getUrl(),
data: {
'install': {
'ignoreFile': $button.data('filepath'),
'token': executeToken,
action: 'upgradeDocsUnmarkRead'
}
},
error: function(xhr) {
Router.handleAjaxError(xhr, modalContent);
}
});
},
trimExplodeAndUnique: function(delimiter, string) {
var result = [];
var items = string.split(delimiter);
for (var i = 0; i < items.length; i++) {
var item = items[i].trim();
if (item.length > 0) {
if ($.inArray(item, result) === -1) {
result.push(item);
}
}
}
return result;
}
};
});
@@ -0,0 +1,485 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Install/UpgradeWizards
*/
define([
'jquery',
'TYPO3/CMS/Install/Router',
'TYPO3/CMS/Install/FlashMessage',
'TYPO3/CMS/Install/ProgressBar',
'TYPO3/CMS/Install/InfoBox',
'TYPO3/CMS/Install/Severity',
'TYPO3/CMS/Backend/Notification',
'TYPO3/CMS/Core/SecurityUtility'
],
function ($, Router, FlashMessage, ProgressBar, InfoBox, Severity, Notification, SecurityUtility) {
'use strict';
var securityUtility = new SecurityUtility();
return {
selectorModalBody: '.t3js-modal-body',
selectorModuleContent: '.t3js-module-content',
selectorOutputWizardsContainer: '.t3js-upgradeWizards-wizards-output',
selectorOutputDoneContainer: '.t3js-upgradeWizards-done-output',
selectorWizardsBlockingAddsTemplate: '.t3js-upgradeWizards-blocking-adds-template',
selectorWizardsBlockingAddsRows: '.t3js-upgradeWizards-blocking-adds-rows',
selectorWizardsBlockingAddsExecute: '.t3js-upgradeWizards-blocking-adds-execute',
selectorWizardsBlockingCharsetTemplate: '.t3js-upgradeWizards-blocking-charset-template',
selectorWizardsBlockingCharsetFix: '.t3js-upgradeWizards-blocking-charset-fix',
selectorWizardsDoneBodyTemplate: '.t3js-upgradeWizards-done-body-template',
selectorWizardsDoneRows: '.t3js-upgradeWizards-done-rows',
selectorWizardsDoneRowTemplate: '.t3js-upgradeWizards-done-row-template table tr',
selectorWizardsDoneRowMarkUndone: '.t3js-upgradeWizards-done-markUndone',
selectorWizardsDoneRowTitle: '.t3js-upgradeWizards-done-title',
selectorWizardsListTemplate: '.t3js-upgradeWizards-list-template',
selectorWizardsListRows: '.t3js-upgradeWizards-list-rows',
selectorWizardsListRowTemplate: '.t3js-upgradeWizards-list-row-template',
selectorWizardsListRowTitle: '.t3js-upgradeWizards-list-row-title',
selectorWizardsListRowExplanation: '.t3js-upgradeWizards-list-row-explanation',
selectorWizardsListRowExecute: '.t3js-upgradeWizards-list-row-execute',
selectorWizardsInputTemplate: '.t3js-upgradeWizards-input',
selectorWizardsInputTitle: '.t3js-upgradeWizards-input-title',
selectorWizardsInputHtml: '.t3js-upgradeWizards-input-html',
selectorWizardsInputPerform: '.t3js-upgradeWizards-input-perform',
initialize: function (currentModal) {
var self = this;
this.currentModal = currentModal;
this.getData().done(function() {
self.doneUpgrades();
});
// Mark a done wizard undone
currentModal.on('click', this.selectorWizardsDoneRowMarkUndone, function (e) {
self.markUndone(e.target.dataset.identifier);
});
// Execute "fix default mysql connection db charset" blocking wizard
currentModal.on('click', this.selectorWizardsBlockingCharsetFix, function (e) {
self.blockingUpgradesDatabaseCharsetFix();
});
// Execute "add required fields + tables" blocking wizard
currentModal.on('click', this.selectorWizardsBlockingAddsExecute, function (e) {
self.blockingUpgradesDatabaseAddsExecute();
});
// Get user input of a single upgrade wizard
currentModal.on('click', this.selectorWizardsListRowExecute, function (e) {
self.wizardInput(e.target.dataset.identifier, e.target.dataset.title);
});
// Execute one upgrade wizard
currentModal.on('click', this.selectorWizardsInputPerform, function (e) {
self.wizardExecute(e.target.dataset.identifier, e.target.dataset.title);
});
},
renderProgressBar: function (title) {
return ProgressBar.render(Severity.loading, title, '')
},
getData: function () {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
return $.ajax({
url: Router.getUrl('upgradeWizardsGetData'),
cache: false,
success: function (data) {
if (data.success === true) {
modalContent.empty().append(data.html);
self.blockingUpgradesDatabaseCharsetTest();
} else {
Notification.error('Something went wrong');
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
blockingUpgradesDatabaseCharsetTest: function () {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputWizardsContainer);
$outputContainer.empty().html(this.renderProgressBar('Checking database charset...'));
$.ajax({
url: Router.getUrl('upgradeWizardsBlockingDatabaseCharsetTest'),
cache: false,
success: function (data) {
self.removeLoadingMessage($outputContainer);
if (data.success === true) {
if (data.needsUpdate === true) {
modalContent.find(self.selectorOutputWizardsContainer).append(modalContent.find(self.selectorWizardsBlockingCharsetTemplate)).clone();
} else {
self.blockingUpgradesDatabaseAdds();
}
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
blockingUpgradesDatabaseCharsetFix: function () {
var self = this;
var $outputContainer = $(this.selectorOutputWizardsContainer);
$outputContainer.empty().html(this.renderProgressBar('Setting database charset to UTF-8...'));
$.ajax({
url: Router.getUrl('upgradeWizardsBlockingDatabaseCharsetFix'),
cache: false,
success: function (data) {
self.removeLoadingMessage($outputContainer);
if (data.success === true) {
if (Array.isArray(data.status) && data.status.length > 0) {
data.status.forEach(function (element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.append(message);
});
}
} else {
var message = FlashMessage.render(Severity.error, 'Something went wrong', '');
self.removeLoadingMessage($outputContainer);
$outputContainer.append(message);
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
blockingUpgradesDatabaseAdds: function () {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputWizardsContainer);
$outputContainer.empty().html(this.renderProgressBar('Check for missing mandatory database tables and fields...'));
$.ajax({
url: Router.getUrl('upgradeWizardsBlockingDatabaseAdds'),
cache: false,
success: function (data) {
self.removeLoadingMessage($outputContainer);
if (data.success === true) {
if (data.needsUpdate === true) {
var adds = modalContent.find(self.selectorWizardsBlockingAddsTemplate).clone();
if (typeof(data.adds.tables) === 'object') {
data.adds.tables.forEach(function (element) {
var title = 'Table: ' + securityUtility.encodeHtml(element.table);
adds.find(self.selectorWizardsBlockingAddsRows).append(title, '<br>');
});
}
if (typeof(data.adds.columns) === 'object') {
data.adds.columns.forEach(function (element) {
var title = 'Table: ' + securityUtility.encodeHtml(element.table)
+ ', Field: ' + securityUtility.encodeHtml(element.field);
adds.find(self.selectorWizardsBlockingAddsRows).append(title, '<br>');
});
}
if (typeof(data.adds.indexes) === 'object') {
data.adds.indexes.forEach(function (element) {
var title = 'Table: ' + securityUtility.encodeHtml(element.table)
+ ', Index: ' + securityUtility.encodeHtml(element.index);
adds.find(self.selectorWizardsBlockingAddsRows).append(title, '<br>');
});
}
modalContent.find(self.selectorOutputWizardsContainer).append(adds);
} else {
self.wizardsList();
}
} else {
Notification.error('Something went wrong');
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
blockingUpgradesDatabaseAddsExecute: function () {
var self = this;
var $outputContainer = this.currentModal.find(this.selectorOutputWizardsContainer);
$outputContainer.empty().html(this.renderProgressBar('Adding database tables and fields...'));
$.ajax({
url: Router.getUrl('upgradeWizardsBlockingDatabaseExecute'),
cache: false,
success: function (data) {
self.removeLoadingMessage($outputContainer);
if (data.success === true) {
if (Array.isArray(data.status) && data.status.length > 0) {
data.status.forEach(function (element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.append(message);
});
self.wizardsList();
}
} else {
var message = FlashMessage.render(Severity.error, 'Something went wrong', '');
self.removeLoadingMessage($outputContainer);
$outputContainer.append(message);
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
wizardsList: function () {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputWizardsContainer);
$outputContainer.append(this.renderProgressBar('Loading upgrade wizards...'));
$.ajax({
url: Router.getUrl('upgradeWizardsList'),
cache: false,
success: function (data) {
self.removeLoadingMessage($outputContainer);
var list = modalContent.find(self.selectorWizardsListTemplate).clone();
list.removeClass('t3js-upgradeWizards-list-template');
if (data.success === true) {
var numberOfWizardsTodo = 0;
var numberOfWizards = 0;
if (Array.isArray(data.wizards) && data.wizards.length > 0) {
numberOfWizards = data.wizards.length;
data.wizards.forEach(function (element) {
if (element.shouldRenderWizard === true) {
var aRow = modalContent.find(self.selectorWizardsListRowTemplate).clone();
numberOfWizardsTodo = numberOfWizardsTodo + 1;
aRow.removeClass('t3js-upgradeWizards-list-row-template');
aRow.find(self.selectorWizardsListRowTitle).empty().text(element.title);
aRow.find(self.selectorWizardsListRowExplanation).empty().text(element.explanation);
aRow.find(self.selectorWizardsListRowExecute).attr('data-identifier', element.identifier).attr('data-title', element.title);
list.find(self.selectorWizardsListRows).append(aRow);
}
});
list.find(self.selectorWizardsListRows + ' hr:last').remove();
}
var percent = 100;
var $progressBar = list.find('.progress-bar');
if (numberOfWizardsTodo > 0) {
percent = Math.round((numberOfWizards - numberOfWizardsTodo) / data.wizards.length * 100);
} else {
$progressBar
.removeClass('progress-bar-info')
.addClass('progress-bar-success');
}
$progressBar
.removeClass('progress-bar-striped')
.css('width', percent + '%')
.attr('aria-valuenow', percent)
.find('span')
.text(parseInt(percent) + '%');
modalContent.find(self.selectorOutputWizardsContainer).append(list);
self.currentModal.find(self.selectorWizardsDoneRowMarkUndone).prop("disabled", false);
} else {
Notification.error('Something went wrong');
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
wizardInput: function (identifier, title) {
var self = this;
var executeToken = this.currentModal.find(this.selectorModuleContent).data('upgrade-wizards-input-token');
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputWizardsContainer);
$outputContainer.empty().html(this.renderProgressBar('Loading "' + title + '"...'));
modalContent.animate({
scrollTop: modalContent.scrollTop() - Math.abs(modalContent.find('.t3js-upgrade-status-section').position().top)
}, 250);
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'upgradeWizardsInput',
'token': executeToken,
'identifier': identifier
}
},
cache: false,
success: function (data) {
$outputContainer.empty();
var input = modalContent.find(self.selectorWizardsInputTemplate).clone();
input.removeClass('t3js-upgradeWizards-input');
if (data.success === true) {
if (Array.isArray(data.status)) {
data.status.forEach(function (element) {
var message = FlashMessage.render(element.severity, element.title, element.message);
$outputContainer.append(message);
});
}
if (data.userInput.wizardHtml.length > 0) {
input.find(self.selectorWizardsInputHtml).html(data.userInput.wizardHtml);
}
input.find(self.selectorWizardsInputTitle).text(data.userInput.title);
input.find(self.selectorWizardsInputPerform).attr('data-identifier', data.userInput.identifier).attr('data-title', data.userInput.title);
}
modalContent.find(self.selectorOutputWizardsContainer).append(input);
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
wizardExecute: function (identifier, title) {
var self = this;
var executeToken = this.currentModal.find(this.selectorModuleContent).data('upgrade-wizards-execute-token');
var modalContent = this.currentModal.find(this.selectorModalBody);
var postData = {
'install[action]': 'upgradeWizardsExecute',
'install[token]': executeToken,
'install[identifier]': identifier
};
$(this.currentModal.find(this.selectorOutputWizardsContainer + ' form').serializeArray()).each(function () {
postData[this.name] = this.value;
});
var $outputContainer = this.currentModal.find(this.selectorOutputWizardsContainer);
// modalContent.find(self.selectorOutputWizardsContainer).empty();
$outputContainer.empty().html(this.renderProgressBar('Executing "' + title + '"...'));
this.currentModal.find(this.selectorWizardsDoneRowMarkUndone).prop("disabled", true);
$.ajax({
method: 'POST',
data: postData,
url: Router.getUrl(),
cache: false,
success: function (data) {
$outputContainer.empty();
if (data.success === true) {
if (Array.isArray(data.status)) {
data.status.forEach(function (element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.append(message);
});
}
self.wizardsList();
modalContent.find(self.selectorOutputDoneContainer).empty();
self.doneUpgrades();
} else {
Notification.error('Something went wrong');
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
doneUpgrades: function () {
var self = this;
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = modalContent.find(this.selectorOutputDoneContainer);
$outputContainer.empty().html(this.renderProgressBar('Loading executed upgrade wizards...'));
$.ajax({
url: Router.getUrl('upgradeWizardsDoneUpgrades'),
cache: false,
success: function (data) {
self.removeLoadingMessage($outputContainer);
if (data.success === true) {
if (Array.isArray(data.status) && data.status.length > 0) {
data.status.forEach(function (element) {
var message = InfoBox.render(element.severity, element.title, element.message);
$outputContainer.append(message);
});
}
var body = modalContent.find(self.selectorWizardsDoneBodyTemplate).clone();
var hasBodyContent = false;
var $wizardsDoneContainer = body.find(self.selectorWizardsDoneRows);
if (Array.isArray(data.wizardsDone) && data.wizardsDone.length > 0) {
data.wizardsDone.forEach(function (element) {
hasBodyContent = true;
var aRow = modalContent.find(self.selectorWizardsDoneRowTemplate).clone();
aRow.find(self.selectorWizardsDoneRowMarkUndone).attr('data-identifier', element.identifier);
aRow.find(self.selectorWizardsDoneRowTitle).text(element.title);
$wizardsDoneContainer.append(aRow);
});
}
if (Array.isArray(data.rowUpdatersDone) && data.rowUpdatersDone.length > 0) {
data.rowUpdatersDone.forEach(function (element) {
hasBodyContent = true;
var aRow = modalContent.find(self.selectorWizardsDoneRowTemplate).clone();
aRow.find(self.selectorWizardsDoneRowMarkUndone).attr('data-identifier', element.identifier);
aRow.find(self.selectorWizardsDoneRowTitle).text(element.title);
$wizardsDoneContainer.append(aRow);
});
}
if (hasBodyContent === true) {
modalContent.find(self.selectorOutputDoneContainer).append(body);
self.currentModal.find(self.selectorWizardsDoneRowMarkUndone).prop("disabled", true);
}
} else {
Notification.error('Something went wrong');
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
markUndone: function (identifier) {
var self = this;
var executeToken = this.currentModal.find(this.selectorModuleContent).data('upgrade-wizards-mark-undone-token');
var modalContent = this.currentModal.find(this.selectorModalBody);
var $outputContainer = this.currentModal.find(this.selectorOutputDoneContainer);
$outputContainer.empty().html(this.renderProgressBar('Marking upgrade wizard as undone...'));
$.ajax({
url: Router.getUrl(),
method: 'POST',
data: {
'install': {
'action': 'upgradeWizardsMarkUndone',
'token': executeToken,
'identifier': identifier
}
},
cache: false,
success: function (data) {
$outputContainer.empty();
modalContent.find(self.selectorOutputDoneContainer).empty();
if (data.success === true && Array.isArray(data.status)) {
data.status.forEach(function (element) {
Notification.success(element.message);
self.doneUpgrades();
self.blockingUpgradesDatabaseCharsetTest();
});
} else {
Notification.error('Something went wrong');
}
},
error: function (xhr) {
Router.handleAjaxError(xhr, $outputContainer);
}
});
},
removeLoadingMessage: function ($container) {
$container.find('.alert-loading').remove();
}
};
});
@@ -0,0 +1,23 @@
var require = {
baseUrl: '',
urlArgs: 'bust=' + (new Date()).getTime(),
paths: {
'TYPO3/CMS/Core': 'sysext/core/Resources/Public/JavaScript',
'TYPO3/CMS/Backend': 'sysext/backend/Resources/Public/JavaScript',
'TYPO3/CMS/Install': 'sysext/install/Resources/Public/JavaScript/Modules',
'jquery': 'sysext/core/Resources/Public/JavaScript/Contrib/jquery/jquery.min',
'jquery.clearable': 'sysext/backend/Resources/Public/JavaScript/jquery.clearable',
'TYPO3/CMS/Core/Contrib/jquery.minicolors': 'sysext/core/Resources/Public/JavaScript/Contrib/jquery.minicolors',
'bootstrap': 'sysext/core/Resources/Public/JavaScript/Contrib/bootstrap/bootstrap',
'chosen': 'sysext/install/Resources/Public/JavaScript/chosen.jquery.min',
'nprogress': 'sysext/core/Resources/Public/JavaScript/Contrib/nprogress'
},
shim: {
jQuery: {
exports: '$'
},
'bootstrap': {
'deps': ['jquery']
}
}
};
@@ -0,0 +1 @@
(function(){var t,e,s,i,r={}.hasOwnProperty;(i=function(){function t(){this.options_index=0,this.parsed=[]}return t.prototype.add_node=function(t){return"OPTGROUP"===t.nodeName.toUpperCase()?this.add_group(t):this.add_option(t)},t.prototype.add_group=function(t){var e,s,i,r,n,o;for(e=this.parsed.length,this.parsed.push({array_index:e,group:!0,label:t.label,title:t.title?t.title:void 0,children:0,disabled:t.disabled,classes:t.className}),o=[],s=0,i=(n=t.childNodes).length;s<i;s++)r=n[s],o.push(this.add_option(r,e,t.disabled));return o},t.prototype.add_option=function(t,e,s){if("OPTION"===t.nodeName.toUpperCase())return""!==t.text?(null!=e&&(this.parsed[e].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:t.value,text:t.text,html:t.innerHTML,title:t.title?t.title:void 0,selected:t.selected,disabled:!0===s?s:t.disabled,group_array_index:e,group_label:null!=e?this.parsed[e].label:null,classes:t.className,style:t.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},t}()).select_to_array=function(t){var e,s,r,n,o;for(n=new i,s=0,r=(o=t.childNodes).length;s<r;s++)e=o[s],n.add_node(e);return n.parsed},e=function(){function t(e,s){var i,r;this.form_field=e,this.options=null!=s?s:{},this.label_click_handler=(i=this.label_click_handler,r=this,function(){return i.apply(r,arguments)}),t.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers(),this.on_ready())}return t.prototype.set_default_values=function(){var t;return this.click_test_action=(t=this,function(e){return t.test_active_click(e)}),this.activate_action=function(t){return function(e){return t.activate_field(e)}}(this),this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.is_rtl=this.options.rtl||/\bchosen-rtl\b/.test(this.form_field.className),this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text&&this.options.allow_single_deselect,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null==this.options.enable_split_word_search||this.options.enable_split_word_search,this.group_search=null==this.options.group_search||this.options.group_search,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null==this.options.single_backstroke_delete||this.options.single_backstroke_delete,this.max_selected_options=this.options.max_selected_options||1/0,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null==this.options.display_selected_options||this.options.display_selected_options,this.display_disabled_options=null==this.options.display_disabled_options||this.options.display_disabled_options,this.include_group_label_in_selected=this.options.include_group_label_in_selected||!1,this.max_shown_results=this.options.max_shown_results||Number.POSITIVE_INFINITY,this.case_sensitive_search=this.options.case_sensitive_search||!1,this.hide_results_on_select=null==this.options.hide_results_on_select||this.options.hide_results_on_select},t.prototype.set_default_text=function(){return this.form_field.getAttribute("data-placeholder")?this.default_text=this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.default_text=this.options.placeholder_text_multiple||this.options.placeholder_text||t.default_multiple_text:this.default_text=this.options.placeholder_text_single||this.options.placeholder_text||t.default_single_text,this.default_text=this.escape_html(this.default_text),this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||t.default_no_result_text},t.prototype.choice_label=function(t){return this.include_group_label_in_selected&&null!=t.group_label?"<b class='group-name'>"+this.escape_html(t.group_label)+"</b>"+t.html:t.html},t.prototype.mouse_enter=function(){return this.mouse_on_container=!0},t.prototype.mouse_leave=function(){return this.mouse_on_container=!1},t.prototype.input_focus=function(t){if(this.is_multiple){if(!this.active_field)return setTimeout((e=this,function(){return e.container_mousedown()}),50)}else if(!this.active_field)return this.activate_field();var e},t.prototype.input_blur=function(t){if(!this.mouse_on_container)return this.active_field=!1,setTimeout((e=this,function(){return e.blur_test()}),100);var e},t.prototype.label_click_handler=function(t){return this.is_multiple?this.container_mousedown(t):this.activate_field()},t.prototype.results_option_build=function(t){var e,s,i,r,n,o,h;for(e="",h=0,r=0,n=(o=this.results_data).length;r<n&&(i="",""!==(i=(s=o[r]).group?this.result_add_group(s):this.result_add_option(s))&&(h++,e+=i),(null!=t?t.Line truncated