‪TYPO3CMS  ‪main
TcaSiteLanguage.php
Go to the documentation of this file.
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 
19 
29 
36 {
37  private const ‪FOREIGN_TABLE = 'site_language';
38  private const ‪FOREIGN_FIELD = 'languageId';
39 
40  public function ‪addData(array $result): array
41  {
42  foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
43  if (($fieldConfig['config']['type'] ?? '') !== 'siteLanguage') {
44  continue;
45  }
46 
47  if (!(‪$GLOBALS['TCA'][self::FOREIGN_TABLE] ?? false)) {
48  throw new \RuntimeException('Table ' . self::FOREIGN_TABLE . ' does not exists', 1624029932);
49  }
50 
51  $childConfiguration = ‪$GLOBALS['TCA'][‪self::FOREIGN_TABLE]['columns'][‪self::FOREIGN_FIELD]['config'] ?? [];
52 
53  if (($childConfiguration['type'] ?? '') !== 'select') {
54  throw new \UnexpectedValueException(
55  'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points to field '
56  . self::FOREIGN_FIELD . ' of table ' . self::FOREIGN_TABLE . ', but this field '
57  . 'is either not defined or is not of type select',
58  1624029933
59  );
60  }
61 
62  if (!($childConfiguration['itemsProcFunc'] ?? false)) {
63  throw new \UnexpectedValueException(
64  'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points to field '
65  . self::FOREIGN_FIELD . ' of table ' . self::FOREIGN_TABLE . '. This field must define '
66  . 'an \'itemsProcFunc\'.',
67  1624029934
68  );
69  }
70 
71  $result = $this->‪addInlineRelatedConfig($result, $fieldName);
72  $result = $this->‪initializeMinMaxItems($result, $fieldName);
73  $result = $this->‪initializeAppearance($result, $fieldName);
74  $result = $this->‪addInlineFirstPid($result);
75  $result = $this->‪resolveSiteLanguageChildren($result, $fieldName);
76  $result = $this->‪addUniquePossibleRecords($result, $fieldName);
77  }
78 
79  return $result;
80  }
81 
82  protected function ‪addInlineRelatedConfig(array $result, string $fieldName): array
83  {
84  $config = $result['processedTca']['columns'][$fieldName]['config'];
85  $config['foreign_table'] = ‪self::FOREIGN_TABLE;
86  $config['foreign_selector'] = ‪self::FOREIGN_FIELD;
87  $result['processedTca']['columns'][$fieldName]['config'] = $config;
88 
89  return $result;
90  }
91 
92  protected function ‪initializeMinMaxItems(array $result, string $fieldName): array
93  {
94  $config = $result['processedTca']['columns'][$fieldName]['config'];
95  $config['minitems'] = isset($config['minitems']) ? ‪MathUtility::forceIntegerInRange($config['minitems'], 1) : 1;
96  $config['maxitems'] = isset($config['maxitems']) ? ‪MathUtility::forceIntegerInRange($config['maxitems'], 2) : 99999;
97  $result['processedTca']['columns'][$fieldName]['config'] = $config;
98 
99  return $result;
100  }
101 
102  protected function ‪initializeAppearance(array $result, string $fieldName): array
103  {
104  $config = $result['processedTca']['columns'][$fieldName]['config'];
105  if (!is_array($config['appearance'] ?? false)) {
106  $config['appearance'] = [];
107  }
108  $config['appearance']['showPossibleLocalizationRecords'] = false;
109  $config['appearance']['collapseAll'] = true;
110  $config['appearance']['expandSingle'] = false;
111  $config['appearance']['enabledControls'] = [
112  'info' => false,
113  'new' => false,
114  'dragdrop' => false,
115  'sort' => false,
116  'hide' => false,
117  'delete' => true,
118  'localize' => false,
119  ];
120 
121  $config['size'] = (int)($config['size'] ?? 4);
122 
123  $result['processedTca']['columns'][$fieldName]['config'] = $config;
124 
125  return $result;
126  }
127 
128  protected function ‪addInlineFirstPid(array $result): array
129  {
130  if (($result['inlineFirstPid'] ?? null) !== null || ($result['tableName'] ?? '') !== self::FOREIGN_TABLE) {
131  return $result;
132  }
133 
134  $pid = $result['databaseRow']['pid'] ?? 0;
135 
136  if (!‪MathUtility::canBeInterpretedAsInteger($pid) || !str_starts_with($pid, 'NEW')) {
137  throw new \RuntimeException(
138  'inlineFirstPid should either be an integer or a "NEW..." string',
139  1624310264
140  );
141  }
142 
143  $result['inlineFirstPid'] = $pid;
144 
145  return $result;
146  }
147 
148  protected function ‪resolveSiteLanguageChildren(array $result, string $fieldName): array
149  {
150  $connectedUids = [];
151  $result['processedTca']['columns'][$fieldName]['children'] = [];
152 
153  if ($result['command'] === 'edit') {
154  $siteConfiguration = [];
155  try {
156  $site = GeneralUtility::makeInstance(SiteFinder::class)
157  ->getSiteByRootPageId((int)($result['databaseRow']['rootPageId'][0] ?? 0));
158  $siteConfiguration = $site->getConfiguration();
159  } catch (‪SiteNotFoundException $e) {
160  }
161  if (is_array($siteConfiguration[$fieldName] ?? false)) {
162  // Add uids of existing site languages
163  $connectedUids = array_keys($siteConfiguration[$fieldName]);
164  }
165  } elseif ($result['command'] === 'new') {
166  // If new, *always* force a relation to the default language ("0")
167  $child = $this->‪compileDefaultSiteLanguageChild($result, $fieldName);
168  $connectedUids[] = $child['databaseRow']['uid'];
169  $result['processedTca']['columns'][$fieldName]['children'][] = $child;
170  }
171 
172  // Add connected uids as csv field value
173  $result['databaseRow'][$fieldName] = implode(',', $connectedUids);
174 
175  if ($result['inlineCompileExistingChildren']) {
176  foreach ($connectedUids as ‪$uid) {
177  // Compile existing (persisted) site languages
178  if (!str_starts_with((string)‪$uid, 'NEW')) {
179  $compiledChild = $this->‪compileChild($result, $fieldName, ‪$uid);
180  $result['processedTca']['columns'][$fieldName]['children'][] = $compiledChild;
181  }
182  }
183  }
184 
185  if ($result['command'] === 'edit') {
186  // If edit, find out if a default language ("0") exists, else add it on top
187  $defaultSysSiteLanguageChildFound = false;
188  foreach ($result['processedTca']['columns'][$fieldName]['children'] as $child) {
189  if (isset($child['databaseRow']['languageId'][0]) && (int)$child['databaseRow']['languageId'][0] === 0) {
190  $defaultSysSiteLanguageChildFound = true;
191  }
192  }
193  if (!$defaultSysSiteLanguageChildFound) {
194  // Compile and add child as first child, since non exists yet
195  $child = $this->‪compileDefaultSiteLanguageChild($result, $fieldName);
196  $result['databaseRow'][$fieldName] = $child['databaseRow']['uid'] . ',' . $result['databaseRow'][$fieldName];
197  array_unshift($result['processedTca']['columns'][$fieldName]['children'], $child);
198  }
199  }
200 
201  return $result;
202  }
203 
204  protected function ‪compileDefaultSiteLanguageChild(array $result, string $parentFieldName): array
205  {
206  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
207  $inlineStackProcessor->initializeByGivenStructure($result['inlineStructure']);
208  $inlineTopMostParent = $inlineStackProcessor->getStructureLevel(0);
209 
210  return GeneralUtility::makeInstance(FormDataCompiler::class)
211  ->compile(
212  [
213  'request' => $result['request'],
214  'command' => 'new',
215  'tableName' => self::FOREIGN_TABLE,
216  'vanillaUid' => $result['inlineFirstPid'],
217  'databaseRow' => $this->‪getDefaultDatabaseRow(),
218  'returnUrl' => $result['returnUrl'],
219  'isInlineChild' => true,
220  'inlineStructure' => [],
221  'inlineExpandCollapseStateArray' => $result['inlineExpandCollapseStateArray'],
222  'inlineFirstPid' => $result['inlineFirstPid'],
223  'inlineParentConfig' => $result['processedTca']['columns'][$parentFieldName]['config'],
224  'inlineParentUid' => $result['databaseRow']['uid'],
225  'inlineParentTableName' => $result['tableName'],
226  'inlineParentFieldName' => $parentFieldName,
227  'inlineTopMostParentUid' => $result['inlineTopMostParentUid'] ?: ($inlineTopMostParent['uid'] ?? null),
228  'inlineTopMostParentTableName' => $result['inlineTopMostParentTableName'] ?: ($inlineTopMostParent['table'] ?? ''),
229  'inlineTopMostParentFieldName' => $result['inlineTopMostParentFieldName'] ?: ($inlineTopMostParent['field'] ?? ''),
230  'inlineChildChildUid' => 0,
231  ],
232  GeneralUtility::makeInstance(SiteConfigurationDataGroup::class)
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(FormDataCompiler::class)
243  ->compile(
244  [
245  'request' => $result['request'],
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  GeneralUtility::makeInstance(SiteConfigurationDataGroup::class)
263  );
264  }
265 
266  protected function ‪addUniquePossibleRecords(array $result, string $fieldName): array
267  {
268  $formDataGroup = GeneralUtility::makeInstance(OnTheFly::class);
269  $formDataGroup->setProviderList([TcaSelectItems::class]);
270 
271  // Add unique possible records, so they can be used in the selector field
272  $result['processedTca']['columns'][$fieldName]['config']['uniquePossibleRecords'] = GeneralUtility::makeInstance(FormDataCompiler::class)
273  ->compile(
274  [
275  'request' => $result['request'],
276  'command' => 'new',
277  'tableName' => self::FOREIGN_TABLE,
278  'pageTsConfig' => $result['pageTsConfig'],
279  'userTsConfig' => $result['userTsConfig'],
280  'databaseRow' => $result['databaseRow'],
281  'processedTca' => [
282  'ctrl' => [],
283  'columns' => [
284  self::FOREIGN_FIELD => [
285  'config' => ‪$GLOBALS['TCA'][self::FOREIGN_TABLE]['columns'][self::FOREIGN_FIELD]['config'],
286  ],
287  ],
288  ],
289  'inlineExpandCollapseStateArray' => $result['inlineExpandCollapseStateArray'],
290  ],
291  $formDataGroup
292  )['processedTca']['columns'][‪self::FOREIGN_FIELD]['config']['items'] ?? [];
293 
294  return $result;
295  }
296 
301  protected function ‪getDefaultDatabaseRow(): array
302  {
303  $defaultDatabaseRow = [];
304 
305  foreach (GeneralUtility::makeInstance(SiteFinder::class)->getAllSites() as $site) {
306  foreach ($site->getAllLanguages() as $language) {
307  if ($language->getLanguageId() === 0) {
308  $defaultDatabaseRow['locale'] = $language->getLocale()->posixFormatted();
309  if ($language->getTitle() !== '') {
310  $defaultDatabaseRow['title'] = $language->getTitle();
311  }
312  if ($language->getNavigationTitle() !== '') {
313  $defaultDatabaseRow['navigationTitle'] = $language->getNavigationTitle();
314  }
315  if ($language->getHreflang(true) !== '') {
316  $defaultDatabaseRow['hreflang'] = $language->getHreflang();
317  }
318  if (str_starts_with($language->getFlagIdentifier(), 'flags-')) {
319  $flagIdentifier = str_replace('flags-', '', $language->getFlagIdentifier());
320  $defaultDatabaseRow['flag'] = ($flagIdentifier === 'multiple') ? 'global' : $flagIdentifier;
321  }
322  break 2;
323  }
324  }
325  }
326 
327  return $defaultDatabaseRow;
328  }
329 }
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\resolveSiteLanguageChildren
‪resolveSiteLanguageChildren(array $result, string $fieldName)
Definition: TcaSiteLanguage.php:148
‪TYPO3\CMS\Backend\Form\FormDataGroup\OnTheFly
Definition: OnTheFly.php:27
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\FOREIGN_FIELD
‪const FOREIGN_FIELD
Definition: TcaSiteLanguage.php:38
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\addData
‪addData(array $result)
Definition: TcaSiteLanguage.php:40
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage
Definition: TcaSiteLanguage.php:36
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\addUniquePossibleRecords
‪addUniquePossibleRecords(array $result, string $fieldName)
Definition: TcaSiteLanguage.php:266
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractDatabaseRecordProvider
Definition: AbstractDatabaseRecordProvider.php:31
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\compileChild
‪compileChild(array $result, string $parentFieldName, int $childUid)
Definition: TcaSiteLanguage.php:236
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\addInlineRelatedConfig
‪addInlineRelatedConfig(array $result, string $fieldName)
Definition: TcaSiteLanguage.php:82
‪TYPO3\CMS\Backend\Form\FormDataProvider
Definition: AbstractDatabaseRecordProvider.php:16
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\addInlineFirstPid
‪addInlineFirstPid(array $result)
Definition: TcaSiteLanguage.php:128
‪TYPO3\CMS\Backend\Form\FormDataProviderInterface
Definition: FormDataProviderInterface.php:23
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\getDefaultDatabaseRow
‪getDefaultDatabaseRow()
Definition: TcaSiteLanguage.php:301
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\FOREIGN_TABLE
‪const FOREIGN_TABLE
Definition: TcaSiteLanguage.php:37
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\initializeAppearance
‪initializeAppearance(array $result, string $fieldName)
Definition: TcaSiteLanguage.php:102
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\compileDefaultSiteLanguageChild
‪compileDefaultSiteLanguageChild(array $result, string $parentFieldName)
Definition: TcaSiteLanguage.php:204
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage\initializeMinMaxItems
‪initializeMinMaxItems(array $result, string $fieldName)
Definition: TcaSiteLanguage.php:92
‪TYPO3\CMS\Backend\Form\InlineStackProcessor
Definition: InlineStackProcessor.php:32
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Backend\Form\FormDataCompiler
Definition: FormDataCompiler.php:26
‪TYPO3\CMS\Backend\Form\FormDataGroup\SiteConfigurationDataGroup
Definition: SiteConfigurationDataGroup.php:33