Commit 211821fb authored by Andreas Fernandez's avatar Andreas Fernandez Committed by Richard Haeser

[TASK] Replace FormEngine update request invocation

This patchs adds a new LitElement that invokes the update request of
FormEngine if a field's value with such configuration changes. This
replaces the current approach of huge onchange/onclick attributes
rendered per affected field.

A further patch will cleanup the necessity of TBE_EDITOR.fieldChanged().

Resolves: #93899
Releases: master
Change-Id: I79aa5b6ab7cca9b555efaafcadabcc623567af64
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/68756Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Richard Haeser's avatarRichard Haeser <richard@richardhaeser.com>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Richard Haeser's avatarRichard Haeser <richard@richardhaeser.com>
parent 23644892
......@@ -93,6 +93,7 @@ export class SelectTreeElement {
// check all nodes again, to ensure correct display of indeterminate state
this.calculateIndeterminate(this.tree.nodes);
this.saveCheckboxes();
this.tree.setup.input.dispatchEvent(new Event('change', {bubbles: true, cancelable: true}));
}
/**
......
/*
* 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 {customElement, property, LitElement} from 'lit-element';
import FormEngine = require('TYPO3/CMS/Backend/FormEngine');
enum UpdateMode {
ask = 'ask',
enforce = 'enforce'
}
const selectorConverter = {
fromAttribute(selector: string) {
return document.querySelectorAll(selector);
}
};
@customElement('typo3-formengine-updater')
class RequestUpdate extends LitElement {
@property({type: String, attribute: 'mode'}) mode: String = UpdateMode.ask;
@property({attribute: 'field', converter: selectorConverter}) fields: NodeList;
public connectedCallback(): void {
super.connectedCallback();
for (let field of this.fields) {
field.addEventListener('change', this.requestFormEngineUpdate);
}
}
public disconnectedCallback(): void {
super.disconnectedCallback();
for (let field of this.fields) {
field.removeEventListener('change', this.requestFormEngineUpdate);
}
}
private requestFormEngineUpdate = (): void => {
const askForUpdate = this.mode === UpdateMode.ask;
FormEngine.requestFormEngineUpdate(askForUpdate);
}
}
......@@ -66,6 +66,7 @@ declare namespace TYPO3 {
public openPopupWindow(mode: string, params: string): JQuery;
public initializeNullNoPlaceholderCheckboxes(): void;
public initializeNullWithPlaceholderCheckboxes(): void;
public requestFormEngineUpdate(askForUpdate: boolean): void
}
export class MultiStepWizard {
......
......@@ -116,26 +116,6 @@ class SingleFieldContainer extends AbstractContainer
} else {
$typeField = substr($this->data['processedTca']['ctrl']['type'], 0, strpos($this->data['processedTca']['ctrl']['type'], ':'));
}
// Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
// This is used for eg. "type" fields and others configured with "onChange"
if (!empty($this->data['processedTca']['ctrl']['type']) && $fieldName === $typeField
|| isset($parameterArray['fieldConf']['onChange']) && $parameterArray['fieldConf']['onChange'] === 'reload'
) {
if ($backendUser->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
$alertMsgOnChange = 'Modal.confirm('
. 'TYPO3.lang["FormEngine.refreshRequiredTitle"],'
. ' TYPO3.lang["FormEngine.refreshRequiredContent"]'
. ')'
. '.on('
. '"button.clicked",'
. ' function(e) { if (e.target.name == "ok") { FormEngine.saveDocument(); } Modal.dismiss(); }'
. ');';
} else {
$alertMsgOnChange = 'FormEngine.saveDocument();';
}
} else {
$alertMsgOnChange = '';
}
// JavaScript code for event handlers:
$parameterArray['fieldChangeFunc'] = [];
......@@ -145,9 +125,6 @@ class SingleFieldContainer extends AbstractContainer
. GeneralUtility::quoteJSvalue($fieldName) . ','
. GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'])
. ');';
if ($alertMsgOnChange) {
$parameterArray['fieldChangeFunc']['alert'] = 'require([\'TYPO3/CMS/Backend/FormEngine\', \'TYPO3/CMS/Backend/Modal\'], function (FormEngine, Modal) {' . $alertMsgOnChange . '});';
}
// Based on the type of the item, call a render function on a child element
$options = $this->data;
......@@ -160,6 +137,18 @@ class SingleFieldContainer extends AbstractContainer
$options['renderType'] = $parameterArray['fieldConf']['config']['type'];
}
$resultArray = $this->nodeFactory->create($options)->render();
// Render a custom HTML element which will ask the user to save/update the form due to changing the element.
// This is used for eg. "type" fields and others configured with "onChange"
$requestFormEngineUpdate =
(!empty($this->data['processedTca']['ctrl']['type']) && $fieldName === $typeField)
|| (isset($parameterArray['fieldConf']['onChange']) && $parameterArray['fieldConf']['onChange'] === 'reload');
if ($requestFormEngineUpdate) {
$askForUpdate = $backendUser->jsConfirmation(JsConfirmation::TYPE_CHANGE);
$requestMode = $askForUpdate ? 'ask' : 'enforce';
$fieldSelector = sprintf('[name="%s"]', $parameterArray['itemFormElName']);
$resultArray['html'] .= '<typo3-formengine-updater mode="' . htmlspecialchars($requestMode) . '" field="' . htmlspecialchars($fieldSelector) . '"></typo3-formengine-updater>';
}
return $resultArray;
}
......
......@@ -339,7 +339,9 @@ abstract class AbstractFormElement extends AbstractNode
$checked = !$checked;
}
$onClick = $elementName . '.value=' . ($invert ? '!' : '') . 'this.checked?(' . $elementName . '.value|' . $checkboxPow . '):('
. $elementName . '.value&' . ((2 ** $checkboxesCount) - 1 - $checkboxPow) . ');' . $additionalJavaScript;
. $elementName . '.value&' . ((2 ** $checkboxesCount) - 1 - $checkboxPow) . ');';
$onClick .= $elementName . '.dispatchEvent(new Event(\'change\', {bubbles: true, cancelable: true}));';
$onClick .= $additionalJavaScript;
return ' onclick="' . htmlspecialchars($onClick) . '"' . ($checked ? ' checked="checked"' : '');
}
......
......@@ -220,6 +220,8 @@ class FormResultCompiler
}
}
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/FormEngine/RequestUpdate');
// todo: change these things in JS
$pageRenderer->addInlineLanguageLabelArray([
'FormEngine.noRecordTitle' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title'),
......
......@@ -698,25 +698,30 @@ define(['jquery',
};
FormEngine.requestConfirmationOnFieldChange = function(fieldName, showConfirmation) {
console.warn('FormEngine.requestConfirmationOnFieldChange() has been marked as deprecated without substitution. Configure the TCA of field "' + fieldName + '" properly to request an FormEngine update on it\'s own.');
const $field = FormEngine.getFieldElement(fieldName);
$field.on('change', function() {
if (showConfirmation) {
const $modal = Modal.confirm(
TYPO3.lang['FormEngine.refreshRequiredTitle'],
TYPO3.lang['FormEngine.refreshRequiredContent']
);
FormEngine.requestFormEngineUpdate(showConfirmation);
});
}
$modal.on('button.clicked', function(e) {
if (e.target.name === 'ok') {
FormEngine.saveDocument();
}
FormEngine.requestFormEngineUpdate = function(showConfirmation) {
if (showConfirmation) {
const $modal = Modal.confirm(
TYPO3.lang['FormEngine.refreshRequiredTitle'],
TYPO3.lang['FormEngine.refreshRequiredContent']
);
Modal.dismiss();
});
} else {
FormEngine.saveDocument();
}
});
$modal.on('button.clicked', function(e) {
if (e.target.name === 'ok') {
FormEngine.saveDocument();
}
Modal.dismiss();
});
} else {
FormEngine.saveDocument();
}
};
/**
......
......@@ -10,7 +10,7 @@
*
* The TYPO3 project - inspiring people to share!
*/
var __decorate=this&&this.__decorate||function(e,t,i,r){var a,n=arguments.length,s=n<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,i):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,i,r);else for(var d=e.length-1;d>=0;d--)(a=e[d])&&(s=(n<3?a(s):n>3?a(t,i,s):a(t,i))||s);return n>3&&s&&Object.defineProperty(t,i,s),s};define(["require","exports","bootstrap","lit-element","TYPO3/CMS/Core/lit-helper","TYPO3/CMS/Backend/FormEngineValidation","TYPO3/CMS/Backend/Element/IconElement","./SelectTree"],(function(e,t,i,r,a,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SelectTreeElement=void 0;t.SelectTreeElement=class{constructor(e,t,i){this.recordField=null,this.tree=null,this.selectNode=e=>{const t=e.detail.node;this.updateAncestorsIndeterminateState(t),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes()},this.loadDataAfter=()=>{this.tree.nodes=this.tree.nodes.map(e=>(e.indeterminate=!1,e)),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),n.validateField(this.recordField)},this.saveCheckboxes=()=>{void 0!==this.recordField&&(this.recordField.value=this.tree.getSelectedNodes().map(e=>e.identifier).join(","))},this.recordField=document.getElementById(t);const r=document.getElementById(e);this.tree=document.createElement("typo3-backend-form-selecttree"),this.tree.classList.add("svg-tree-wrapper"),this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.loadDataAfter),this.tree.addEventListener("typo3:svg-tree:node-selected",this.selectNode),this.tree.addEventListener("typo3:svg-tree:node-selected",()=>{i()});const a={dataUrl:this.generateRequestUrl(),readOnlyMode:1===parseInt(this.recordField.dataset.readOnly,10),input:this.recordField,exclusiveNodesIdentifiers:this.recordField.dataset.treeExclusiveKeys,validation:JSON.parse(this.recordField.dataset.formengineValidationRules)[0],expandUpToLevel:this.recordField.dataset.treeExpandUpToLevel,unselectableElements:[]};this.tree.addEventListener("svg-tree:initialized",()=>{const e=document.createElement("typo3-backend-form-selecttree-toolbar");e.tree=this.tree,this.tree.prepend(e)}),this.tree.setup=a,r.append(this.tree),this.listenForVisibleTree()}listenForVisibleTree(){if(!this.tree.offsetParent){let e=this.tree.closest(".tab-pane").getAttribute("id");if(e){document.querySelector('[aria-controls="'+e+'"]').addEventListener("shown.bs.tab",()=>{this.tree.dispatchEvent(new Event("svg-tree:visible"))})}}}generateRequestUrl(){const e={tableName:this.recordField.dataset.tablename,fieldName:this.recordField.dataset.fieldname,uid:this.recordField.dataset.uid,recordTypeValue:this.recordField.dataset.recordtypevalue,dataStructureIdentifier:this.recordField.dataset.datastructureidentifier,flexFormSheetName:this.recordField.dataset.flexformsheetname,flexFormFieldName:this.recordField.dataset.flexformfieldname,flexFormContainerName:this.recordField.dataset.flexformcontainername,flexFormContainerIdentifier:this.recordField.dataset.flexformcontaineridentifier,flexFormContainerFieldName:this.recordField.dataset.flexformcontainerfieldname,flexFormSectionContainerIsNew:this.recordField.dataset.flexformsectioncontainerisnew,command:this.recordField.dataset.command};return TYPO3.settings.ajaxUrls.record_tree_data+"&"+new URLSearchParams(e)}updateAncestorsIndeterminateState(e){let t=!1;e.parents.forEach(e=>{const i=this.tree.nodes[e];i.indeterminate=i.checked||i.indeterminate||t,t=i.checked||i.indeterminate||i.checked||i.indeterminate})}calculateIndeterminate(e){e.forEach(t=>{(t.checked||t.indeterminate)&&t.parents&&t.parents.length>0&&t.parents.forEach(t=>{e[t].indeterminate=!0})})}};let s=class extends r.LitElement{constructor(){super(...arguments),this.settings={collapseAllBtn:"collapse-all-btn",expandAllBtn:"expand-all-btn",searchInput:"search-input",toggleHideUnchecked:"hide-unchecked-btn"},this.hideUncheckedState=!1}createRenderRoot(){return this}firstUpdated(){this.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(e=>new i.Tooltip(e))}render(){return r.html`
var __decorate=this&&this.__decorate||function(e,t,i,r){var a,n=arguments.length,s=n<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,i):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,i,r);else for(var d=e.length-1;d>=0;d--)(a=e[d])&&(s=(n<3?a(s):n>3?a(t,i,s):a(t,i))||s);return n>3&&s&&Object.defineProperty(t,i,s),s};define(["require","exports","bootstrap","lit-element","TYPO3/CMS/Core/lit-helper","TYPO3/CMS/Backend/FormEngineValidation","TYPO3/CMS/Backend/Element/IconElement","./SelectTree"],(function(e,t,i,r,a,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SelectTreeElement=void 0;t.SelectTreeElement=class{constructor(e,t,i){this.recordField=null,this.tree=null,this.selectNode=e=>{const t=e.detail.node;this.updateAncestorsIndeterminateState(t),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),this.tree.setup.input.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},this.loadDataAfter=()=>{this.tree.nodes=this.tree.nodes.map(e=>(e.indeterminate=!1,e)),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),n.validateField(this.recordField)},this.saveCheckboxes=()=>{void 0!==this.recordField&&(this.recordField.value=this.tree.getSelectedNodes().map(e=>e.identifier).join(","))},this.recordField=document.getElementById(t);const r=document.getElementById(e);this.tree=document.createElement("typo3-backend-form-selecttree"),this.tree.classList.add("svg-tree-wrapper"),this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.loadDataAfter),this.tree.addEventListener("typo3:svg-tree:node-selected",this.selectNode),this.tree.addEventListener("typo3:svg-tree:node-selected",()=>{i()});const a={dataUrl:this.generateRequestUrl(),readOnlyMode:1===parseInt(this.recordField.dataset.readOnly,10),input:this.recordField,exclusiveNodesIdentifiers:this.recordField.dataset.treeExclusiveKeys,validation:JSON.parse(this.recordField.dataset.formengineValidationRules)[0],expandUpToLevel:this.recordField.dataset.treeExpandUpToLevel,unselectableElements:[]};this.tree.addEventListener("svg-tree:initialized",()=>{const e=document.createElement("typo3-backend-form-selecttree-toolbar");e.tree=this.tree,this.tree.prepend(e)}),this.tree.setup=a,r.append(this.tree),this.listenForVisibleTree()}listenForVisibleTree(){if(!this.tree.offsetParent){let e=this.tree.closest(".tab-pane").getAttribute("id");if(e){document.querySelector('[aria-controls="'+e+'"]').addEventListener("shown.bs.tab",()=>{this.tree.dispatchEvent(new Event("svg-tree:visible"))})}}}generateRequestUrl(){const e={tableName:this.recordField.dataset.tablename,fieldName:this.recordField.dataset.fieldname,uid:this.recordField.dataset.uid,recordTypeValue:this.recordField.dataset.recordtypevalue,dataStructureIdentifier:this.recordField.dataset.datastructureidentifier,flexFormSheetName:this.recordField.dataset.flexformsheetname,flexFormFieldName:this.recordField.dataset.flexformfieldname,flexFormContainerName:this.recordField.dataset.flexformcontainername,flexFormContainerIdentifier:this.recordField.dataset.flexformcontaineridentifier,flexFormContainerFieldName:this.recordField.dataset.flexformcontainerfieldname,flexFormSectionContainerIsNew:this.recordField.dataset.flexformsectioncontainerisnew,command:this.recordField.dataset.command};return TYPO3.settings.ajaxUrls.record_tree_data+"&"+new URLSearchParams(e)}updateAncestorsIndeterminateState(e){let t=!1;e.parents.forEach(e=>{const i=this.tree.nodes[e];i.indeterminate=i.checked||i.indeterminate||t,t=i.checked||i.indeterminate||i.checked||i.indeterminate})}calculateIndeterminate(e){e.forEach(t=>{(t.checked||t.indeterminate)&&t.parents&&t.parents.length>0&&t.parents.forEach(t=>{e[t].indeterminate=!0})})}};let s=class extends r.LitElement{constructor(){super(...arguments),this.settings={collapseAllBtn:"collapse-all-btn",expandAllBtn:"expand-all-btn",searchInput:"search-input",toggleHideUnchecked:"hide-unchecked-btn"},this.hideUncheckedState=!1}createRenderRoot(){return this}firstUpdated(){this.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(e=>new i.Tooltip(e))}render(){return r.html`
<div class="tree-toolbar btn-toolbar">
<div class="input-group">
<span class="input-group-addon input-group-icon filter">
......
/*
* 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=this&&this.__decorate||function(e,t,r,o){var n,c=arguments.length,i=c<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(e,t,r,o);else for(var s=e.length-1;s>=0;s--)(n=e[s])&&(i=(c<3?n(i):c>3?n(t,r,i):n(t,r))||i);return c>3&&i&&Object.defineProperty(t,r,i),i};define(["require","exports","lit-element","TYPO3/CMS/Backend/FormEngine"],(function(e,t,r,o){"use strict";var n;Object.defineProperty(t,"__esModule",{value:!0}),function(e){e.ask="ask",e.enforce="enforce"}(n||(n={}));const c={fromAttribute:e=>document.querySelectorAll(e)};let i=class extends r.LitElement{constructor(){super(...arguments),this.mode=n.ask,this.requestFormEngineUpdate=()=>{const e=this.mode===n.ask;o.requestFormEngineUpdate(e)}}connectedCallback(){super.connectedCallback();for(let e of this.fields)e.addEventListener("change",this.requestFormEngineUpdate)}disconnectedCallback(){super.disconnectedCallback();for(let e of this.fields)e.removeEventListener("change",this.requestFormEngineUpdate)}};__decorate([r.property({type:String,attribute:"mode"})],i.prototype,"mode",void 0),__decorate([r.property({attribute:"field",converter:c})],i.prototype,"fields",void 0),i=__decorate([r.customElement("typo3-formengine-updater")],i)}));
\ No newline at end of file
.. include:: ../../Includes.txt
==============================================================================
Deprecation: #93899 - FormEngine's requestConfirmationOnFieldChange deprecated
==============================================================================
See :issue:`93899`
Description
===========
FormEngine's JavaScript method :js:`FormEngine.requestConfirmationOnFieldChange()`
to register and trigger the update request when a field's value changes, has
been marked as deprecated.
Impact
======
Calling the method :js:`FormEngine.requestConfirmationOnFieldChange()` will
trigger a deprecation warning in the browser's console.
Affected Installations
======================
Any 3rd party extension using the aforementioned method is affected.
Migration
=========
There is no migration available. If a field is properly configured to update the
FormEngine after updating a field, a LitElement is rendered into DOM which will
handle the update request.
.. index:: Backend, JavaScript, NotScanned, ext:backend
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment