getLayoutData( $dataProviderContext->fieldName, $dataProviderContext->pageTsConfig, $dataProviderContext->pageId ); foreach ($layoutData as $data) { $backendLayout = $this->createBackendLayout($data); $backendLayoutCollection->add($backendLayout); } } /** * Gets a backend layout by (regular) identifier. * * @param string|int $identifier * @param int $pageId */ public function getBackendLayout($identifier, $pageId): ?BackendLayout { $backendLayout = null; if ((string)$identifier === 'default') { return $this->createDefaultBackendLayout(); } $data = BackendUtility::getRecordWSOL('backend_layout', (int)$identifier); if (is_array($data)) { $backendLayout = $this->createBackendLayout($data); } return $backendLayout; } public function getIdentifier(): string { return 'default'; } /** * Creates a backend layout with the default configuration. */ protected function createDefaultBackendLayout(): BackendLayout { return BackendLayout::create( 'default', 'LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.backend_layout.default', self::DEFAULT_COLUMNS_LAYOUT ); } /** * Creates a new backend layout using the given record data. */ protected function createBackendLayout(array $data): BackendLayout { $backendLayout = BackendLayout::create((string)$data['uid'], $data['title'], $data['config']); $backendLayout->setIconPath($this->getIconPath($data)); $backendLayout->setData($data); return $backendLayout; } /** * Resolves the icon from the database record */ protected function getIconPath(array $icon): string { $references = $this->fileRepository->findByRelation('backend_layout', 'icon', (int)$icon['uid']); if (!empty($references)) { $icon = reset($references); return $icon->getPublicUrl() ?? ''; } return ''; } /** * Get all layouts from the core's default data provider. * * @param string $fieldName the name of the field the layouts are provided for (either backend_layout or backend_layout_next_level) * @param array $pageTsConfig PageTSconfig of the given page * @param int $pageUid the ID of the page wea re getting the layouts for * @return array $layouts A collection of layout data of the registered provider */ protected function getLayoutData(string $fieldName, array $pageTsConfig, int $pageUid): array { // @todo: This depends on backend_layout TCA being available for both the query and // TcaSchemaFactory. backend_layout TCA comes from ext:frontend, so we have // an indirect cross dependency between ext:backend and ext:frontend here. // There should be an explicit exception here when core is decoupled to run // an instance with ext:backend but without ext:frontend, or backend_layout TCA // should be relocated to ext:core? There are probably many more cases like this. $storagePid = $this->getStoragePid($pageTsConfig); $pageTsConfigId = $this->getPageTSconfigIds($pageTsConfig); // Add layout records $queryBuilder = $this->connectionPool->getQueryBuilderForTable('backend_layout'); $queryBuilder->getRestrictions() ->add( GeneralUtility::makeInstance( WorkspaceRestriction::class, $this->context->getPropertyFromAspect('workspace', 'id') ) ); $queryBuilder ->select('*') ->from('backend_layout') ->where( $queryBuilder->expr()->or( $queryBuilder->expr()->and( $queryBuilder->expr()->comparison( $queryBuilder->createNamedParameter($pageTsConfigId[$fieldName], Connection::PARAM_INT), ExpressionBuilder::EQ, $queryBuilder->createNamedParameter(0, Connection::PARAM_INT) ), $queryBuilder->expr()->comparison( $queryBuilder->createNamedParameter($storagePid, Connection::PARAM_INT), ExpressionBuilder::EQ, $queryBuilder->createNamedParameter(0, Connection::PARAM_INT) ) ), $queryBuilder->expr()->or( $queryBuilder->expr()->eq( 'backend_layout.pid', $queryBuilder->createNamedParameter($pageTsConfigId[$fieldName], Connection::PARAM_INT) ), $queryBuilder->expr()->eq( 'backend_layout.pid', $queryBuilder->createNamedParameter($storagePid, Connection::PARAM_INT) ) ), $queryBuilder->expr()->and( $queryBuilder->expr()->comparison( $queryBuilder->createNamedParameter($pageTsConfigId[$fieldName], Connection::PARAM_INT), ExpressionBuilder::EQ, $queryBuilder->createNamedParameter(0, Connection::PARAM_INT) ), $queryBuilder->expr()->eq( 'backend_layout.pid', $queryBuilder->createNamedParameter($pageUid, Connection::PARAM_INT) ) ) ) ); // Not catching UndefinedSchemaException here since backend_layout must exist at // this point, or the entire query would fail already. $schema = $this->tcsSchemaFactory->get('backend_layout'); if ($schema->hasCapability(TcaSchemaCapability::SortByField)) { $queryBuilder->orderBy((string)$schema->getCapability(TcaSchemaCapability::SortByField)); } $statement = $queryBuilder->executeQuery(); $results = []; while ($record = $statement->fetchAssociative()) { BackendUtility::workspaceOL('backend_layout', $record); if (is_array($record)) { $results[$record['t3ver_oid'] ?: $record['uid']] = $record; } } return $results; } /** * Returns the storage PID from TCEFORM. */ protected function getStoragePid(array $pageTsConfig): int { $storagePid = 0; if (!empty($pageTsConfig['TCEFORM.']['pages.']['_STORAGE_PID'])) { $storagePid = (int)$pageTsConfig['TCEFORM.']['pages.']['_STORAGE_PID']; } return $storagePid; } /** * Returns the page TSconfig from TCEFORM. */ protected function getPageTSconfigIds(array $pageTsConfig): array { $pageTsConfigIds = [ 'backend_layout' => 0, 'backend_layout_next_level' => 0, ]; if (!empty($pageTsConfig['TCEFORM.']['pages.']['backend_layout.']['PAGE_TSCONFIG_ID'])) { $pageTsConfigIds['backend_layout'] = (int)$pageTsConfig['TCEFORM.']['pages.']['backend_layout.']['PAGE_TSCONFIG_ID']; } if (!empty($pageTsConfig['TCEFORM.']['pages.']['backend_layout_next_level.']['PAGE_TSCONFIG_ID'])) { $pageTsConfigIds['backend_layout_next_level'] = (int)$pageTsConfig['TCEFORM.']['pages.']['backend_layout_next_level.']['PAGE_TSCONFIG_ID']; } return $pageTsConfigIds; } }