‪TYPO3CMS  ‪main
TcaPreparation.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 
21 
28 {
41  public function ‪prepare(array ‪$tca): array
42  {
44  ‪$tca = $this->‪configureFileReferences($tca);
45  return ‪$tca;
46  }
47 
67  protected function ‪configureCategoryRelations(array ‪$tca): array
68  {
69  foreach (‪$tca as $table => &$tableDefinition) {
70  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
71  continue;
72  }
73  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
74  if (($fieldConfig['config']['type'] ?? '') !== 'category') {
75  continue;
76  }
77 
78  if (!isset($fieldConfig['label'])) {
79  $fieldConfig['label'] = 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_category.categories';
80  }
81 
82  // Force foreign_table for type category
83  $fieldConfig['config']['foreign_table'] = 'sys_category';
84 
85  // Initialize default column configuration and merge it with already defined
86  $fieldConfig['config']['size'] ??= 20;
87  $fieldConfig['config']['foreign_table_where'] ??= ' AND {#sys_category}.{#sys_language_uid} IN (-1, 0)';
88 
89  // In case no relationship is given, fall back to "manyToMany"
90  if (empty($fieldConfig['config']['relationship'])) {
91  $fieldConfig['config']['relationship'] = 'manyToMany';
92  }
93 
94  if (!in_array($fieldConfig['config']['relationship'], ['oneToOne', 'oneToMany', 'manyToMany'], true)) {
95  throw new \RuntimeException(
96  $fieldName . ' of table ' . $table . ' is defined as type category with relationship "'
97  . $fieldConfig['config']['relationship'] . '", but only "oneToOne", "oneToMany" and "manyToMany"'
98  . ' are allowed.',
99  1627898896
100  );
101  }
102 
103  // Set the maxitems value (necessary for DataHandling and FormEngine)
104  if ($fieldConfig['config']['relationship'] === 'oneToOne') {
105  // In case relationship is set to "oneToOne", the database column for this
106  // field will be an integer column. This means, only one uid can be stored.
107  // Therefore maxitems must be 1.
108  if ((int)($fieldConfig['config']['maxitems'] ?? 0) > 1) {
109  throw new \RuntimeException(
110  $fieldName . ' of table ' . $table . ' is defined as type category with an oneToOne relationship. ' .
111  'Therefore maxitems must be 1. Otherwise, use oneToMany or manyToMany as relationship instead.',
112  1627335016
113  );
114  }
115  $fieldConfig['config']['maxitems'] = 1;
116  } elseif (!($fieldConfig['config']['maxitems'] ?? false)) {
117  // In case maxitems is not set or set to 0, set the default value "99999"
118  $fieldConfig['config']['maxitems'] = 99999;
119  } elseif ($fieldConfig['config']['relationship'] === 'oneToMany'
120  && (int)($fieldConfig['config']['maxitems'] ?? 0) === 1
121  ) {
122  throw new \RuntimeException(
123  $fieldName . ' of table ' . $table . ' is defined as type category with a ' . $fieldConfig['config']['relationship'] .
124  ' relationship. Therefore, maxitems can not be set to 1. Use oneToOne as relationship instead.',
125  1627335017
126  );
127  }
128 
129  // Add the default value if not set
130  if (!isset($fieldConfig['config']['default'])
131  && $fieldConfig['config']['relationship'] !== 'oneToMany'
132  ) {
133  // @todo: This is db wise not accurate: A oneToOne relation without a relation being assigned,
134  // @todo: should have NULL as value, not 0, since 0 looks like a uid, but it isn't.
135  // @todo: The field should be nullable and DH should handle that.
136  $fieldConfig['config']['default'] = 0;
137  }
138 
139  // Add MM related properties in case relationship is set to "manyToMany".
140  // This will not be done for the sys_category table itself.
141  if ($fieldConfig['config']['relationship'] === 'manyToMany' && $table !== 'sys_category') {
142  // Note these settings are hard coded here and can't be overridden.
143  $fieldConfig['config'] = array_replace_recursive($fieldConfig['config'], [
144  'MM' => 'sys_category_record_mm',
145  'MM_opposite_field' => 'items',
146  'MM_match_fields' => [
147  'tablenames' => $table,
148  'fieldname' => $fieldName,
149  ],
150  ]);
151 
152  // Register opposite references for the foreign side of a category relation
153  if (empty(‪$tca['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$table])) {
154  ‪$tca['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$table] = [];
155  }
156  if (!in_array($fieldName, ‪$tca['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$table], true)) {
157  ‪$tca['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$table][] = $fieldName;
158  }
159 
160  // Take specific value of exclude flag into account
161  if (!isset($fieldConfig['exclude'])) {
162  $fieldConfig['exclude'] = true;
163  }
164  }
165  }
166  }
167 
168  return ‪$tca;
169  }
170 
171  protected function ‪configureFileReferences(array ‪$tca): array
172  {
173  $prepareFileExtensionLambda = static function (array $config): array {
174  if (!empty($allowed = ($config['allowed'] ?? null))) {
175  $config['allowed'] = ‪self::prepareFileExtensions($allowed);
176  }
177  if (!empty($disallowed = ($config['disallowed'] ?? null))) {
178  $config['disallowed'] = ‪self::prepareFileExtensions($disallowed);
179  }
180  return $config;
181  };
182 
183  $migrateOverrideChildTcaExtensions = static function ($overrideChildTcaConfig) use ($prepareFileExtensionLambda): array {
184  if (is_array($overrideChildTcaConfig['columns'] ?? null)) {
185  foreach ($overrideChildTcaConfig['columns'] as &$overrideChildTcaColumnConfig) {
186  if (!isset($overrideChildTcaColumnConfig['config'])) {
187  continue;
188  }
189  $overrideChildTcaColumnConfig['config'] = $prepareFileExtensionLambda($overrideChildTcaColumnConfig['config']);
190  }
191  unset($overrideChildTcaColumnConfig);
192  }
193  if (is_array($overrideChildTcaConfig['types'] ?? null)) {
194  foreach ($overrideChildTcaConfig['types'] as &$overrideChildTcaTypeConfig) {
195  if (!isset($overrideChildTcaTypeConfig['config'])) {
196  continue;
197  }
198  $overrideChildTcaTypeConfig['config'] = $prepareFileExtensionLambda($overrideChildTcaTypeConfig['config']);
199  }
200  }
201  return $overrideChildTcaConfig;
202  };
203 
204  foreach (‪$tca as $table => &$tableDefinition) {
205  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
206  continue;
207  }
208  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
209  if (($fieldConfig['config']['type'] ?? '') !== 'file') {
210  continue;
211  }
212 
213  // Set static values for this type. Most of them are not needed due to the
214  // dedicated TCA type. However a lot of underlying code in DataHandler and
215  // friends relies on those keys, especially "foreign_table" and "foreign_selector".
216  // @todo Check which of those values can be removed since only used by FormEngine
217  $fieldConfig['config'] = array_replace_recursive($fieldConfig['config'], [
218  'foreign_table' => 'sys_file_reference',
219  'foreign_field' => 'uid_foreign',
220  'foreign_sortby' => 'sorting_foreign',
221  'foreign_table_field' => 'tablenames',
222  'foreign_match_fields' => [
223  'fieldname' => $fieldName,
224  'tablenames' => $table,
225  ],
226  'foreign_label' => 'uid_local',
227  'foreign_selector' => 'uid_local',
228  ]);
229  $fieldConfig['config'] = $prepareFileExtensionLambda($fieldConfig['config']);
230  if (is_array($fieldConfig['config']['overrideChildTca'] ?? null)) {
231  $fieldConfig['config']['overrideChildTca'] = $migrateOverrideChildTcaExtensions($fieldConfig['config']['overrideChildTca']);
232  }
233  }
234  unset($fieldConfig);
235 
236  if (is_array($tableDefinition['types'] ?? null)) {
237  foreach ($tableDefinition['types'] as &$typeConfig) {
238  if (!isset($typeConfig['columnsOverrides']) || !is_array($typeConfig['columnsOverrides'])) {
239  continue;
240  }
241  foreach ($typeConfig['columnsOverrides'] as &$columnsOverridesConfig) {
242  if (!isset($columnsOverridesConfig['config']) || !is_array($columnsOverridesConfig['config'])) {
243  continue;
244  }
245 
246  $columnsOverridesConfig['config'] = $prepareFileExtensionLambda($columnsOverridesConfig['config']);
247  if (is_array($columnsOverridesConfig['config']['overrideChildTca'] ?? null)) {
248  $columnsOverridesConfig['config']['overrideChildTca'] = $migrateOverrideChildTcaExtensions($columnsOverridesConfig['config']['overrideChildTca']);
249  }
250  }
251  }
252  }
253  }
254 
255  return ‪$tca;
256  }
257 
263  public static function ‪prepareFileExtensions(mixed $fileExtensions): string
264  {
265  if (is_array($fileExtensions)) {
266  $fileExtensions = implode(',', $fileExtensions);
267  } else {
268  $fileExtensions = (string)$fileExtensions;
269  }
270 
271  // Replace placeholders with the corresponding $GLOBALS value for now
272  if (preg_match_all('/common-(image|text|media)-types/', $fileExtensions, $matches)) {
273  foreach ($matches[1] as $key => $type) {
274  $fileExtensions = str_replace(
275  $matches[0][$key],
276  ‪$GLOBALS['TYPO3_CONF_VARS'][$type === 'image' ? 'GFX' : 'SYS'][$type . 'file_ext'] ?? '',
277  $fileExtensions
278  );
279  }
280  }
281 
282  return ‪StringUtility::uniqueList($fileExtensions);
283  }
284 }
‪TYPO3\CMS\Core\Configuration\Tca\TcaPreparation
Definition: TcaPreparation.php:28
‪TYPO3\CMS\Core\Utility\StringUtility\uniqueList
‪static string uniqueList(string $list)
Definition: StringUtility.php:122
‪TYPO3\CMS\Core\Configuration\Tca\TcaPreparation\configureCategoryRelations
‪configureCategoryRelations(array $tca)
Definition: TcaPreparation.php:67
‪TYPO3\CMS\Core\Configuration\Tca
Definition: TcaFactory.php:18
‪TYPO3\CMS\Core\Configuration\Tca\TcaPreparation\configureFileReferences
‪configureFileReferences(array $tca)
Definition: TcaPreparation.php:171
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Configuration\Tca\TcaPreparation\prepare
‪prepare(array $tca)
Definition: TcaPreparation.php:41
‪$tca
‪$tca
Definition: sys_file_metadata.php:5
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Core\Configuration\Tca\TcaPreparation\prepareFileExtensions
‪static prepareFileExtensions(mixed $fileExtensions)
Definition: TcaPreparation.php:263