Passed
Push — master ( fb6744...da9c6d )
by
unknown
17:04
created

TcaSiteLanguage::addInlineFirstPid()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 18
rs 9.6111
cc 5
nc 3
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Backend\Form\FormDataProvider;
19
20
use TYPO3\CMS\Backend\Form\FormDataCompiler;
21
use TYPO3\CMS\Backend\Form\FormDataGroup\OnTheFly;
22
use TYPO3\CMS\Backend\Form\FormDataGroup\SiteConfigurationDataGroup;
23
use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
24
use TYPO3\CMS\Backend\Form\InlineStackProcessor;
25
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
26
use TYPO3\CMS\Core\Localization\Locales;
27
use TYPO3\CMS\Core\Site\SiteFinder;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Core\Utility\MathUtility;
30
31
/**
32
 * Resolve and prepare site language data
33
 *
34
 * @internal This FormDataProvider is only used in the site configuration module and is not public API
35
 */
36
class TcaSiteLanguage extends AbstractDatabaseRecordProvider implements FormDataProviderInterface
37
{
38
    private const FOREIGN_TABLE = 'site_language';
39
    private const FOREIGN_FIELD = 'languageId';
40
41
    public function addData(array $result): array
42
    {
43
        foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
44
            if (($fieldConfig['config']['type'] ?? '') !== 'siteLanguage') {
45
                continue;
46
            }
47
48
            if (!($GLOBALS['TCA'][self::FOREIGN_TABLE] ?? false)) {
49
                throw new \RuntimeException('Table ' . self::FOREIGN_TABLE . ' does not exists', 1624029932);
50
            }
51
52
            $childConfiguration = $GLOBALS['TCA'][self::FOREIGN_TABLE]['columns'][self::FOREIGN_FIELD]['config'] ?? [];
53
54
            if (($childConfiguration['type'] ?? '') !== 'select') {
55
                throw new \UnexpectedValueException(
56
                    'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points to field '
57
                    . self::FOREIGN_FIELD . ' of table ' . self::FOREIGN_TABLE . ', but this field '
58
                    . 'is either not defined or is not of type select',
59
                    1624029933
60
                );
61
            }
62
63
            if (!($childConfiguration['itemsProcFunc'] ?? false)) {
64
                throw new \UnexpectedValueException(
65
                    'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points to field '
66
                    . self::FOREIGN_FIELD . ' of table ' . self::FOREIGN_TABLE . '. This field must define '
67
                    . 'an \'itemsProcFunc\'.',
68
                    1624029934
69
                );
70
            }
71
72
            $result = $this->addInlineRelatedConfig($result, $fieldName);
73
            $result = $this->initializeMinMaxItems($result, $fieldName);
74
            $result = $this->initializeAppearance($result, $fieldName);
75
            $result = $this->addInlineFirstPid($result);
76
            $result = $this->resolveSiteLanguageChildren($result, $fieldName);
77
            $result = $this->addUniquePossibleRecords($result, $fieldName);
78
        }
79
80
        return $result;
81
    }
82
83
    protected function addInlineRelatedConfig(array $result, string $fieldName): array
84
    {
85
        $config = $result['processedTca']['columns'][$fieldName]['config'];
86
        $config['foreign_table'] = self::FOREIGN_TABLE;
87
        $config['foreign_selector'] = self::FOREIGN_FIELD;
88
        $result['processedTca']['columns'][$fieldName]['config'] = $config;
89
90
        return $result;
91
    }
92
93
    protected function initializeMinMaxItems(array $result, string $fieldName): array
94
    {
95
        $config = $result['processedTca']['columns'][$fieldName]['config'];
96
        $config['minitems'] = isset($config['minitems']) ? MathUtility::forceIntegerInRange($config['minitems'], 1) : 1;
97
        $config['maxitems'] = isset($config['maxitems']) ? MathUtility::forceIntegerInRange($config['maxitems'], 2) : 99999;
98
        $result['processedTca']['columns'][$fieldName]['config'] = $config;
99
100
        return $result;
101
    }
102
103
    protected function initializeAppearance(array $result, string $fieldName): array
104
    {
105
        $config = $result['processedTca']['columns'][$fieldName]['config'];
106
        if (!is_array($config['appearance'] ?? false)) {
107
            $config['appearance'] = [];
108
        }
109
        $config['appearance']['showPossibleLocalizationRecords'] = false;
110
        $config['appearance']['showRemovedLocalizationRecords'] = false;
111
        $config['appearance']['collapseAll'] = true;
112
        $config['appearance']['expandSignle'] = false;
113
        $config['appearance']['enabledControls'] = [
114
            'info' => false,
115
            'new' => false,
116
            'dragdrop' => false,
117
            'sort' => false,
118
            'hide' => false,
119
            'delete' => true,
120
            'localize' => false,
121
        ];
122
123
        $config['size'] = (int)($config['size'] ?? 4);
124
125
        $result['processedTca']['columns'][$fieldName]['config'] = $config;
126
127
        return $result;
128
    }
129
130
    protected function addInlineFirstPid(array $result): array
131
    {
132
        if (($result['inlineFirstPid'] ?? null) !== null || ($result['tableName'] ?? '') !== self::FOREIGN_TABLE) {
133
            return $result;
134
        }
135
136
        $pid = $result['databaseRow']['pid'] ?? 0;
137
138
        if (!MathUtility::canBeInterpretedAsInteger($pid) || strpos($pid, 'NEW') !== 0) {
139
            throw new \RuntimeException(
140
                'inlineFirstPid should either be an integer or a "NEW..." string',
141
                1624310264
142
            );
143
        }
144
145
        $result['inlineFirstPid'] = $pid;
146
147
        return $result;
148
    }
149
150
    protected function resolveSiteLanguageChildren(array $result, string $fieldName): array
151
    {
152
        $connectedUids = [];
153
        $result['processedTca']['columns'][$fieldName]['children'] = [];
154
155
        if ($result['command'] === 'edit') {
156
            $siteConfiguration = [];
157
            try {
158
                $site = GeneralUtility::makeInstance(SiteFinder::class)
159
                    ->getSiteByRootPageId((int)($result['databaseRow']['rootPageId'][0] ?? 0));
160
                $siteConfiguration = $site->getConfiguration();
161
            } catch (SiteNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
162
            }
163
            if (is_array($siteConfiguration[$fieldName] ?? false)) {
164
                // Add uids of existing site languages
165
                $connectedUids = array_keys($siteConfiguration[$fieldName]);
166
            }
167
        } elseif ($result['command'] === 'new') {
168
            // If new, *always* force a relation to the default language ("0")
169
            $child = $this->compileDefaultSiteLanguageChild($result, $fieldName);
170
            $connectedUids[] = $child['databaseRow']['uid'];
171
            $result['processedTca']['columns'][$fieldName]['children'][] = $child;
172
        }
173
174
        // Add connected uids as csv field value
175
        $result['databaseRow'][$fieldName] = implode(',', $connectedUids);
176
177
        if ($result['inlineCompileExistingChildren']) {
178
            foreach ($connectedUids as $uid) {
179
                // Compile existing (persisted) site languages
180
                if (strpos((string)$uid, 'NEW') !== 0) {
181
                    $compiledChild = $this->compileChild($result, $fieldName, $uid);
182
                    $result['processedTca']['columns'][$fieldName]['children'][] = $compiledChild;
183
                }
184
            }
185
        }
186
187
        if ($result['command'] === 'edit') {
188
            // If edit, find out if a default language ("0") exists, else add it on top
189
            $defaultSysSiteLanguageChildFound = false;
190
            foreach ($result['processedTca']['columns'][$fieldName]['children'] as $child) {
191
                if (isset($child['databaseRow']['languageId'][0]) && (int)$child['databaseRow']['languageId'][0] === 0) {
192
                    $defaultSysSiteLanguageChildFound = true;
193
                }
194
            }
195
            if (!$defaultSysSiteLanguageChildFound) {
196
                // Compile and add child as first child, since non exists yet
197
                $child = $this->compileDefaultSiteLanguageChild($result, $fieldName);
198
                $result['databaseRow'][$fieldName] = $child['databaseRow']['uid'] . ',' . $result['databaseRow'][$fieldName];
199
                array_unshift($result['processedTca']['columns'][$fieldName]['children'], $child);
200
            }
201
        }
202
203
        return $result;
204
    }
205
206
    protected function compileDefaultSiteLanguageChild(array $result, string $parentFieldName): array
207
    {
208
        $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
209
        $inlineStackProcessor->initializeByGivenStructure($result['inlineStructure']);
210
        $inlineTopMostParent = $inlineStackProcessor->getStructureLevel(0);
211
212
        return GeneralUtility::makeInstance(
213
            FormDataCompiler::class,
214
            GeneralUtility::makeInstance(SiteConfigurationDataGroup::class)
215
        )->compile([
216
            'command' => 'new',
217
            'tableName' => self::FOREIGN_TABLE,
218
            'vanillaUid' => $result['inlineFirstPid'],
219
            'databaseRow' => $this->getDefaultDatabaseRow(),
220
            'returnUrl' => $result['returnUrl'],
221
            'isInlineChild' => true,
222
            'inlineStructure' => [],
223
            'inlineExpandCollapseStateArray' => $result['inlineExpandCollapseStateArray'],
224
            'inlineFirstPid' => $result['inlineFirstPid'],
225
            'inlineParentConfig' => $result['processedTca']['columns'][$parentFieldName]['config'],
226
            'inlineParentUid' => $result['databaseRow']['uid'],
227
            'inlineParentTableName' => $result['tableName'],
228
            'inlineParentFieldName' => $parentFieldName,
229
            'inlineTopMostParentUid' => $result['inlineTopMostParentUid'] ?: ($inlineTopMostParent['uid'] ?? null),
230
            'inlineTopMostParentTableName' => $result['inlineTopMostParentTableName'] ?: ($inlineTopMostParent['table'] ?? ''),
231
            'inlineTopMostParentFieldName' => $result['inlineTopMostParentFieldName'] ?: ($inlineTopMostParent['field'] ?? ''),
232
            'inlineChildChildUid' => 0,
233
        ]);
234
    }
235
236
    protected function compileChild(array $result, string $parentFieldName, int $childUid): array
237
    {
238
        $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
239
        $inlineStackProcessor->initializeByGivenStructure($result['inlineStructure']);
240
        $inlineTopMostParent = $inlineStackProcessor->getStructureLevel(0);
241
242
        return GeneralUtility::makeInstance(
243
            FormDataCompiler::class,
244
            GeneralUtility::makeInstance(SiteConfigurationDataGroup::class)
245
        )->compile([
246
            'command' => 'edit',
247
            'tableName' => self::FOREIGN_TABLE,
248
            'vanillaUid' => $childUid,
249
            'returnUrl' => $result['returnUrl'],
250
            'isInlineChild' => true,
251
            'inlineStructure' => $result['inlineStructure'],
252
            'inlineExpandCollapseStateArray' => $result['inlineExpandCollapseStateArray'],
253
            'inlineFirstPid' => $result['inlineFirstPid'],
254
            'inlineParentConfig' => $result['processedTca']['columns'][$parentFieldName]['config'],
255
            'inlineParentUid' => $result['databaseRow']['uid'],
256
            'inlineParentTableName' => $result['tableName'],
257
            'inlineParentFieldName' => $parentFieldName,
258
            'inlineTopMostParentUid' => $result['inlineTopMostParentUid'] ?: ($inlineTopMostParent['uid'] ?? null),
259
            'inlineTopMostParentTableName' => $result['inlineTopMostParentTableName'] ?: ($inlineTopMostParent['table'] ?? ''),
260
            'inlineTopMostParentFieldName' => $result['inlineTopMostParentFieldName'] ?: ($inlineTopMostParent['field'] ?? ''),
261
        ]);
262
    }
263
264
    protected function addUniquePossibleRecords(array $result, string $fieldName): array
265
    {
266
        $formDataGroup = GeneralUtility::makeInstance(OnTheFly::class);
267
        $formDataGroup->setProviderList([TcaSelectItems::class]);
268
269
        // Add unique possible records, so they can be used in the selector field
270
        $result['processedTca']['columns'][$fieldName]['config']['uniquePossibleRecords'] = GeneralUtility::makeInstance(
271
            FormDataCompiler::class,
272
            $formDataGroup
273
        )->compile([
274
            'command' => 'new',
275
            'tableName' => self::FOREIGN_TABLE,
276
            'pageTsConfig' => $result['pageTsConfig'],
277
            'userTsConfig' => $result['userTsConfig'],
278
            'databaseRow' => $result['databaseRow'],
279
            'processedTca' => [
280
                'ctrl' => [],
281
                'columns' => [
282
                    self::FOREIGN_FIELD => [
283
                        'config' => $GLOBALS['TCA'][self::FOREIGN_TABLE]['columns'][self::FOREIGN_FIELD]['config'],
284
                    ],
285
                ],
286
            ],
287
            'inlineExpandCollapseStateArray' => $result['inlineExpandCollapseStateArray'],
288
        ])['processedTca']['columns'][self::FOREIGN_FIELD]['config']['items'] ?? [];
289
290
        return $result;
291
    }
292
293
    /**
294
     * Create the database row for the default site language based
295
     * on an already existing default language from another site.
296
     *
297
     * @return array
298
     */
299
    protected function getDefaultDatabaseRow(): array
300
    {
301
        $defaultDatabaseRow = [];
302
303
        foreach (GeneralUtility::makeInstance(SiteFinder::class)->getAllSites() as $site) {
304
            foreach ($site->getAllLanguages() as $language) {
305
                if ($language->getLanguageId() === 0) {
306
                    $defaultDatabaseRow['locale'] = $language->getLocale();
307
                    if ($language->getTitle() !== '') {
308
                        $defaultDatabaseRow['title'] = $language->getTitle();
309
                    }
310
                    if ($language->getTypo3Language() !== '') {
311
                        $locales = GeneralUtility::makeInstance(Locales::class);
312
                        $allLanguages = $locales->getLanguages();
313
                        if (isset($allLanguages[$language->getTypo3Language()])) {
314
                            $defaultDatabaseRow['typo3Language'] = $language->getTypo3Language();
315
                        }
316
                    }
317
                    if ($language->getTwoLetterIsoCode() !== '') {
318
                        $defaultDatabaseRow['iso-639-1'] = $language->getTwoLetterIsoCode();
319
                    }
320
                    if ($language->getNavigationTitle() !== '') {
321
                        $defaultDatabaseRow['navigationTitle'] = $language->getNavigationTitle();
322
                    }
323
                    if ($language->getHreflang() !== '') {
324
                        $defaultDatabaseRow['hreflang'] = $language->getHreflang();
325
                    }
326
                    if ($language->getDirection() !== '') {
327
                        $defaultDatabaseRow['direction'] = $language->getDirection();
328
                    }
329
                    if (strpos($language->getFlagIdentifier(), 'flags-') === 0) {
330
                        $flagIdentifier = str_replace('flags-', '', $language->getFlagIdentifier());
331
                        $defaultDatabaseRow['flag'] = ($flagIdentifier === 'multiple') ? 'global' : $flagIdentifier;
332
                    }
333
                    break 2;
334
                }
335
            }
336
        }
337
338
        return $defaultDatabaseRow;
339
    }
340
}
341