[TASK] Category fields are not added to TCA interface section
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Category / CategoryRegistry.php
1 <?php
2 namespace TYPO3\CMS\Core\Category;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\SingletonInterface;
18 use TYPO3\CMS\Core\Utility\ArrayUtility;
19 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Lang\LanguageService;
22
23 /**
24 * Class to register category configurations.
25 *
26 * @author Fabien Udriot <fabien.udriot@typo3.org>
27 * @author Oliver Hader <oliver.hader@typo3.org>
28 */
29 class CategoryRegistry implements SingletonInterface {
30
31 /**
32 * @var array
33 */
34 protected $registry = array();
35
36 /**
37 * @var array
38 */
39 protected $extensions = array();
40
41 /**
42 * @var array
43 */
44 protected $addedCategoryTabs = array();
45
46 /**
47 * @var string
48 */
49 protected $template = '';
50
51 /**
52 * Returns a class instance
53 *
54 * @return CategoryRegistry
55 */
56 static public function getInstance() {
57 return GeneralUtility::makeInstance(__CLASS__);
58 }
59
60 /**
61 * Creates this object.
62 */
63 public function __construct() {
64 $this->template = str_repeat(PHP_EOL, 3) . 'CREATE TABLE %s (' . PHP_EOL
65 . ' %s int(11) DEFAULT \'0\' NOT NULL' . PHP_EOL . ');' . str_repeat(PHP_EOL, 3);
66 }
67
68 /**
69 * Adds a new category configuration to this registry.
70 * TCA changes are directly applied
71 *
72 * @param string $extensionKey Extension key to be used
73 * @param string $tableName Name of the table to be registered
74 * @param string $fieldName Name of the field to be registered
75 * @param array $options Additional configuration options
76 * + fieldList: field configuration to be added to showitems
77 * + typesList: list of types that shall visualize the categories field
78 * + position: insert position of the categories field
79 * + label: backend label of the categories field
80 * + fieldConfiguration: TCA field config array to override defaults
81 * @return bool
82 * @throws \InvalidArgumentException
83 * @throws \RuntimeException
84 */
85 public function add($extensionKey, $tableName, $fieldName = 'categories', array $options = array()) {
86 $didRegister = FALSE;
87 if (empty($tableName) || !is_string($tableName)) {
88 throw new \InvalidArgumentException('No or invalid table name "' . $tableName . '" given.', 1369122038);
89 }
90 if (empty($extensionKey) || !is_string($extensionKey)) {
91 throw new \InvalidArgumentException('No or invalid extension key "' . $extensionKey . '" given.', 1397836158);
92 }
93
94 if (!$this->isRegistered($tableName, $fieldName)) {
95 $this->registry[$tableName][$fieldName] = $options;
96 $this->extensions[$extensionKey][$tableName][$fieldName] = $fieldName;
97
98 if (!isset($GLOBALS['TCA'][$tableName]['columns']) && isset($GLOBALS['TCA'][$tableName]['ctrl']['dynamicConfigFile'])) {
99 // Handle deprecated old style dynamic TCA column loading.
100 ExtensionManagementUtility::loadNewTcaColumnsConfigFiles();
101 }
102
103 if (isset($GLOBALS['TCA'][$tableName]['columns'])) {
104 $this->applyTcaForTableAndField($tableName, $fieldName);
105 $didRegister = TRUE;
106 }
107 }
108
109 return $didRegister;
110 }
111
112 /**
113 * Gets all extension keys that registered a category configuration.
114 *
115 * @return array
116 */
117 public function getExtensionKeys() {
118 return array_keys($this->extensions);
119 }
120
121 /**
122 * Gets all categorized tables
123 *
124 * @return array
125 */
126 public function getCategorizedTables() {
127 return array_keys($this->registry);
128 }
129
130 /**
131 * Returns a list of category fields for a given table for populating selector "category_field"
132 * in tt_content table (called as itemsProcFunc).
133 *
134 * @param array $configuration Current field configuration
135 * @throws \UnexpectedValueException
136 * @return void
137 */
138 public function getCategoryFieldsForTable(array &$configuration) {
139 $table = '';
140 // Define the table being looked up from the type of menu
141 if ($configuration['row']['menu_type'] == 'categorized_pages') {
142 $table = 'pages';
143 } elseif ($configuration['row']['menu_type'] == 'categorized_content') {
144 $table = 'tt_content';
145 }
146 // Return early if no table is defined
147 if (empty($table)) {
148 throw new \UnexpectedValueException('The given menu_type is not supported.', 1381823570);
149 }
150 // Loop on all registries and find entries for the correct table
151 foreach ($this->registry as $tableName => $fields) {
152 if ($tableName === $table) {
153 foreach ($fields as $fieldName => $options) {
154 $fieldLabel = $this->getLanguageService()->sL($GLOBALS['TCA'][$tableName]['columns'][$fieldName]['label']);
155 $configuration['items'][] = array($fieldLabel, $fieldName);
156 }
157 }
158 }
159 }
160
161 /**
162 * Tells whether a table has a category configuration in the registry.
163 *
164 * @param string $tableName Name of the table to be looked up
165 * @param string $fieldName Name of the field to be looked up
166 * @return bool
167 */
168 public function isRegistered($tableName, $fieldName = 'categories') {
169 return isset($this->registry[$tableName][$fieldName]);
170 }
171
172 /**
173 * Generates tables definitions for all registered tables.
174 *
175 * @return string
176 */
177 public function getDatabaseTableDefinitions() {
178 $sql = '';
179 foreach ($this->getExtensionKeys() as $extensionKey) {
180 $sql .= $this->getDatabaseTableDefinition($extensionKey);
181 }
182 return $sql;
183 }
184
185 /**
186 * Generates table definitions for registered tables by an extension.
187 *
188 * @param string $extensionKey Extension key to have the database definitions created for
189 * @return string
190 */
191 public function getDatabaseTableDefinition($extensionKey) {
192 if (!isset($this->extensions[$extensionKey]) || !is_array($this->extensions[$extensionKey])) {
193 return '';
194 }
195 $sql = '';
196
197 foreach ($this->extensions[$extensionKey] as $tableName => $fields) {
198 foreach ($fields as $fieldName) {
199 $sql .= sprintf($this->template, $tableName, $fieldName);
200 }
201 }
202 return $sql;
203 }
204
205 /**
206 * Apply TCA to all registered tables
207 *
208 * @return void
209 * @internal
210 */
211 public function applyTcaForPreRegisteredTables() {
212 $this->registerDefaultCategorizedTables();
213 foreach ($this->registry as $tableName => $fields) {
214 foreach ($fields as $fieldName => $_) {
215 $this->applyTcaForTableAndField($tableName, $fieldName);
216 }
217 }
218 }
219
220 /**
221 * Applies the additions directly to the TCA
222 *
223 * @param string $tableName
224 * @param string $fieldName
225 */
226 protected function applyTcaForTableAndField($tableName, $fieldName) {
227 $this->addTcaColumn($tableName, $fieldName, $this->registry[$tableName][$fieldName]);
228 $this->addToAllTCAtypes($tableName, $fieldName, $this->registry[$tableName][$fieldName]);
229 }
230
231 /**
232 * Add default categorized tables to the registry
233 *
234 * @return void
235 */
236 protected function registerDefaultCategorizedTables() {
237 $defaultCategorizedTables = GeneralUtility::trimExplode(
238 ',',
239 $GLOBALS['TYPO3_CONF_VARS']['SYS']['defaultCategorizedTables'],
240 TRUE
241 );
242 foreach ($defaultCategorizedTables as $defaultCategorizedTable) {
243 if (!$this->isRegistered($defaultCategorizedTable)) {
244 $this->add('core', $defaultCategorizedTable, 'categories');
245 }
246 }
247 }
248
249 /**
250 * Add a new field into the TCA types -> showitem
251 *
252 * @param string $tableName Name of the table to be categorized
253 * @param string $fieldName Name of the field to be used to store categories
254 * @param array $options Additional configuration options
255 * + fieldList: field configuration to be added to showitems
256 * + typesList: list of types that shall visualize the categories field
257 * + position: insert position of the categories field
258 * @return void
259 */
260 protected function addToAllTCAtypes($tableName, $fieldName, array $options) {
261
262 // Makes sure to add more TCA to an existing structure
263 if (isset($GLOBALS['TCA'][$tableName]['columns'])) {
264
265 if (empty($options['fieldList'])) {
266 $fieldList = $this->addCategoryTab($tableName, $fieldName);
267 } else {
268 $fieldList = $options['fieldList'];
269 }
270
271 $typesList = '';
272 if (!empty($options['typesList'])) {
273 $typesList = $options['typesList'];
274 }
275
276 $position = '';
277 if (!empty($options['position'])) {
278 $position = $options['position'];
279 }
280
281 // Makes the new "categories" field to be visible in TSFE.
282 ExtensionManagementUtility::addToAllTCAtypes($tableName, $fieldList, $typesList, $position);
283
284 }
285 }
286
287 /**
288 * Creates the 'fieldList' string for $fieldName which includes a categories tab.
289 * But only one categories tab is added per table.
290 *
291 * @param string $tableName
292 * @param string $fieldName
293 * @return string
294 */
295 protected function addCategoryTab($tableName, $fieldName) {
296 $fieldList = '';
297 if (!in_array($tableName, $this->addedCategoryTabs)) {
298 $fieldList .= '--div--;LLL:EXT:lang/locallang_tca.xlf:sys_category.tabs.category, ';
299 $this->addedCategoryTabs[] = $tableName;
300 }
301 $fieldList .= $fieldName;
302 return $fieldList;
303 }
304
305 /**
306 * Add a new TCA Column
307 *
308 * @param string $tableName Name of the table to be categorized
309 * @param string $fieldName Name of the field to be used to store categories
310 * @param array $options Additional configuration options
311 * + fieldConfiguration: TCA field config array to override defaults
312 * + label: backend label of the categories field
313 * + interface: boolean if the category should be included in the "interface" section of the TCA table
314 * @return void
315 */
316 protected function addTcaColumn($tableName, $fieldName, array $options) {
317 // Makes sure to add more TCA to an existing structure
318 if (isset($GLOBALS['TCA'][$tableName]['columns'])) {
319 // Take specific label into account
320 $label = 'LLL:EXT:lang/locallang_tca.xlf:sys_category.categories';
321 if (!empty($options['label'])) {
322 $label = $options['label'];
323 }
324
325 // Take specific value of exclude flag into account
326 $exclude = TRUE;
327 if (isset($options['exclude'])) {
328 $exclude = (bool)$options['exclude'];
329 }
330
331 $fieldConfiguration = empty($options['fieldConfiguration']) ? array() : $options['fieldConfiguration'];
332
333 $columns = array(
334 $fieldName => array(
335 'exclude' => $exclude,
336 'label' => $label,
337 'config' => static::getTcaFieldConfiguration($tableName, $fieldName, $fieldConfiguration),
338 ),
339 );
340
341 // Register opposite references for the foreign side of a relation
342 if (empty($GLOBALS['TCA']['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$tableName])) {
343 $GLOBALS['TCA']['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$tableName] = array();
344 }
345 if (!in_array($fieldName, $GLOBALS['TCA']['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$tableName])) {
346 $GLOBALS['TCA']['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$tableName][] = $fieldName;
347 }
348
349 // Add field to interface list per default (unless the 'interface' property is FALSE)
350 if (
351 (!isset($options['interface']) || $options['interface'])
352 && !empty($GLOBALS['TCA'][$tableName]['interface']['showRecordFieldList'])
353 && !GeneralUtility::inList($GLOBALS['TCA'][$tableName]['interface']['showRecordFieldList'], $fieldName)
354 ) {
355 $GLOBALS['TCA'][$tableName]['interface']['showRecordFieldList'] .= ',' . $fieldName;
356 }
357
358 // Adding fields to an existing table definition
359 ExtensionManagementUtility::addTCAcolumns($tableName, $columns);
360 }
361 }
362
363 /**
364 * Get the config array for given table and field.
365 * This method does NOT take care of adding sql fields, adding the field to TCA types
366 * nor does it set the MM_oppositeUsage in the sys_category TCA. This has to be taken care of manually!
367 *
368 * @param string $tableName The table name
369 * @param string $fieldName The field name (default categories)
370 * @param array $fieldConfigurationOverride Changes to the default configuration
371 * @return array
372 * @api
373 */
374 static public function getTcaFieldConfiguration($tableName, $fieldName = 'categories', array $fieldConfigurationOverride = array()) {
375 // Forges a new field, default name is "categories"
376 $fieldConfiguration = array(
377 'type' => 'select',
378 'foreign_table' => 'sys_category',
379 'foreign_table_where' => ' AND sys_category.sys_language_uid IN (-1, 0) ORDER BY sys_category.sorting ASC',
380 'MM' => 'sys_category_record_mm',
381 'MM_opposite_field' => 'items',
382 'MM_match_fields' => array(
383 'tablenames' => $tableName,
384 'fieldname' => $fieldName,
385 ),
386 'size' => 10,
387 'autoSizeMax' => 50,
388 'maxitems' => 9999,
389 'renderMode' => 'tree',
390 'treeConfig' => array(
391 'parentField' => 'parent',
392 'appearance' => array(
393 'expandAll' => TRUE,
394 'showHeader' => TRUE,
395 'maxLevels' => 99,
396 ),
397 ),
398 );
399
400 // Merge changes to TCA configuration
401 if (!empty($fieldConfigurationOverride)) {
402 ArrayUtility::mergeRecursiveWithOverrule(
403 $fieldConfiguration,
404 $fieldConfigurationOverride
405 );
406 }
407
408 return $fieldConfiguration;
409 }
410
411 /**
412 * A slot method to inject the required category database fields to the
413 * tables definition string
414 *
415 * @param array $sqlString
416 * @return array
417 */
418 public function addCategoryDatabaseSchemaToTablesDefinition(array $sqlString) {
419 $this->registerDefaultCategorizedTables();
420 $sqlString[] = $this->getDatabaseTableDefinitions();
421 return array('sqlString' => $sqlString);
422 }
423
424 /**
425 * A slot method to inject the required category database fields of an
426 * extension to the tables definition string
427 *
428 * @param array $sqlString
429 * @param string $extensionKey
430 * @return array
431 */
432 public function addExtensionCategoryDatabaseSchemaToTablesDefinition(array $sqlString, $extensionKey) {
433 $sqlString[] = $this->getDatabaseTableDefinition($extensionKey);
434 return array('sqlString' => $sqlString, 'extensionKey' => $extensionKey);
435 }
436
437 /**
438 * @return LanguageService
439 */
440 protected function getLanguageService() {
441 return $GLOBALS['LANG'];
442 }
443
444 }