diff --git a/typo3/sysext/backend/Classes/Controller/RecordListController.php b/typo3/sysext/backend/Classes/Controller/RecordListController.php index 1ad35ade7885b9d6e40bff843df49a4ec54ed7b1..65ffd86040647f2302900776f3a92bc5a35add49 100644 --- a/typo3/sysext/backend/Classes/Controller/RecordListController.php +++ b/typo3/sysext/backend/Classes/Controller/RecordListController.php @@ -180,9 +180,9 @@ class RecordListController if (!($this->modTSconfig['disableSearchBox'] ?? false) && ($tableListHtml || !empty($search_field))) { $searchBoxHtml = $this->renderSearchBox($request, $view, $dbList, $search_field, $search_levels); } - $toggleClipboardHtml = ''; + $renderToggleClipBoard = false; if ($tableListHtml && ($this->modTSconfig['enableClipBoard'] ?? '') === 'selectable') { - $toggleClipboardHtml = $this->renderToggleClipboardHtml($this->id, $singleTable, $clipboardEnabled); + $renderToggleClipBoard = true; } $clipboardHtml = ''; if ($clipboardEnabled && ($tableListHtml || $clipboard->hasElements())) { @@ -197,6 +197,12 @@ class RecordListController $view->getDocHeaderComponent()->setMetaInformation($pageinfo); } $this->getDocHeaderButtons($view, $clipboard, $queryParams, $singleTable, $dbList->listURL(), []); + $route = $request->getAttribute('route'); + $params = ['id' => $this->id]; + if ($singleTable) { + $params['table'] = $singleTable; + } + $url = $this->uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $params) . '&clipBoard=${value}'; $view->assignMultiple([ 'pageId' => $this->id, 'pageTitle' => $title, @@ -207,7 +213,11 @@ class RecordListController 'pageTranslationsHtml' => $pageTranslationsHtml, 'searchBoxHtml' => $searchBoxHtml, 'tableListHtml' => $tableListHtml, - 'toggleClipboardHtml' => $toggleClipboardHtml, + 'displayToggleClipboard' => $renderToggleClipBoard, + 'toggleClipboardName' => 'clipBoard', + 'toggleClipboardId' => 'checkShowClipBoard', + 'toggleClipboardChecked' => $clipboardEnabled ? ' checked="checked"' : '', + 'toggleClipboardFormUrl' => $url, 'clipboardHtml' => $clipboardHtml, 'additionalContentBottom' => $additionalRecordListEvent->getAdditionalContentBelow(), ]); @@ -588,21 +598,6 @@ class RecordListController return $pageTranslationsDatabaseRecordList->getTable('pages'); } - public function renderToggleClipboardHtml(int $pageId, string $singleTable, bool $checked): string - { - $languageService = $this->getLanguageService(); - $html = []; - $html[] = '<div class="mb-3">'; - $html[] = '<div class="form-check form-switch">'; - $html[] = BackendUtility::getFuncCheck($pageId, 'clipBoard', $checked, '', $singleTable ? '&table=' . $singleTable : '', 'id="checkShowClipBoard"'); - $html[] = '<label class="form-check-label" for="checkShowClipBoard">'; - $html[] = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:showClipBoard')); - $html[] = '</label>'; - $html[] = '</div>'; - $html[] = '</div>'; - return implode(LF, $html); - } - /** * Check if page can be edited by current user */ diff --git a/typo3/sysext/backend/Classes/ElementBrowser/FileBrowser.php b/typo3/sysext/backend/Classes/ElementBrowser/FileBrowser.php index 9b1ab976d78fceca0f21729cef6d98f154350f80..cc7cff88d956cb28e3199c953255d8d99b895b1f 100644 --- a/typo3/sysext/backend/Classes/ElementBrowser/FileBrowser.php +++ b/typo3/sysext/backend/Classes/ElementBrowser/FileBrowser.php @@ -16,7 +16,9 @@ namespace TYPO3\CMS\Backend\ElementBrowser; use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\ElementBrowser\Event\IsFileSelectableEvent; +use TYPO3\CMS\Backend\Routing\Route; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface; use TYPO3\CMS\Backend\Utility\BackendUtility; @@ -24,6 +26,7 @@ use TYPO3\CMS\Backend\View\BackendViewFactory; use TYPO3\CMS\Backend\View\FolderUtilityRenderer; use TYPO3\CMS\Backend\View\RecordSearchBoxComponent; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; +use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Messaging\FlashMessage; @@ -42,6 +45,7 @@ use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\HttpUtility; use TYPO3\CMS\Core\Utility\MathUtility; +use TYPO3\CMS\Core\Utility\PathUtility; /** * Browser for files. This is used when adding a FAL inline image with the 'add image' button in FormEngine. @@ -463,7 +467,7 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf return ' <div class="col-auto"> <div class="form-check form-switch"> - ' . BackendUtility::getFuncCheck('', 'SET[displayThumbs]', $currentValue, $this->thisScript, $addParams, 'id="checkDisplayThumbs"') . ' + ' . self::getFuncCheck('', 'SET[displayThumbs]', $currentValue, $request, $this->thisScript, $addParams, 'id="checkDisplayThumbs"') . ' <label for="checkDisplayThumbs" class="form-check-label"> ' . htmlspecialchars($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:displayThumbs')) . ' </label> @@ -516,4 +520,80 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf { return $this->thisScript; } + + //################################ + // copied over from BackendUtility to enable deprecation of the original method + // @todo finish fluidification of template and remove HTML generation from controller + //################################ + + /** + * Checkbox function menu. + * Works like ->getFuncMenu() but takes no $menuItem array since this is a simple checkbox. + * + * @param mixed $mainParams $id is the "&id=" parameter value to be sent to the module, but it can be also a parameter array which will be passed instead of the &id=... + * @param string $elementName The form elements name, probably something like "SET[...] + * @param string|bool $currentValue The value to be selected currently. + * @param string $script The script to send the &id to, if empty it's automatically found + * @param string $addParams Additional parameters to pass to the script. + * @param string $tagParams Additional attributes for the checkbox input tag + * @return string HTML code for checkbox + * @see getFuncMenu() + */ + protected static function getFuncCheck( + $mainParams, + $elementName, + $currentValue, + ServerRequestInterface $request, + $script = '', + $addParams = '', + $tagParams = '' + ) { + // relies on module 'TYPO3/CMS/Backend/ActionDispatcher' + $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $request, $script); + $attributes = GeneralUtility::implodeAttributes([ + 'type' => 'checkbox', + 'class' => 'form-check-input', + 'name' => $elementName, + 'value' => '1', + 'data-global-event' => 'change', + 'data-action-navigate' => '$data=~s/$value/', + 'data-navigate-value' => sprintf('%s&%s=${value}', $scriptUrl, $elementName), + 'data-empty-value' => '0', + ], true); + return + '<input ' . $attributes . + ($currentValue ? ' checked="checked"' : '') . + ($tagParams ? ' ' . $tagParams : '') . + ' />'; + } + + /** + * Builds the URL to the current script with given arguments + * + * @param mixed $mainParams $id is the "&id=" parameter value to be sent to the module, but it can be also a parameter array which will be passed instead of the &id=... + * @param string $addParams Additional parameters to pass to the script. + * @param string $script The script to send the &id to, if empty it's automatically found + * @return string The complete script URL + * @todo Check if this can be removed or replaced by routing + */ + protected static function buildScriptUrl($mainParams, $addParams, ServerRequestInterface $request, $script = '') + { + if (!is_array($mainParams)) { + $mainParams = ['id' => $mainParams]; + } + + $route = $request->getAttribute('route'); + if ($route instanceof Route) { + $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); + $scriptUrl = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $mainParams); + $scriptUrl .= $addParams; + } else { + if (!$script) { + $script = PathUtility::basename(Environment::getCurrentScript()); + } + $scriptUrl = $script . HttpUtility::buildQueryString($mainParams, '?') . $addParams; + } + + return $scriptUrl; + } } diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php index 82bc5e774f15caca4f4674cebeafa5f18e1ffd14..c719af5262233311361331cd30ac48aaaf22c161 100644 --- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php +++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php @@ -2218,6 +2218,7 @@ class BackendUtility * @param string $tagParams Additional attributes for the checkbox input tag * @return string HTML code for checkbox * @see getFuncMenu() + * @deprecated since TYPO3 v12.2. will be removed in TYPO3 v13.0. */ public static function getFuncCheck( $mainParams, @@ -2227,6 +2228,11 @@ class BackendUtility $addParams = '', $tagParams = '' ) { + trigger_error( + 'BackendUtility::getFuncCheck will be removed in TYPO3 v13.0, use Fluid based templating instead.', + E_USER_DEPRECATED + ); + // relies on module 'TYPO3/CMS/Backend/ActionDispatcher' $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $script); $attributes = GeneralUtility::implodeAttributes([ diff --git a/typo3/sysext/backend/Resources/Private/Templates/RecordList.html b/typo3/sysext/backend/Resources/Private/Templates/RecordList.html index b01c231319ac5c3e383bf18fe4a299c2e7582e0d..e07c704e1f8d5a3c31372b0244445187efddba74 100644 --- a/typo3/sysext/backend/Resources/Private/Templates/RecordList.html +++ b/typo3/sysext/backend/Resources/Private/Templates/RecordList.html @@ -46,7 +46,19 @@ <f:format.raw>{tableListHtml}</f:format.raw> - <f:format.raw>{toggleClipboardHtml}</f:format.raw> + <f:if condition="{displayToggleClipboard}"> + <div class="mb-3"> + <div class="form-check form-switch"> + <input type="checkbox" class="form-check-input" name="{toggleClipboardName}" value="1" data-global-event="change" + data-action-navigate="$data=~s/$value/" data-empty-value="0" {toggleClipboardChecked} id="{toggleClipboardId}" + data-navigate-value="{toggleClipboardFormUrl}" + > + <label class="form-check-label" for="{toggleClipboardId}"> + <f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:showClipBoard" /> + </label> + </div> + </div> + </f:if> <f:format.raw>{clipboardHtml}</f:format.raw> <f:format.raw>{additionalContentBottom}</f:format.raw> diff --git a/typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php b/typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php index ff710525998c1bdb76a8565c83ecb5fccbf14b97..ee0f96ca49979ea3c2b5273ce615c2a298cc476e 100644 --- a/typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php +++ b/typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php @@ -854,14 +854,6 @@ class BackendUtilityTest extends UnitTestCase self::assertEquals($expectedLabel, LabelFromItemListMergedReturnsCorrectFieldsFixture::getLabelFromItemListMerged($pageId, $table, $column, $key)); } - /** - * @test - */ - public function getFuncCheckReturnsInputTagWithValueAttribute(): void - { - self::assertStringMatchesFormat('<input %Svalue="1"%S/>', BackendUtility::getFuncCheck('params', 'test', true)); - } - public function getLabelsFromItemsListDataProvider(): array { return [ diff --git a/typo3/sysext/core/Documentation/Changelog/12.2/Deprecation-99579-BackendUtilityGetFuncCheck.rst b/typo3/sysext/core/Documentation/Changelog/12.2/Deprecation-99579-BackendUtilityGetFuncCheck.rst new file mode 100644 index 0000000000000000000000000000000000000000..78aaa9ddccec9b4373faf7d6be45193709abd068 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/12.2/Deprecation-99579-BackendUtilityGetFuncCheck.rst @@ -0,0 +1,59 @@ +.. include:: /Includes.rst.txt + +.. _deprecation-99579-1673983578: + +==================================================== +Deprecation: #99579 - BackendUtility::getFuncCheck() +==================================================== + +See :issue:`99579` + +Description +=========== + +Method :php:`\TYPO3\CMS\Backend\Utility\BackendUtility::getFuncCheck()` has +been marked as deprecated and should not be used any longer. + + +Impact +====== + +Calling the method will raise a deprecation level log error and will +stop working with TYPO3 v13. + + +Affected installations +====================== + +The method may be used in extensions that add backend modules. The +extension scanner finds usages with a strong match. + + +Migration +========= + +:php:`BackendUtility::getFuncCheck()` is a helper method that renders a +select drop down and is typically used to trigger a GET request when +the user selects an option. + +In general, such HTML should not be generated by PHP, but by Fluid templating. +The method is thus typically used by old school backend modules that have not +or only partially been transferred to Fluid, improving the controller-view +separation. + +The most simple and ugly migration is to copy the method to an own controller +that consumes the method. The better solution is to add the arguments as variables +to the Fluid template and render the drop down in Fluid. This should be a pretty +straight transition as well and typically avoids another :html:`<f:format.raw>` +ViewHelper usage. + +Note that requests send by these drop downs should switch from GET to POST in +case they change server state (eg. changing records) along the way: State changing +requests should be restricted to POST as an additional security measure, and to +not violate the HTTP protocol. The TYPO3 core comes with more and more examples +on how to handle this properly. For instance all drop downs and checkbox toggles +of the tstemplate extension (Web->TypoScript backend module) have been changed to +do this and can be studied on how to trigger immediate POST actions when clicking +such elements. + +.. index:: Backend, PHP-API, FullyScanned, ext:backend diff --git a/typo3/sysext/info/Classes/Controller/InfoPageTyposcriptConfigController.php b/typo3/sysext/info/Classes/Controller/InfoPageTyposcriptConfigController.php index 0139a4486c6f78fc8aeb66a1c918100c6c21fc81..7eea80480f407bc978b4efd0dbe9a995e2595923 100644 --- a/typo3/sysext/info/Classes/Controller/InfoPageTyposcriptConfigController.php +++ b/typo3/sysext/info/Classes/Controller/InfoPageTyposcriptConfigController.php @@ -129,6 +129,7 @@ class InfoPageTyposcriptConfigController } $viewOption = (int)$moduleData->get('tsconf_parts'); + $alphaSortStatus = $moduleData->get('tsconf_alphaSort'); if ($viewOption === 99) { $rootLine = BackendUtility::BEgetRootLine($this->id, '', true); @@ -210,7 +211,7 @@ class InfoPageTyposcriptConfigController $this->view->assign('tsconfParts99', false); $pageTsConfig = BackendUtility::getPagesTSconfig($this->id); - if ($moduleData->get('tsconf_alphaSort')) { + if ($alphaSortStatus) { $pageTsConfig = ArrayUtility::sortByKeyRecursive($pageTsConfig); } @@ -219,7 +220,13 @@ class InfoPageTyposcriptConfigController ]); } if ($viewOption !== 99) { - $this->view->assign('alphaSort', BackendUtility::getFuncCheck($this->id, 'tsconf_alphaSort', (bool)$moduleData->get('tsconf_alphaSort'), '', '', 'id="checkTsconf_alphaSort"')); + $route = $request->getAttribute('route'); + $params = ['id' => $this->id]; + $this->view->assignMultiple([ + 'displayAlphaSort' => true, + 'alphaSortChecked' => (bool)$alphaSortStatus === true ? 'checked="checked"' : '', + 'alphaSortUrl' => $this->uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $params) . '&tsconf_alphaSort=${value}', + ]); } $this->view->assignMultiple([ 'dropdownMenuOptions' => $allowedModuleOptions['tsconf_parts'], diff --git a/typo3/sysext/info/Resources/Private/Partials/PageTsConfig/Page.html b/typo3/sysext/info/Resources/Private/Partials/PageTsConfig/Page.html index 43e76084571b727d499feec3ac55fdd5781a7435..9bef35f7217baf635abf3270acda11cffd447212 100644 --- a/typo3/sysext/info/Resources/Private/Partials/PageTsConfig/Page.html +++ b/typo3/sysext/info/Resources/Private/Partials/PageTsConfig/Page.html @@ -13,13 +13,17 @@ </label> <f:render partial="DropdownMenu" arguments="{name: 'tsconf_parts', id: 'tsconf_parts', options: dropdownMenuOptions, currentValue: dropdownMenuCurrentValue}"/> </div> - <f:if condition="{alphaSort}"> + <f:if condition="{displayAlphaSort}"> <div class="col"> <div class="form-check form-switch"> <label class="form-check-label" for="checkTsconf_alphaSort"> {f:translate(key: 'LLL:EXT:info/Resources/Private/Language/InfoPageTsConfig.xlf:sort_alphabetic')} </label> - <f:format.raw>{alphaSort}</f:format.raw> + <input type="checkbox" class="form-check-input" name="tsconf_alphaSort" value="1" data-global-event="change" + data-action-navigate="$data=~s/$value/" + data-navigate-value="{alphaSortUrl}" + data-empty-value="0" id="checkTsconf_alphaSort" {alphaSortChecked} + > </div> </div> </f:if> diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php index 7558bec3c3e6d68e8501398e70c58a23239bd17a..c61f16d47cb7c1c076e9a782fe05b5d375a2020b 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php @@ -1436,4 +1436,11 @@ return [ 'Deprecation-99564-DeprecatedBackendUtilityGetDropdownMenu.rst', ], ], + 'TYPO3\CMS\Backend\Utility\BackendUtility::getFuncCheck' => [ + 'numberOfMandatoryArguments' => 3, + 'maximumNumberOfArguments' => 6, + 'restFiles' => [ + 'Deprecation-99579-BackendUtilityGetFuncCheck.rst', + ], + ], ]; diff --git a/typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php b/typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php index d45ee4bfce95e5e7eae91ff750f7a29765d1aff8..e9c36902a18eadd462bc9a03df5aba1d7d51caf5 100644 --- a/typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php +++ b/typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php @@ -273,17 +273,17 @@ class DatabaseIntegrityController } $submenu .= '</div>'; if ($this->MOD_SETTINGS['search'] === 'query') { - $submenu .= '<div class="form-check">' . BackendUtility::getFuncCheck(0, 'SET[search_query_smallparts]', $this->MOD_SETTINGS['search_query_smallparts'] ?? '', '', '', 'id="checkSearch_query_smallparts"') . '<label class="form-check-label" for="checkSearch_query_smallparts">' . $lang->getLL('showSQL') . '</label></div>'; - $submenu .= '<div class="form-check">' . BackendUtility::getFuncCheck(0, 'SET[search_result_labels]', $this->MOD_SETTINGS['search_result_labels'] ?? '', '', '', 'id="checkSearch_result_labels"') . '<label class="form-check-label" for="checkSearch_result_labels">' . $lang->getLL('useFormattedStrings') . '</label></div>'; - $submenu .= '<div class="form-check">' . BackendUtility::getFuncCheck(0, 'SET[labels_noprefix]', $this->MOD_SETTINGS['labels_noprefix'] ?? '', '', '', 'id="checkLabels_noprefix"') . '<label class="form-check-label" for="checkLabels_noprefix">' . $lang->getLL('dontUseOrigValues') . '</label></div>'; - $submenu .= '<div class="form-check">' . BackendUtility::getFuncCheck(0, 'SET[options_sortlabel]', $this->MOD_SETTINGS['options_sortlabel'] ?? '', '', '', 'id="checkOptions_sortlabel"') . '<label class="form-check-label" for="checkOptions_sortlabel">' . $lang->getLL('sortOptions') . '</label></div>'; - $submenu .= '<div class="form-check">' . BackendUtility::getFuncCheck(0, 'SET[show_deleted]', $this->MOD_SETTINGS['show_deleted'] ?? 0, '', '', 'id="checkShow_deleted"') . '<label class="form-check-label" for="checkShow_deleted">' . $lang->getLL('showDeleted') . '</label></div>'; + $submenu .= '<div class="form-check">' . self::getFuncCheck(0, 'SET[search_query_smallparts]', $this->MOD_SETTINGS['search_query_smallparts'] ?? '', $request, '', '', 'id="checkSearch_query_smallparts"') . '<label class="form-check-label" for="checkSearch_query_smallparts">' . $lang->getLL('showSQL') . '</label></div>'; + $submenu .= '<div class="form-check">' . self::getFuncCheck(0, 'SET[search_result_labels]', $this->MOD_SETTINGS['search_result_labels'] ?? '', $request, '', '', 'id="checkSearch_result_labels"') . '<label class="form-check-label" for="checkSearch_result_labels">' . $lang->getLL('useFormattedStrings') . '</label></div>'; + $submenu .= '<div class="form-check">' . self::getFuncCheck(0, 'SET[labels_noprefix]', $this->MOD_SETTINGS['labels_noprefix'] ?? '', $request, '', '', 'id="checkLabels_noprefix"') . '<label class="form-check-label" for="checkLabels_noprefix">' . $lang->getLL('dontUseOrigValues') . '</label></div>'; + $submenu .= '<div class="form-check">' . self::getFuncCheck(0, 'SET[options_sortlabel]', $this->MOD_SETTINGS['options_sortlabel'] ?? '', $request, '', '', 'id="checkOptions_sortlabel"') . '<label class="form-check-label" for="checkOptions_sortlabel">' . $lang->getLL('sortOptions') . '</label></div>'; + $submenu .= '<div class="form-check">' . self::getFuncCheck(0, 'SET[show_deleted]', $this->MOD_SETTINGS['show_deleted'] ?? 0, $request, '', '', 'id="checkShow_deleted"') . '<label class="form-check-label" for="checkShow_deleted">' . $lang->getLL('showDeleted') . '</label></div>'; } $view->assign('submenu', $submenu); $view->assign('searchMode', $searchMode); switch ($searchMode) { case 'query': - $view->assign('queryMaker', $fullSearch->queryMaker()); + $view->assign('queryMaker', $fullSearch->queryMaker($request)); break; case 'raw': default: @@ -489,6 +489,47 @@ class DatabaseIntegrityController </div>'; } + /** + * Checkbox function menu. + * Works like ->getFuncMenu() but takes no $menuItem array since this is a simple checkbox. + * + * @param mixed $mainParams $id is the "&id=" parameter value to be sent to the module, but it can be also a parameter array which will be passed instead of the &id=... + * @param string $elementName The form elements name, probably something like "SET[...] + * @param string|bool $currentValue The value to be selected currently. + * @param string $script The script to send the &id to, if empty it's automatically found + * @param string $addParams Additional parameters to pass to the script. + * @param string $tagParams Additional attributes for the checkbox input tag + * @return string HTML code for checkbox + * @see getFuncMenu() + */ + protected static function getFuncCheck( + $mainParams, + $elementName, + $currentValue, + ServerRequestInterface $request, + $script = '', + $addParams = '', + $tagParams = '' + ) { + // relies on module 'TYPO3/CMS/Backend/ActionDispatcher' + $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $request, $script); + $attributes = GeneralUtility::implodeAttributes([ + 'type' => 'checkbox', + 'class' => 'form-check-input', + 'name' => $elementName, + 'value' => '1', + 'data-global-event' => 'change', + 'data-action-navigate' => '$data=~s/$value/', + 'data-navigate-value' => sprintf('%s&%s=${value}', $scriptUrl, $elementName), + 'data-empty-value' => '0', + ], true); + return + '<input ' . $attributes . + ($currentValue ? ' checked="checked"' : '') . + ($tagParams ? ' ' . $tagParams : '') . + ' />'; + } + /** * Builds the URL to the current script with given arguments * diff --git a/typo3/sysext/lowlevel/Classes/Database/QueryGenerator.php b/typo3/sysext/lowlevel/Classes/Database/QueryGenerator.php index bbb76525fee3912fe49eb23ef158c47fb76e8dd5..93392539756d319d0cd2a0e3b636f6368a4f7c6c 100644 --- a/typo3/sysext/lowlevel/Classes/Database/QueryGenerator.php +++ b/typo3/sysext/lowlevel/Classes/Database/QueryGenerator.php @@ -18,9 +18,12 @@ namespace TYPO3\CMS\Lowlevel\Database; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Types\Types; +use Psr\Http\Message\ServerRequestInterface; +use TYPO3\CMS\Backend\Routing\Route; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\QueryHelper; @@ -38,6 +41,7 @@ use TYPO3\CMS\Core\Utility\DebugUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\HttpUtility; use TYPO3\CMS\Core\Utility\MathUtility; +use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Core\Utility\StringUtility; /** @@ -322,7 +326,7 @@ class QueryGenerator * * @return string */ - public function queryMaker() + public function queryMaker(ServerRequestInterface $request) { $output = ''; $this->hookArray = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3lib_fullsearch'] ?? []; @@ -338,7 +342,7 @@ class QueryGenerator if ($this->formName) { $this->setFormName($this->formName); } - $tmpCode = $this->makeSelectorTable($this->settings); + $tmpCode = $this->makeSelectorTable($this->settings, $request); $output .= '<div id="query"></div><h2>Make query</h2><div>' . $tmpCode . '</div>'; $mQ = $this->settings['search_query_makeQuery']; // Make form elements: @@ -2447,7 +2451,7 @@ class QueryGenerator * @param string $enableList * @return string */ - protected function makeSelectorTable($modSettings, $enableList = 'table,fields,query,group,order,limit') + protected function makeSelectorTable($modSettings, ServerRequestInterface $request, $enableList = 'table,fields,query,group,order,limit') { $out = []; $enableArr = explode(',', $enableList); @@ -2529,7 +2533,7 @@ class QueryGenerator $orderBy[] = '</div>'; $orderBy[] = '<div class="col mt-2">'; $orderBy[] = '<div class="form-check">'; - $orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'] ?? '', '', '', 'id="checkQueryOrderDesc"'); + $orderBy[] = self::getFuncCheck(0, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'] ?? '', $request, '', '', 'id="checkQueryOrderDesc"'); $orderBy[] = '<label class="form-check-label" for="checkQueryOrderDesc">Descending</label>'; $orderBy[] = '</div>'; $orderBy[] = '</div>'; @@ -2544,7 +2548,7 @@ class QueryGenerator $orderBy[] = '</div>'; $orderBy[] = '<div class="col mt-2">'; $orderBy[] = '<div class="form-check">'; - $orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'] ?? false, '', '', 'id="checkQueryOrder2Desc"'); + $orderBy[] = self::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'] ?? false, $request, '', '', 'id="checkQueryOrder2Desc"'); $orderBy[] = '<label class="form-check-label" for="checkQueryOrder2Desc">Descending</label>'; $orderBy[] = '</div>'; $orderBy[] = '</div>'; @@ -2729,4 +2733,80 @@ class QueryGenerator { return $GLOBALS['LANG']; } + + //################################ + // copied over from BackendUtility to enable deprecation of the original method + // @todo finish fluidification of template and remove HTML generation from controller + //################################ + + /** + * Checkbox function menu. + * Works like ->getFuncMenu() but takes no $menuItem array since this is a simple checkbox. + * + * @param mixed $mainParams $id is the "&id=" parameter value to be sent to the module, but it can be also a parameter array which will be passed instead of the &id=... + * @param string $elementName The form elements name, probably something like "SET[...] + * @param string|bool $currentValue The value to be selected currently. + * @param string $script The script to send the &id to, if empty it's automatically found + * @param string $addParams Additional parameters to pass to the script. + * @param string $tagParams Additional attributes for the checkbox input tag + * @return string HTML code for checkbox + * @see getFuncMenu() + */ + protected static function getFuncCheck( + $mainParams, + $elementName, + $currentValue, + ServerRequestInterface $request, + $script = '', + $addParams = '', + $tagParams = '' + ) { + // relies on module 'TYPO3/CMS/Backend/ActionDispatcher' + $scriptUrl = self::buildScriptUrl($mainParams, $addParams, $request, $script); + $attributes = GeneralUtility::implodeAttributes([ + 'type' => 'checkbox', + 'class' => 'form-check-input', + 'name' => $elementName, + 'value' => '1', + 'data-global-event' => 'change', + 'data-action-navigate' => '$data=~s/$value/', + 'data-navigate-value' => sprintf('%s&%s=${value}', $scriptUrl, $elementName), + 'data-empty-value' => '0', + ], true); + return + '<input ' . $attributes . + ($currentValue ? ' checked="checked"' : '') . + ($tagParams ? ' ' . $tagParams : '') . + ' />'; + } + + /** + * Builds the URL to the current script with given arguments + * + * @param mixed $mainParams $id is the "&id=" parameter value to be sent to the module, but it can be also a parameter array which will be passed instead of the &id=... + * @param string $addParams Additional parameters to pass to the script. + * @param string $script The script to send the &id to, if empty it's automatically found + * @return string The complete script URL + * @todo Check if this can be removed or replaced by routing + */ + protected static function buildScriptUrl($mainParams, $addParams, ServerRequestInterface $request, $script = '') + { + if (!is_array($mainParams)) { + $mainParams = ['id' => $mainParams]; + } + + $route = $request->getAttribute('route'); + if ($route instanceof Route) { + $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); + $scriptUrl = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $mainParams); + $scriptUrl .= $addParams; + } else { + if (!$script) { + $script = PathUtility::basename(Environment::getCurrentScript()); + } + $scriptUrl = $script . HttpUtility::buildQueryString($mainParams, '?') . $addParams; + } + + return $scriptUrl; + } } diff --git a/typo3/sysext/lowlevel/Tests/Functional/Database/QueryGeneratorTest.php b/typo3/sysext/lowlevel/Tests/Functional/Database/QueryGeneratorTest.php index 04f1be51b1d19e363e55c24c707c08a84bbc7f02..515823bd3720aac9efe436b8c4c1f2a3f72fe7d9 100644 --- a/typo3/sysext/lowlevel/Tests/Functional/Database/QueryGeneratorTest.php +++ b/typo3/sysext/lowlevel/Tests/Functional/Database/QueryGeneratorTest.php @@ -17,7 +17,9 @@ declare(strict_types=1); namespace TYPO3\CMS\Lowlevel\Tests\Functional\Database; +use TYPO3\CMS\Backend\Routing\Route; use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Localization\LanguageServiceFactory; @@ -366,10 +368,14 @@ class QueryGeneratorTest extends FunctionalTestCase $iconFactoryMock = $this->getMockBuilder(IconFactory::class)->disableOriginalConstructor()->getMock(); $iconFactoryMock->method('getIcon')->willReturn($iconMock); + // the route is not important for the test, it only satisfies the dependencies + $route = new \stdClass(); + $request = (new ServerRequest())->withAttribute('route', $route); + $subject = $this->getAccessibleMock(QueryGenerator::class, null, [], '', false); $subject->_set('iconFactory', $iconFactoryMock); $subject->_call('init', 'queryConfig', $settings['queryTable']); - $subject->_call('makeSelectorTable', $settings); + $subject->_call('makeSelectorTable', $settings, $request); $subject->_set('enablePrefix', true); $queryString = $subject->_call('getQuery', $subject->_get('queryConfig'));