1<?php
2
3/*
4 * This file is part of the TYPO3 CMS project.
5 *
6 * It is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License, either version 2
8 * of the License, or any later version.
9 *
10 * For the full copyright and license information, please read the
11 * LICENSE.txt file that was distributed with this source code.
12 *
13 * The TYPO3 project - inspiring people to share!
14 */
15
16namespace TYPO3\CMS\Backend\View\BackendLayout;
17
18use TYPO3\CMS\Backend\Utility\BackendUtility;
19use TYPO3\CMS\Backend\View\BackendLayoutView;
20use TYPO3\CMS\Core\Context\Context;
21use TYPO3\CMS\Core\Database\ConnectionPool;
22use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
23use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
24use TYPO3\CMS\Core\Resource\FileRepository;
25use TYPO3\CMS\Core\Utility\GeneralUtility;
26
27/**
28 * Backend layout data provider class
29 */
30class DefaultDataProvider implements DataProviderInterface
31{
32
33    /**
34     * @var string
35     * Table name for backend_layouts
36     */
37    protected $tableName = 'backend_layout';
38
39    /**
40     * Adds backend layouts to the given backend layout collection.
41     * The default backend layout ('default_default') is not added
42     * since it's the default fallback if nothing is specified.
43     *
44     * @param DataProviderContext $dataProviderContext
45     * @param BackendLayoutCollection $backendLayoutCollection
46     */
47    public function addBackendLayouts(
48        DataProviderContext $dataProviderContext,
49        BackendLayoutCollection $backendLayoutCollection
50    ) {
51        $layoutData = $this->getLayoutData(
52            $dataProviderContext->getFieldName(),
53            $dataProviderContext->getPageTsConfig(),
54            $dataProviderContext->getPageId()
55        );
56
57        foreach ($layoutData as $data) {
58            $backendLayout = $this->createBackendLayout($data);
59            $backendLayoutCollection->add($backendLayout);
60        }
61    }
62
63    /**
64     * Gets a backend layout by (regular) identifier.
65     *
66     * @param string|int $identifier
67     * @param int $pageId
68     * @return BackendLayout|null
69     */
70    public function getBackendLayout($identifier, $pageId)
71    {
72        $backendLayout = null;
73
74        if ((string)$identifier === 'default') {
75            return $this->createDefaultBackendLayout();
76        }
77
78        $data = BackendUtility::getRecordWSOL($this->tableName, (int)$identifier);
79
80        if (is_array($data)) {
81            $backendLayout = $this->createBackendLayout($data);
82        }
83
84        return $backendLayout;
85    }
86
87    /**
88     * Creates a backend layout with the default configuration.
89     *
90     * @return BackendLayout
91     */
92    protected function createDefaultBackendLayout()
93    {
94        return BackendLayout::create(
95            'default',
96            'LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.backend_layout.default',
97            BackendLayoutView::getDefaultColumnLayout()
98        );
99    }
100
101    /**
102     * Creates a new backend layout using the given record data.
103     *
104     * @param array $data
105     * @return BackendLayout
106     */
107    protected function createBackendLayout(array $data)
108    {
109        $backendLayout = BackendLayout::create($data['uid'], $data['title'], $data['config']);
110        $backendLayout->setIconPath($this->getIconPath($data));
111        $backendLayout->setData($data);
112        return $backendLayout;
113    }
114
115    /**
116     * Resolves the icon from the database record
117     *
118     * @param array $icon
119     * @return string
120     */
121    protected function getIconPath(array $icon)
122    {
123        $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
124        $references = $fileRepository->findByRelation($this->tableName, 'icon', $icon['uid']);
125        if (!empty($references)) {
126            $icon = reset($references);
127            return $icon->getPublicUrl();
128        }
129        return '';
130    }
131
132    /**
133     * Get all layouts from the core's default data provider.
134     *
135     * @param string $fieldName the name of the field the layouts are provided for (either backend_layout or backend_layout_next_level)
136     * @param array $pageTsConfig PageTSconfig of the given page
137     * @param int $pageUid the ID of the page wea re getting the layouts for
138     * @return array $layouts A collection of layout data of the registered provider
139     */
140    protected function getLayoutData($fieldName, array $pageTsConfig, $pageUid)
141    {
142        $storagePid = $this->getStoragePid($pageTsConfig);
143        $pageTsConfigId = $this->getPageTSconfigIds($pageTsConfig);
144
145        // Add layout records
146        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
147            ->getQueryBuilderForTable($this->tableName);
148        $queryBuilder->getRestrictions()
149            ->add(
150                GeneralUtility::makeInstance(
151                    WorkspaceRestriction::class,
152                    GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('workspace', 'id')
153                )
154            );
155        $queryBuilder
156            ->select('*')
157            ->from($this->tableName)
158            ->where(
159                $queryBuilder->expr()->orX(
160                    $queryBuilder->expr()->andX(
161                        $queryBuilder->expr()->comparison(
162                            $queryBuilder->createNamedParameter($pageTsConfigId[$fieldName], \PDO::PARAM_INT),
163                            ExpressionBuilder::EQ,
164                            $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
165                        ),
166                        $queryBuilder->expr()->comparison(
167                            $queryBuilder->createNamedParameter($storagePid, \PDO::PARAM_INT),
168                            ExpressionBuilder::EQ,
169                            $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
170                        )
171                    ),
172                    $queryBuilder->expr()->orX(
173                        $queryBuilder->expr()->eq(
174                            'backend_layout.pid',
175                            $queryBuilder->createNamedParameter($pageTsConfigId[$fieldName], \PDO::PARAM_INT)
176                        ),
177                        $queryBuilder->expr()->eq(
178                            'backend_layout.pid',
179                            $queryBuilder->createNamedParameter($storagePid, \PDO::PARAM_INT)
180                        )
181                    ),
182                    $queryBuilder->expr()->andX(
183                        $queryBuilder->expr()->comparison(
184                            $queryBuilder->createNamedParameter($pageTsConfigId[$fieldName], \PDO::PARAM_INT),
185                            ExpressionBuilder::EQ,
186                            $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
187                        ),
188                        $queryBuilder->expr()->eq(
189                            'backend_layout.pid',
190                            $queryBuilder->createNamedParameter($pageUid, \PDO::PARAM_INT)
191                        )
192                    )
193                )
194            );
195
196        if (!empty($GLOBALS['TCA'][$this->tableName]['ctrl']['sortby'])) {
197            $queryBuilder->orderBy($GLOBALS['TCA'][$this->tableName]['ctrl']['sortby']);
198        }
199
200        $statement = $queryBuilder->execute();
201
202        $results = [];
203        while ($record = $statement->fetch()) {
204            BackendUtility::workspaceOL($this->tableName, $record);
205            if (is_array($record)) {
206                $results[$record['t3ver_oid'] ?: $record['uid']] = $record;
207            }
208        }
209
210        return $results;
211    }
212
213    /**
214     * Returns the storage PID from TCEFORM.
215     *
216     * @param array $pageTsConfig
217     * @return int
218     */
219    protected function getStoragePid(array $pageTsConfig)
220    {
221        $storagePid = 0;
222
223        if (!empty($pageTsConfig['TCEFORM.']['pages.']['_STORAGE_PID'])) {
224            $storagePid = (int)$pageTsConfig['TCEFORM.']['pages.']['_STORAGE_PID'];
225        }
226
227        return $storagePid;
228    }
229
230    /**
231     * Returns the page TSconfig from TCEFORM.
232     *
233     * @param array $pageTsConfig
234     * @return array
235     */
236    protected function getPageTSconfigIds(array $pageTsConfig)
237    {
238        $pageTsConfigIds = [
239            'backend_layout' => 0,
240            'backend_layout_next_level' => 0,
241        ];
242
243        if (!empty($pageTsConfig['TCEFORM.']['pages.']['backend_layout.']['PAGE_TSCONFIG_ID'])) {
244            $pageTsConfigIds['backend_layout'] = (int)$pageTsConfig['TCEFORM.']['pages.']['backend_layout.']['PAGE_TSCONFIG_ID'];
245        }
246
247        if (!empty($pageTsConfig['TCEFORM.']['pages.']['backend_layout_next_level.']['PAGE_TSCONFIG_ID'])) {
248            $pageTsConfigIds['backend_layout_next_level'] = (int)$pageTsConfig['TCEFORM.']['pages.']['backend_layout_next_level.']['PAGE_TSCONFIG_ID'];
249        }
250
251        return $pageTsConfigIds;
252    }
253}
254