[BUGFIX] $_EXTKEY not available in global scope in ext_tables.php
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_extmgm.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Extension Management functions
30 *
31 * This class is never instantiated, rather the methods inside is called as functions like
32 * t3lib_extMgm::isLoaded('my_extension');
33 *
34 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
35 * @package TYPO3
36 * @subpackage t3lib
37 */
38 class t3lib_extMgm {
39
40 protected static $extensionKeyMap;
41
42 /**
43 * TRUE, if ext_tables file was read from cache for this script run.
44 * The frontend tends to do that multiple times, but the caching framework does
45 * not allow this (via a require_once call). This variable is used to track
46 * the access to the cache file to read the single ext_tables.php if it was
47 * already read from cache
48 *
49 * @TODO: See if we can get rid of the 'load multiple times' scenario in fe
50 * @var boolean
51 */
52 protected static $extTablesWasReadFromCacheOnce = FALSE;
53
54 /**************************************
55 *
56 * PATHS and other evaluation
57 *
58 ***************************************/
59
60 /**
61 * Returns TRUE if the extension with extension key $key is loaded.
62 *
63 * @param string $key Extension key to test
64 * @param boolean $exitOnError If $exitOnError is TRUE and the extension is not loaded the function will die with an error message
65 * @return boolean
66 */
67 public static function isLoaded($key, $exitOnError = 0) {
68 if ($exitOnError && !isset($GLOBALS['TYPO3_LOADED_EXT'][$key])) {
69 throw new BadFunctionCallException(
70 'TYPO3 Fatal Error: Extension "' . $key . '" was not loaded!',
71 1270853910
72 );
73 }
74 return isset($GLOBALS['TYPO3_LOADED_EXT'][$key]);
75 }
76
77 /**
78 * Returns the absolute path to the extension with extension key $key
79 * If the extension is not loaded the function will die with an error message
80 * Useful for internal fileoperations
81 *
82 * @param $key string Extension key
83 * @param $script string $script is appended to the output if set.
84 * @throws BadFunctionCallException
85 * @return string
86 */
87 public static function extPath($key, $script = '') {
88 if (isset($GLOBALS['TYPO3_LOADED_EXT'])) {
89 if (!isset($GLOBALS['TYPO3_LOADED_EXT'][$key])) {
90 throw new BadFunctionCallException(
91 'TYPO3 Fatal Error: Extension key "' . $key . '" was NOT loaded!',
92 1270853878
93 );
94 }
95
96 $extensionPath = PATH_site . $GLOBALS['TYPO3_LOADED_EXT'][$key]['siteRelPath'];
97 } else {
98 $extensionList = self::getRequiredExtensionList() . ',' . $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'];
99 $loadedExtensions = array_flip(array_unique(t3lib_div::trimExplode(',', $extensionList, TRUE)));
100
101 if (!isset($loadedExtensions[$key])) {
102 throw new BadFunctionCallException(
103 'TYPO3 Fatal Error: Extension key "' . $key . '" was NOT loaded!',
104 1294430950
105 );
106 }
107
108 if (@is_dir(PATH_typo3conf . 'ext/' . $key . '/')) {
109 $extensionPath = PATH_typo3conf . 'ext/' . $key . '/';
110 } elseif (@is_dir(PATH_typo3 . 'ext/' . $key . '/')) {
111 $extensionPath = PATH_typo3 . 'ext/' . $key . '/';
112 } elseif (@is_dir(PATH_typo3 . 'sysext/' . $key . '/')) {
113 $extensionPath = PATH_typo3 . 'sysext/' . $key . '/';
114 } else {
115 throw new BadFunctionCallException(
116 'TYPO3 Fatal Error: Extension "' . $key . '" was NOT found!',
117 1294430951
118 );
119 }
120 }
121
122 return $extensionPath . $script;
123 }
124
125 /**
126 * Returns the relative path to the extension as measured from from the TYPO3_mainDir
127 * If the extension is not loaded the function will die with an error message
128 * Useful for images and links from backend
129 *
130 * @param string $key Extension key
131 * @return string
132 */
133 public static function extRelPath($key) {
134 if (!isset($GLOBALS['TYPO3_LOADED_EXT'][$key])) {
135 throw new BadFunctionCallException(
136 'TYPO3 Fatal Error: Extension key "' . $key . '" was NOT loaded!',
137 1270853879
138 );
139 }
140 return $GLOBALS['TYPO3_LOADED_EXT'][$key]['typo3RelPath'];
141 }
142
143 /**
144 * Returns the relative path to the extension as measured from the PATH_site (frontend)
145 * If the extension is not loaded the function will die with an error message
146 * Useful for images and links from the frontend
147 *
148 * @param string $key Extension key
149 * @return string
150 */
151 public static function siteRelPath($key) {
152 return substr(self::extPath($key), strlen(PATH_site));
153 }
154
155 /**
156 * Returns the correct class name prefix for the extension key $key
157 *
158 * @param string $key Extension key
159 * @return string
160 * @internal
161 */
162 public static function getCN($key) {
163 return substr($key, 0, 5) == 'user_' ? 'user_' . str_replace('_', '', substr($key, 5)) : 'tx_' . str_replace('_', '', $key);
164 }
165
166 /**
167 * Returns the real extension key like 'tt_news' from an extension prefix like 'tx_ttnews'.
168 *
169 * @param string $prefix The extension prefix (e.g. 'tx_ttnews')
170 * @return mixed Real extension key (string) or FALSE (boolean) if something went wrong
171 */
172 public static function getExtensionKeyByPrefix($prefix) {
173 $result = FALSE;
174 // Build map of short keys referencing to real keys:
175 if (!isset(self::$extensionKeyMap)) {
176 self::$extensionKeyMap = array();
177 foreach (array_keys($GLOBALS['TYPO3_LOADED_EXT']) as $extensionKey) {
178 $shortKey = str_replace('_', '', $extensionKey);
179 self::$extensionKeyMap[$shortKey] = $extensionKey;
180 }
181 }
182 // Lookup by the given short key:
183 $parts = explode('_', $prefix);
184 if (isset(self::$extensionKeyMap[$parts[1]])) {
185 $result = self::$extensionKeyMap[$parts[1]];
186 }
187 return $result;
188 }
189
190 /**
191 * Clears the extension key map.
192 *
193 * @return void
194 */
195 public static function clearExtensionKeyMap() {
196 self::$extensionKeyMap = NULL;
197 }
198
199 /**
200 * Retrieves the version of an installed extension.
201 * If the extension is not installed, this function returns an empty string.
202 *
203 * @param string $key The key of the extension to look up, must not be empty
204 * @return string The extension version as a string in the format "x.y.z",
205 * will be an empty string if the extension is not loaded
206 */
207 public static function getExtensionVersion($key) {
208 if (!is_string($key) || empty($key)) {
209 throw new InvalidArgumentException('Extension key must be a non-empty string.', 1294586096);
210 }
211 if (!self::isLoaded($key)) {
212 return '';
213 }
214 $runtimeCache = $GLOBALS['typo3CacheManager']->getCache('cache_runtime');
215 $cacheIdentifier = 'extMgmExtVersion-' . $key;
216
217 if (!($extensionVersion = $runtimeCache->get($cacheIdentifier))) {
218
219 $EM_CONF = array();
220 $_EXTKEY = $key;
221 include(self::extPath($key) . 'ext_emconf.php');
222 $extensionVersion = $EM_CONF[$key]['version'];
223 $runtimeCache->set($cacheIdentifier, $extensionVersion);
224 }
225 return $extensionVersion;
226 }
227
228 /**************************************
229 *
230 * Adding BACKEND features
231 * (related to core features)
232 *
233 ***************************************/
234
235 /**
236 * Adding fields to an existing table definition in $GLOBALS['TCA']
237 * Adds an array with $GLOBALS['TCA'] column-configuration to the $GLOBALS['TCA']-entry for that table.
238 * This function adds the configuration needed for rendering of the field in TCEFORMS - but it does NOT add the field names to the types lists!
239 * So to have the fields displayed you must also call fx. addToAllTCAtypes or manually add the fields to the types list.
240 * FOR USE IN ext_tables.php FILES
241 *
242 * @param string $table The table name of a table already present in $GLOBALS['TCA'] with a columns section
243 * @param array $columnArray The array with the additional columns (typical some fields an extension wants to add)
244 * @param boolean $addTofeInterface If $addTofeInterface is TRUE the list of fields are also added to the fe_admin_fieldList.
245 * @return void
246 */
247 public static function addTCAcolumns($table, $columnArray, $addTofeInterface = 0) {
248 t3lib_div::loadTCA($table);
249 if (is_array($columnArray) && is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'])) {
250 // Candidate for t3lib_div::array_merge() if integer-keys will some day make trouble...
251 $GLOBALS['TCA'][$table]['columns'] = array_merge($GLOBALS['TCA'][$table]['columns'], $columnArray);
252 if ($addTofeInterface) {
253 $GLOBALS['TCA'][$table]['feInterface']['fe_admin_fieldList'] .= ',' . implode(',', array_keys($columnArray));
254 }
255 }
256 }
257
258 /**
259 * Makes fields visible in the TCEforms, adding them to the end of (all) "types"-configurations
260 *
261 * Adds a string $str (comma list of field names) to all ["types"][xxx]["showitem"] entries for table $table (unless limited by $specificTypesList)
262 * This is needed to have new fields shown automatically in the TCEFORMS of a record from $table.
263 * Typically this function is called after having added new columns (database fields) with the addTCAcolumns function
264 * FOR USE IN ext_tables.php FILES
265 *
266 * @param string $table Table name
267 * @param string $str Field list to add.
268 * @param string $specificTypesList List of specific types to add the field list to. (If empty, all type entries are affected)
269 * @param string $position Insert fields before (default) or after one
270 * of this fields (commalist with "before:", "after:" or "replace:" commands).
271 * Example: "before:keywords,--palette--;;4,after:description".
272 * Palettes must be passed like in the example no matter how the palette definition looks like in TCA.
273 * It will add the list of new fields before or after a palette or replace the field inside the palette,
274 * when the field given in $position is found inside a palette used by the type.
275 * @return void
276 */
277 public static function addToAllTCAtypes($table, $str, $specificTypesList = '', $position = '') {
278 t3lib_div::loadTCA($table);
279 $str = trim($str);
280 $palettesChanged = array();
281 if ($str && is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['types'])) {
282 foreach ($GLOBALS['TCA'][$table]['types'] as $type => &$typeDetails) {
283 if ($specificTypesList === '' || t3lib_div::inList($specificTypesList, $type)) {
284 $fieldExists = FALSE;
285 if ($position != '' && is_array($GLOBALS['TCA'][$table]['palettes'])) {
286 $positionArray = t3lib_div::trimExplode(':', $position);
287 if ($positionArray[0] == 'replace') {
288 foreach ($GLOBALS['TCA'][$table]['palettes'] as $palette => $paletteDetails) {
289 if (preg_match('/\b' . $palette . '\b/', $typeDetails['showitem']) > 0
290 && preg_match('/\b' . $positionArray[1] . '\b/', $paletteDetails['showitem']) > 0) {
291 self::addFieldsToPalette($table, $palette, $str, $position);
292 // Save that palette in case other types use it
293 $palettesChanged[] = $palette;
294 $fieldExists = TRUE;
295 } elseif (in_array($palette, $palettesChanged)) {
296 $fieldExists = TRUE;
297 }
298 }
299 } else {
300 if (strpos($typeDetails['showitem'], $str) !== FALSE) {
301 $fieldExists = TRUE;
302 } else {
303 foreach ($GLOBALS['TCA'][$table]['palettes'] as $palette => $paletteDetails) {
304 if (preg_match('/\b' . $palette . '\b/', $typeDetails['showitem']) > 0
305 && preg_match('/\b' . $positionArray[1] . '\b/', $paletteDetails['showitem']) > 0) {
306 $position = $positionArray[0] . ':--palette--;;' . $palette;
307 }
308 }
309 }
310 }
311 } else {
312 if (strpos($typeDetails['showitem'], $str) !== FALSE) {
313 $fieldExists = TRUE;
314 } elseif (is_array($GLOBALS['TCA'][$table]['palettes'])) {
315 foreach ($GLOBALS['TCA'][$table]['palettes'] as $palette => $paletteDetails) {
316 if (preg_match('/\b' . $palette . '\b/', $typeDetails['showitem']) > 0
317 && strpos($paletteDetails['showitem'], $str) !== FALSE) {
318 $fieldExists = TRUE;
319 }
320 }
321 }
322 }
323 if ($fieldExists === FALSE) {
324 $typeDetails['showitem'] = self::executePositionedStringInsertion(
325 $typeDetails['showitem'],
326 $str,
327 $position
328 );
329 }
330 }
331 }
332 unset($typeDetails);
333 }
334 }
335
336 /**
337 * Adds new fields to all palettes of an existing field.
338 * If the field does not have a palette yet, it's created automatically and
339 * gets called "generatedFor-$field".
340 *
341 * @param string $table Name of the table
342 * @param string $field Name of the field that has the palette to be extended
343 * @param string $addFields List of fields to be added to the palette
344 * @param string $insertionPosition Insert fields before (default) or after one
345 * of this fields (commalist with "before:", "after:" or "replace:" commands).
346 * Example: "before:keywords,--palette--;;4,after:description".
347 * Palettes must be passed like in the example no matter how the
348 * palette definition looks like in TCA.
349 * @return void
350 */
351 public static function addFieldsToAllPalettesOfField($table, $field, $addFields, $insertionPosition = '') {
352 $generatedPalette = '';
353 $processedPalettes = array();
354 t3lib_div::loadTCA($table);
355
356 if (isset($GLOBALS['TCA'][$table]['columns'][$field])) {
357 $types =& $GLOBALS['TCA'][$table]['types'];
358 if (is_array($types)) {
359 // Iterate through all types and search for the field that defines the palette to be extended:
360 foreach (array_keys($types) as $type) {
361 $items = self::explodeItemList($types[$type]['showitem']);
362 if (isset($items[$field])) {
363 // If the field already has a palette, extend it:
364 if ($items[$field]['details']['palette']) {
365 $palette = $items[$field]['details']['palette'];
366 if (!isset($processedPalettes[$palette])) {
367 self::addFieldsToPalette($table, $palette, $addFields, $insertionPosition);
368 $processedPalettes[$palette] = TRUE;
369 }
370 // If there's not palette yet, create one:
371 } else {
372 if ($generatedPalette) {
373 $palette = $generatedPalette;
374 } else {
375 $palette = $generatedPalette = 'generatedFor-' . $field;
376 self::addFieldsToPalette($table, $palette, $addFields, $insertionPosition);
377 }
378 $items[$field]['details']['palette'] = $palette;
379 $types[$type]['showitem'] = self::generateItemList($items);
380 }
381 }
382 }
383 }
384 }
385 }
386
387 /**
388 * Adds new fields to a palette.
389 * If the palette does not exist yet, it's created automatically.
390 *
391 * @param string $table Name of the table
392 * @param string $palette Name of the palette to be extended
393 * @param string $addFields List of fields to be added to the palette
394 * @param string $insertionPosition Insert fields before (default) or after one
395 * of this fields (commalist with "before:", "after:" or "replace:" commands).
396 * Example: "before:keywords,--palette--;;4,after:description".
397 * Palettes must be passed like in the example no matter how the
398 * palette definition looks like in TCA.
399 * @return void
400 */
401 public static function addFieldsToPalette($table, $palette, $addFields, $insertionPosition = '') {
402 t3lib_div::loadTCA($table);
403
404 if (isset($GLOBALS['TCA'][$table])) {
405 $paletteData =& $GLOBALS['TCA'][$table]['palettes'][$palette];
406 // If palette already exists, merge the data:
407 if (is_array($paletteData)) {
408 $paletteData['showitem'] = self::executePositionedStringInsertion(
409 $paletteData['showitem'],
410 $addFields,
411 $insertionPosition
412 );
413 // If it's a new palette, just set the data:
414 } else {
415 $paletteData['showitem'] = self::removeDuplicatesForInsertion($addFields);
416 }
417 }
418 }
419
420 /**
421 * Add an item to a select field item list.
422 *
423 * Warning: Do not use this method for radio or check types, especially not
424 * with $relativeToField and $relativePosition parameters. This would shift
425 * existing database data 'off by one'.
426 *
427 * As an example, this can be used to add an item to tt_content CType select
428 * drop-down after the existing 'mailform' field with these parameters:
429 * - $table = 'tt_content'
430 * - $field = 'CType'
431 * - $item = array(
432 * 'LLL:EXT:cms/locallang_ttc.xml:CType.I.10',
433 * 'login',
434 * 'i/tt_content_login.gif',
435 * ),
436 * - $relativeToField = mailform
437 * - $relativePosition = after
438 *
439 * @throws InvalidArgumentException If given paramenters are not of correct
440 * type or out of bounds
441 * @throws RuntimeException If reference to related position fields can not
442 * be found or if select field is not defined
443 *
444 * @param string $table Name of TCA table
445 * @param string $field Name of TCA field
446 * @param array $item New item to add
447 * @param string $relativeToField Add item relative to existing field
448 * @param string $relativePosition Valid keywords: 'before', 'after'
449 * or 'replace' to relativeToField field
450 *
451 * @return void
452 */
453 public static function addTcaSelectItem($table, $field, array $item, $relativeToField = '', $relativePosition = '') {
454 if (!is_string($table)) {
455 throw new InvalidArgumentException(
456 'Given table is of type "' . gettype($table) . '" but a string is expected.',
457 1303236963
458 );
459 }
460 if (!is_string($field)) {
461 throw new InvalidArgumentException(
462 'Given field is of type "' . gettype($field) . '" but a string is expected.',
463 1303236964
464 );
465 }
466 if (!is_string($relativeToField)) {
467 throw new InvalidArgumentException(
468 'Given relative field is of type "' . gettype($relativeToField) . '" but a string is expected.',
469 1303236965
470 );
471 }
472 if (!is_string($relativePosition)) {
473 throw new InvalidArgumentException(
474 'Given relative position is of type "' . gettype($relativePosition) . '" but a string is expected.',
475 1303236966
476 );
477 }
478 if ($relativePosition !== '' && $relativePosition !== 'before' && $relativePosition !== 'after' && $relativePosition !== 'replace') {
479 throw new InvalidArgumentException(
480 'Relative position must be either empty or one of "before", "after", "replace".',
481 1303236967
482 );
483 }
484
485 t3lib_div::loadTCA($table);
486
487 if (!is_array($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'])) {
488 throw new RuntimeException(
489 'Given select field item list was not found.',
490 1303237468
491 );
492 }
493
494 // Make sure item keys are integers
495 $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'] = array_values($GLOBALS['TCA'][$table]['columns'][$field]['config']['items']);
496
497 if (strlen($relativePosition) > 0) {
498 // Insert at specified position
499 $matchedPosition = t3lib_utility_Array::filterByValueRecursive(
500 $relativeToField,
501 $GLOBALS['TCA'][$table]['columns'][$field]['config']['items']
502 );
503 if (count($matchedPosition) > 0) {
504 $relativeItemKey = key($matchedPosition);
505 if ($relativePosition === 'replace') {
506 $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][$relativeItemKey] = $item;
507 } else {
508 if ($relativePosition === 'before') {
509 $offset = $relativeItemKey;
510 } else {
511 $offset = $relativeItemKey + 1;
512 }
513 array_splice($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'], $offset, 0, array(0 => $item));
514 }
515 } else {
516 // Insert at new item at the end of the array if relative position was not found
517 $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][] = $item;
518 }
519 } else {
520 // Insert at new item at the end of the array
521 $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][] = $item;
522 }
523 }
524
525 /**
526 * Gets the TCA configuration for a field handling (FAL) files.
527 *
528 * @param string $fieldName Name of the field to be used
529 * @param array $customSettingOverride Custom field settings overriding the basics
530 * @param string $allowedFileExtensions Comma list of allowed file extensions (e.g. "jpg,gif,pdf")
531 * @return array
532 */
533 public static function getFileFieldTCAConfig($fieldName, array $customSettingOverride = array(), $allowedFileExtensions = '', $disallowedFileExtensions = '') {
534 $fileFieldTCAConfig = array(
535 'type' => 'inline',
536 'foreign_table' => 'sys_file_reference',
537 'foreign_field' => 'uid_foreign',
538 'foreign_sortby' => 'sorting_foreign',
539 'foreign_table_field' => 'tablenames',
540 'foreign_match_fields' => array(
541 'fieldname' => $fieldName,
542 ),
543 'foreign_label' => 'uid_local',
544 'foreign_selector' => 'uid_local',
545 'foreign_selector_fieldTcaOverride' => array(
546 'config' => array(
547 'appearance' => array(
548 'elementBrowserType' => 'file',
549 'elementBrowserAllowed' => $allowedFileExtensions,
550 ),
551 ),
552 ),
553 'filter' => array(
554 array(
555 'userFunc' => 't3lib_file_Utility_FileExtensionFilter->filterInlineChildren',
556 'parameters' => array(
557 'allowedFileExtensions' => $allowedFileExtensions,
558 'disallowedFileExtensions' => $disallowedFileExtensions,
559 ),
560 ),
561 ),
562 'appearance' => array(
563 'useSortable' => TRUE,
564 'headerThumbnail' => 'uid_local',
565 'enabledControls' => array(
566 'info' => FALSE,
567 'new' => FALSE,
568 'dragdrop' => TRUE,
569 'sort' => FALSE,
570 'hide' => TRUE,
571 'delete' => TRUE,
572 ),
573 ),
574 );
575
576 return t3lib_div::array_merge_recursive_overrule($fileFieldTCAConfig, $customSettingOverride);
577 }
578
579 /**
580 * Adds a list of new fields to the TYPO3 USER SETTINGS configuration "showitem" list, the array with
581 * the new fields itself needs to be added additionally to show up in the user setup, like
582 * $GLOBALS['TYPO3_USER_SETTINGS']['columns'] += $tempColumns
583 *
584 * @param string $addFields List of fields to be added to the user settings
585 * @param string $insertionPosition Insert fields before (default) or after one
586 * of this fields (commalist with "before:", "after:" or "replace:" commands).
587 * Example: "before:password,after:email".
588 * @return void
589 */
590 public static function addFieldsToUserSettings($addFields, $insertionPosition = '') {
591 $GLOBALS['TYPO3_USER_SETTINGS']['showitem'] = self::executePositionedStringInsertion(
592 $GLOBALS['TYPO3_USER_SETTINGS']['showitem'],
593 $addFields,
594 $insertionPosition
595 );
596 }
597
598 /**
599 * Inserts as list of data into an existing list.
600 * The insertion position can be defined accordant before of after existing list items.
601 *
602 * @param string $list The list of items to be extended
603 * @param string $insertionList The list of items to inserted
604 * @param string $insertionPosition Insert fields before (default) or after one
605 * of this fields (commalist with "before:" or "after:" commands).
606 * Example: "before:keywords,--palette--;;4,after:description".
607 * Palettes must be passed like in the example no matter how the
608 * palette definition looks like in TCA.
609 * @return string The extended list
610 */
611 protected static function executePositionedStringInsertion($list, $insertionList, $insertionPosition = '') {
612 $list = trim($list);
613 $insertionList = self::removeDuplicatesForInsertion($insertionList, $list);
614
615 if ($insertionList) {
616 // Append data to the end (default):
617 if ($insertionPosition === '') {
618 $list .= ($list ? ', ' : '') . $insertionList;
619 // Insert data before or after insertion points:
620 } else {
621 $positions = t3lib_div::trimExplode(',', $insertionPosition, TRUE);
622 $items = self::explodeItemList($list);
623 $isInserted = FALSE;
624 // Iterate through all fields an check whether it's possible to inserte there:
625 foreach ($items as $item => &$itemDetails) {
626 $needles = self::getInsertionNeedles($item, $itemDetails['details']);
627 // Insert data before:
628 foreach ($needles['before'] as $needle) {
629 if (in_array($needle, $positions)) {
630 $itemDetails['rawData'] = $insertionList . ', ' . $itemDetails['rawData'];
631 $isInserted = TRUE;
632 break;
633 }
634 }
635 // Insert data after:
636 foreach ($needles['after'] as $needle) {
637 if (in_array($needle, $positions)) {
638 $itemDetails['rawData'] .= ', ' . $insertionList;
639 $isInserted = TRUE;
640 break;
641 }
642 }
643 // Replace with data:
644 foreach ($needles['replace'] as $needle) {
645 if (in_array($needle, $positions)) {
646 $itemDetails['rawData'] = $insertionList;
647 $isInserted = TRUE;
648 break;
649 }
650 }
651 // Break if insertion was already done:
652 if ($isInserted) {
653 break;
654 }
655 }
656 // If insertion point could not be determined, append the data:
657 if (!$isInserted) {
658 $list .= ($list ? ', ' : '') . $insertionList;
659 // If data was correctly inserted before or after existing items, recreate the list:
660 } else {
661 $list = self::generateItemList($items, TRUE);
662 }
663 }
664 }
665
666 return $list;
667 }
668
669 /**
670 * Compares an existing list of items and a list of items to be inserted
671 * and returns a duplicate-free variant of that insertion list.
672 *
673 * Example:
674 * + list: 'field_a, field_b;;;;2-2-2, field_c;;;;3-3-3'
675 * + insertion: 'field_b, field_d, field_c;;;4-4-4'
676 * -> new insertion: 'field_d'
677 *
678 * @param string $insertionList The list of items to inserted
679 * @param string $list The list of items to be extended (default: '')
680 * @return string Duplicate-free list of items to be inserted
681 */
682 protected static function removeDuplicatesForInsertion($insertionList, $list = '') {
683 $pattern = '/(^|,)\s*\b([^;,]+)\b[^,]*/';
684 $listItems = array();
685
686 if ($list && preg_match_all($pattern, $list, $listMatches)) {
687 $listItems = $listMatches[2];
688 }
689
690 if ($insertionList && preg_match_all($pattern, $insertionList, $insertionListMatches)) {
691 $insertionItems = array();
692 $insertionDuplicates = FALSE;
693
694 foreach ($insertionListMatches[2] as $insertionIndex => $insertionItem) {
695 if (!isset($insertionItems[$insertionItem]) && !in_array($insertionItem, $listItems)) {
696 $insertionItems[$insertionItem] = TRUE;
697 } else {
698 unset($insertionListMatches[0][$insertionIndex]);
699 $insertionDuplicates = TRUE;
700 }
701 }
702
703 if ($insertionDuplicates) {
704 $insertionList = implode('', $insertionListMatches[0]);
705 }
706 }
707
708 return $insertionList;
709 }
710
711 /**
712 * Generates search needles that are used for inserting fields/items into an existing list.
713 *
714 * @see executePositionedStringInsertion
715 * @param string $item The name of the field/item
716 * @param array $itemDetails Additional details of the field/item like e.g. palette information
717 * (this array gets created by the function explodeItemList())
718 * @return array The needled to be used for inserting content before or after existing fields/items
719 */
720 protected static function getInsertionNeedles($item, array $itemDetails) {
721 if (strstr($item, '--')) {
722 // If $item is a separator (--div--) or palette (--palette--) then it may have been appended by a unique number. This must be stripped away here.
723 $item = preg_replace('/[0-9]+$/', '', $item);
724 }
725
726 $needles = array(
727 'before' => array($item, 'before:' . $item),
728 'after' => array('after:' . $item),
729 'replace' => array('replace:' . $item),
730 );
731
732 if ($itemDetails['palette']) {
733 $palette = $item . ';;' . $itemDetails['palette'];
734 $needles['before'][] = $palette;
735 $needles['before'][] = 'before:' . $palette;
736 $needles['after'][] = 'after:' . $palette;
737 $needles['replace'][] = 'replace:' . $palette;
738 }
739
740 return $needles;
741 }
742
743 /**
744 * Generates an array of fields/items with additional information such as e.g. the name of the palette.
745 *
746 * @param string $itemList List of fields/items to be splitted up
747 * (this mostly reflects the data in $GLOBALS['TCA'][<table>]['types'][<type>]['showitem'])
748 * @return array An array with the names of the fields/items as keys and additional information
749 */
750 protected static function explodeItemList($itemList) {
751 $items = array();
752 $itemParts = t3lib_div::trimExplode(',', $itemList, TRUE);
753
754 foreach ($itemParts as $itemPart) {
755 $itemDetails = t3lib_div::trimExplode(';', $itemPart, FALSE, 5);
756 $key = $itemDetails[0];
757 if (strstr($key, '--')) {
758 // If $key is a separator (--div--) or palette (--palette--) then it will be appended by a unique number. This must be removed again when using this value!
759 $key .= count($items);
760 }
761
762 if (!isset($items[$key])) {
763 $items[$key] = array(
764 'rawData' => $itemPart,
765 'details' => array(
766 'field' => $itemDetails[0],
767 'label' => $itemDetails[1],
768 'palette' => $itemDetails[2],
769 'special' => $itemDetails[3],
770 'styles' => $itemDetails[4],
771 ),
772 );
773 }
774 }
775
776 return $items;
777 }
778
779 /**
780 * Generates a list of fields/items out of an array provided by the function getFieldsOfFieldList().
781 *
782 * @see explodeItemList
783 * @param array $items The array of fields/items with optional additional information
784 * @param boolean $useRawData Use raw data instead of building by using the details (default: FALSE)
785 * @return string The list of fields/items which gets used for $GLOBALS['TCA'][<table>]['types'][<type>]['showitem']
786 * or $GLOBALS['TCA'][<table>]['palettes'][<palette>]['showitem'] in most cases
787 */
788 protected static function generateItemList(array $items, $useRawData = FALSE) {
789 $itemParts = array();
790
791 foreach ($items as $item => $itemDetails) {
792 if (strstr($item, '--')) {
793 // If $item is a separator (--div--) or palette (--palette--) then it may have been appended by a unique number. This must be stripped away here.
794 $item = preg_replace('/[0-9]+$/', '', $item);
795 }
796
797 if ($useRawData) {
798 $itemParts[] = $itemDetails['rawData'];
799 } else {
800 $itemParts[] = (count($itemDetails['details']) > 1 ? implode(';', $itemDetails['details']) : $item);
801 }
802 }
803
804 return implode(', ', $itemParts);
805 }
806
807 /**
808 * Add tablename to default list of allowed tables on pages (in $PAGES_TYPES)
809 * Will add the $table to the list of tables allowed by default on pages as setup by $PAGES_TYPES['default']['allowedTables']
810 * FOR USE IN ext_tables.php FILES
811 *
812 * @param string $table Table name
813 * @return void
814 */
815 public static function allowTableOnStandardPages($table) {
816 $GLOBALS['PAGES_TYPES']['default']['allowedTables'] .= ',' . $table;
817 }
818 /**
819 * Adds a ExtJS module (main or sub) to the backend interface
820 * FOR USE IN ext_tables.php FILES
821 *
822 * @static
823 * @param string $extensionName
824 * @param string $mainModuleName Is the main module key
825 * @param string $subModuleName Is the submodule key, if blank a plain main module is generated
826 * @param string $position Passed to t3lib_extMgm::addModule, see reference there
827 * @param array $moduleConfiguration Icon with array keys: access, icon, labels to configure the module
828 * @throws InvalidArgumentException
829 */
830 public static function addExtJSModule($extensionName, $mainModuleName, $subModuleName = '', $position = '', array $moduleConfiguration = array()) {
831 if (empty($extensionName)) {
832 throw new InvalidArgumentException('The extension name must not be empty', 1325938973);
833 }
834
835 $extensionKey = t3lib_div::camelCaseToLowerCaseUnderscored($extensionName);
836 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
837
838 $defaultModuleConfiguration = array(
839 'access' => 'admin',
840 'icon' => 'gfx/typo3.png',
841 'labels' => '',
842 'extRelPath' => self::extRelPath($extensionKey) . 'Classes/'
843 );
844
845 // Add mandatory parameter to use new pagetree
846 if ($mainModuleName === 'web') {
847 $defaultModuleConfiguration['navigationComponentId'] = 'typo3-pagetree';
848 }
849
850 $moduleConfiguration = t3lib_div::array_merge_recursive_overrule($defaultModuleConfiguration, $moduleConfiguration);
851
852 if ((strlen($subModuleName) > 0)) {
853 $moduleSignature = $mainModuleName . '_' . $subModuleName;
854 } else {
855 $moduleSignature = $mainModuleName;
856 }
857
858 $moduleConfiguration['name'] = $moduleSignature;
859 $moduleConfiguration['script'] = 'extjspaneldummy.html';
860 $moduleConfiguration['extensionName'] = $extensionName;
861 $moduleConfiguration['configureModuleFunction'] = array('t3lib_extMgm', 'configureModule');
862
863 $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature] = $moduleConfiguration;
864
865 self::addModule($mainModuleName, $subModuleName, $position);
866 }
867
868 /**
869 * This method is called from t3lib_loadModules::checkMod and it replaces old conf.php.
870 *
871 * The original function for is called
872 * Tx_Extbase_Utility_Extension::configureModule, the refered function can
873 * be deprecated now
874 *
875 * @param string $moduleSignature The module name
876 * @param string $modulePath Absolute path to module (not used by Extbase currently)
877 * @return array Configuration of the module
878 */
879 public static function configureModule($moduleSignature, $modulePath) {
880 $moduleConfiguration = $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature];
881 $iconPathAndFilename = $moduleConfiguration['icon'];
882 if (substr($iconPathAndFilename, 0, 4) === 'EXT:') {
883 list($extensionKey, $relativePath) = explode('/', substr($iconPathAndFilename, 4), 2);
884 $iconPathAndFilename = self::extPath($extensionKey) . $relativePath;
885 }
886 // TODO: skin support
887
888 $moduleLabels = array(
889 'tabs_images' => array(
890 'tab' => $iconPathAndFilename,
891 ),
892 'labels' => array(
893 'tablabel' => $GLOBALS['LANG']->sL($moduleConfiguration['labels'] . ':mlang_labels_tablabel'),
894 'tabdescr' => $GLOBALS['LANG']->sL($moduleConfiguration['labels'] . ':mlang_labels_tabdescr'),
895 ),
896 'tabs' => array(
897 'tab' => $GLOBALS['LANG']->sL($moduleConfiguration['labels'] . ':mlang_tabs_tab')
898 )
899 );
900 $GLOBALS['LANG']->addModuleLabels($moduleLabels, $moduleSignature . '_');
901
902 return $moduleConfiguration;
903 }
904
905 /**
906 * Adds a module (main or sub) to the backend interface
907 * FOR USE IN ext_tables.php FILES
908 *
909 * @param string $main The main module key, $sub is the submodule key. So $main would be an index in the $TBE_MODULES array and $sub could be an element in the lists there.
910 * @param string $sub The submodule key. If $sub is not set a blank $main module is created.
911 * @param string $position Can be used to set the position of the $sub module within the list of existing submodules for the main module. $position has this syntax: [cmd]:[submodule-key]. cmd can be "after", "before" or "top" (or blank which is default). If "after"/"before" then submodule will be inserted after/before the existing submodule with [submodule-key] if found. If not found, the bottom of list. If "top" the module is inserted in the top of the submodule list.
912 * @param string $path The absolute path to the module. If this value is defined the path is added as an entry in $TBE_MODULES['_PATHS'][ main_sub ] = $path; and thereby tells the backend where the newly added modules is found in the system.
913 * @return void
914 */
915 public static function addModule($main, $sub = '', $position = '', $path = '') {
916 if (isset($GLOBALS['TBE_MODULES'][$main]) && $sub) {
917 // If there is already a main module by this name:
918
919 // Adding the submodule to the correct position:
920 list($place, $modRef) = t3lib_div::trimExplode(':', $position, 1);
921 $mods = t3lib_div::trimExplode(',', $GLOBALS['TBE_MODULES'][$main], 1);
922 if (!in_array($sub, $mods)) {
923 switch (strtolower($place)) {
924 case 'after':
925 case 'before':
926 $pointer = 0;
927 $found = FALSE;
928 foreach ($mods as $k => $m) {
929 if (!strcmp($m, $modRef)) {
930 $pointer = strtolower($place) == 'after' ? $k + 1 : $k;
931 $found = TRUE;
932 }
933 }
934 if ($found) {
935 array_splice(
936 $mods, // The modules array
937 $pointer, // To insert one position from the end of the list
938 0, // Don't remove any items, just insert
939 $sub // Module to insert
940 );
941 } else {
942 // If requested module is not found: Add at the end
943 array_push($mods, $sub);
944 }
945 break;
946 default:
947 if (strtolower($place) == 'top') {
948 array_unshift($mods, $sub);
949 } else {
950 array_push($mods, $sub);
951 }
952 break;
953 }
954 }
955 // Re-inserting the submodule list:
956 $GLOBALS['TBE_MODULES'][$main] = implode(',', $mods);
957 } else { // Create new main modules with only one submodule, $sub (or none if $sub is blank)
958 $GLOBALS['TBE_MODULES'][$main] = $sub;
959 }
960
961 // Adding path:
962 if ($path) {
963 $GLOBALS['TBE_MODULES']['_PATHS'][$main . ($sub ? '_' . $sub : '')] = $path;
964 }
965 }
966
967 /**
968 * Registers an Ext.Direct component with access restrictions.
969 *
970 * @param string $endpointName
971 * @param string $callbackClass
972 * @param string $moduleName Optional: must be <mainmodule> or <mainmodule>_<submodule>
973 * @param string $accessLevel Optional: can be 'admin' or 'user,group'
974 * @return void
975 */
976 public static function registerExtDirectComponent($endpointName, $callbackClass, $moduleName = NULL, $accessLevel = NULL) {
977 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ExtDirect'][$endpointName] = array(
978 'callbackClass' => $callbackClass,
979 'moduleName' => $moduleName,
980 'accessLevel' => $accessLevel,
981 );
982 }
983
984 /**
985 * Adds a module path to $GLOBALS['TBE_MODULES'] for used with the module dispatcher, mod.php
986 * Used only for modules that are not placed in the main/sub menu hierarchy by the traditional mechanism of addModule()
987 * Examples for this is context menu functionality (like import/export) which runs as an independent module through mod.php
988 * FOR USE IN ext_tables.php FILES
989 * Example: t3lib_extMgm::addModulePath('xMOD_tximpexp', t3lib_extMgm::extPath($_EXTKEY).'app/');
990 *
991 * @param string $name The name of the module, refer to conf.php of the module.
992 * @param string $path The absolute path to the module directory inside of which "index.php" and "conf.php" is found.
993 * @return void
994 */
995 public static function addModulePath($name, $path) {
996 $GLOBALS['TBE_MODULES']['_PATHS'][$name] = $path;
997 }
998
999 /**
1000 * Adds a "Function menu module" ('third level module') to an existing function menu for some other backend module
1001 * The arguments values are generally determined by which function menu this is supposed to interact with
1002 * See Inside TYPO3 for information on how to use this function.
1003 * FOR USE IN ext_tables.php FILES
1004 *
1005 * @param string $modname Module name
1006 * @param string $className Class name
1007 * @param string $classPath Class path
1008 * @param string $title Title of module
1009 * @param string $MM_key Menu array key - default is "function"
1010 * @param string $WS Workspace conditions. Blank means all workspaces, any other string can be a comma list of "online", "offline" and "custom"
1011 * @return void
1012 * @see t3lib_SCbase::mergeExternalItems()
1013 */
1014 public static function insertModuleFunction($modname, $className, $classPath, $title, $MM_key = 'function', $WS = '') {
1015 $GLOBALS['TBE_MODULES_EXT'][$modname]['MOD_MENU'][$MM_key][$className] = array(
1016 'name' => $className,
1017 'path' => $classPath,
1018 'title' => $title,
1019 'ws' => $WS
1020 );
1021 }
1022
1023 /**
1024 * Adds some more content to a key of TYPO3_CONF_VARS array.
1025 *
1026 * This also tracks which content was added by extensions (in TYPO3_CONF_VARS_extensionAdded)
1027 * so that they cannot be editted again through the Install Tool.
1028 *
1029 * @static
1030 * @param string $group The group ('FE', 'BE', 'SYS' ...)
1031 * @param string $key The key of this setting within the group
1032 * @param string $content The text to add (include leading "\n" in case of multi-line entries)
1033 * @return void
1034 */
1035 public static function appendToTypoConfVars($group, $key, $content) {
1036 $GLOBALS['TYPO3_CONF_VARS_extensionAdded'][$group][$key] .= $content;
1037 $GLOBALS['TYPO3_CONF_VARS'][$group][$key] .= $content;
1038 }
1039
1040 /**
1041 * Adds $content to the default Page TSconfig as set in $GLOBALS['TYPO3_CONF_VARS'][BE]['defaultPageTSconfig']
1042 * Prefixed with a [GLOBAL] line
1043 * FOR USE IN ext_tables.php/ext_localconf.php FILES
1044 *
1045 * @param string $content Page TSconfig content
1046 * @return void
1047 */
1048 public static function addPageTSConfig($content) {
1049 self::appendToTypoConfVars('BE', 'defaultPageTSconfig', "\n[GLOBAL]\n" . $content);
1050 }
1051
1052 /**
1053 * Adds $content to the default User TSconfig as set in $GLOBALS['TYPO3_CONF_VARS'][BE]['defaultUserTSconfig']
1054 * Prefixed with a [GLOBAL] line
1055 * FOR USE IN ext_tables.php/ext_localconf.php FILES
1056 *
1057 * @param string $content User TSconfig content
1058 * @return void
1059 */
1060 public static function addUserTSConfig($content) {
1061 self::appendToTypoConfVars('BE', 'defaultUserTSconfig', "\n[GLOBAL]\n" . $content);
1062 }
1063
1064 /**
1065 * Adds a reference to a locallang file with $GLOBALS['TCA_DESCR'] labels
1066 * FOR USE IN ext_tables.php FILES
1067 * eg. t3lib_extMgm::addLLrefForTCAdescr('pages', 'EXT:lang/locallang_csh_pages.xml'); for the pages table or t3lib_extMgm::addLLrefForTCAdescr('_MOD_web_layout', 'EXT:cms/locallang_csh_weblayout.php'); for the Web > Page module.
1068 *
1069 * @param string $tca_descr_key Description key. Typically a database table (like "pages") but for applications can be other strings, but prefixed with "_MOD_")
1070 * @param string $file_ref File reference to locallang file, eg. "EXT:lang/locallang_csh_pages.php" (or ".xml")
1071 * @return void
1072 */
1073 public static function addLLrefForTCAdescr($tca_descr_key, $file_ref) {
1074 if ($tca_descr_key) {
1075 if (!is_array($GLOBALS['TCA_DESCR'][$tca_descr_key])) {
1076 $GLOBALS['TCA_DESCR'][$tca_descr_key] = array();
1077 }
1078 if (!is_array($GLOBALS['TCA_DESCR'][$tca_descr_key]['refs'])) {
1079 $GLOBALS['TCA_DESCR'][$tca_descr_key]['refs'] = array();
1080 }
1081 $GLOBALS['TCA_DESCR'][$tca_descr_key]['refs'][] = $file_ref;
1082 }
1083 }
1084
1085 /**
1086 * Registers a navigation component
1087 *
1088 * @param string $module
1089 * @param string $componentId
1090 * @return void
1091 */
1092 public static function addNavigationComponent($module, $componentId) {
1093 $GLOBALS['TBE_MODULES']['_navigationComponents'][$module] = array(
1094 'componentId' => $componentId,
1095 'extKey' => $GLOBALS['_EXTKEY'],
1096 'isCoreComponent' => FALSE,
1097 );
1098 }
1099
1100 /**
1101 * Registers a core navigation component
1102 *
1103 * @param string $module
1104 * @param string $componentId
1105 * @return void
1106 */
1107 public static function addCoreNavigationComponent($module, $componentId) {
1108 self::addNavigationComponent($module, $componentId);
1109 $GLOBALS['TBE_MODULES']['_navigationComponents'][$module]['isCoreComponent'] = TRUE;
1110 }
1111
1112 /**************************************
1113 *
1114 * Adding SERVICES features
1115 *
1116 ***************************************/
1117
1118 /**
1119 * Adds a service to the global services array
1120 *
1121 * @param string $extKey Extension key
1122 * @param string $serviceType Service type, must not be prefixed "tx_" or "Tx_"
1123 * @param string $serviceKey Service key, must be prefixed "tx_", "Tx_" or "user_"
1124 * @param array $info Service description array
1125 * @return void
1126 */
1127 public static function addService($extKey, $serviceType, $serviceKey, $info) {
1128 if ($serviceType && t3lib_div::hasValidClassPrefix($serviceKey) && is_array($info)) {
1129 $info['priority'] = max(0, min(100, $info['priority']));
1130
1131 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey] = $info;
1132
1133 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['extKey'] = $extKey;
1134 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['serviceKey'] = $serviceKey;
1135 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['serviceType'] = $serviceType;
1136
1137 // Change the priority (and other values) from $GLOBALS['TYPO3_CONF_VARS']
1138 // $GLOBALS['TYPO3_CONF_VARS']['T3_SERVICES'][$serviceType][$serviceKey]['priority']
1139 // even the activation is possible (a unix service might be possible on windows for some reasons)
1140 if (is_array($GLOBALS['TYPO3_CONF_VARS']['T3_SERVICES'][$serviceType][$serviceKey])) {
1141
1142 // No check is done here - there might be configuration values only the service type knows about, so
1143 // we pass everything
1144 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey] = array_merge(
1145 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey],
1146 $GLOBALS['TYPO3_CONF_VARS']['T3_SERVICES'][$serviceType][$serviceKey]
1147 );
1148 }
1149
1150 // OS check
1151 // Empty $os means 'not limited to one OS', therefore a check is not needed
1152 if ($GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['available']
1153 && $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['os'] != '') {
1154
1155 // TYPO3_OS is not yet defined
1156 $os_type = stristr(PHP_OS, 'win') && !stristr(PHP_OS, 'darwin') ? 'WIN' : 'UNIX';
1157
1158 $os = t3lib_div::trimExplode(',', strtoupper($GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['os']));
1159
1160 if (!in_array($os_type, $os)) {
1161 self::deactivateService($serviceType, $serviceKey);
1162 }
1163 }
1164
1165 // Convert subtype list to array for quicker access
1166 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['serviceSubTypes'] = array();
1167 $serviceSubTypes = t3lib_div::trimExplode(',', $info['subtype']);
1168 foreach ($serviceSubTypes as $subtype) {
1169 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['serviceSubTypes'][$subtype] = $subtype;
1170 }
1171 }
1172 }
1173
1174 /**
1175 * Find the available service with highest priority
1176 *
1177 * @param string $serviceType Service type
1178 * @param string $serviceSubType Service sub type
1179 * @param mixed $excludeServiceKeys Service keys that should be excluded in the search for a service. Array or comma list.
1180 * @return mixed Service info array if a service was found, FALSE otherwise
1181 */
1182 public static function findService($serviceType, $serviceSubType = '', $excludeServiceKeys = array()) {
1183 $serviceKey = FALSE;
1184 $serviceInfo = FALSE;
1185 $priority = 0;
1186 $quality = 0;
1187
1188 if (!is_array($excludeServiceKeys)) {
1189 $excludeServiceKeys = t3lib_div::trimExplode(',', $excludeServiceKeys, 1);
1190 }
1191
1192 if (is_array($GLOBALS['T3_SERVICES'][$serviceType])) {
1193
1194 foreach ($GLOBALS['T3_SERVICES'][$serviceType] as $key => $info) {
1195
1196 if (in_array($key, $excludeServiceKeys)) {
1197 continue;
1198 }
1199
1200 // Select a subtype randomly
1201 // Useful to start a service by service key without knowing his subtypes - for testing purposes
1202 if ($serviceSubType == '*') {
1203 $serviceSubType = key($info['serviceSubTypes']);
1204 }
1205
1206 // This matches empty subtype too
1207 if ($info['available'] && ($info['subtype'] == $serviceSubType || $info['serviceSubTypes'][$serviceSubType]) && $info['priority'] >= $priority) {
1208
1209 // Has a lower quality than the already found, therefore we skip this service
1210 if ($info['priority'] == $priority && $info['quality'] < $quality) {
1211 continue;
1212 }
1213
1214 // Check if the service is available
1215 $info['available'] = self::isServiceAvailable($serviceType, $key, $info);
1216
1217 // Still available after exec check?
1218 if ($info['available']) {
1219 $serviceKey = $key;
1220 $priority = $info['priority'];
1221 $quality = $info['quality'];
1222 }
1223 }
1224 }
1225 }
1226
1227 if ($serviceKey) {
1228 $serviceInfo = $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey];
1229 }
1230 return $serviceInfo;
1231 }
1232
1233 /**
1234 * Find a specific service identified by its key
1235 * Note that this completely bypasses the notions of priority and quality
1236 *
1237 * @param string $serviceKey Service key
1238 * @return array Service info array if a service was found
1239 * @throws t3lib_exception
1240 */
1241 public static function findServiceByKey($serviceKey) {
1242 if (is_array($GLOBALS['T3_SERVICES'])) {
1243 // Loop on all service types
1244 // NOTE: we don't care about the actual type, we are looking for a specific key
1245 foreach ($GLOBALS['T3_SERVICES'] as $serviceType => $servicesPerType) {
1246 if (isset($servicesPerType[$serviceKey])) {
1247 $serviceDetails = $servicesPerType[$serviceKey];
1248 // Test if service is available
1249 if (self::isServiceAvailable($serviceType, $serviceKey, $serviceDetails)) {
1250 // We have found the right service, return its information
1251 return $serviceDetails;
1252 }
1253 }
1254 }
1255 }
1256 throw new t3lib_exception('Service not found for key: ' . $serviceKey, 1319217244);
1257 }
1258
1259 /**
1260 * Check if a given service is available, based on the executable files it depends on
1261 *
1262 * @param string $serviceType Type of service
1263 * @param string $serviceKey Specific key of the service
1264 * @param array $serviceDetails Information about the service
1265 * @return boolean Service availability
1266 */
1267 public static function isServiceAvailable($serviceType, $serviceKey, $serviceDetails) {
1268
1269 // If the service depends on external programs - check if they exists
1270 if (trim($serviceDetails['exec'])) {
1271 $executables = t3lib_div::trimExplode(',', $serviceDetails['exec'], 1);
1272 foreach ($executables as $executable) {
1273 // If at least one executable file is not available, exit early returning FALSE
1274 if (!t3lib_exec::checkCommand($executable)) {
1275 self::deactivateService($serviceType, $serviceKey);
1276 return FALSE;
1277 }
1278 }
1279 }
1280 // The service is available
1281 return TRUE;
1282 }
1283
1284 /**
1285 * Deactivate a service
1286 *
1287 * @param string $serviceType Service type
1288 * @param string $serviceKey Service key
1289 * @return void
1290 */
1291 public static function deactivateService($serviceType, $serviceKey) {
1292 // ... maybe it's better to move non-available services to a different array??
1293 $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['available'] = FALSE;
1294 }
1295
1296 /**************************************
1297 *
1298 * Adding FRONTEND features
1299 * (related specifically to "cms" extension)
1300 *
1301 ***************************************/
1302
1303 /**
1304 * Adds an entry to the list of plugins in content elements of type "Insert plugin"
1305 * Takes the $itemArray (label, value[,icon]) and adds to the items-array of $GLOBALS['TCA'][tt_content] elements with CType "listtype" (or another field if $type points to another fieldname)
1306 * If the value (array pos. 1) is already found in that items-array, the entry is substituted, otherwise the input array is added to the bottom.
1307 * Use this function to add a frontend plugin to this list of plugin-types - or more generally use this function to add an entry to any selectorbox/radio-button set in the TCEFORMS
1308 * FOR USE IN ext_tables.php FILES
1309 *
1310 * @param array $itemArray Item Array
1311 * @param string $type Type (eg. "list_type") - basically a field from "tt_content" table
1312 * @return void
1313 */
1314 public static function addPlugin($itemArray, $type = 'list_type') {
1315 $_EXTKEY = $GLOBALS['_EXTKEY'];
1316 if ($_EXTKEY && !$itemArray[2]) {
1317 $itemArray[2] = self::extRelPath($_EXTKEY) . 'ext_icon.gif';
1318 }
1319
1320 t3lib_div::loadTCA('tt_content');
1321 if (is_array($GLOBALS['TCA']['tt_content']['columns']) && is_array($GLOBALS['TCA']['tt_content']['columns'][$type]['config']['items'])) {
1322 foreach ($GLOBALS['TCA']['tt_content']['columns'][$type]['config']['items'] as $k => $v) {
1323 if (!strcmp($v[1], $itemArray[1])) {
1324 $GLOBALS['TCA']['tt_content']['columns'][$type]['config']['items'][$k] = $itemArray;
1325 return;
1326 }
1327 }
1328 $GLOBALS['TCA']['tt_content']['columns'][$type]['config']['items'][] = $itemArray;
1329 }
1330 }
1331
1332 /**
1333 * Adds an entry to the "ds" array of the tt_content field "pi_flexform".
1334 * This is used by plugins to add a flexform XML reference / content for use when they are selected as plugin or content element.
1335 *
1336 * @param string $piKeyToMatch Plugin key as used in the list_type field. Use the asterisk * to match all list_type values.
1337 * @param string $value Either a reference to a flex-form XML file (eg. "FILE:EXT:newloginbox/flexform_ds.xml") or the XML directly.
1338 * @param string $CTypeToMatch Value of tt_content.CType (Content Type) to match. The default is "list" which corresponds to the "Insert Plugin" content element. Use the asterisk * to match all CType values.
1339 * @return void
1340 * @see addPlugin()
1341 */
1342 public static function addPiFlexFormValue($piKeyToMatch, $value, $CTypeToMatch = 'list') {
1343 t3lib_div::loadTCA('tt_content');
1344
1345 if (is_array($GLOBALS['TCA']['tt_content']['columns']) && is_array($GLOBALS['TCA']['tt_content']['columns']['pi_flexform']['config']['ds'])) {
1346 $GLOBALS['TCA']['tt_content']['columns']['pi_flexform']['config']['ds'][$piKeyToMatch . ',' . $CTypeToMatch] = $value;
1347 }
1348 }
1349
1350 /**
1351 * Adds the $table tablename to the list of tables allowed to be includes by content element type "Insert records"
1352 * By using $content_table and $content_field you can also use the function for other tables.
1353 * FOR USE IN ext_tables.php FILES
1354 *
1355 * @param string $table Table name to allow for "insert record"
1356 * @param string $content_table Table name TO WHICH the $table name is applied. See $content_field as well.
1357 * @param string $content_field Field name in the database $content_table in which $table is allowed to be added as a reference ("Insert Record")
1358 * @return void
1359 */
1360 public static function addToInsertRecords($table, $content_table = 'tt_content', $content_field = 'records') {
1361 t3lib_div::loadTCA($content_table);
1362 if (is_array($GLOBALS['TCA'][$content_table]['columns']) && isset($GLOBALS['TCA'][$content_table]['columns'][$content_field]['config']['allowed'])) {
1363 $GLOBALS['TCA'][$content_table]['columns'][$content_field]['config']['allowed'] .= ',' . $table;
1364 }
1365 }
1366
1367 /**
1368 * Add PlugIn to Static Template #43
1369 *
1370 * When adding a frontend plugin you will have to add both an entry to the TCA definition of tt_content table AND to the TypoScript template which must initiate the rendering.
1371 * Since the static template with uid 43 is the "content.default" and practically always used for rendering the content elements it's very useful to have this function automatically adding the necessary TypoScript for calling your plugin. It will also work for the extension "css_styled_content"
1372 * $type determines the type of frontend plugin:
1373 * "list_type" (default) - the good old "Insert plugin" entry
1374 * "menu_type" - a "Menu/Sitemap" entry
1375 * "splash_layout" - a "Textbox" entry
1376 * "CType" - a new content element type
1377 * "header_layout" - an additional header type (added to the selection of layout1-5)
1378 * "includeLib" - just includes the library for manual use somewhere in TypoScript.
1379 * (Remember that your $type definition should correspond to the column/items array in $GLOBALS['TCA'][tt_content] where you added the selector item for the element! See addPlugin() function)
1380 * FOR USE IN ext_localconf.php FILES
1381 *
1382 * @param string $key The extension key
1383 * @param string $classFile The PHP-class filename relative to the extension root directory. If set to blank a default value is chosen according to convensions.
1384 * @param string $prefix Is used as a - yes, suffix - of the class name (fx. "_pi1")
1385 * @param string $type See description above
1386 * @param boolean $cached If $cached is set as USER content object (cObject) is created - otherwise a USER_INT object is created.
1387 * @return void
1388 */
1389 public static function addPItoST43($key, $classFile = '', $prefix = '', $type = 'list_type', $cached = 0) {
1390 $classFile = $classFile ? $classFile : 'pi/class.tx_' . str_replace('_', '', $key) . $prefix . '.php';
1391 $cN = self::getCN($key);
1392
1393 // General plugin
1394 $pluginContent = trim('
1395 plugin.' . $cN . $prefix . ' = USER' . ($cached ? '' : '_INT') . '
1396 plugin.' . $cN . $prefix . ' {
1397 includeLibs = ' . $GLOBALS['TYPO3_LOADED_EXT'][$key]['siteRelPath'] . $classFile . '
1398 userFunc = ' . $cN . $prefix . '->main
1399 }');
1400 self::addTypoScript($key, 'setup', '
1401 # Setting ' . $key . ' plugin TypoScript
1402 ' . $pluginContent);
1403
1404 // After ST43
1405 switch ($type) {
1406 case 'list_type':
1407 $addLine = 'tt_content.list.20.' . $key . $prefix . ' = < plugin.' . $cN . $prefix;
1408 break;
1409 case 'menu_type':
1410 $addLine = 'tt_content.menu.20.' . $key . $prefix . ' = < plugin.' . $cN . $prefix;
1411 break;
1412 case 'splash_layout':
1413 $addLine = 'tt_content.splash.' . $key . $prefix . ' = < plugin.' . $cN . $prefix;
1414 break;
1415 case 'CType':
1416 $addLine = trim('
1417 tt_content.' . $key . $prefix . ' = COA
1418 tt_content.' . $key . $prefix . ' {
1419 10 = < lib.stdheader
1420 20 = < plugin.' . $cN . $prefix . '
1421 }
1422 ');
1423 break;
1424 case 'header_layout':
1425 $addLine = 'lib.stdheader.10.' . $key . $prefix . ' = < plugin.' . $cN . $prefix;
1426 break;
1427 case 'includeLib':
1428 $addLine = 'page.1000 = < plugin.' . $cN . $prefix;
1429 break;
1430 default:
1431 $addLine = '';
1432 break;
1433 }
1434 if ($addLine) {
1435 self::addTypoScript($key, 'setup', '
1436 # Setting ' . $key . ' plugin TypoScript
1437 ' . $addLine . '
1438 ', 43);
1439 }
1440 }
1441
1442 /**
1443 * Call this method to add an entry in the static template list found in sys_templates
1444 * "static template files" are the modern equivalent (provided from extensions) to the traditional records in "static_templates"
1445 * FOR USE IN ext_localconf.php FILES
1446 *
1447 * @param string $extKey Is of course the extension key
1448 * @param string $path Is the path where the template files (fixed names) include_static.txt (integer list of uids from the table "static_templates"), constants.txt, setup.txt, and include_static_file.txt is found (relative to extPath, eg. 'static/'). The file include_static_file.txt, allows you to include other static templates defined in files, from your static template, and thus corresponds to the field 'include_static_file' in the sys_template table. The syntax for this is a commaseperated list of static templates to include, like: EXT:css_styled_content/static/,EXT:da_newsletter_subscription/static/,EXT:cc_random_image/pi2/static/
1449 * @param string $title Is the title in the selector box.
1450 * @return void
1451 * @see addTypoScript()
1452 */
1453 public static function addStaticFile($extKey, $path, $title) {
1454 t3lib_div::loadTCA('sys_template');
1455 if ($extKey && $path && is_array($GLOBALS['TCA']['sys_template']['columns'])) {
1456 $value = str_replace(',', '', 'EXT:' . $extKey . '/' . $path);
1457 $itemArray = array(trim($title . ' (' . $extKey . ')'), $value);
1458 $GLOBALS['TCA']['sys_template']['columns']['include_static_file']['config']['items'][] = $itemArray;
1459 }
1460 }
1461
1462 /**
1463 * Adds $content to the default TypoScript setup code as set in $GLOBALS['TYPO3_CONF_VARS'][FE]['defaultTypoScript_setup']
1464 * Prefixed with a [GLOBAL] line
1465 * FOR USE IN ext_localconf.php FILES
1466 *
1467 * @param string $content TypoScript Setup string
1468 * @return void
1469 */
1470 public static function addTypoScriptSetup($content) {
1471 self::appendToTypoConfVars('FE', 'defaultTypoScript_setup', "\n[GLOBAL]\n" . $content);
1472 }
1473
1474 /**
1475 * Adds $content to the default TypoScript constants code as set in $GLOBALS['TYPO3_CONF_VARS'][FE]['defaultTypoScript_constants']
1476 * Prefixed with a [GLOBAL] line
1477 * FOR USE IN ext_localconf.php FILES
1478 *
1479 * @param string $content TypoScript Constants string
1480 * @return void
1481 */
1482 public static function addTypoScriptConstants($content) {
1483 self::appendToTypoConfVars('FE', 'defaultTypoScript_constants', "\n[GLOBAL]\n" . $content);
1484 }
1485
1486 /**
1487 * Adds $content to the default TypoScript code for either setup or constants as set in $GLOBALS['TYPO3_CONF_VARS'][FE]['defaultTypoScript_*']
1488 * (Basically this function can do the same as addTypoScriptSetup and addTypoScriptConstants - just with a little more hazzle, but also with some more options!)
1489 * FOR USE IN ext_localconf.php FILES
1490 *
1491 * @param string $key Is the extension key (informative only).
1492 * @param string $type Is either "setup" or "constants" and obviously determines which kind of TypoScript code we are adding.
1493 * @param string $content Is the TS content, prefixed with a [GLOBAL] line and a comment-header.
1494 * @param string $afterStaticUid Is either an integer pointing to a uid of a static_template or a string pointing to the "key" of a static_file template ([reduced extension_key]/[local path]). The points is that the TypoScript you add is included only IF that static template is included (and in that case, right after). So effectively the TypoScript you set can specifically overrule settings from those static templates.
1495 * @return void
1496 */
1497 public static function addTypoScript($key, $type, $content, $afterStaticUid = 0) {
1498 if ($type == 'setup' || $type == 'constants') {
1499 $content = '
1500
1501 [GLOBAL]
1502 #############################################
1503 ## TypoScript added by extension "' . $key . '"
1504 #############################################
1505
1506 ' . $content;
1507 if ($afterStaticUid) {
1508 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type . '.'][$afterStaticUid] .= $content;
1509 // If 'content (default)' is targeted, also add to other 'content rendering templates', eg. css_styled_content
1510 if ($afterStaticUid == 43 && is_array($GLOBALS['TYPO3_CONF_VARS']['FE']['contentRenderingTemplates'])) {
1511 foreach ($GLOBALS['TYPO3_CONF_VARS']['FE']['contentRenderingTemplates'] as $templateName) {
1512 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type . '.'][$templateName] .= $content;
1513 }
1514 }
1515 } else {
1516 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type] .= $content;
1517 }
1518 }
1519 }
1520
1521 /***************************************
1522 *
1523 * Internal extension management methods
1524 *
1525 ***************************************/
1526
1527 /**
1528 * Load the extension information array. This array is set as
1529 * $GLOBALS['TYPO3_LOADED_EXT'] in bootstrap. It contains basic information
1530 * about every loaded extension.
1531 *
1532 * This is an internal method. It is only used during bootstrap and
1533 * extensions should not use it!
1534 *
1535 * @param boolean $allowCaching If FALSE, the array will not be read / created from cache
1536 * @return array Result array that will be set as $GLOBALS['TYPO3_LOADED_EXT']
1537 * @access private
1538 * @createTypo3LoadedExtensionInformationArray
1539 */
1540 public static function loadTypo3LoadedExtensionInformation($allowCaching = TRUE) {
1541 if ($allowCaching) {
1542 $cacheIdentifier = self::getTypo3LoadedExtensionInformationCacheIdentifier();
1543 /** @var $phpCodeCache t3lib_cache_frontend_PhpFrontend */
1544 $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_phpcode');
1545 if ($phpCodeCache->has($cacheIdentifier)) {
1546 $typo3LoadedExtensionArray = $phpCodeCache->requireOnce($cacheIdentifier);
1547 } else {
1548 $typo3LoadedExtensionArray = self::createTypo3LoadedExtensionInformationArray();
1549 $phpCodeCache->set(
1550 $cacheIdentifier,
1551 'return ' . var_export($typo3LoadedExtensionArray, TRUE) . ';',
1552 array('typo3LoadedExtensionArray', 'core')
1553 );
1554 }
1555 } else {
1556 $typo3LoadedExtensionArray = self::createTypo3LoadedExtensionInformationArray();
1557 }
1558
1559 return $typo3LoadedExtensionArray;
1560 }
1561
1562 /**
1563 * Set up array with basic information about loaded extension:
1564 *
1565 * array(
1566 * 'extensionKey' => array(
1567 * 'type' => Either S, L or G, inidicating if the extension is a system, a local or a global extension
1568 * 'siteRelPath' => Relative path to the extension from document root
1569 * 'typo3RelPath' => Relative path to extension from typo3/ subdirectory
1570 * 'ext_localconf.php' => Absolute path to ext_localconf.php file of extension
1571 * 'ext_...' => Further absolute path of extension files, see $extensionFilesToCheckFor var for details
1572 * ),
1573 * );
1574 *
1575 * @return array Result array that will be set as $GLOBALS['TYPO3_LOADED_EXT']
1576 */
1577 protected static function createTypo3LoadedExtensionInformationArray() {
1578 $loadedExtensions = array_unique(t3lib_div::trimExplode(',', self::getEnabledExtensionList(), 1));
1579 $loadedExtensionInformation = array();
1580
1581 $extensionFilesToCheckFor = array(
1582 'ext_localconf.php',
1583 'ext_tables.php',
1584 'ext_tables.sql',
1585 'ext_tables_static+adt.sql',
1586 'ext_typoscript_constants.txt',
1587 'ext_typoscript_setup.txt'
1588 );
1589
1590 // Clear file status cache to make sure we get good results from is_dir()
1591 clearstatcache();
1592
1593 foreach ($loadedExtensions as $extensionKey) {
1594 // Determine if extension is installed locally, globally or system (in this order)
1595 if (@is_dir(PATH_typo3conf . 'ext/' . $extensionKey . '/')) {
1596 // local
1597 $loadedExtensionInformation[$extensionKey] = array(
1598 'type' => 'L',
1599 'siteRelPath' => 'typo3conf/ext/' . $extensionKey . '/',
1600 'typo3RelPath' => '../typo3conf/ext/' . $extensionKey . '/'
1601 );
1602 } elseif (@is_dir(PATH_typo3 . 'ext/' . $extensionKey . '/')) {
1603 // global
1604 $loadedExtensionInformation[$extensionKey] = array(
1605 'type' => 'G',
1606 'siteRelPath' => TYPO3_mainDir . 'ext/' . $extensionKey . '/',
1607 'typo3RelPath' => 'ext/' . $extensionKey . '/'
1608 );
1609 } elseif (@is_dir(PATH_typo3 . 'sysext/' . $extensionKey . '/')) {
1610 // system
1611 $loadedExtensionInformation[$extensionKey] = array(
1612 'type' => 'S',
1613 'siteRelPath' => TYPO3_mainDir . 'sysext/' . $extensionKey . '/',
1614 'typo3RelPath' => 'sysext/' . $extensionKey . '/'
1615 );
1616 }
1617
1618 // Register found files in extension array if extension was found
1619 if (isset($loadedExtensionInformation[$extensionKey])) {
1620 foreach ($extensionFilesToCheckFor as $fileName) {
1621 $absolutePathToFile = PATH_site . $loadedExtensionInformation[$extensionKey]['siteRelPath'] . $fileName;
1622 if (@is_file($absolutePathToFile)) {
1623 $loadedExtensionInformation[$extensionKey][$fileName] = $absolutePathToFile;
1624 }
1625 }
1626 }
1627 }
1628
1629 return $loadedExtensionInformation;
1630 }
1631
1632 /**
1633 * Cache identifier of cached Typo3LoadedExtensionInformation array
1634 *
1635 * @return string
1636 */
1637 protected static function getTypo3LoadedExtensionInformationCacheIdentifier() {
1638 return 'loaded_extensions_' . sha1(TYPO3_version . PATH_site . 'loadedExtensions');
1639 }
1640
1641 /**
1642 * Execute all ext_localconf.php files of loaded extensions.
1643 * The method implements an optionally used caching mechanism that concatenates all
1644 * ext_localconf.php files in one file.
1645 *
1646 * This is an internal method. It is only used during bootstrap and
1647 * extensions should not use it!
1648 *
1649 * @param boolean $allowCaching Whether or not to load / create concatenated cache file
1650 * @return void
1651 * @access private
1652 */
1653 public static function loadExtLocalconf($allowCaching = TRUE) {
1654 if ($allowCaching) {
1655 $cacheIdentifier = self::getExtLocalconfCacheIdentifier();
1656 /** @var $phpCodeCache t3lib_cache_frontend_PhpFrontend */
1657 $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_phpcode');
1658 if ($phpCodeCache->has($cacheIdentifier)) {
1659 $phpCodeCache->requireOnce($cacheIdentifier);
1660 } else {
1661 self::loadSingleExtLocalconfFiles();
1662 self::createExtLocalconfCacheEntry();
1663 }
1664 } else {
1665 self::loadSingleExtLocalconfFiles();
1666 }
1667 }
1668
1669 /**
1670 * Execute ext_localconf.php files from extensions
1671 *
1672 * @return void
1673 */
1674 protected static function loadSingleExtLocalconfFiles() {
1675 // This is the main array meant to be manipulated in the ext_localconf.php files
1676 // In general it is recommended to not rely on it to be globally defined in that
1677 // scope but to use $GLOBALS['TYPO3_CONF_VARS'] instead.
1678 // Nevertheless we define it here as global for backwards compatibility.
1679 global $TYPO3_CONF_VARS;
1680
1681 // These globals for internal use only. Manipulating them directly is highly discouraged!
1682 // We set them here as global for backwards compatibility, but this will change in
1683 // future versions.
1684 // @deprecated since 6.0 Will be removed in two versions.
1685 global $T3_SERVICES, $T3_VAR;
1686
1687 foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $_EXTKEY => $extensionInformation) {
1688 if (is_array($extensionInformation) && $extensionInformation['ext_localconf.php']) {
1689 // $_EXTKEY and $_EXTCONF are available in ext_localconf.php
1690 // and are explicitly set in cached file as well
1691 $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
1692 require($extensionInformation['ext_localconf.php']);
1693 }
1694 }
1695 }
1696
1697 /**
1698 * Create cache entry for concatenated ext_localconf.php files
1699 *
1700 * @return void
1701 */
1702 protected static function createExtLocalconfCacheEntry() {
1703 $extensionInformation = $GLOBALS['TYPO3_LOADED_EXT'];
1704 $phpCodeToCache = array();
1705
1706 // Set same globals as in loadSingleExtLocalconfFiles()
1707 $phpCodeToCache[] = '/**';
1708 $phpCodeToCache[] = ' * Compiled ext_localconf.php cache file';
1709 $phpCodeToCache[] = ' */';
1710 $phpCodeToCache[] = '';
1711 $phpCodeToCache[] = 'global $TYPO3_CONF_VARS, $T3_SERVICES, $T3_VAR;';
1712 $phpCodeToCache[] = '';
1713
1714 // Iterate through loaded extensions and add ext_localconf content
1715 foreach ($extensionInformation as $extensionKey => $extensionDetails) {
1716 // Include a header per extension to make the cache file more readable
1717 $phpCodeToCache[] = '/**';
1718 $phpCodeToCache[] = ' * Extension: ' . $extensionKey;
1719 $phpCodeToCache[] = ' * File: ' . $extensionDetails['ext_localconf.php'];
1720 $phpCodeToCache[] = ' */';
1721 $phpCodeToCache[] = '';
1722
1723 // Set $_EXTKEY and $_EXTCONF for this extension
1724 $phpCodeToCache[] = '$_EXTKEY = \'' . $extensionKey . '\';';
1725 $phpCodeToCache[] = '$_EXTCONF = $GLOBALS[\'TYPO3_CONF_VARS\'][\'EXT\'][\'extConf\'][$_EXTKEY];';
1726 $phpCodeToCache[] = '';
1727
1728 // Add ext_localconf.php content of extension
1729 $phpCodeToCache[] = trim(t3lib_div::getUrl($extensionDetails['ext_localconf.php']));
1730 $phpCodeToCache[] = '';
1731 $phpCodeToCache[] = '';
1732 }
1733
1734 $phpCodeToCache = implode(LF, $phpCodeToCache);
1735 // Remove all start and ending php tags from content
1736 $phpCodeToCache = preg_replace('/<\?php|\?>/is', '', $phpCodeToCache);
1737
1738 $GLOBALS['typo3CacheManager']->getCache('cache_phpcode')->set(
1739 self::getExtLocalconfCacheIdentifier(),
1740 $phpCodeToCache,
1741 array('concatenatedExtLocalconf', 'core')
1742 );
1743 }
1744
1745 /**
1746 * Cache identifier of concatenated ext_localconf file
1747 *
1748 * @return string
1749 */
1750 protected static function getExtLocalconfCacheIdentifier() {
1751 return 'ext_localconf_' . sha1(TYPO3_version . PATH_site . 'extLocalconf');
1752 }
1753
1754 /**
1755 * Execute all ext_tables.php files of loaded extensions.
1756 * The method implements an optionally used caching mechanism that concatenates all
1757 * ext_tables.php files in one file.
1758 *
1759 * This is an internal method. It is only used during bootstrap and
1760 * extensions should not use it!
1761 *
1762 * @param boolean $allowCaching Whether to load / create concatenated cache file
1763 * @return void
1764 * @access private
1765 */
1766 public static function loadExtTables($allowCaching = TRUE) {
1767 if ($allowCaching && !self::$extTablesWasReadFromCacheOnce) {
1768 self::$extTablesWasReadFromCacheOnce = TRUE;
1769 $cacheIdentifier = self::getExtTablesCacheIdentifier();
1770 /** @var $phpCodeCache t3lib_cache_frontend_PhpFrontend */
1771 $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_phpcode');
1772 if ($phpCodeCache->has($cacheIdentifier)) {
1773 $phpCodeCache->requireOnce($cacheIdentifier);
1774 } else {
1775 self::loadSingleExtTablesFiles();
1776 self::createExtTablesCacheEntry();
1777 }
1778 } else {
1779 self::loadSingleExtTablesFiles();
1780 }
1781 }
1782
1783 /**
1784 * Load ext_tables.php as single files
1785 *
1786 * @return void
1787 */
1788 protected static function loadSingleExtTablesFiles() {
1789 // In general it is recommended to not rely on it to be globally defined in that
1790 // scope, but we can not prohibit this without breaking backwards compatibility
1791 global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
1792 global $TBE_MODULES, $TBE_MODULES_EXT, $TCA;
1793 global $PAGES_TYPES, $TBE_STYLES, $FILEICONS;
1794 global $_EXTKEY;
1795
1796 // Load each ext_tables.php file of loaded extensions
1797 foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $_EXTKEY => $extensionInformation) {
1798 if (is_array($extensionInformation) && $extensionInformation['ext_tables.php']) {
1799 // $_EXTKEY and $_EXTCONF are available in ext_tables.php
1800 // and are explicitly set in cached file as well
1801 $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
1802 require($extensionInformation['ext_tables.php']);
1803 }
1804 }
1805 }
1806
1807 /**
1808 * Create concatenated ext_tables.php cache file
1809 *
1810 * @return void
1811 */
1812 protected static function createExtTablesCacheEntry() {
1813 $extensionInformation = $GLOBALS['TYPO3_LOADED_EXT'];
1814 $phpCodeToCache = array();
1815
1816 // Set same globals as in loadSingleExtTablesFiles()
1817 $phpCodeToCache[] = '/**';
1818 $phpCodeToCache[] = ' * Compiled ext_tables.php cache file';
1819 $phpCodeToCache[] = ' */';
1820 $phpCodeToCache[] = '';
1821 $phpCodeToCache[] = 'global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;';
1822 $phpCodeToCache[] = 'global $TBE_MODULES, $TBE_MODULES_EXT, $TCA;';
1823 $phpCodeToCache[] = 'global $PAGES_TYPES, $TBE_STYLES, $FILEICONS;';
1824 $phpCodeToCache[] = 'global $_EXTKEY;';
1825 $phpCodeToCache[] = '';
1826
1827 // Iterate through loaded extensions and add ext_tables content
1828 foreach ($extensionInformation as $extensionKey => $extensionDetails) {
1829 // Include a header per extension to make the cache file more readable
1830 $phpCodeToCache[] = '/**';
1831 $phpCodeToCache[] = ' * Extension: ' . $extensionKey;
1832 $phpCodeToCache[] = ' * File: ' . $extensionDetails['ext_tables.php'];
1833 $phpCodeToCache[] = ' */';
1834 $phpCodeToCache[] = '';
1835
1836 // Set $_EXTKEY and $_EXTCONF for this extension
1837 $phpCodeToCache[] = '$_EXTKEY = \'' . $extensionKey . '\';';
1838 $phpCodeToCache[] = '$_EXTCONF = $GLOBALS[\'TYPO3_CONF_VARS\'][\'EXT\'][\'extConf\'][$_EXTKEY];';
1839 $phpCodeToCache[] = '';
1840
1841 // Add ext_tables.php content of extension
1842 $phpCodeToCache[] = trim(t3lib_div::getUrl($extensionDetails['ext_tables.php']));
1843 $phpCodeToCache[] = '';
1844 $phpCodeToCache[] = '';
1845 }
1846
1847 $phpCodeToCache = implode(LF, $phpCodeToCache);
1848 // Remove all start and ending php tags from content
1849 $phpCodeToCache = preg_replace('/<\?php|\?>/is', '', $phpCodeToCache);
1850
1851 $GLOBALS['typo3CacheManager']->getCache('cache_phpcode')->set(
1852 self::getExtTablesCacheIdentifier(),
1853 $phpCodeToCache,
1854 array('combinedExtTables', 'core')
1855 );
1856 }
1857
1858 /**
1859 * Cache identifier for concatenated ext_tables.php files
1860 *
1861 * @return string
1862 */
1863 protected static function getExtTablesCacheIdentifier() {
1864 return 'ext_tables_' . sha1(TYPO3_version . PATH_site . 'extTables');
1865 }
1866
1867 /**
1868 * Loading extensions configured in $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList']
1869 *
1870 * Usages of this function can be seen in bootstrap
1871 * Extensions are always detected in the order local - global - system.
1872 *
1873 * @return array Extension Array
1874 * @internal
1875 * @deprecated since 6.0, will be removed in two versions
1876 */
1877 public static function typo3_loadExtensions() {
1878 t3lib_div::logDeprecatedFunction();
1879 return self::loadTypo3LoadedExtensionInformation(TRUE);
1880 }
1881
1882 /**
1883 * Returns the section headers for the compiled cache-files.
1884 *
1885 * @param string $key Is the extension key
1886 * @param string $file Is the filename (only informative for comment)
1887 * @return string
1888 * @internal
1889 * @deprecated since 6.0, will be removed in two versions
1890 */
1891 public static function _makeIncludeHeader($key, $file) {
1892 t3lib_div::logDeprecatedFunction();
1893 return '';
1894 }
1895
1896 /**
1897 * Returns TRUE if both the localconf and tables cache file exists
1898 * (with $cacheFilePrefix) and if they are not empty
1899 *
1900 * @param $cacheFilePrefix string Prefix of the cache file to check
1901 * @return boolean
1902 * @deprecated since 6.0, will be removed in two versions
1903 */
1904 public static function isCacheFilesAvailable($cacheFilePrefix) {
1905 t3lib_div::logDeprecatedFunction();
1906 return FALSE;
1907 }
1908
1909 /**
1910 * Returns TRUE if the "localconf.php" file in "typo3conf/" is writable
1911 *
1912 * @return boolean
1913 * @internal
1914 */
1915 public static function isLocalconfWritable() {
1916 return @is_writable(PATH_typo3conf) && @is_writable(PATH_typo3conf . 'localconf.php');
1917 }
1918
1919 /**
1920 * Returns an error string if typo3conf/ or cache-files with $cacheFilePrefix are NOT writable
1921 * Returns FALSE if no problem.
1922 *
1923 * @param string $cacheFilePrefix Prefix of the cache file to check
1924 * @return string
1925 * @internal
1926 * @deprecated since 6.0, will be removed in two versions
1927 */
1928 public static function cannotCacheFilesWritable($cacheFilePrefix) {
1929 t3lib_div::logDeprecatedFunction();
1930 return '';
1931 }
1932
1933 /**
1934 * Returns an array with the two cache-files (0=>localconf, 1=>tables)
1935 * from typo3conf/ if they (both) exist. Otherwise FALSE.
1936 * Evaluation relies on $GLOBALS['TYPO3_LOADED_EXT']['_CACHEFILE']
1937 *
1938 * @param string $cacheFilePrefix Cache file prefix to be used (optional)
1939 * @return array
1940 * @internal
1941 * @deprecated since 6.0, will be removed in versions
1942 */
1943 public static function currentCacheFiles($cacheFilePrefix = NULL) {
1944 t3lib_div::logDeprecatedFunction();
1945 return array();
1946 }
1947
1948 /**
1949 * Compiles/Creates the two cache-files in typo3conf/ based on $cacheFilePrefix
1950 * Returns a array with the key "_CACHEFILE" set to the $cacheFilePrefix value
1951 *
1952 * @param array $extensions Extension information array
1953 * @param string $cacheFilePrefix Prefix for the cache files
1954 * @return array
1955 * @internal
1956 * @deprecated since 6.0, will be removed in two versions
1957 */
1958 public static function writeCacheFiles($extensions, $cacheFilePrefix) {
1959 t3lib_div::logDeprecatedFunction();
1960 return array();
1961 }
1962
1963 /**
1964 * Remove cache files from php code cache, tagged with 'core'
1965 *
1966 * This removes the following cache entries:
1967 * - autoloader cache registry
1968 * - cache loaded extension array
1969 * - ext_localconf concatenation
1970 * - ext_tables concatenation
1971 *
1972 * This method is usually only used by extension that fiddle
1973 * with the loaded extensions. An example is the extension
1974 * manager and the install tool.
1975 *
1976 * @return void
1977 */
1978 public static function removeCacheFiles() {
1979 /** @var $phpCodeCache t3lib_cache_frontend_PhpFrontend */
1980 $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_phpcode');
1981 $phpCodeCache->flushByTag('core');
1982 }
1983
1984 /**
1985 * Gets the behaviour for caching ext_tables.php and ext_localconf.php files
1986 * (see $GLOBALS['TYPO3_CONF_VARS']['EXT']['extCache'] setting in the install tool).
1987 *
1988 * @param boolean $usePlainValue Whether to use the value as it is without modifications
1989 * @return integer
1990 * @deprecated since 6.0, will be removed in two versions
1991 */
1992 public static function getExtensionCacheBehaviour($usePlainValue = FALSE) {
1993 t3lib_div::logDeprecatedFunction();
1994 return 1;
1995 }
1996
1997 /**
1998 * Gets the prefix used for the ext_tables.php and ext_localconf.php cached files.
1999 *
2000 * @return string
2001 */
2002 public static function getCacheFilePrefix() {
2003 t3lib_div::logDeprecatedFunction();
2004 }
2005
2006 /**
2007 * Gets the list of enabled extensions
2008 *
2009 * @return string
2010 */
2011 public static function getEnabledExtensionList() {
2012 $extensionList = self::getRequiredExtensionList() . ',' . $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'];
2013 $ignoredExtensionList = self::getIgnoredExtensionList();
2014
2015 // Remove the extensions to be ignored:
2016 if ($ignoredExtensionList && (defined('TYPO3_enterInstallScript') && TYPO3_enterInstallScript) === FALSE) {
2017 $extensions = array_diff(
2018 explode(',', $extensionList),
2019 explode(',', $ignoredExtensionList)
2020 );
2021 $extensionList = implode(',', $extensions);
2022 }
2023
2024 return $extensionList;
2025 }
2026
2027 /**
2028 * Gets the list of required extensions.
2029 *
2030 * @return string
2031 */
2032 public static function getRequiredExtensionList() {
2033 $requiredExtensionList = t3lib_div::uniqueList(
2034 REQUIRED_EXTENSIONS . ',' . $GLOBALS['TYPO3_CONF_VARS']['EXT']['requiredExt']
2035 );
2036
2037 return $requiredExtensionList;
2038 }
2039
2040 /**
2041 * Gets the list of extensions to be ignored (not to be loaded).
2042 *
2043 * @return string
2044 */
2045 public static function getIgnoredExtensionList() {
2046 $ignoredExtensionList = t3lib_div::uniqueList(
2047 $GLOBALS['TYPO3_CONF_VARS']['EXT']['ignoredExt']
2048 );
2049
2050 return $ignoredExtensionList;
2051 }
2052
2053 /**
2054 * Makes a table categorizable by extending its TCA.
2055 *
2056 * @param string $extensionKey Extension key to be used
2057 * @param string $tableName Name of the table to be categoriezed
2058 * @param string $fieldName Name of the field to be used to store categories
2059 * @param array $options Additional configuration options
2060 * + fieldList: field configuration to be added to showitems
2061 * + typesList: list of types that shall visualize the categories field
2062 * + position: insert position of the categories field
2063 * + fieldConfiguration: TCA field config array to override defaults
2064 * @see addTCAcolumns
2065 * @see addToAllTCAtypes
2066 */
2067 public static function makeCategorizable($extensionKey, $tableName, $fieldName = 'categories', array $options = array()) {
2068 // Load TCA first
2069 t3lib_div::loadTCA($tableName);
2070
2071 // Update the category registry
2072 $result = t3lib_category_Registry::getInstance()->add($extensionKey, $tableName, $fieldName);
2073
2074 if ($result === FALSE) {
2075 $message = 't3lib_categoryRegistry: no category registered for table "%s". Double check if there is a TCA configured';
2076 t3lib_div::devLog(sprintf($message, $tableName), 'Core', 2);
2077 }
2078
2079 // Makes sure to add more TCA to an existing structure
2080 if (isset($GLOBALS['TCA'][$tableName]['columns'])) {
2081 // Forges a new field, default name is "categories"
2082 $fieldConfiguration = array(
2083 'type' => 'select',
2084 'foreign_table' => 'sys_category',
2085 'foreign_table_where' => ' ORDER BY sys_category.title ASC',
2086 'MM' => 'sys_category_record_mm',
2087 'MM_opposite_field' => 'items',
2088 'MM_match_fields' => array('tablenames' => $tableName),
2089 'size' => 10,
2090 'autoSizeMax' => 50,
2091 'maxitems' => 9999,
2092 'renderMode' => 'tree',
2093 'treeConfig' => array(
2094 'parentField' => 'parent',
2095 'appearance' => array(
2096 'expandAll' => TRUE,
2097 'showHeader' => TRUE,
2098 ),
2099 ),
2100 'wizards' => array(
2101 '_PADDING' => 1,
2102 '_VERTICAL' => 1,
2103 'edit' => array(
2104 'type' => 'popup',
2105 'title' => 'Edit',
2106 'script' => 'wizard_edit.php',
2107 'icon' => 'edit2.gif',
2108 'popup_onlyOpenIfSelected' => 1,
2109 'JSopenParams' => 'height=350,width=580,status=0,menubar=0,scrollbars=1',
2110 ),
2111 'add' => Array(
2112 'type' => 'script',
2113 'title' => 'Create new',
2114 'icon' => 'add.gif',
2115 'params' => array(
2116 'table' => 'sys_category',
2117 'pid' => '###CURRENT_PID###',
2118 'setValue' => 'prepend'
2119 ),
2120 'script' => 'wizard_add.php',
2121 ),
2122 ),
2123 );
2124
2125 if (!empty($options['fieldConfiguration'])) {
2126 $fieldConfiguration = t3lib_div::array_merge_recursive_overrule(
2127 $fieldConfiguration,
2128 $options['fieldConfiguration']
2129 );
2130 }
2131
2132 $columns = array(
2133 $fieldName => array(
2134 'exclude' => 0,
2135 'label' => 'LLL:EXT:lang/locallang_tca.xlf:sys_category.categories',
2136 'config' => $fieldConfiguration,
2137 ),
2138 );
2139
2140 // Adding fields to an existing table definition
2141 self::addTCAcolumns($tableName, $columns);
2142
2143 $fieldList = '--div--;LLL:EXT:lang/locallang_tca.xlf:sys_category.tabs.category, ' . $fieldName;
2144 if (!empty($options['fieldList'])) {
2145 $fieldList = $options['fieldList'];
2146 }
2147
2148 $typesList = '';
2149 if (!empty($options['typesList'])) {
2150 $typesList = $options['typesList'];
2151 }
2152
2153 $position = '';
2154 if (!empty($options['position'])) {
2155 $position = $options['position'];
2156 }
2157
2158 // Makes the new "categories" field to be visible in TSFE.
2159 self::addToAllTCAtypes($tableName, $fieldList, $typesList, $position);
2160 }
2161 }
2162 }
2163
2164 ?>