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