From 3b1982e62178148562b54a8faa58ab26f3edfed6 Mon Sep 17 00:00:00 2001 From: Oliver Bartsch <bo@cedev.de> Date: Wed, 13 Mar 2024 16:22:50 +0100 Subject: [PATCH] [BUGFIX] Prevent TypeErrors in FormEngine for missing elements Resolves: #103391 Releases: main, 12.4 Change-Id: I48d55c7b66669f9836886f257aefba2171b6910e Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/83455 Tested-by: Jochen Roth <rothjochen@gmail.com> Tested-by: core-ci <typo3@b13.com> Reviewed-by: Jochen Roth <rothjochen@gmail.com> Tested-by: Andreas Kienast <a.fernandez@scripting-base.de> Reviewed-by: Oliver Bartsch <bo@cedev.de> Tested-by: Oliver Bartsch <bo@cedev.de> Reviewed-by: Andreas Kienast <a.fernandez@scripting-base.de> --- .../TypeScript/backend/form-engine/element/group-element.ts | 3 +++ .../element/select-multiple-side-by-side-element.ts | 3 +++ .../backend/form-engine/element/select-single-element.ts | 3 +++ .../TypeScript/backend/form-engine/field-control/link-popup.ts | 3 +++ .../Public/JavaScript/form-engine/element/group-element.js | 2 +- .../element/select-multiple-side-by-side-element.js | 2 +- .../JavaScript/form-engine/element/select-single-element.js | 2 +- .../Public/JavaScript/form-engine/field-control/link-popup.js | 2 +- 8 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Build/Sources/TypeScript/backend/form-engine/element/group-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/group-element.ts index cb807ca30753..71e61da0a312 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/group-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/group-element.ts @@ -23,6 +23,9 @@ class GroupElement extends AbstractSortableSelectItems { DocumentService.ready().then((): void => { this.element = <HTMLSelectElement>document.getElementById(elementId); + if (this.element === null) { + return; + } this.registerEventHandler(); this.registerSuggest(); }); diff --git a/Build/Sources/TypeScript/backend/form-engine/element/select-multiple-side-by-side-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/select-multiple-side-by-side-element.ts index a45c7b62870d..9fbc1e5ba4d1 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/select-multiple-side-by-side-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/select-multiple-side-by-side-element.ts @@ -27,6 +27,9 @@ class SelectMultipleSideBySideElement extends AbstractSortableSelectItems { DocumentService.ready().then((document: Document): void => { this.selectedOptionsElement = <HTMLSelectElement>document.getElementById(selectedOptionsElementId); this.availableOptionsElement = <HTMLSelectElement>document.getElementById(availableOptionsElementId); + if (this.selectedOptionsElement === null || this.availableOptionsElement === null) { + return; + } this.registerEventHandler(); }); } diff --git a/Build/Sources/TypeScript/backend/form-engine/element/select-single-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/select-single-element.ts index 85c077e5e9ac..4354208e3db7 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/select-single-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/select-single-element.ts @@ -34,6 +34,9 @@ class SelectSingleElement { public initialize = (elementSelector: string, options: SelectSingleElementOptions): void => { const selectElement: HTMLSelectElement = document.querySelector(elementSelector); + if (selectElement === null) { + return; + } options = options || {}; new RegularEvent('change', (e: Event): void => { diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/link-popup.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/link-popup.ts index 5d54e97a2b92..98340691aba6 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/link-popup.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/link-popup.ts @@ -24,6 +24,9 @@ class LinkPopup { constructor(controlElementId: string) { DocumentService.ready().then((): void => { this.controlElement = <HTMLElement>document.querySelector(controlElementId); + if (this.controlElement === null) { + return; + } this.controlElement.addEventListener('click', this.handleControlClick); }); } diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/group-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/group-element.js index c29a1a02f5e8..1e25091bd629 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/group-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/group-element.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{AbstractSortableSelectItems}from"@typo3/backend/form-engine/element/abstract-sortable-select-items.js";import DocumentService from"@typo3/core/document-service.js";import FormEngineSuggest from"@typo3/backend/form-engine-suggest.js";class GroupElement extends AbstractSortableSelectItems{constructor(e){super(),this.element=null,DocumentService.ready().then((()=>{this.element=document.getElementById(e),this.registerEventHandler(),this.registerSuggest()}))}registerEventHandler(){this.registerSortableEventHandler(this.element)}registerSuggest(){let e;null!==(e=this.element.closest(".t3js-formengine-field-item").querySelector(".t3-form-suggest"))&&new FormEngineSuggest(e)}}export default GroupElement; \ No newline at end of file +import{AbstractSortableSelectItems}from"@typo3/backend/form-engine/element/abstract-sortable-select-items.js";import DocumentService from"@typo3/core/document-service.js";import FormEngineSuggest from"@typo3/backend/form-engine-suggest.js";class GroupElement extends AbstractSortableSelectItems{constructor(e){super(),this.element=null,DocumentService.ready().then((()=>{this.element=document.getElementById(e),null!==this.element&&(this.registerEventHandler(),this.registerSuggest())}))}registerEventHandler(){this.registerSortableEventHandler(this.element)}registerSuggest(){let e;null!==(e=this.element.closest(".t3js-formengine-field-item").querySelector(".t3-form-suggest"))&&new FormEngineSuggest(e)}}export default GroupElement; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-multiple-side-by-side-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-multiple-side-by-side-element.js index ccc77c062fde..be8b7a62b3cf 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-multiple-side-by-side-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-multiple-side-by-side-element.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{AbstractSortableSelectItems}from"@typo3/backend/form-engine/element/abstract-sortable-select-items.js";import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";import SelectBoxFilter from"@typo3/backend/form-engine/element/extra/select-box-filter.js";import RegularEvent from"@typo3/core/event/regular-event.js";class SelectMultipleSideBySideElement extends AbstractSortableSelectItems{constructor(e,t){super(),this.selectedOptionsElement=null,this.availableOptionsElement=null,DocumentService.ready().then((n=>{this.selectedOptionsElement=n.getElementById(e),this.availableOptionsElement=n.getElementById(t),this.registerEventHandler()}))}registerEventHandler(){this.registerSortableEventHandler(this.selectedOptionsElement),this.registerKeyboardEvents(),this.availableOptionsElement.addEventListener("click",(e=>{const t=e.currentTarget;this.handleOptionChecked(t)})),new SelectBoxFilter(this.availableOptionsElement)}handleOptionChecked(e){const t=e.dataset.relatedfieldname;if(t){const n=e.dataset.exclusivevalues,l=e.querySelectorAll("option:checked");l.length>0&&l.forEach((e=>{FormEngine.setSelectOptionFromExternalSource(t,e.value,e.textContent,e.getAttribute("title"),n,e)}))}}registerKeyboardEvents(){new RegularEvent("keydown",(e=>{const t=e.currentTarget;"Enter"===e.code&&(e.preventDefault(),this.handleOptionChecked(t))})).bindTo(this.availableOptionsElement)}}export default SelectMultipleSideBySideElement; \ No newline at end of file +import{AbstractSortableSelectItems}from"@typo3/backend/form-engine/element/abstract-sortable-select-items.js";import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";import SelectBoxFilter from"@typo3/backend/form-engine/element/extra/select-box-filter.js";import RegularEvent from"@typo3/core/event/regular-event.js";class SelectMultipleSideBySideElement extends AbstractSortableSelectItems{constructor(e,t){super(),this.selectedOptionsElement=null,this.availableOptionsElement=null,DocumentService.ready().then((l=>{this.selectedOptionsElement=l.getElementById(e),this.availableOptionsElement=l.getElementById(t),null!==this.selectedOptionsElement&&null!==this.availableOptionsElement&&this.registerEventHandler()}))}registerEventHandler(){this.registerSortableEventHandler(this.selectedOptionsElement),this.registerKeyboardEvents(),this.availableOptionsElement.addEventListener("click",(e=>{const t=e.currentTarget;this.handleOptionChecked(t)})),new SelectBoxFilter(this.availableOptionsElement)}handleOptionChecked(e){const t=e.dataset.relatedfieldname;if(t){const l=e.dataset.exclusivevalues,n=e.querySelectorAll("option:checked");n.length>0&&n.forEach((e=>{FormEngine.setSelectOptionFromExternalSource(t,e.value,e.textContent,e.getAttribute("title"),l,e)}))}}registerKeyboardEvents(){new RegularEvent("keydown",(e=>{const t=e.currentTarget;"Enter"===e.code&&(e.preventDefault(),this.handleOptionChecked(t))})).bindTo(this.availableOptionsElement)}}export default SelectMultipleSideBySideElement; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-single-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-single-element.js index 8b4426e96441..ffcf8ad8b532 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-single-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-single-element.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import RegularEvent from"@typo3/core/event/regular-event.js";import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";import{selector}from"@typo3/core/literals.js";class SelectSingleElement{constructor(){this.initialize=(e,t)=>{const n=document.querySelector(e);t=t||{},new RegularEvent("change",(e=>{const t=e.target,n=t.parentElement.querySelector(".input-group-icon");null!==n&&(n.innerHTML=t.options[t.selectedIndex].dataset.icon);const i=t.closest(".t3js-formengine-field-item").querySelector(".t3js-forms-select-single-icons");if(null!==i){const e=i.querySelector(".form-wizard-icon-list-item a.active");null!==e&&e.classList.remove("active");const n=i.querySelector(selector`[data-select-index="${t.selectedIndex.toString(10)}"]`);null!==n&&n.closest(".form-wizard-icon-list-item a").classList.add("active")}})).bindTo(n),t.onChange instanceof Array&&new RegularEvent("change",(()=>FormEngine.processOnFieldChange(t.onChange))).bindTo(n),new RegularEvent("click",((e,t)=>{const i=t.closest(".t3js-forms-select-single-icons").querySelector(".form-wizard-icon-list-item a.active");null!==i&&i.classList.remove("active"),n.selectedIndex=parseInt(t.dataset.selectIndex,10),n.dispatchEvent(new Event("change")),t.closest(".form-wizard-icon-list-item a").classList.add("active")})).delegateTo(n.closest(".form-control-wrap"),".t3js-forms-select-single-icons .form-wizard-icon-list-item a:not(.active)")}}initializeOnReady(e,t){DocumentService.ready().then((()=>{this.initialize(e,t)}))}}export default new SelectSingleElement; \ No newline at end of file +import RegularEvent from"@typo3/core/event/regular-event.js";import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";import{selector}from"@typo3/core/literals.js";class SelectSingleElement{constructor(){this.initialize=(e,t)=>{const n=document.querySelector(e);null!==n&&(t=t||{},new RegularEvent("change",(e=>{const t=e.target,n=t.parentElement.querySelector(".input-group-icon");null!==n&&(n.innerHTML=t.options[t.selectedIndex].dataset.icon);const i=t.closest(".t3js-formengine-field-item").querySelector(".t3js-forms-select-single-icons");if(null!==i){const e=i.querySelector(".form-wizard-icon-list-item a.active");null!==e&&e.classList.remove("active");const n=i.querySelector(selector`[data-select-index="${t.selectedIndex.toString(10)}"]`);null!==n&&n.closest(".form-wizard-icon-list-item a").classList.add("active")}})).bindTo(n),t.onChange instanceof Array&&new RegularEvent("change",(()=>FormEngine.processOnFieldChange(t.onChange))).bindTo(n),new RegularEvent("click",((e,t)=>{const i=t.closest(".t3js-forms-select-single-icons").querySelector(".form-wizard-icon-list-item a.active");null!==i&&i.classList.remove("active"),n.selectedIndex=parseInt(t.dataset.selectIndex,10),n.dispatchEvent(new Event("change")),t.closest(".form-wizard-icon-list-item a").classList.add("active")})).delegateTo(n.closest(".form-control-wrap"),".t3js-forms-select-single-icons .form-wizard-icon-list-item a:not(.active)"))}}initializeOnReady(e,t){DocumentService.ready().then((()=>{this.initialize(e,t)}))}}export default new SelectSingleElement; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/link-popup.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/link-popup.js index 2ff0b89b99d9..d0c0966a16e6 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/link-popup.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/link-popup.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";import Modal from"@typo3/backend/modal.js";class LinkPopup{constructor(e){this.controlElement=null,this.handleControlClick=e=>{e.preventDefault();const t=this.controlElement.dataset.itemName,o=this.controlElement.getAttribute("href")+"&P[currentValue]="+encodeURIComponent(document.forms.namedItem("editform")[t].value)+"&P[currentSelectedValues]="+encodeURIComponent(FormEngine.getFieldElement(t).val());Modal.advanced({type:Modal.types.iframe,content:o,size:Modal.sizes.large})},DocumentService.ready().then((()=>{this.controlElement=document.querySelector(e),this.controlElement.addEventListener("click",this.handleControlClick)}))}}export default LinkPopup; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";import Modal from"@typo3/backend/modal.js";class LinkPopup{constructor(e){this.controlElement=null,this.handleControlClick=e=>{e.preventDefault();const t=this.controlElement.dataset.itemName,o=this.controlElement.getAttribute("href")+"&P[currentValue]="+encodeURIComponent(document.forms.namedItem("editform")[t].value)+"&P[currentSelectedValues]="+encodeURIComponent(FormEngine.getFieldElement(t).val());Modal.advanced({type:Modal.types.iframe,content:o,size:Modal.sizes.large})},DocumentService.ready().then((()=>{this.controlElement=document.querySelector(e),null!==this.controlElement&&this.controlElement.addEventListener("click",this.handleControlClick)}))}}export default LinkPopup; \ No newline at end of file -- GitLab