TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Backend\View\BackendLayout;
|
||||
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
|
||||
use TYPO3\CMS\Core\Resource\FileRepository;
|
||||
use TYPO3\CMS\Core\Schema\Capability\TcaSchemaCapability;
|
||||
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Backend layout data provider class
|
||||
*
|
||||
* @internal Specific DataProviderInterface implementation, not considered public API.
|
||||
*/
|
||||
readonly class DefaultDataProvider implements DataProviderInterface
|
||||
{
|
||||
private const string DEFAULT_COLUMNS_LAYOUT = '
|
||||
backend_layout {
|
||||
colCount = 1
|
||||
rowCount = 1
|
||||
rows {
|
||||
1 {
|
||||
columns {
|
||||
1 {
|
||||
name = LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:colPos.I.1
|
||||
colPos = 0
|
||||
identifier = main
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
';
|
||||
|
||||
public function __construct(
|
||||
private FileRepository $fileRepository,
|
||||
private ConnectionPool $connectionPool,
|
||||
private TcaSchemaFactory $tcsSchemaFactory,
|
||||
private Context $context,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Adds backend layouts to the given backend layout collection.
|
||||
* The default backend layout ('default_default') is not added
|
||||
* since it's the default fallback if nothing is specified.
|
||||
*/
|
||||
public function addBackendLayouts(
|
||||
DataProviderContext $dataProviderContext,
|
||||
BackendLayoutCollection $backendLayoutCollection
|
||||
): void {
|
||||
$layoutData = $this->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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user