Skip to content

Commit

Permalink
[TASK] Install Tool: Move action buttons into modal footer
Browse files Browse the repository at this point in the history
Resolves: #88523
Releases: master
Change-Id: I26404c120702bac669b3ad3aaf9280a76eb8f0e2
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/60899
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Daniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Daniel Goerz <daniel.goerz@posteo.de>
  • Loading branch information
andreaskienast authored and ervaude committed Jun 28, 2019
1 parent e4cc43a commit e13e304
Show file tree
Hide file tree
Showing 69 changed files with 327 additions and 261 deletions.
Expand Up @@ -279,6 +279,56 @@ class Modal {
return this.generate(<Configuration>configuration);
}

/**
* Sets action buttons for the modal window or removed the footer, if no buttons are given.
*
* @param {Array<Button>} buttons
*/
public setButtons(buttons: Array<Button>): JQuery {
if (buttons.length > 0) {
this.currentModal.find(Identifiers.footer).empty();

for (let i = 0; i < buttons.length; i++) {
const button = buttons[i];
const $button = $('<button />', {'class': 'btn'});
$button.html('<span>' + this.securityUtility.encodeHtml(button.text, false) + '</span>');
if (button.active) {
$button.addClass('t3js-active');
}
if (button.btnClass !== '') {
$button.addClass(button.btnClass);
}
if (button.name !== '') {
$button.attr('name', button.name);
}
if (button.trigger) {
$button.on('click', button.trigger);
}
if (button.dataAttributes) {
if (Object.keys(button.dataAttributes).length > 0) {
Object.keys(button.dataAttributes).map((value: string): any => {
$button.attr('data-' + value, button.dataAttributes[value]);
});
}
}
if (button.icon) {
$button.prepend('<span class="t3js-modal-icon-placeholder" data-icon="' + button.icon + '"></span>');
}
this.currentModal.find(Identifiers.footer).append($button);
}
this.currentModal.find(Identifiers.footer).show();
this.currentModal
.find(Identifiers.footer).find('button')
.on('click', (e: JQueryEventObject): void => {
$(e.currentTarget).trigger('button.clicked');
});
} else {
this.currentModal.find(Identifiers.footer).hide();
}

return this.currentModal;
}

/**
* Initialize markup with data attributes
*
Expand Down Expand Up @@ -386,45 +436,6 @@ class Modal {
currentModal.find(Identifiers.body).append(configuration.content);
}

// Add buttons
if (configuration.buttons.length > 0) {
for (let i = 0; i < configuration.buttons.length; i++) {
const button = configuration.buttons[i];
const $button = $('<button />', {'class': 'btn'});
$button.html('<span>' + this.securityUtility.encodeHtml(button.text, false) + '</span>');
if (button.active) {
$button.addClass('t3js-active');
}
if (button.btnClass !== '') {
$button.addClass(button.btnClass);
}
if (button.name !== '') {
$button.attr('name', button.name);
}
if (button.trigger) {
$button.on('click', button.trigger);
}
if (button.dataAttributes) {
if (Object.keys(button.dataAttributes).length > 0) {
Object.keys(button.dataAttributes).map((value: string): any => {
$button.attr('data-' + value, button.dataAttributes[value]);
});
}
}
if (button.icon) {
$button.prepend('<span class="t3js-modal-icon-placeholder" data-icon="' + button.icon + '"></span>');
}
currentModal.find(Identifiers.footer).append($button);
}
currentModal
.find(Identifiers.footer).find('button')
.on('click', (e: JQueryEventObject): void => {
$(e.currentTarget).trigger('button.clicked');
});
} else {
currentModal.find(Identifiers.footer).remove();
}

currentModal.on('shown.bs.modal', (e: JQueryEventObject): void => {
const $me = $(e.currentTarget);
// focus the button which was configured as active button
Expand Down Expand Up @@ -455,6 +466,8 @@ class Modal {
// When modal is opened/shown add it to Modal.instances and make it Modal.currentModal
currentModal.on('show.bs.modal', (e: JQueryEventObject): void => {
this.currentModal = $(e.currentTarget);
// Add buttons
this.setButtons(configuration.buttons);
this.instances.push(this.currentModal);
});
currentModal.on('modal-dismiss', (e: JQueryEventObject): void => {
Expand Down
Expand Up @@ -14,6 +14,7 @@
export abstract class AbstractInteractableModule {
private readonly selectorModalBody: string = '.t3js-modal-body';
private readonly selectorModalContent: string = '.t3js-module-content';
private readonly selectorModalFooter: string = '.t3js-modal-footer';
protected currentModal: JQuery;

abstract initialize(currentModal: JQuery): void;
Expand All @@ -26,6 +27,10 @@ export abstract class AbstractInteractableModule {
return this.findInModal(this.selectorModalContent);
}

protected getModalFooter(): JQuery {
return this.findInModal(this.selectorModalFooter);
}

protected findInModal(selector: string): JQuery {
return this.currentModal.find(selector);
}
Expand Down
Expand Up @@ -25,7 +25,7 @@ class Cache implements InlineModuleInterface {
url: Router.getUrl('cacheClearAll', 'maintenance'),
cache: false,
beforeSend: (): void => {
$trigger.addClass('disabled');
$trigger.addClass('disabled').prop('disabled', true);
},
success: (data: any): void => {
if (data.success === true && Array.isArray(data.status)) {
Expand All @@ -46,7 +46,7 @@ class Cache implements InlineModuleInterface {
);
},
complete: (): void => {
$trigger.removeClass('disabled');
$trigger.removeClass('disabled').prop('disabled', false);
},
});
}
Expand Down
Expand Up @@ -15,19 +15,20 @@ import {AbstractInteractableModule} from './AbstractInteractableModule';
import * as $ from 'jquery';
import Router = require('../Router');
import PasswordStrength = require('./PasswordStrength');
import Modal = require('TYPO3/CMS/Backend/Modal');
import Notification = require('TYPO3/CMS/Backend/Notification');

/**
* Module: TYPO3/CMS/Install/Module/ChangeInstallToolPassword
*/
class ChangeInstallToolPassword extends AbstractInteractableModule {
private selectorChangeForm: string = '#t3js-changeInstallToolPassword-form';
private selectorChangeButton: string = '.t3js-changeInstallToolPassword-change';

public initialize(currentModal: JQuery): void {
this.currentModal = currentModal;
this.getData();

currentModal.on('submit', this.selectorChangeForm, (e: JQueryEventObject): void => {
currentModal.on('click', this.selectorChangeButton, (e: JQueryEventObject): void => {
e.preventDefault();
this.change();
});
Expand All @@ -44,6 +45,7 @@ class ChangeInstallToolPassword extends AbstractInteractableModule {
success: (data: any): void => {
if (data.success === true) {
modalContent.empty().append(data.html);
Modal.setButtons(data.buttons);
} else {
Notification.error('Something went wrong');
}
Expand Down
Expand Up @@ -14,6 +14,7 @@
import {AbstractInteractableModule} from './AbstractInteractableModule';
import * as $ from 'jquery';
import Router = require('../Router');
import Modal = require('TYPO3/CMS/Backend/Modal');
import Notification = require('TYPO3/CMS/Backend/Notification');

/**
Expand Down Expand Up @@ -54,6 +55,7 @@ class ClearTables extends AbstractInteractableModule {
success: (data: any): void => {
if (data.success === true) {
modalContent.empty().append(data.html);
Modal.setButtons(data.buttons);
if (Array.isArray(data.stats) && data.stats.length > 0) {
data.stats.forEach((element: any): void => {
if (element.rowCount > 0) {
Expand Down
Expand Up @@ -14,6 +14,7 @@
import {AbstractInteractableModule} from './AbstractInteractableModule';
import * as $ from 'jquery';
import Router = require('../Router');
import Modal = require('TYPO3/CMS/Backend/Modal');
import Notification = require('TYPO3/CMS/Backend/Notification');

/**
Expand Down Expand Up @@ -53,6 +54,7 @@ class ClearTypo3tempFiles extends AbstractInteractableModule {
success: (data: any): void => {
if (data.success === true) {
modalContent.empty().append(data.html);
Modal.setButtons(data.buttons);
if (Array.isArray(data.stats) && data.stats.length > 0) {
data.stats.forEach((element: any): void => {
if (element.numberOfFiles > 0) {
Expand Down
Expand Up @@ -16,6 +16,7 @@ import * as $ from 'jquery';
import Router = require('../Router');
import FlashMessage = require('../Renderable/FlashMessage');
import Severity = require('../Renderable/Severity');
import Modal = require('TYPO3/CMS/Backend/Modal');
import Notification = require('TYPO3/CMS/Backend/Notification');

interface ActionItem {
Expand Down Expand Up @@ -68,7 +69,7 @@ class CoreUpdate extends AbstractInteractableModule {
};

private selectorOutput: string = '.t3js-coreUpdate-output';
private selectorTemplate: string = '.t3js-coreUpdate-buttonTemplate';
private updateButton: string = '.t3js-coreUpdate-button';

/**
* Clone of a DOM object acts as button template
Expand All @@ -81,18 +82,29 @@ class CoreUpdate extends AbstractInteractableModule {
public initialize(currentModal: JQuery): void {
this.currentModal = currentModal;
this.getData().done((): void => {
const buttonTemplateSection = currentModal.find(this.selectorTemplate);
this.buttonTemplate = buttonTemplateSection.children().clone();
this.buttonTemplate = this.findInModal(this.updateButton).clone();
});

currentModal.on('click', '.t3js-coreUpdate-init', (e: JQueryEventObject): void => {
e.preventDefault();
// Don't use jQuery's data() function, as the DOM is re-rendered and any set data attribute gets lost.
// See showActionButton()
const action = $(e.target).attr('data-action');
const action = $(e.currentTarget).attr('data-action');

currentModal.find(this.selectorOutput).empty();
CoreUpdate.call(action);
this.findInModal(this.selectorOutput).empty();
switch (action) {
case 'checkForUpdate':
this.callAction('coreUpdateIsUpdateAvailable');
break;
case 'updateDevelopment':
this.update('development');
break;
case 'updateRegular':
this.update('regular');
break;
default:
throw 'Unknown update action "' + action + '"';
}
});
}

Expand All @@ -104,6 +116,7 @@ class CoreUpdate extends AbstractInteractableModule {
success: (data: any): void => {
if (data.success === true) {
modalContent.empty().append(data.html);
Modal.setButtons(data.buttons);
} else {
Notification.error('Something went wrong');
}
Expand All @@ -114,27 +127,6 @@ class CoreUpdate extends AbstractInteractableModule {
});
}

/**
* Internal action called by callAction()
*/
private checkForUpdate(): void {
this.callAction('coreUpdateIsUpdateAvailable');
}

/**
* Internal action called by callAction()
*/
private updateDevelopment(): void {
this.update('development');
}

/**
* Internal action called by callAction()
*/
private updateRegular(): void {
this.update('regular');
}

/**
* Execute core update.
*
Expand Down Expand Up @@ -256,7 +248,7 @@ class CoreUpdate extends AbstractInteractableModule {
if (title) {
domButton.text(title);
}
this.findInModal(this.selectorOutput).append(domButton);
this.findInModal(this.updateButton).replaceWith(domButton);
}

/**
Expand Down
Expand Up @@ -15,19 +15,20 @@ import {AbstractInteractableModule} from './AbstractInteractableModule';
import * as $ from 'jquery';
import Router = require('../Router');
import PasswordStrength = require('./PasswordStrength');
import Modal = require('TYPO3/CMS/Backend/Modal');
import Notification = require('TYPO3/CMS/Backend/Notification');

/**
* Module: TYPO3/CMS/Install/Module/CreateAdmin
*/
class CreateAdmin extends AbstractInteractableModule {
private selectorCreateForm: string = '#t3js-createAdmin-form';
private selectorAdminCreateButton: string = '.t3js-createAdmin-create';

public initialize(currentModal: JQuery): void {
this.currentModal = currentModal;
this.getData();

currentModal.on('submit', this.selectorCreateForm, (e: JQueryEventObject): void => {
currentModal.on('click', this.selectorAdminCreateButton, (e: JQueryEventObject): void => {
e.preventDefault();
this.create();
});
Expand All @@ -45,6 +46,7 @@ class CreateAdmin extends AbstractInteractableModule {
success: (data: any): void => {
if (data.success === true) {
modalContent.empty().append(data.html);
Modal.setButtons(data.buttons);
} else {
Notification.error('Something went wrong');
}
Expand Down
Expand Up @@ -17,6 +17,7 @@ import Router = require('../Router');
import ProgressBar = require('../Renderable/ProgressBar');
import InfoBox = require('../Renderable/InfoBox');
import Severity = require('../Renderable/Severity');
import Modal = require('TYPO3/CMS/Backend/Modal');
import Notification = require('TYPO3/CMS/Backend/Notification');

/**
Expand Down Expand Up @@ -57,6 +58,7 @@ class DatabaseAnalyzer extends AbstractInteractableModule {
success: (data: any): void => {
if (data.success === true) {
modalContent.empty().append(data.html);
Modal.setButtons(data.buttons);
this.analyze();
} else {
Notification.error('Something went wrong');
Expand All @@ -70,9 +72,10 @@ class DatabaseAnalyzer extends AbstractInteractableModule {

private analyze(): void {
const modalContent = this.getModalBody();
const modalFooter = this.getModalFooter();
const outputContainer = modalContent.find(this.selectorOutputContainer);
const executeTrigger = modalContent.find(this.selectorExecuteTrigger);
const analyzeTrigger = modalContent.find(this.selectorAnalyzeTrigger);
const executeTrigger = modalFooter.find(this.selectorExecuteTrigger);
const analyzeTrigger = modalFooter.find(this.selectorAnalyzeTrigger);

outputContainer.empty().append(ProgressBar.render(Severity.loading, 'Analyzing current database schema...', ''));

Expand Down
Expand Up @@ -25,7 +25,7 @@ class DumpAutoload implements InlineModuleInterface {
url: Router.getUrl('dumpAutoload'),
cache: false,
beforeSend: (): void => {
$trigger.addClass('disabled');
$trigger.addClass('disabled').prop('disabled', true);
},
success: (data: any): void => {
if (data.success === true && Array.isArray(data.status)) {
Expand All @@ -44,7 +44,7 @@ class DumpAutoload implements InlineModuleInterface {
Notification.error('Dumping autoload files went wrong on the server side. Check the system for broken extensions and try again');
},
complete: (): void => {
$trigger.removeClass('disabled');
$trigger.removeClass('disabled').prop('disabled', false);
},
});
}
Expand Down

0 comments on commit e13e304

Please sign in to comment.