* ``` * * **Note:** This feature is experimental! * * @see https://docs.typo3.org/permalink/t3viewhelper:typo3-fluid-be-tablelist */ final class TableListViewHelper extends AbstractViewHelper { /** * As this ViewHelper renders HTML, the output must not be escaped. * * @var bool */ protected $escapeOutput = false; public function __construct( private readonly ConfigurationManagerInterface $configurationManager, private readonly PageRenderer $pageRenderer, ) {} public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('tableName', 'string', 'name of the database table', true); $this->registerArgument('fieldList', 'array', 'list of fields to be displayed. If empty, only the title column (configured in $TCA[$tableName][\'ctrl\'][\'title\']) is shown', false, []); $this->registerArgument('storagePid', 'int', 'by default, records are fetched from the storage PID configured in persistence.storagePid. With this argument, the storage PID can be overwritten'); $this->registerArgument('levels', 'int', 'corresponds to the level selector of the TYPO3 records module. By default only records from the current storagePid are fetched', false, 0); $this->registerArgument('filter', 'string', 'corresponds to the "Search String" textbox of the TYPO3 records module. If not empty, only records matching the string will be fetched', false, ''); $this->registerArgument('recordsPerPage', 'int', 'amount of records to be displayed at once. Defaults to 100', false, 0); $this->registerArgument('sortField', 'string', 'table field to sort the results by', false, ''); $this->registerArgument('sortDescending', 'bool', 'if TRUE records will be sorted in descending order', false, false); $this->registerArgument('readOnly', 'bool', 'if TRUE, the edit icons won\'t be shown. Otherwise edit icons will be shown, if the current BE user has edit rights for the specified table!', false, false); $this->registerArgument('enableClickMenu', 'bool', 'enables context menu', false, true); $this->registerArgument('enableControlPanels', 'bool', 'enables control panels', false, false); $this->registerArgument('clickTitleMode', 'string', 'one of "edit", "show" (only pages, tt_content), "info', false, ''); } /** * Renders a record list as known from the TYPO3 records module * Note: This feature is experimental! * * @see DatabaseRecordList */ public function render(): string { $tableName = $this->arguments['tableName']; $fieldList = $this->arguments['fieldList']; $storagePid = $this->arguments['storagePid']; $levels = $this->arguments['levels']; $filter = $this->arguments['filter']; $recordsPerPage = $this->arguments['recordsPerPage']; $sortField = $this->arguments['sortField']; $sortDescending = $this->arguments['sortDescending']; $readOnly = $this->arguments['readOnly']; $enableClickMenu = $this->arguments['enableClickMenu']; $clickTitleMode = $this->arguments['clickTitleMode']; $enableControlPanels = $this->arguments['enableControlPanels']; $backendUser = $this->getBackendUser(); if (!$this->renderingContext->hasAttribute(ServerRequestInterface::class)) { // All views in backend have at least ServerRequestInterface. Should be fine // to assume having a request here, the early return is just sanitation. return ''; } $request = $this->renderingContext->getAttribute(ServerRequestInterface::class); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/recordlist.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/record-download-button.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/action-dispatcher.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/page-wizard/new-page-wizard-button.js'); if ($enableControlPanels === true) { $this->pageRenderer->loadJavaScriptModule('@typo3/backend/multi-record-selection.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/multi-record-selection-delete-action.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/context-menu.js'); } $pageId = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0); $pointer = (int)($request->getParsedBody()['pointer'] ?? $request->getQueryParams()['pointer'] ?? 0); $pageInfo = BackendUtility::readPageAccess($pageId, $backendUser->getPagePermsClause(Permission::PAGE_SHOW)) ?: []; $existingModuleData = $backendUser->getModuleData('records'); $moduleData = new ModuleData('records', is_array($existingModuleData) ? $existingModuleData : []); $dbList = GeneralUtility::makeInstance(DatabaseRecordList::class); $dbList->setRequest($request->withoutAttribute('pageContext')); $dbList->setModuleData($moduleData); $dbList->pageRow = $pageInfo; if ($readOnly) { $dbList->setIsEditable(false); } else { $dbList->calcPerms = new Permission($backendUser->calcPerms($pageInfo)); } $dbList->disableSingleTableView = true; $dbList->clickTitleMode = $clickTitleMode; $dbList->clickMenuEnabled = $enableClickMenu; if ($storagePid === null) { $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); $storagePid = $frameworkConfiguration['persistence']['storagePid']; } $dbList->start($storagePid, $tableName, $pointer, $filter, $levels, $recordsPerPage); // Column selector is disabled since fields are defined by the "fieldList" argument $dbList->displayColumnSelector = false; $dbList->setFields = [$tableName => $fieldList]; $dbList->noControlPanels = !$enableControlPanels; $dbList->sortField = $sortField; $dbList->sortRev = $sortDescending; return $dbList->generateList(); } private function getBackendUser(): BackendUserAuthentication { return $GLOBALS['BE_USER']; } }