[FEATURE] Add translation params for category
[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 * + l10n_mode
315 * + l10n_display
316 * @return void
317 */
318 protected function addTcaColumn($tableName, $fieldName, array $options) {
319 // Makes sure to add more TCA to an existing structure
320 if (isset($GLOBALS['TCA'][$tableName]['columns'])) {
321 // Take specific label into account
322 $label = 'LLL:EXT:lang/locallang_tca.xlf:sys_category.categories';
323 if (!empty($options['label'])) {
324 $label = $options['label'];
325 }
326
327 // Take specific value of exclude flag into account
328 $exclude = TRUE;
329 if (isset($options['exclude'])) {
330 $exclude = (bool)$options['exclude'];
331 }
332
333 $fieldConfiguration = empty($options['fieldConfiguration']) ? array() : $options['fieldConfiguration'];
334
335 $columns = array(
336 $fieldName => array(
337 'exclude' => $exclude,
338 'label' => $label,
339 'config' => static::getTcaFieldConfiguration($tableName, $fieldName, $fieldConfiguration),
340 ),
341 );
342
343 if (isset($options['l10n_mode'])) {
344 $columns[$fieldName]['l10n_mode'] = $options['l10n_mode'];
345 }
346 if (isset($options['l10n_display'])) {
347 $columns[$fieldName]['l10n_display'] = $options['l10n_display'];
348 }
349
350 // Register opposite references for the foreign side of a relation
351 if (empty($GLOBALS['TCA']['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$tableName])) {
352 $GLOBALS['TCA']['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$tableName] = array();
353 }
354 if (!in_array($fieldName, $GLOBALS['TCA']['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$tableName])) {
355 $GLOBALS['TCA']['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$tableName][] = $fieldName;
356 }
357
358 // Add field to interface list per default (unless the 'interface' property is FALSE)
359 if (
360 (!isset($options['interface']) || $options['interface'])
361 && !empty($GLOBALS['TCA'][$tableName]['interface']['showRecordFieldList'])
362 && !GeneralUtility::inList($GLOBALS['TCA'][$tableName]['interface']['showRecordFieldList'], $fieldName)
363 ) {
364 $GLOBALS['TCA'][$tableName]['interface']['showRecordFieldList'] .= ',' . $fieldName;
365 }
366
367 // Adding fields to an existing table definition
368 ExtensionManagementUtility::addTCAcolumns($tableName, $columns);
369 }
370 }
371
372 /**
373 * Get the config array for given table and field.
374 * This method does NOT take care of adding sql fields, adding the field to TCA types
375 * nor does it set the MM_oppositeUsage in the sys_category TCA. This has to be taken care of manually!
376 *
377 * @param string $tableName The table name
378 * @param string $fieldName The field name (default categories)
379 * @param array $fieldConfigurationOverride Changes to the default configuration
380 * @return array
381 * @api
382 */
383 static public function getTcaFieldConfiguration($tableName, $fieldName = 'categories', array $fieldConfigurationOverride = array()) {
384 // Forges a new field, default name is "categories"
385 $fieldConfiguration = array(
386 'type' => 'select',
387 'foreign_table' => 'sys_category',
388 'foreign_table_where' => ' AND sys_category.sys_language_uid IN (-1, 0) ORDER BY sys_category.sorting ASC',
389 'MM' => 'sys_category_record_mm',
390 'MM_opposite_field' => 'items',
391 'MM_match_fields' => array(
392 'tablenames' => $tableName,
393 'fieldname' => $fieldName,
394 ),
395 'size' => 10,
396 'autoSizeMax' => 50,
397 'maxitems' => 9999,
398 'renderMode' => 'tree',
399 'treeConfig' => array(
400 'parentField' => 'parent',
401 'appearance' => array(
402 'expandAll' => TRUE,
403 'showHeader' => TRUE,
404 'maxLevels' => 99,
405 ),
406 ),
407 );
408
409 // Merge changes to TCA configuration
410 if (!empty($fieldConfigurationOverride)) {
411 ArrayUtility::mergeRecursiveWithOverrule(
412 $fieldConfiguration,
413 $fieldConfigurationOverride
414 );
415 }
416
417 return $fieldConfiguration;
418 }
419
420 /**
421 * A slot method to inject the required category database fields to the
422 * tables definition string
423 *
424 * @param array $sqlString
425 * @return array
426 */
427 public function addCategoryDatabaseSchemaToTablesDefinition(array $sqlString) {
428 $this->registerDefaultCategorizedTables();
429 $sqlString[] = $this->getDatabaseTableDefinitions();
430 return array('sqlString' => $sqlString);
431 }
432
433 /**
434 * A slot method to inject the required category database fields of an
435 * extension to the tables definition string
436 *
437 * @param array $sqlString
438 * @param string $extensionKey
439 * @return array
440 */
441 public function addExtensionCategoryDatabaseSchemaToTablesDefinition(array $sqlString, $extensionKey) {
442 $sqlString[] = $this->getDatabaseTableDefinition($extensionKey);
443 return array('sqlString' => $sqlString, 'extensionKey' => $extensionKey);
444 }
445
446 /**
447 * @return LanguageService
448 */
449 protected function getLanguageService() {
450 return $GLOBALS['LANG'];
451 }
452
453 }