[!!!][FEATURE] Support Content Security Policy

Do not use any inline style or javascript anymore.
Instead js logic is moved to js file.
Information are passed via data attributes.

Some options are not respected anymore. That's why this change is
breaking. Those options can be considered as obsolete anyway for current
state of the art.

Those options are:

TSConfig:

* options.feedit.popupWidth
* options.feedit.popupHeight

TypoScript:

* stdWrap.editIcons.styleAttribute

Resolves: #5
This commit is contained in:
Daniel Siepmann 2020-01-31 15:07:39 +01:00
parent 5013c755a8
commit 7e2b48aab2
Signed by: Daniel Siepmann
GPG key ID: 33D6629915560EF4
4 changed files with 66 additions and 26 deletions

View file

@ -96,7 +96,7 @@ class FrontendEditPanel
$this->frontendController->set_no_cache('Frontend edit panel is shown', true); $this->frontendController->set_no_cache('Frontend edit panel is shown', true);
$formName = 'TSFE_EDIT_FORM_' . substr($this->frontendController->uniqueHash(), 0, 4); $formName = 'TSFE_EDIT_FORM_' . substr($this->frontendController->uniqueHash(), 0, 4);
$formTag = '<form name="' . $formName . '" id ="' . $formName . '" action="' . htmlspecialchars($this->getReturnUrl($dataArr['uid'] ?? null)) . '" method="post" enctype="multipart/form-data" onsubmit="return TBE_EDITOR.checkSubmit(1);">'; $formTag = '<form name="' . $formName . '" id ="' . $formName . '" action="' . htmlspecialchars($this->getReturnUrl($dataArr['uid'] ?? null)) . '" method="post" enctype="multipart/form-data">';
$sortField = $GLOBALS['TCA'][$table]['ctrl']['sortby']; $sortField = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
$labelField = $GLOBALS['TCA'][$table]['ctrl']['label']; $labelField = $GLOBALS['TCA'][$table]['ctrl']['label'];
$hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']; $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
@ -156,16 +156,16 @@ class FrontendEditPanel
} }
$panel = '<!-- BE_USER Edit Panel: --> $panel = '<!-- BE_USER Edit Panel: -->
' . $formTag . $hiddenFieldString . ' ' . $formTag . $hiddenFieldString . '
<input type="hidden" name="TSFE_EDIT[cmd]" value="" /> <input type="hidden" class="typo3-feedit-cmd" name="TSFE_EDIT[cmd]" value="" />
<input type="hidden" name="TSFE_EDIT[record]" value="' . $currentRecord . '" /> <input type="hidden" name="TSFE_EDIT[record]" value="' . $currentRecord . '" />
<div class="typo3-editPanel">' <div class="typo3-editPanel">'
. '<div class="typo3-editPanel-btn-group">' . '<div class="typo3-editPanel-btn-group">'
. $panel . $panel
. '</div>' . . '</div>' .
($labelTxt ? '<div class="typo3-editPanel-label">' . sprintf($labelTxt, htmlspecialchars(GeneralUtility::fixed_lgd_cs($dataArr[$labelField], 50))) . '</div>' : '') . ' ($labelTxt ? '<div class="typo3-editPanel-label">' . sprintf($labelTxt, htmlspecialchars(GeneralUtility::fixed_lgd_cs($dataArr[$labelField], 50))) . '</div>' : '') . '
</div> </div>
</form>'; </form>';
// Wrap the panel // Wrap the panel
if ($conf['innerWrap']) { if ($conf['innerWrap']) {
@ -215,7 +215,7 @@ class FrontendEditPanel
// Special content is about to be shown, so the cache must be disabled. // Special content is about to be shown, so the cache must be disabled.
$this->frontendController->set_no_cache('Display frontend edit icons', true); $this->frontendController->set_no_cache('Display frontend edit icons', true);
$iconTitle = $this->cObj->stdWrap($conf['iconTitle'], $conf['iconTitle.']); $iconTitle = $this->cObj->stdWrap($conf['iconTitle'], $conf['iconTitle.']);
$iconImg = '<span title="' . htmlspecialchars($iconTitle, ENT_COMPAT, 'UTF-8', false) . '" style="' . ($conf['styleAttribute'] ? htmlspecialchars($conf['styleAttribute']) : '') . '">' $iconImg = '<span title="' . htmlspecialchars($iconTitle, ENT_COMPAT, 'UTF-8', false) . '" >'
. $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render('inline') . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render('inline')
. '</span>'; . '</span>';
$noView = GeneralUtility::_GP('ADMCMD_view') ? 1 : 0; $noView = GeneralUtility::_GP('ADMCMD_view') ? 1 : 0;
@ -317,14 +317,10 @@ class FrontendEditPanel
); );
} }
} else { } else {
if ($confirm && $this->backendUser->jsConfirmation(JsConfirmation::FE_EDIT)) { if ($confirm && $this->backendUser->jsConfirmation(JsConfirmation::FE_EDIT) === false) {
// Gets htmlspecialchared later $confirm = '';
$cf1 = 'if (confirm(' . GeneralUtility::quoteJSvalue($confirm) . ')) {';
$cf2 = '}';
} else {
$cf1 = ($cf2 = '');
} }
$out = '<a href="#" class="typo3-editPanel-btn typo3-editPanel-btn-default" onclick="' . htmlspecialchars($cf1 . 'document.' . $formName . '[\'TSFE_EDIT[cmd]\'].value=\'' . $cmd . '\'; document.' . $formName . '.submit();' . $cf2 . ' return false;') . '">' . $string . '</a>'; $out = '<a href="#" class="typo3-editPanel-btn typo3-editPanel-btn-default typo3-feedit-btn-submitForm" data-feedit-confirm="' . htmlspecialchars($confirm) . '" data-feedit-formname="' . htmlspecialchars($formName) . '" data-feedit-cmd="' . htmlspecialchars($cmd) . '">' . $string . '</a>';
} }
return $out; return $out;
} }
@ -340,10 +336,10 @@ class FrontendEditPanel
*/ */
protected function editPanelLinkWrap_doWrap($string, $url, $additionalClasses = '') protected function editPanelLinkWrap_doWrap($string, $url, $additionalClasses = '')
{ {
$width = MathUtility::forceIntegerInRange($this->backendUser->getTSConfig()['options.']['feedit.']['popupWidth'] ?? 690, 690, 5000, 690); $classes = 'typo3-editPanel-btn typo3-editPanel-btn-default typo3-feedit-btn-openBackend frontEndEditIconLinks ' . htmlspecialchars($additionalClasses);
$height = MathUtility::forceIntegerInRange($this->backendUser->getTSConfig()['options.']['feedit.']['popupHeight'] ?? 500, 500, 5000, 500); return '<a href="#" class="' . $classes . '" ' . $this->getDataAttributes($url) . '>' .
$onclick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($url . '&returnUrl=' . rawurlencode(PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Html/Close.html')))) . ',\'FEquickEditWindow\',\'width=' . $width . ',height=' . $height . ',status=0,menubar=0,scrollbars=1,resizable=1\');vHWin.focus();return false;'; $string .
return '<a href="#" class="typo3-editPanel-btn typo3-editPanel-btn-default frontEndEditIconLinks ' . htmlspecialchars($additionalClasses) . '" onclick="' . htmlspecialchars($onclick) . '">' . $string . '</a>'; '</a>';
} }
/** /**
@ -395,6 +391,22 @@ class FrontendEditPanel
return htmlspecialchars($this->getLanguageService()->getLL($key)); return htmlspecialchars($this->getLanguageService()->getLL($key));
} }
/**
* Returns data attributes to call the provided url via JavaScript.
*
* @param string $url The url to call via JavaScript.
* @return string Data attributes without whitespace at beginning or end.
*/
protected function getDataAttributes(string $url): string
{
$t3BeSitenameMd5 = md5('Typo3Backend-' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']);
return implode(' ', [
'data-backendScript="' . $url . '"',
'data-t3BeSitenameMd5="' . $t3BeSitenameMd5 . '"',
]);
}
/** /**
* Returns the returnUrl used by TYPO3. Add this as "returnUrl=" to any url that allows the user to go back or close an form. * Returns the returnUrl used by TYPO3. Add this as "returnUrl=" to any url that allows the user to go back or close an form.
* *

View file

@ -56,7 +56,7 @@ class EditToolbarService
$langAllowed = $this->getBackendUser()->checkLanguageAccess($languageAspect->getId()); $langAllowed = $this->getBackendUser()->checkLanguageAccess($languageAspect->getId());
$id = $tsfe->id; $id = $tsfe->id;
$returnUrl = GeneralUtility::getIndpEnv('REQUEST_URI'); $returnUrl = GeneralUtility::getIndpEnv('REQUEST_URI');
$classes = 'typo3-adminPanel-btn typo3-adminPanel-btn-default typo3-adminPanel-btn-openBackend'; $classes = 'typo3-adminPanel-btn typo3-adminPanel-btn-default typo3-feedit-btn-openBackend';
$output = []; $output = [];
$output[] = '<div class="typo3-adminPanel-form-group">'; $output[] = '<div class="typo3-adminPanel-form-group">';
$output[] = ' <div class="typo3-adminPanel-btn-group" role="group">'; $output[] = ' <div class="typo3-adminPanel-btn-group" role="group">';

View file

@ -9,7 +9,7 @@
value: display.displayIcons }" debug="false"/> value: display.displayIcons }" debug="false"/>
<f:format.raw>{toolbar}</f:format.raw> <f:format.raw>{toolbar}</f:format.raw>
<div class="typo3-adminPanel-form-group"> <div class="typo3-adminPanel-form-group">
<a class="typo3-adminPanel-btn typo3-adminPanel-btn-default typo3-adminPanel-btn-openBackend" href="#" data-t3BeSitenameMd5="{script.t3BeSitenameMd5}" data-backendScript="{script.backendScript}"> <a class="typo3-adminPanel-btn typo3-adminPanel-btn-default typo3-feedit-btn-openBackend" href="#" data-t3BeSitenameMd5="{script.t3BeSitenameMd5}" data-backendScript="{script.backendScript}">
<f:translate key="LLL:EXT:feedit/Resources/Private/Language/locallang_edit.xlf:openAB"/> <f:translate key="LLL:EXT:feedit/Resources/Private/Language/locallang_edit.xlf:openAB"/>
</a> </a>
</div> </div>

View file

@ -7,12 +7,12 @@ this.Element && function(ElementPrototype) {
} }
}(Element.prototype); }(Element.prototype);
function editModuleOnClickHandler(event) { function openBackendHandler(event) {
event.preventDefault(); event.preventDefault();
var element = event.target; var element = event.target;
if (element.tagName !== 'A') { if (element.tagName !== 'A') {
element = element.closest('A.typo3-adminPanel-btn-openBackend'); element = element.closest('a.typo3-feedit-btn-openBackend');
} }
var vHWin = window.open(element.getAttribute('data-backendScript'), element.getAttribute('data-t3BeSitenameMd5')); var vHWin = window.open(element.getAttribute('data-backendScript'), element.getAttribute('data-t3BeSitenameMd5'));
@ -20,12 +20,40 @@ function editModuleOnClickHandler(event) {
return false; return false;
} }
function submitFormHandler(event) {
event.preventDefault();
var element = event.target;
if (element.tagName !== 'A') {
element = element.closest('a.typo3-feedit-btn-submitForm');
}
var execute = true;
var form = document[element.getAttribute('data-feedit-formname')];
var confirmText = element.getAttribute('data-feedit-confirm');
if (confirmText) {
execute = confirm(confirmText);
}
if (execute) {
form.querySelector('.typo3-feedit-cmd').value = element.getAttribute('data-feedit-cmd');
form.submit();
}
return false;
}
function initializeEditModule() { function initializeEditModule() {
var editModuleBtnsOpenBackend = document.querySelectorAll('.typo3-adminPanel-btn-openBackend'); var editModuleBtnsOpenBackend = document.querySelectorAll('.typo3-feedit-btn-openBackend');
for (var i = 0, len = editModuleBtnsOpenBackend.length; i < len; i++ ) { for (var i = 0, len = editModuleBtnsOpenBackend.length; i < len; i++ ) {
editModuleBtnsOpenBackend[i].addEventListener('click', editModuleOnClickHandler); editModuleBtnsOpenBackend[i].addEventListener('click', openBackendHandler);
}
var editModuleBtnsSubmitForm = document.querySelectorAll('.typo3-feedit-btn-submitForm');
for (var i = 0, len = editModuleBtnsSubmitForm.length; i < len; i++ ) {
editModuleBtnsSubmitForm[i].addEventListener('click', submitFormHandler);
} }
} }
window.addEventListener('load', initializeEditModule, false); window.addEventListener('load', initializeEditModule, false);