Commit 66302a14 authored by Andreas Fernandez's avatar Andreas Fernandez Committed by Frank Nägler
Browse files

[!!!][TASK] Remove `additionalJavaScriptSubmit` from FormEngine

With RequireJS in place, it is possible to minimize the amount of inline
JavaScript in the backend. This patch removes FormEngine's
`additionalJavaScriptSubmit` feature that registers additional submit
handlers and allows to abort the form submission.

This feature is rarely used at all, but can be achieved using a RequireJS
module that registers a callback in `TYPO3/CMS/Backend/DocumentSaveActions`.

Since the feature is gone, a lot of global code within TBE_EDITOR is
removed as well.

Resolves: #88667
Releases: master
Change-Id: I92f26aa52cafab5df30a706ea95f1e1702a4a778
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/60861

Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Frank Nägler's avatarFrank Naegler <frank.naegler@typo3.org>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
Reviewed-by: Frank Nägler's avatarFrank Naegler <frank.naegler@typo3.org>
parent b72e2a51
......@@ -106,6 +106,7 @@ class DocumentSaveActions {
});
if ((e.currentTarget.tagName === 'A' || $me.attr('form')) && !e.isDefaultPrevented()) {
$form.find('[name="doSave"]').val('1');
$form.submit();
e.preventDefault();
}
......
......@@ -1881,7 +1881,6 @@ class EditDocumentController
enctype="multipart/form-data"
name="editform"
id="EditDocumentController"
onsubmit="TBE_EDITOR.checkAndDoSubmit(1); return false;"
>
' . $editForm . '
<input type="hidden" name="returnUrl" value="' . htmlspecialchars($this->retUrl) . '" />
......
......@@ -134,11 +134,6 @@ class FormFlexAjaxController extends AbstractFormEngineAjaxController
'scriptCall' => [],
];
if (!empty($newContainerResult['additionalJavaScriptSubmit'])) {
$additionalJavaScriptSubmit = implode('', $newContainerResult['additionalJavaScriptSubmit']);
$additionalJavaScriptSubmit = str_replace([CR, LF], '', $additionalJavaScriptSubmit);
$jsonResult['scriptCall'][] = 'TBE_EDITOR.addActionChecks("submit", "' . addslashes($additionalJavaScriptSubmit) . '");';
}
foreach ($newContainerResult['additionalJavaScriptPost'] as $singleAdditionalJavaScriptPost) {
$jsonResult['scriptCall'][] = $singleAdditionalJavaScriptPost;
}
......
......@@ -529,11 +529,6 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
if (!empty($childResult['inlineData'])) {
$jsonResult['inlineData'] = $childResult['inlineData'];
}
if (!empty($childResult['additionalJavaScriptSubmit'])) {
$additionalJavaScriptSubmit = implode('', $childResult['additionalJavaScriptSubmit']);
$additionalJavaScriptSubmit = str_replace([CR, LF], '', $additionalJavaScriptSubmit);
$jsonResult['scriptCall'][] = 'TBE_EDITOR.addActionChecks("submit", "' . addslashes($additionalJavaScriptSubmit) . '");';
}
foreach ($childResult['additionalJavaScriptPost'] as $singleAdditionalJavaScriptPost) {
$jsonResult['scriptCall'][] = $singleAdditionalJavaScriptPost;
}
......
......@@ -295,11 +295,6 @@ class SiteInlineAjaxController extends AbstractFormEngineAjaxController
if (!empty($childResult['inlineData'])) {
$jsonResult['inlineData'] = $childResult['inlineData'];
}
if (!empty($childResult['additionalJavaScriptSubmit'])) {
$additionalJavaScriptSubmit = implode('', $childResult['additionalJavaScriptSubmit']);
$additionalJavaScriptSubmit = str_replace([CR, LF], '', $additionalJavaScriptSubmit);
$jsonResult['scriptCall'][] = 'TBE_EDITOR.addActionChecks("submit", "' . addslashes($additionalJavaScriptSubmit) . '");';
}
foreach ($childResult['additionalJavaScriptPost'] as $singleAdditionalJavaScriptPost) {
$jsonResult['scriptCall'][] = $singleAdditionalJavaScriptPost;
}
......
......@@ -94,7 +94,6 @@ abstract class AbstractNode implements NodeInterface, LoggerAwareInterface
{
return [
'additionalJavaScriptPost' => [],
'additionalJavaScriptSubmit' => [],
'additionalHiddenFields' => [],
'additionalInlineLanguageLabelFiles' => [],
'stylesheetFiles' => [],
......@@ -125,9 +124,6 @@ abstract class AbstractNode implements NodeInterface, LoggerAwareInterface
foreach ($childReturn['additionalJavaScriptPost'] ?? [] as $value) {
$existing['additionalJavaScriptPost'][] = $value;
}
foreach ($childReturn['additionalJavaScriptSubmit'] ?? [] as $value) {
$existing['additionalJavaScriptSubmit'][] = $value;
}
foreach ($childReturn['additionalHiddenFields'] ?? [] as $value) {
$existing['additionalHiddenFields'][] = $value;
}
......
......@@ -59,14 +59,6 @@ class FormResultCompiler
*/
protected $additionalJavaScriptPost = [];
/**
* Additional JavaScript executed on submit; If you set "OK" variable it will raise an error
* about RTEs not being loaded and offer to block further submission.
*
* @var array
*/
protected $additionalJavaScriptSubmit = [];
/**
* Additional language label files to include.
*
......@@ -99,9 +91,6 @@ class FormResultCompiler
foreach ($resultArray['additionalJavaScriptPost'] as $element) {
$this->additionalJavaScriptPost[] = $element;
}
foreach ($resultArray['additionalJavaScriptSubmit'] as $element) {
$this->additionalJavaScriptSubmit[] = $element;
}
if (!empty($resultArray['requireJsModules'])) {
foreach ($resultArray['requireJsModules'] as $module) {
$moduleName = null;
......@@ -112,11 +101,8 @@ class FormResultCompiler
$callback = null;
} elseif (is_array($module)) {
// if $module is an array, callback is possible
foreach ($module as $key => $value) {
$moduleName = $key;
$callback = $value;
break;
}
$callback = reset($module);
$moduleName = key($module);
}
if ($moduleName !== null) {
if (!empty($this->requireJsModules[$moduleName]) && $callback !== null) {
......@@ -251,19 +237,11 @@ class FormResultCompiler
'FormEngine.remainingCharacters' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.remainingCharacters'),
]);
$out = LF . 'TBE_EDITOR.doSaveFieldName = "' . ($this->doSaveFieldName ? addslashes($this->doSaveFieldName) : '') . '";';
// Add JS required for inline fields
if (!empty($this->inlineData)) {
$pageRenderer->addInlineSettingArray('FormEngineInline', $this->inlineData);
}
// $this->additionalJS_submit:
if ($this->additionalJavaScriptSubmit) {
$additionalJS_submit = implode('', $this->additionalJavaScriptSubmit);
$additionalJS_submit = str_replace([CR, LF], '', $additionalJS_submit);
$out .= 'TBE_EDITOR.addActionChecks("submit", "' . addslashes($additionalJS_submit) . '");';
}
$out .= LF . implode(LF, $this->additionalJavaScriptPost);
$out = LF . implode(LF, $this->additionalJavaScriptPost);
return $html . LF . "\t" . GeneralUtility::wrapJS($out);
}
......
......@@ -5,7 +5,6 @@
enctype="multipart/form-data"
name="editform"
id="siteConfigurationController"
onsubmit="TBE_EDITOR.checkAndDoSubmit(1); return false;"
>
{formEngineHtml -> f:format.raw()}
......
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
define(["require","exports","jquery","./Icons"],function(t,e,n,a){"use strict";return function(){function t(){var t=this;this.preSubmitCallbacks=[],n(function(){t.initializeSaveHandling()})}return t.getInstance=function(){return null===t.instance&&(t.instance=new t),t.instance},t.prototype.addPreSubmitCallback=function(t){if("function"!=typeof t)throw"callback must be a function.";this.preSubmitCallbacks.push(t)},t.prototype.initializeSaveHandling=function(){var t=this,e=!1,r=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(",");n(".t3js-module-docheader").on("click",r,function(r){if(!e){e=!0;for(var i=n(r.currentTarget),u=i.attr("form")||i.attr("data-form")||null,o=u?n("#"+u):i.closest("form"),l=i.data("name")||r.currentTarget.getAttribute("name"),s=i.data("value")||r.currentTarget.getAttribute("value"),c=n("<input />").attr("type","hidden").attr("name",l).attr("value",s),f=0;f<t.preSubmitCallbacks.length;++f)if(t.preSubmitCallbacks[f](r),r.isPropagationStopped())return e=!1,!1;o.append(c),o.on("submit",function(){if(o.find(".has-error").length>0)return e=!1,!1;var t,n=i.closest(".t3js-splitbutton");return n.length>0?(n.find("button").prop("disabled",!0),t=n.children().first()):(i.prop("disabled",!0),t=i),a.getIcon("spinner-circle-dark",a.sizes.small).done(function(e){t.find(".t3js-icon").replaceWith(e)}),!0}),"A"!==r.currentTarget.tagName&&!i.attr("form")||r.isDefaultPrevented()||(o.submit(),r.preventDefault())}return!0})},t.instance=null,t}()});
\ No newline at end of file
define(["require","exports","jquery","./Icons"],function(t,e,n,a){"use strict";return function(){function t(){var t=this;this.preSubmitCallbacks=[],n(function(){t.initializeSaveHandling()})}return t.getInstance=function(){return null===t.instance&&(t.instance=new t),t.instance},t.prototype.addPreSubmitCallback=function(t){if("function"!=typeof t)throw"callback must be a function.";this.preSubmitCallbacks.push(t)},t.prototype.initializeSaveHandling=function(){var t=this,e=!1,r=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(",");n(".t3js-module-docheader").on("click",r,function(r){if(!e){e=!0;for(var i=n(r.currentTarget),u=i.attr("form")||i.attr("data-form")||null,o=u?n("#"+u):i.closest("form"),l=i.data("name")||r.currentTarget.getAttribute("name"),s=i.data("value")||r.currentTarget.getAttribute("value"),c=n("<input />").attr("type","hidden").attr("name",l).attr("value",s),d=0;d<t.preSubmitCallbacks.length;++d)if(t.preSubmitCallbacks[d](r),r.isPropagationStopped())return e=!1,!1;o.append(c),o.on("submit",function(){if(o.find(".has-error").length>0)return e=!1,!1;var t,n=i.closest(".t3js-splitbutton");return n.length>0?(n.find("button").prop("disabled",!0),t=n.children().first()):(i.prop("disabled",!0),t=i),a.getIcon("spinner-circle-dark",a.sizes.small).done(function(e){t.find(".t3js-icon").replaceWith(e)}),!0}),"A"!==r.currentTarget.tagName&&!i.attr("form")||r.isDefaultPrevented()||(o.find('[name="doSave"]').val("1"),o.submit(),r.preventDefault())}return!0})},t.instance=null,t}()});
\ No newline at end of file
......@@ -525,7 +525,6 @@ define(['jquery',
e.preventDefault();
FormEngine.deleteAction(e, FormEngine.deleteActionCallback);
}).on('click', '.t3js-editform-submitButton', function(event) {
// remember the clicked submit button. we need to know that in TBE_EDITOR.submitForm();
var $me = $(this),
name = $me.data('name') || this.name,
$elem = $('<input />').attr('type', 'hidden').attr('name', name).attr('value', '1');
......@@ -1233,8 +1232,12 @@ define(['jquery',
* @param {Number} mode
*/
FormEngine.initialize = function(browserUrl, mode) {
// This is required to register the click handler
DocumentSaveActions.getInstance();
DocumentSaveActions.getInstance().addPreSubmitCallback(function() {
$('[data-active-password]:not([type="password"])').each(function(index, element) {
element.setAttribute('type', 'password');
element.blur();
});
});
FormEngine.browserUrl = browserUrl;
FormEngine.Validation.setUsMode(mode);
......
......@@ -32,14 +32,11 @@ var TBE_EDITOR = {
elements: {},
nested: {'field': {}, 'level': {}},
ignoreElements: [],
actionChecks: {submit: []},
customEvalFunctions: {},
formname: 'editform',
isChanged: 0,
doSaveFieldName: 0,
labels: {},
clearBeforeSettingFormValueFromBrowseWin: [],
......@@ -59,13 +56,6 @@ var TBE_EDITOR = {
return result;
},
checkElements: function() {
return (document.getElementsByClassName('has-error').length == 0);
},
addActionChecks: function(type, checks) {
TBE_EDITOR.actionChecks[type].push(checks);
},
fieldChanged_fName: function(fName, el) {
var idx = 2;
var table = TBE_EDITOR.split(fName, "[", idx);
......@@ -101,90 +91,6 @@ var TBE_EDITOR = {
}
return TBE_EDITOR.isChanged;
},
checkAndDoSubmit: function(sendAlert) {
if (TBE_EDITOR.checkSubmit(sendAlert)) {
TBE_EDITOR.submitForm();
}
},
/**
* Checks if the form can be submitted according to any possible restrains like required values, item numbers etc.
* Returns true if the form can be submitted, otherwise false (and might issue an alert message, if "sendAlert" is 1)
* If "sendAlert" is false, no error message will be shown upon false return value (if "1" then it will).
* If "sendAlert" is "-1" then the function will ALWAYS return true regardless of constraints (except if login has expired) - this is used in the case where a form field change requests a form update and where it is accepted that constraints are not observed (form layout might change so other fields are shown...)
*/
checkSubmit: function(sendAlert) {
var funcIndex, funcMax, funcRes;
var OK = 1;
var STOP = 0;
// $this->additionalJS_submit:
if (TBE_EDITOR.actionChecks && TBE_EDITOR.actionChecks.submit) {
for (funcIndex = 0, funcMax = TBE_EDITOR.actionChecks.submit.length; funcIndex < funcMax; funcIndex++) {
try {
eval(TBE_EDITOR.actionChecks.submit[funcIndex]);
} catch (error) {
}
}
}
if (STOP) {
// return false immediately, if the code in additionalJS_submit set STOP variable.
return false;
}
if (!OK) {
if (!confirm(unescape("SYSTEM ERROR: One or more Rich Text Editors on the page could not be contacted. This IS an error, although it should not be regular.\nYou can save the form now by pressing OK, but you will loose the Rich Text Editor content if you do.\n\nPlease report the error to your administrator if it persists."))) {
return false;
} else {
OK = 1;
}
}
if (!TBE_EDITOR.checkElements()) {
OK = 0;
}
if (OK || sendAlert == -1) {
return true;
} else {
if (sendAlert) {
var t = (opener != null && typeof opener.top.TYPO3 !== 'undefined' ? opener.top : top);
t.TYPO3.Modal.confirm(
t.TYPO3.lang['alert'] || 'Alert',
TYPO3.lang['FormEngine.fieldsMissing'],
t.TYPO3.Severity.error,
[
{
text: t.TYPO3.lang['button.ok'] || 'OK',
active: true,
btnClass: 'btn-default',
name: 'ok'
}
]
).on('button.clicked', function(e) {
t.TYPO3.Modal.dismiss();
});
}
return false;
}
},
submitForm: function() {
if (TBE_EDITOR.doSaveFieldName) {
document[TBE_EDITOR.formname][TBE_EDITOR.doSaveFieldName].value = 1;
}
// Set a short timeout to allow other JS processes to complete, in particular those from
// EXT:backend/Resources/Public/JavaScript/FormEngine.js (reference: http://forge.typo3.org/issues/58755).
// TODO: This should be solved in a better way when this script is refactored.
window.setTimeout(function() {
var formElement = document.getElementsByName(TBE_EDITOR.formname).item(0);
$('[data-active-password]:not([type=password])').each(
function(index, element) {
element.setAttribute('type', 'password');
element.blur();
}
);
formElement.submit();
}, 100);
},
split: function(theStr1, delim, index) {
var theStr = "" + theStr1;
var lengthOfDelim = delim.length;
......@@ -250,8 +156,6 @@ var TBE_EDITOR_isChanged = TBE_EDITOR.isChanged;
var TBE_EDITOR_fieldChanged_fName = TBE_EDITOR.fieldChanged_fName;
var TBE_EDITOR_fieldChanged = TBE_EDITOR.fieldChanged;
var TBE_EDITOR_isFormChanged = TBE_EDITOR.isFormChanged;
var TBE_EDITOR_checkAndDoSubmit = TBE_EDITOR.checkAndDoSubmit;
var TBE_EDITOR_checkSubmit = TBE_EDITOR.checkSubmit;
var TBE_EDITOR_submitForm = TBE_EDITOR.submitForm;
var TBE_EDITOR_split = TBE_EDITOR.split;
var TBE_EDITOR_curSelected = TBE_EDITOR.curSelected;
......
......@@ -37,7 +37,6 @@ class PaletteAndSingleContainerTest extends UnitTestCase
$singleFieldContainerProphecy = $this->prophesize(SingleFieldContainer::class);
$singleFieldContainerReturn = [
'additionalJavaScriptPost' => [],
'additionalJavaScriptSubmit' => [],
'additionalHiddenFields' => [],
'additionalInlineLanguageLabelFiles' => [],
'stylesheetFiles' => [],
......@@ -94,7 +93,6 @@ class PaletteAndSingleContainerTest extends UnitTestCase
$singleFieldContainerProphecy = $this->prophesize(SingleFieldContainer::class);
$singleFieldContainerReturn = [
'additionalJavaScriptPost' => [],
'additionalJavaScriptSubmit' => [],
'additionalHiddenFields' => [],
'additionalInlineLanguageLabelFiles' => [],
'stylesheetFiles' => [],
......@@ -152,7 +150,6 @@ class PaletteAndSingleContainerTest extends UnitTestCase
$singleFieldContainerProphecy = $this->prophesize(SingleFieldContainer::class);
$singleFieldContainerReturn = [
'additionalJavaScriptPost' => [],
'additionalJavaScriptSubmit' => [],
'additionalHiddenFields' => [],
'additionalInlineLanguageLabelFiles' => [],
'stylesheetFiles' => [],
......
......@@ -115,7 +115,6 @@ class InputDateTimeElementTest extends UnitTestCase
$abstractNode = $this->prophesize(AbstractNode::class);
$abstractNode->render(Argument::cetera())->willReturn([
'additionalJavaScriptPost' => [],
'additionalJavaScriptSubmit' => [],
'additionalHiddenFields' => [],
'stylesheetFiles' => [],
]);
......
......@@ -96,7 +96,6 @@ class FieldControlTest extends UnitTestCase
'additionalJavaScriptPost' => [
'someJavaScript',
],
'additionalJavaScriptSubmit' => [],
'additionalHiddenFields' => [],
'additionalInlineLanguageLabelFiles' => [],
'stylesheetFiles' => [],
......
.. include:: ../../Includes.txt
=====================================================================
Breaking: #88667 - Removed additionalJavaScriptSubmit from FormEngine
=====================================================================
See :issue:`88667`
Description
===========
FormEngine had the feature to add additional submit handlers via the option :php:`additionalJavaScriptSubmit`, that can
be set by form element renderables. TYPO3 uses RequireJS and a rewritten FormEngine since version 7, the property
:php:`additionalJavaScriptSubmit` has been removed.
Additional, functions of :js:`TBE_EDITOR` that are associated with that feature (namely :js:`addActionChecks`) were removed as well.
Impact
======
The option has no effect anymore, the code won't get executed at all.
Affected Installations
======================
All 3rd-party extensions using this option are affected.
Migration
=========
It is possible to create and register a AMD module.
.. code-block:: php
$resultArray['requireJsModules'][] = 'TYPO3/CMS/MyExtension/SubmitHandler';
.. code-block:: javascript
// typo3conf/ext/my_extension/Resources/Public/JavaScript/SubmitHandler.js
define(['TYPO3/CMS/Backend/DocumentSaveActions'], function (DocumentSaveActions) {
DocumentSaveActions.getInstance().addPreSubmitCallback(function (e) {
// e is the submit event
// Do stuff here
// e.stopPropagation() stops the execution chain
});
});
.. index:: Backend, JavaScript, PHP-API, NotScanned, ext:backend
Supports Markdown
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