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