From 8c1eae497425c13661f5fe9270ba6669c7be021e Mon Sep 17 00:00:00 2001 From: Oliver Bartsch <bo@cedev.de> Date: Tue, 8 Nov 2022 15:29:08 +0100 Subject: [PATCH] [BUGFIX] Also consider select fields for slug generation Generation of a slug proposal based on defined generator fields does now also work for basic select fields, which were previously not considered by the JS component, because they do not use the FormEngine specific visible / hidden field combination. Resolves: #94371 Releases: main, 11.5 Change-Id: Iedd2d17e97db8abd9cf5c4a3b476825fcd854191 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/76485 Tested-by: core-ci <typo3@b13.com> Tested-by: Susanne Moog <look@susi.dev> Tested-by: Benni Mack <benni@typo3.org> Tested-by: Oliver Bartsch <bo@cedev.de> Reviewed-by: Susanne Moog <look@susi.dev> Reviewed-by: Benni Mack <benni@typo3.org> Reviewed-by: Oliver Bartsch <bo@cedev.de> --- .../form-engine/element/slug-element.ts | 20 +++++++++++-------- .../form-engine/element/slug-element.js | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Build/Sources/TypeScript/backend/form-engine/element/slug-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/slug-element.ts index 26c824b5df5e..bf3bfde07fcd 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/slug-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/slug-element.ts @@ -88,7 +88,7 @@ class SlugElement { } private registerEvents(): void { - const fieldsToListenOnList = Object.values(this.getAvailableFieldsForProposalGeneration()).map((selector: string) => `[data-formengine-input-name="${selector}"]`); + const fieldsToListenOnList = Object.values(this.getAvailableFieldsForProposalGeneration()).map((field: HTMLElement) => `[id="${field.id}"]`); const recreateButton: HTMLButtonElement = this.fullElement.querySelector(Selectors.recreateButton); // Listen on 'listenerFieldNames' for new pages. This is typically the 'title' field @@ -158,9 +158,9 @@ class SlugElement { private sendSlugProposal(mode: ProposalModes): void { const input: { [key: string]: string } = {}; if (mode === ProposalModes.AUTO || mode === ProposalModes.RECREATE) { - for (const [fieldName, selector] of Object.entries(this.getAvailableFieldsForProposalGeneration())) { - input[fieldName] = (document.querySelector('[data-formengine-input-name="' + selector + '"]') as HTMLInputElement).value; - } + Object.entries(this.getAvailableFieldsForProposalGeneration()).forEach((entry: [fieldName: string, field: HTMLInputElement|HTMLSelectElement]) => { + input[entry[0]] = entry[1].value; + }); if (this.options.includeUidInValues === true) { input.uid = this.options.recordId.toString(); } @@ -214,12 +214,16 @@ class SlugElement { * * @return { [key: string]: string } */ - private getAvailableFieldsForProposalGeneration(): { [key: string]: string } { - const availableFields: { [key: string]: string } = {}; + private getAvailableFieldsForProposalGeneration(): { [key: string]: HTMLElement } { + const availableFields: { [key: string]: HTMLElement } = {}; for (const [fieldName, selector] of Object.entries(this.fieldsToListenOn)) { - const field = document.querySelector('[data-formengine-input-name="' + selector + '"]'); + let field = document.querySelector('[data-formengine-input-name="' + selector + '"]') as HTMLElement; + if (field === null) { + // Also check for select fields, which do not point to a hidden input field + field = document.querySelector('select[name="' + selector + '"]') as HTMLElement; + } if (field !== null) { - availableFields[fieldName] = selector; + availableFields[fieldName] = field; } } diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/slug-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/slug-element.js index db747b90040f..888057c6ba75 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/slug-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/slug-element.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import DocumentService from"@typo3/core/document-service.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Selectors,ProposalModes;!function(e){e.toggleButton=".t3js-form-field-slug-toggle",e.recreateButton=".t3js-form-field-slug-recreate",e.inputField=".t3js-form-field-slug-input",e.readOnlyField=".t3js-form-field-slug-readonly",e.hiddenField=".t3js-form-field-slug-hidden"}(Selectors||(Selectors={})),function(e){e.AUTO="auto",e.RECREATE="recreate",e.MANUAL="manual"}(ProposalModes||(ProposalModes={}));class SlugElement{constructor(e,t){this.options=null,this.fullElement=null,this.manuallyChanged=!1,this.readOnlyField=null,this.inputField=null,this.hiddenField=null,this.request=null,this.fieldsToListenOn={},this.options=t,this.fieldsToListenOn=this.options.listenerFieldNames||{},DocumentService.ready().then((t=>{this.fullElement=t.querySelector(e),this.inputField=this.fullElement.querySelector(Selectors.inputField),this.readOnlyField=this.fullElement.querySelector(Selectors.readOnlyField),this.hiddenField=this.fullElement.querySelector(Selectors.hiddenField),this.registerEvents()}))}registerEvents(){const e=Object.values(this.getAvailableFieldsForProposalGeneration()).map((e=>`[data-formengine-input-name="${e}"]`)),t=this.fullElement.querySelector(Selectors.recreateButton);e.length>0&&"new"===this.options.command&&new DebounceEvent("input",(()=>{this.manuallyChanged||this.sendSlugProposal(ProposalModes.AUTO)})).delegateTo(document,e.join(",")),e.length>0||this.hasPostModifiersDefined()?new RegularEvent("click",(e=>{e.preventDefault(),this.readOnlyField.classList.contains("hidden")&&(this.readOnlyField.classList.toggle("hidden",!1),this.inputField.classList.toggle("hidden",!0)),this.sendSlugProposal(ProposalModes.RECREATE)})).bindTo(t):(t.classList.add("disabled"),t.disabled=!0),new DebounceEvent("input",(()=>{this.manuallyChanged=!0,this.sendSlugProposal(ProposalModes.MANUAL)})).bindTo(this.inputField);const s=this.fullElement.querySelector(Selectors.toggleButton);new RegularEvent("click",(e=>{e.preventDefault();const t=this.readOnlyField.classList.contains("hidden");this.readOnlyField.classList.toggle("hidden",!t),this.inputField.classList.toggle("hidden",t),t?(this.inputField.value!==this.readOnlyField.value?this.readOnlyField.value=this.inputField.value:(this.manuallyChanged=!1,this.fullElement.querySelector(".t3js-form-proposal-accepted").classList.add("hidden"),this.fullElement.querySelector(".t3js-form-proposal-different").classList.add("hidden")),this.hiddenField.value=this.readOnlyField.value):this.hiddenField.value=this.inputField.value})).bindTo(s)}sendSlugProposal(e){const t={};if(e===ProposalModes.AUTO||e===ProposalModes.RECREATE){for(const[e,s]of Object.entries(this.getAvailableFieldsForProposalGeneration()))t[e]=document.querySelector('[data-formengine-input-name="'+s+'"]').value;!0===this.options.includeUidInValues&&(t.uid=this.options.recordId.toString())}else t.manual=this.inputField.value;this.request instanceof AjaxRequest&&this.request.abort(),this.request=new AjaxRequest(TYPO3.settings.ajaxUrls.record_slug_suggest),this.request.post({values:t,mode:e,tableName:this.options.tableName,pageId:this.options.pageId,parentPageId:this.options.parentPageId,recordId:this.options.recordId,language:this.options.language,fieldName:this.options.fieldName,command:this.options.command,signature:this.options.signature}).then((async t=>{const s=await t.resolve(),l="/"+s.proposal.replace(/^\//,""),i=this.fullElement.querySelector(".t3js-form-proposal-accepted"),o=this.fullElement.querySelector(".t3js-form-proposal-different");i.classList.toggle("hidden",s.hasConflicts),o.classList.toggle("hidden",!s.hasConflicts),(s.hasConflicts?o:i).querySelector("span").innerText=l;this.hiddenField.value!==s.proposal&&this.fullElement.querySelector("input[data-formengine-input-name]").dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0})),e===ProposalModes.AUTO||e===ProposalModes.RECREATE?(this.readOnlyField.value=s.proposal,this.hiddenField.value=s.proposal,this.inputField.value=s.proposal):this.hiddenField.value=s.proposal})).finally((()=>{this.request=null}))}getAvailableFieldsForProposalGeneration(){const e={};for(const[t,s]of Object.entries(this.fieldsToListenOn)){null!==document.querySelector('[data-formengine-input-name="'+s+'"]')&&(e[t]=s)}return e}hasPostModifiersDefined(){return Array.isArray(this.options.config.generatorOptions.postModifiers)&&this.options.config.generatorOptions.postModifiers.length>0}}export default SlugElement; \ No newline at end of file +import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import DocumentService from"@typo3/core/document-service.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Selectors,ProposalModes;!function(e){e.toggleButton=".t3js-form-field-slug-toggle",e.recreateButton=".t3js-form-field-slug-recreate",e.inputField=".t3js-form-field-slug-input",e.readOnlyField=".t3js-form-field-slug-readonly",e.hiddenField=".t3js-form-field-slug-hidden"}(Selectors||(Selectors={})),function(e){e.AUTO="auto",e.RECREATE="recreate",e.MANUAL="manual"}(ProposalModes||(ProposalModes={}));class SlugElement{constructor(e,t){this.options=null,this.fullElement=null,this.manuallyChanged=!1,this.readOnlyField=null,this.inputField=null,this.hiddenField=null,this.request=null,this.fieldsToListenOn={},this.options=t,this.fieldsToListenOn=this.options.listenerFieldNames||{},DocumentService.ready().then((t=>{this.fullElement=t.querySelector(e),this.inputField=this.fullElement.querySelector(Selectors.inputField),this.readOnlyField=this.fullElement.querySelector(Selectors.readOnlyField),this.hiddenField=this.fullElement.querySelector(Selectors.hiddenField),this.registerEvents()}))}registerEvents(){const e=Object.values(this.getAvailableFieldsForProposalGeneration()).map((e=>`[id="${e.id}"]`)),t=this.fullElement.querySelector(Selectors.recreateButton);e.length>0&&"new"===this.options.command&&new DebounceEvent("input",(()=>{this.manuallyChanged||this.sendSlugProposal(ProposalModes.AUTO)})).delegateTo(document,e.join(",")),e.length>0||this.hasPostModifiersDefined()?new RegularEvent("click",(e=>{e.preventDefault(),this.readOnlyField.classList.contains("hidden")&&(this.readOnlyField.classList.toggle("hidden",!1),this.inputField.classList.toggle("hidden",!0)),this.sendSlugProposal(ProposalModes.RECREATE)})).bindTo(t):(t.classList.add("disabled"),t.disabled=!0),new DebounceEvent("input",(()=>{this.manuallyChanged=!0,this.sendSlugProposal(ProposalModes.MANUAL)})).bindTo(this.inputField);const s=this.fullElement.querySelector(Selectors.toggleButton);new RegularEvent("click",(e=>{e.preventDefault();const t=this.readOnlyField.classList.contains("hidden");this.readOnlyField.classList.toggle("hidden",!t),this.inputField.classList.toggle("hidden",t),t?(this.inputField.value!==this.readOnlyField.value?this.readOnlyField.value=this.inputField.value:(this.manuallyChanged=!1,this.fullElement.querySelector(".t3js-form-proposal-accepted").classList.add("hidden"),this.fullElement.querySelector(".t3js-form-proposal-different").classList.add("hidden")),this.hiddenField.value=this.readOnlyField.value):this.hiddenField.value=this.inputField.value})).bindTo(s)}sendSlugProposal(e){const t={};e===ProposalModes.AUTO||e===ProposalModes.RECREATE?(Object.entries(this.getAvailableFieldsForProposalGeneration()).forEach((e=>{t[e[0]]=e[1].value})),!0===this.options.includeUidInValues&&(t.uid=this.options.recordId.toString())):t.manual=this.inputField.value,this.request instanceof AjaxRequest&&this.request.abort(),this.request=new AjaxRequest(TYPO3.settings.ajaxUrls.record_slug_suggest),this.request.post({values:t,mode:e,tableName:this.options.tableName,pageId:this.options.pageId,parentPageId:this.options.parentPageId,recordId:this.options.recordId,language:this.options.language,fieldName:this.options.fieldName,command:this.options.command,signature:this.options.signature}).then((async t=>{const s=await t.resolve(),l="/"+s.proposal.replace(/^\//,""),i=this.fullElement.querySelector(".t3js-form-proposal-accepted"),o=this.fullElement.querySelector(".t3js-form-proposal-different");i.classList.toggle("hidden",s.hasConflicts),o.classList.toggle("hidden",!s.hasConflicts),(s.hasConflicts?o:i).querySelector("span").innerText=l;this.hiddenField.value!==s.proposal&&this.fullElement.querySelector("input[data-formengine-input-name]").dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0})),e===ProposalModes.AUTO||e===ProposalModes.RECREATE?(this.readOnlyField.value=s.proposal,this.hiddenField.value=s.proposal,this.inputField.value=s.proposal):this.hiddenField.value=s.proposal})).finally((()=>{this.request=null}))}getAvailableFieldsForProposalGeneration(){const e={};for(const[t,s]of Object.entries(this.fieldsToListenOn)){let l=document.querySelector('[data-formengine-input-name="'+s+'"]');null===l&&(l=document.querySelector('select[name="'+s+'"]')),null!==l&&(e[t]=l)}return e}hasPostModifiersDefined(){return Array.isArray(this.options.config.generatorOptions.postModifiers)&&this.options.config.generatorOptions.postModifiers.length>0}}export default SlugElement; \ No newline at end of file -- GitLab