From c5931a49821a7d3c7d95b6efc77d7cd252585764 Mon Sep 17 00:00:00 2001 From: Andreas Fernandez <a.fernandez@scripting-base.de> Date: Wed, 18 Jan 2023 11:37:17 +0100 Subject: [PATCH] [TASK] Migrate page title inline editor to web component This patch migrates the inline editor used to modify a page's table to a standalone web component. This allows to cleanup the original `@typo3/backend/page-actions` module and to fix state cross-dependencies within said module. The whole functionality is considered to be used for internal purposes only. Therefore, the deprecated method `\TYPO3\CMS\Backend\Template\ModuleTemplate->header()` is not adjusted, rendering a very unlikely enabled page title editing unusable. Resolves: #99606 Releases: main Change-Id: Ib9fe9ff165280e52c7c775d8943225b34cb7dbff Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/77466 Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Benni Mack <benni@typo3.org> Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Tested-by: core-ci <typo3@b13.com> Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de> Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de> --- .../backend/element/editable-page-title.ts | 137 +++++++++++++++++ .../TypeScript/backend/page-actions.ts | 145 ------------------ .../Controller/NewRecordController.php | 1 - .../Controller/PageLayoutController.php | 18 +-- .../Controller/RecordListController.php | 7 - .../Templates/PageLayout/PageModule.html | 17 +- .../Private/Templates/RecordList.html | 16 +- .../JavaScript/element/editable-page-title.js | 34 ++++ .../Public/JavaScript/page-actions.js | 2 +- .../Application/Page/PageModuleCest.php | 7 +- .../Workspace/WorkspaceModuleCest.php | 6 +- 11 files changed, 199 insertions(+), 191 deletions(-) create mode 100644 Build/Sources/TypeScript/backend/element/editable-page-title.ts create mode 100644 typo3/sysext/backend/Resources/Public/JavaScript/element/editable-page-title.js diff --git a/Build/Sources/TypeScript/backend/element/editable-page-title.ts b/Build/Sources/TypeScript/backend/element/editable-page-title.ts new file mode 100644 index 000000000000..b72edb8a568a --- /dev/null +++ b/Build/Sources/TypeScript/backend/element/editable-page-title.ts @@ -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! + */ + +import {lll} from '@typo3/core/lit-helper'; +import {html, LitElement, TemplateResult} from 'lit'; +import {customElement, property, state} from 'lit/decorators'; +import './icon-element'; +import AjaxDataHandler from '../ajax-data-handler'; + +@customElement('typo3-backend-editable-page-title') +class EditablePageTitle extends LitElement { + @property({type: String}) pageTitle: string = ''; + @property({type: Number}) pageId: number = 0; + @property({type: Number}) localizedPageId: number = 0; + @property({type: Boolean}) editable: boolean = false; + @state() _isEditing: boolean = false; + @state() _isSubmitting: boolean = false; + + public createRenderRoot(): HTMLElement | ShadowRoot { + // Avoid shadow DOM for Bootstrap CSS to be applied + return this; + } + + protected render(): TemplateResult { + if (this.pageTitle === '') { + return html``; + } + + const pageTitleHeadline = html`<h1 @dblclick="${(): void => { this.startEditing(); }}">${this.pageTitle}</h1>`; + if (!this.isEditable()) { + return pageTitleHeadline; + } + + let content; + if (!this._isEditing) { + content = html`<div class="row"> + <div class="col-md-auto"> + ${pageTitleHeadline} + </div> + <div class="col"> + ${this.composeEditButton()} + </div> + </div>`; + } else { + content = this.composeEditForm(); + } + + return content; + } + + private isEditable(): boolean { + return this.editable && this.pageId > 0; + } + + private startEditing(): void { + if (this.isEditable()) { + this._isEditing = true; + } + } + + private endEditing(): void { + if (this.isEditable()) { + this._isEditing = false; + } + } + + private updatePageTitle(e: SubmitEvent): void { + e.preventDefault(); + + const formData = new FormData(e.target as HTMLFormElement); + const submittedData = Object.fromEntries(formData); + const newPageTitle = submittedData.newPageTitle.toString(); + + if (this.pageTitle === newPageTitle) { + // Page title didn't change, no need to update anything + this.endEditing(); + return; + } + + this._isSubmitting = true; + + let parameters: { [k: string]: any } = {}; + let recordUid; + if (this.localizedPageId > 0) { + recordUid = this.localizedPageId; + } else { + recordUid = this.pageId; + } + + parameters.data = { + pages: { + [recordUid]: { + title: newPageTitle + } + } + }; + AjaxDataHandler.process(parameters).then((): void => { + this.pageTitle = newPageTitle; + top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh')); + }).finally((): void => { + this.endEditing(); + this._isSubmitting = false; + }); + } + + private composeEditButton(): TemplateResult { + return html`<button type="button" class="btn btn-link" aria-label="${lll('editPageTitle')}" @click="${(): void => { this.startEditing(); }}"> + <typo3-backend-icon identifier="actions-open" size="small"></typo3-backend-icon> + </button>`; + } + + private composeEditForm(): TemplateResult { + return html`<form class="t3js-title-edit-form" @submit="${ this.updatePageTitle }"> + <div class="form-group"> + <div class="input-group input-group-lg"> + <input class="form-control" name="newPageTitle" ?disabled="${this._isSubmitting}" value="${this.pageTitle}" @keydown="${(e: KeyboardEvent): void => { if (e.key === 'Escape') { this.endEditing(); } }}"> + <button class="btn btn-default" type="submit" ?disabled="${this._isSubmitting}"> + <typo3-backend-icon identifier="actions-save" size="small"></typo3-backend-icon> + </button> + <button class="btn btn-default" type="button" ?disabled="${this._isSubmitting}" @click="${(): void => { this.endEditing(); }}"> + <typo3-backend-icon identifier="actions-close" size="small"></typo3-backend-icon> + </button> + </div> + </div> + </form>`; + } +} diff --git a/Build/Sources/TypeScript/backend/page-actions.ts b/Build/Sources/TypeScript/backend/page-actions.ts index 0affc4b04560..bc2e29a20c35 100644 --- a/Build/Sources/TypeScript/backend/page-actions.ts +++ b/Build/Sources/TypeScript/backend/page-actions.ts @@ -11,11 +11,9 @@ * The TYPO3 project - inspiring people to share! */ -import { KeyTypesEnum } from './enum/key-types'; import $ from 'jquery'; import PersistentStorage from './storage/persistent'; import '@typo3/backend/element/icon-element'; -import '@typo3/backend/new-content-element-wizard-button'; enum IdentifierEnum { pageTitle = '.t3js-title-inlineedit', @@ -27,71 +25,19 @@ enum IdentifierEnum { * JavaScript implementations for page actions */ class PageActions { - private pageId: number = 0; - private pageOverlayId: number = 0; - private $pageTitle: JQuery = null; private $showHiddenElementsCheckbox: JQuery = null; constructor() { $((): void => { this.initializeElements(); this.initializeEvents(); - this.initializePageTitleRenaming(); }); } - /** - * Set the page id (used in the RequireJS callback) - * - * @param {number} pageId - */ - public setPageId(pageId: number): void { - this.pageId = pageId; - } - - /** - * Set the overlay id - * - * @param {number} overlayId - */ - public setLanguageOverlayId(overlayId: number): void { - this.pageOverlayId = overlayId; - } - - /** - * Initialize page title renaming - */ - public initializePageTitleRenaming(): void { - if (!$.isReady) { - $((): void => { - this.initializePageTitleRenaming(); - }); - return; - } - if (this.pageId <= 0) { - return; - } - - const $editActionLink = $( - '<button type="button" class="btn btn-link" aria-label="' + TYPO3.lang.editPageTitle + '" data-action="edit">' + - '<typo3-backend-icon identifier="actions-open" size="small"></typo3-backend-icon>' + - '</button>' - ); - $editActionLink.on('click', (): void => { - this.editPageTitle(); - }); - this.$pageTitle - .on('dblclick', (): void => { - this.editPageTitle(); - }) - .append($editActionLink); - } - /** * Initialize elements */ private initializeElements(): void { - this.$pageTitle = $(IdentifierEnum.pageTitle + ':first'); this.$showHiddenElementsCheckbox = $('#checkShowHidden'); } @@ -124,97 +70,6 @@ class PageActions { $me.show(); }); } - - /** - * Changes the h1 to an edit form - */ - private editPageTitle(): void { - const $inputFieldWrap = $( - '<form class="t3js-title-edit-form">' + - '<div class="form-group">' + - '<div class="input-group input-group-lg">' + - '<input class="form-control t3js-title-edit-input">' + - '<button class="btn btn-default" type="button" data-action="submit"><typo3-backend-icon identifier="actions-save" size="small"></typo3-backend-icon></button> ' + - '<button class="btn btn-default" type="button" data-action="cancel"><typo3-backend-icon identifier="actions-close" size="small"></typo3-backend-icon></button> ' + - '</div>' + - '</div>' + - '</form>', - ), - $inputField = $inputFieldWrap.find('input'); - - $inputFieldWrap.find('[data-action="cancel"]').on('click', (): void => { - $inputFieldWrap.replaceWith(this.$pageTitle); - this.initializePageTitleRenaming(); - }); - - $inputFieldWrap.find('[data-action="submit"]').on('click', (): void => { - const newPageTitle = $inputField.val().trim(); - if (newPageTitle !== '' && this.$pageTitle.text() !== newPageTitle) { - this.saveChanges($inputField); - } else { - $inputFieldWrap.find('[data-action="cancel"]').trigger('click'); - } - }); - - // the form stuff is a wacky workaround to prevent the submission of the docheader form - $inputField.parents('form').on('submit', (e: JQueryEventObject): boolean => { - e.preventDefault(); - return false; - }); - - const $h1 = this.$pageTitle; - $h1.children().last().remove(); - $h1.replaceWith($inputFieldWrap); - $inputField.val($h1.text()).focus(); - - // Use type 'keydown' instead of 'keyup' which would be triggered directly in case a keyboard is used to start editing. - $inputField.on('keydown', (e: JQueryEventObject): void => { - switch (e.which) { - case KeyTypesEnum.ENTER: - $inputFieldWrap.find('[data-action="submit"]').trigger('click'); - break; - case KeyTypesEnum.ESCAPE: - $inputFieldWrap.find('[data-action="cancel"]').trigger('click'); - break; - default: - } - }); - } - - /** - * Save the changes and reload the page tree - * - * @param {JQuery} $field - */ - private saveChanges($field: JQuery): void { - const $inputFieldWrap = $field.parents('form.t3js-title-edit-form'); - $inputFieldWrap.find('button').addClass('disabled'); - $field.attr('disabled', 'disabled'); - - let parameters: { [k: string]: any } = {}; - let recordUid; - - if (this.pageOverlayId > 0) { - recordUid = this.pageOverlayId; - } else { - recordUid = this.pageId; - } - - parameters.data = {}; - parameters.data.pages = {}; - parameters.data.pages[recordUid] = { title: $field.val() }; - - import('@typo3/backend/ajax-data-handler').then(({default: DataHandler}): void => { - DataHandler.process(parameters).then((): void => { - $inputFieldWrap.find('[data-action=cancel]').trigger('click'); - this.$pageTitle.text($field.val()); - this.initializePageTitleRenaming(); - top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh')); - }).catch((): void => { - $inputFieldWrap.find('[data-action=cancel]').trigger('click'); - }); - }); - } } export default new PageActions(); diff --git a/typo3/sysext/backend/Classes/Controller/NewRecordController.php b/typo3/sysext/backend/Classes/Controller/NewRecordController.php index 91b58ef8a24e..1e1b23f4bb4e 100644 --- a/typo3/sysext/backend/Classes/Controller/NewRecordController.php +++ b/typo3/sysext/backend/Classes/Controller/NewRecordController.php @@ -213,7 +213,6 @@ class NewRecordController // Setting up the context sensitive menu: $this->pageRenderer->loadJavaScriptModule('@typo3/backend/context-menu.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/tooltip.js'); - $this->pageRenderer->loadJavaScriptModule('@typo3/backend/page-actions.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/new-content-element-wizard-button.js'); // Id a positive id is supplied, ask for the page record with permission information contained: if ($this->id > 0) { diff --git a/typo3/sysext/backend/Classes/Controller/PageLayoutController.php b/typo3/sysext/backend/Classes/Controller/PageLayoutController.php index a9bcc35c0490..3c17a08a3f89 100644 --- a/typo3/sysext/backend/Classes/Controller/PageLayoutController.php +++ b/typo3/sysext/backend/Classes/Controller/PageLayoutController.php @@ -135,10 +135,13 @@ class PageLayoutController $mainLayoutHtml = $this->backendLayoutRenderer->drawContent($request, $pageLayoutContext); $numberOfHiddenElements = $this->getNumberOfHiddenElements($pageLayoutContext->getDrawingConfiguration()->getLanguageColumns()); + $pageLocalizationRecord = $this->getLocalizedPageRecord($this->currentSelectedLanguage); + $view->setTitle($languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'), $this->pageinfo['title']); $view->getDocHeaderComponent()->setMetaInformation($this->pageinfo); $view->assignMultiple([ 'pageId' => $this->id, + 'localizedPageId' => $pageLocalizationRecord['uid'] ?? 0, 'infoBoxes' => $this->generateMessagesForCurrentPage($request), 'isPageEditable' => $this->isPageEditable($this->currentSelectedLanguage), 'localizedPageTitle' => $this->getLocalizedPageTitle($this->currentSelectedLanguage, $this->pageinfo), @@ -455,18 +458,9 @@ class PageLayoutController protected function addJavaScriptModuleInstructions(): void { - $pageActionsInstruction = JavaScriptModuleInstruction::create('@typo3/backend/page-actions.js'); - if ($this->isPageEditable($this->currentSelectedLanguage)) { - $languageOverlayId = 0; - $pageLocalizationRecord = $this->getLocalizedPageRecord($this->currentSelectedLanguage); - if (!empty($pageLocalizationRecord['uid'])) { - $languageOverlayId = $pageLocalizationRecord['uid']; - } - $pageActionsInstruction - ->invoke('setPageId', $this->id) - ->invoke('setLanguageOverlayId', $languageOverlayId); - } - $this->pageRenderer->getJavaScriptRenderer()->addJavaScriptModuleInstruction($pageActionsInstruction); + $this->pageRenderer->getJavaScriptRenderer()->addJavaScriptModuleInstruction( + JavaScriptModuleInstruction::create('@typo3/backend/page-actions.js') + ); } /** diff --git a/typo3/sysext/backend/Classes/Controller/RecordListController.php b/typo3/sysext/backend/Classes/Controller/RecordListController.php index 65ffd8604064..c61f6d1138aa 100644 --- a/typo3/sysext/backend/Classes/Controller/RecordListController.php +++ b/typo3/sysext/backend/Classes/Controller/RecordListController.php @@ -42,7 +42,6 @@ use TYPO3\CMS\Core\Domain\Repository\PageRepository; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Localization\LanguageService; -use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Type\Bitmask\Permission; use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; @@ -105,12 +104,6 @@ class RecordListController $access = is_array($pageinfo); $this->pageInfo = is_array($pageinfo) ? $pageinfo : []; $this->pagePermissions = new Permission($backendUser->calcPerms($pageinfo)); - $userCanEditPage = $this->pagePermissions->editPagePermissionIsGranted() && !empty($this->id) && ($backendUser->isAdmin() || (int)$pageinfo['editlock'] === 0); - $pageActionsInstruction = JavaScriptModuleInstruction::create('@typo3/backend/page-actions.js'); - if ($userCanEditPage) { - $pageActionsInstruction->invoke('setPageId', $this->id); - } - $this->pageRenderer->getJavaScriptRenderer()->addJavaScriptModuleInstruction($pageActionsInstruction); $clipboardEnabled = (bool)$moduleData->get('clipBoard'); if (isset($this->modTSconfig['enableClipBoard'])) { diff --git a/typo3/sysext/backend/Resources/Private/Templates/PageLayout/PageModule.html b/typo3/sysext/backend/Resources/Private/Templates/PageLayout/PageModule.html index 6447996be668..a465ef0510a1 100644 --- a/typo3/sysext/backend/Resources/Private/Templates/PageLayout/PageModule.html +++ b/typo3/sysext/backend/Resources/Private/Templates/PageLayout/PageModule.html @@ -15,7 +15,8 @@ 3: '@typo3/backend/tooltip.js', 4: '@typo3/backend/localization.js', 5: '@typo3/backend/layout-module/drag-drop.js', - 6: '@typo3/backend/modal.js' + 6: '@typo3/backend/modal.js', + 7: '@typo3/backend/element/editable-page-title.js' }" /> @@ -28,14 +29,12 @@ </f:be.infobox> </f:for> - <f:if condition="{isPageEditable}"> - <f:then> - <h1 class="t3js-title-inlineedit">{localizedPageTitle}</h1> - </f:then> - <f:else> - <h1>{localizedPageTitle}</h1> - </f:else> - </f:if> + <typo3-backend-editable-page-title + pageTitle="{localizedPageTitle}" + pageId="{pageId}" + localizedPageId="{localizedPageId}" + {isPageEditable ? 'editable' : ''} + ></typo3-backend-editable-page-title> <f:format.raw>{eventContentHtmlTop}</f:format.raw> diff --git a/typo3/sysext/backend/Resources/Private/Templates/RecordList.html b/typo3/sysext/backend/Resources/Private/Templates/RecordList.html index e07c704e1f8d..627c9f54059a 100644 --- a/typo3/sysext/backend/Resources/Private/Templates/RecordList.html +++ b/typo3/sysext/backend/Resources/Private/Templates/RecordList.html @@ -19,21 +19,19 @@ 7: '@typo3/backend/clipboard-panel.js', 8: '@typo3/backend/new-content-element-wizard-button.js', 9: '@typo3/backend/element/immediate-action-element.js', - 10: '@typo3/backend/context-menu.js' + 10: '@typo3/backend/context-menu.js', + 11: '@typo3/backend/element/editable-page-title.js' }" /> <f:variable name="immediateActionArgs" value="{0: 'web', 1: pageId}" /> <typo3-immediate-action action="TYPO3.Backend.Storage.ModuleStateStorage.update" args="{immediateActionArgs -> f:format.json() -> f:format.htmlspecialchars()}"></typo3-immediate-action> - <f:if condition="{isPageEditable}"> - <f:then> - <h1 class="t3js-title-inlineedit">{pageTitle}</h1> - </f:then> - <f:else> - <h1>{pageTitle}</h1> - </f:else> - </f:if> + <typo3-backend-editable-page-title + pageTitle="{pageTitle}" + pageId="{pageId}" + {isPageEditable ? 'editable' : ''} + ></typo3-backend-editable-page-title> <f:format.raw>{additionalContentTop}</f:format.raw> diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/element/editable-page-title.js b/typo3/sysext/backend/Resources/Public/JavaScript/element/editable-page-title.js new file mode 100644 index 000000000000..3157fabc3a9d --- /dev/null +++ b/typo3/sysext/backend/Resources/Public/JavaScript/element/editable-page-title.js @@ -0,0 +1,34 @@ +/* + * 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! + */ +var __decorate=function(t,e,i,a){var o,l=arguments.length,s=l<3?e:null===a?a=Object.getOwnPropertyDescriptor(e,i):a;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(t,e,i,a);else for(var d=t.length-1;d>=0;d--)(o=t[d])&&(s=(l<3?o(s):l>3?o(e,i,s):o(e,i))||s);return l>3&&s&&Object.defineProperty(e,i,s),s};import{lll}from"@typo3/core/lit-helper.js";import{html,LitElement}from"lit";import{customElement,property,state}from"lit/decorators.js";import"@typo3/backend/element/icon-element.js";import AjaxDataHandler from"@typo3/backend/ajax-data-handler.js";let EditablePageTitle=class extends LitElement{constructor(){super(...arguments),this.pageTitle="",this.pageId=0,this.localizedPageId=0,this.editable=!1,this._isEditing=!1,this._isSubmitting=!1}createRenderRoot(){return this}render(){if(""===this.pageTitle)return html``;const t=html`<h1 @dblclick="${()=>{this.startEditing()}}">${this.pageTitle}</h1>`;if(!this.isEditable())return t;let e;return e=this._isEditing?this.composeEditForm():html`<div class="row"> + <div class="col-md-auto"> + ${t} + </div> + <div class="col"> + ${this.composeEditButton()} + </div> + </div>`,e}isEditable(){return this.editable&&this.pageId>0}startEditing(){this.isEditable()&&(this._isEditing=!0)}endEditing(){this.isEditable()&&(this._isEditing=!1)}updatePageTitle(t){t.preventDefault();const e=new FormData(t.target),i=Object.fromEntries(e).newPageTitle.toString();if(this.pageTitle===i)return void this.endEditing();this._isSubmitting=!0;let a,o={};a=this.localizedPageId>0?this.localizedPageId:this.pageId,o.data={pages:{[a]:{title:i}}},AjaxDataHandler.process(o).then((()=>{this.pageTitle=i,top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))})).finally((()=>{this.endEditing(),this._isSubmitting=!1}))}composeEditButton(){return html`<button type="button" class="btn btn-link" aria-label="${lll("editPageTitle")}" @click="${()=>{this.startEditing()}}"> + <typo3-backend-icon identifier="actions-open" size="small"></typo3-backend-icon> + </button>`}composeEditForm(){return html`<form class="t3js-title-edit-form" @submit="${this.updatePageTitle}"> + <div class="form-group"> + <div class="input-group input-group-lg"> + <input class="form-control" name="newPageTitle" ?disabled="${this._isSubmitting}" value="${this.pageTitle}" @keydown="${t=>{"Escape"===t.key&&this.endEditing()}}"> + <button class="btn btn-default" type="submit" ?disabled="${this._isSubmitting}"> + <typo3-backend-icon identifier="actions-save" size="small"></typo3-backend-icon> + </button> + <button class="btn btn-default" type="button" ?disabled="${this._isSubmitting}" @click="${()=>{this.endEditing()}}"> + <typo3-backend-icon identifier="actions-close" size="small"></typo3-backend-icon> + </button> + </div> + </div> + </form>`}};__decorate([property({type:String})],EditablePageTitle.prototype,"pageTitle",void 0),__decorate([property({type:Number})],EditablePageTitle.prototype,"pageId",void 0),__decorate([property({type:Number})],EditablePageTitle.prototype,"localizedPageId",void 0),__decorate([property({type:Boolean})],EditablePageTitle.prototype,"editable",void 0),__decorate([state()],EditablePageTitle.prototype,"_isEditing",void 0),__decorate([state()],EditablePageTitle.prototype,"_isSubmitting",void 0),EditablePageTitle=__decorate([customElement("typo3-backend-editable-page-title")],EditablePageTitle); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js b/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js index bbe824792780..1917097de904 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{KeyTypesEnum}from"@typo3/backend/enum/key-types.js";import $ from"jquery";import PersistentStorage from"@typo3/backend/storage/persistent.js";import"@typo3/backend/element/icon-element.js";import"@typo3/backend/new-content-element-wizard-button.js";var IdentifierEnum;!function(e){e.pageTitle=".t3js-title-inlineedit",e.hiddenElements=".t3js-hidden-record"}(IdentifierEnum||(IdentifierEnum={}));class PageActions{constructor(){this.pageId=0,this.pageOverlayId=0,this.$pageTitle=null,this.$showHiddenElementsCheckbox=null,$((()=>{this.initializeElements(),this.initializeEvents(),this.initializePageTitleRenaming()}))}setPageId(e){this.pageId=e}setLanguageOverlayId(e){this.pageOverlayId=e}initializePageTitleRenaming(){if(!$.isReady)return void $((()=>{this.initializePageTitleRenaming()}));if(this.pageId<=0)return;const e=$('<button type="button" class="btn btn-link" aria-label="'+TYPO3.lang.editPageTitle+'" data-action="edit"><typo3-backend-icon identifier="actions-open" size="small"></typo3-backend-icon></button>');e.on("click",(()=>{this.editPageTitle()})),this.$pageTitle.on("dblclick",(()=>{this.editPageTitle()})).append(e)}initializeElements(){this.$pageTitle=$(IdentifierEnum.pageTitle+":first"),this.$showHiddenElementsCheckbox=$("#checkShowHidden")}initializeEvents(){this.$showHiddenElementsCheckbox.on("change",this.toggleContentElementVisibility)}toggleContentElementVisibility(e){const t=$(e.currentTarget),i=$(IdentifierEnum.hiddenElements),n=$('<span class="form-check-spinner"><typo3-backend-icon identifier="spinner-circle" size="small"></typo3-backend-icon></span>');t.hide().after(n),t.prop("checked")?i.slideDown():i.slideUp(),PersistentStorage.set("moduleData.web_layout.showHidden",t.prop("checked")?"1":"0").then((()=>{n.remove(),t.show()}))}editPageTitle(){const e=$('<form class="t3js-title-edit-form"><div class="form-group"><div class="input-group input-group-lg"><input class="form-control t3js-title-edit-input"><button class="btn btn-default" type="button" data-action="submit"><typo3-backend-icon identifier="actions-save" size="small"></typo3-backend-icon></button> <button class="btn btn-default" type="button" data-action="cancel"><typo3-backend-icon identifier="actions-close" size="small"></typo3-backend-icon></button> </div></div></form>'),t=e.find("input");e.find('[data-action="cancel"]').on("click",(()=>{e.replaceWith(this.$pageTitle),this.initializePageTitleRenaming()})),e.find('[data-action="submit"]').on("click",(()=>{const i=t.val().trim();""!==i&&this.$pageTitle.text()!==i?this.saveChanges(t):e.find('[data-action="cancel"]').trigger("click")})),t.parents("form").on("submit",(e=>(e.preventDefault(),!1)));const i=this.$pageTitle;i.children().last().remove(),i.replaceWith(e),t.val(i.text()).focus(),t.on("keydown",(t=>{switch(t.which){case KeyTypesEnum.ENTER:e.find('[data-action="submit"]').trigger("click");break;case KeyTypesEnum.ESCAPE:e.find('[data-action="cancel"]').trigger("click")}}))}saveChanges(e){const t=e.parents("form.t3js-title-edit-form");t.find("button").addClass("disabled"),e.attr("disabled","disabled");let i,n={};i=this.pageOverlayId>0?this.pageOverlayId:this.pageId,n.data={},n.data.pages={},n.data.pages[i]={title:e.val()},import("@typo3/backend/ajax-data-handler.js").then((({default:i})=>{i.process(n).then((()=>{t.find("[data-action=cancel]").trigger("click"),this.$pageTitle.text(e.val()),this.initializePageTitleRenaming(),top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))})).catch((()=>{t.find("[data-action=cancel]").trigger("click")}))}))}}export default new PageActions; \ No newline at end of file +import $ from"jquery";import PersistentStorage from"@typo3/backend/storage/persistent.js";import"@typo3/backend/element/icon-element.js";var IdentifierEnum;!function(e){e.pageTitle=".t3js-title-inlineedit",e.hiddenElements=".t3js-hidden-record"}(IdentifierEnum||(IdentifierEnum={}));class PageActions{constructor(){this.$showHiddenElementsCheckbox=null,$((()=>{this.initializeElements(),this.initializeEvents()}))}initializeElements(){this.$showHiddenElementsCheckbox=$("#checkShowHidden")}initializeEvents(){this.$showHiddenElementsCheckbox.on("change",this.toggleContentElementVisibility)}toggleContentElementVisibility(e){const i=$(e.currentTarget),t=$(IdentifierEnum.hiddenElements),n=$('<span class="form-check-spinner"><typo3-backend-icon identifier="spinner-circle" size="small"></typo3-backend-icon></span>');i.hide().after(n),i.prop("checked")?t.slideDown():t.slideUp(),PersistentStorage.set("moduleData.web_layout.showHidden",i.prop("checked")?"1":"0").then((()=>{n.remove(),i.show()}))}}export default new PageActions; \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Page/PageModuleCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Page/PageModuleCest.php index ac54d388c63a..feb48ba6ebcd 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Page/PageModuleCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Page/PageModuleCest.php @@ -63,11 +63,10 @@ class PageModuleCest private function renamePage(ApplicationTester $I, string $oldTitle, string $newTitle): void { - $editLinkSelector = 'button[data-action="edit"]'; - $inputFieldSelector = 'input[class*="t3js-title-edit-input"]'; + $editLinkSelector = 'typo3-backend-editable-page-title button'; + $inputFieldSelector = 'typo3-backend-editable-page-title input[name="newPageTitle"]'; $I->waitForText($oldTitle, 10, 'h1'); - $I->moveMouseOver('.t3js-title-inlineedit'); $I->comment('Activate inline edit of page title'); $I->waitForElement($editLinkSelector); @@ -76,7 +75,7 @@ class PageModuleCest $I->comment('Set new value and save'); $I->fillField($inputFieldSelector, $newTitle); - $I->click('button[data-action="submit"]'); + $I->click('typo3-backend-editable-page-title button[type="submit"]'); $I->comment('See the new page title'); $I->waitForElementNotVisible($inputFieldSelector); diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Workspace/WorkspaceModuleCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Workspace/WorkspaceModuleCest.php index 1f50f24541c8..daab1f973696 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Workspace/WorkspaceModuleCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Workspace/WorkspaceModuleCest.php @@ -54,9 +54,9 @@ class WorkspaceModuleCest $I->comment('Rename page'); $I->switchToContentFrame(); - $I->click('button[data-action="edit"]'); - $I->fillField('input[class*="t3js-title-edit-input"]', self::$newPageTitle); - $I->click('button[data-action="submit"]'); + $I->click('typo3-backend-editable-page-title button'); + $I->fillField('typo3-backend-editable-page-title input[name="newPageTitle"]', self::$newPageTitle); + $I->click('typo3-backend-editable-page-title button[type="submit"]'); $I->switchToMainFrame(); $I->click(Topbar::$dropdownToggleSelector, self::$topBarModuleSelector); -- GitLab