Fixed bug #10454: t3lib_div::makeInstanceClassName() allows to bypass checks for...
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_extmgm.php
index a0141f0..d7a9024 100644 (file)
@@ -249,59 +249,286 @@ final class t3lib_extMgm {
         * @return      void
         */
        public static function addToAllTCAtypes($table, $str, $specificTypesList = '', $position = '') {
-               global $TCA;
+               t3lib_div::loadTCA($table);
+               $str = trim($str);
 
-               $positionArr = t3lib_div::trimExplode(',', $position, 1);
-               $insert = count($position);
+               if ($str && is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['types'])) {
+                       foreach($GLOBALS['TCA'][$table]['types'] as $type => &$typeDetails) {
+                               if ($specificTypesList === '' || t3lib_div::inList($specificTypesList, $type)) {
+                                       $typeDetails['showitem'] = self::executePositionedStringInsertion(
+                                               $typeDetails['showitem'],
+                                               $str,
+                                               $position
+                                       );
+                               }
+                       }
+               }
+       }
 
+       /**
+        * Adds new fields to all palettes of an existing field.
+        * If the field does not have a palette yet, it's created automatically and
+        * gets called "generatedFor-$field".
+        *
+        * @param       string          $table: Name of the table
+        * @param       string          $field: Name of the field that has the palette to be extended
+        * @param       string          $addFields: List of fields to be added to the palette
+        * @param       string          $insertionPosition: Insert fields before (default) or after one
+        *                                              of this fields (commalist with "before:" or "after:" commands).
+        *                                              Example: "before:keywords,--palette--;;4,after:description".
+        *                                              Palettes must be passed like in the example no matter how the
+        *                                              palette definition looks like in TCA.
+        * @return      void
+        */
+       public static function addFieldsToAllPalettesOfField($table, $field, $addFields, $insertionPosition = '') {
+               $generatedPalette = '';
                t3lib_div::loadTCA($table);
-               $str = trim($str);
-               if ($str && is_array($TCA[$table]) && is_array($TCA[$table]['types'])) {
-                       foreach($TCA[$table]['types'] as $k => $v) {
-                               if ($specificTypesList === '' || t3lib_div::inList($specificTypesList, $k)) {
-                                       if ($insert) {
-                                               if (count($positionArr)) {
-                                                       $append = true;
-                                                       $showItem = t3lib_div::trimExplode(',', $TCA[$table]['types'][$k]['showitem'], 1);
-                                                       foreach($showItem as $key => $fieldInfo)        {
-
-                                                               $parts = explode(';', $fieldInfo);
-                                                               $theField = trim($parts[0]);
-                                                               $palette = trim($parts[0]).';;'.trim($parts[2]);
-
-                                                                       // insert before: find exact field name or palette with number
-                                                               if (in_array($theField, $positionArr) || in_array($palette, $positionArr) || in_array('before:'.$theField, $positionArr) || in_array('before:'.$palette, $positionArr)) {
-                                                                       $showItem[$key] = $str.', '.$fieldInfo;
-                                                                       $append = false;
-                                                                       break;
-                                                               }
-                                                                       // insert after
-                                                               if (in_array('after:'.$theField, $positionArr) || in_array('after:'.$palette, $positionArr)) {
-                                                                       $showItem[$key] = $fieldInfo.', '.$str;
-                                                                       $append = false;
-                                                                       break;
-                                                               }
-                                                       }
 
-                                                               // Not found? Then append.
-                                                       if($append) {
-                                                               $showItem[] = $str;
+               if (isset($GLOBALS['TCA'][$table]['columns'][$field])) {
+                       $types =& $GLOBALS['TCA'][$table]['types'];
+                       if (is_array($types)) {
+                                       // Iterate through all types and search for the field that defines the palette to be extended:
+                               foreach (array_keys($types) as $type) {
+                                       $structure = self::getStructureOfItemList($types[$type]['showitem']);
+                                       if (isset($structure['regularFields'][$field])) {
+                                               $fieldReference =& $structure['regularFields'][$field];
+                                                       // If the field already has a palette, extend it:
+                                               if ($fieldReference['details']['palette']) {
+                                                       $palette = $fieldReference['details']['palette'];
+                                                       self::addFieldsToPalette($table, $palette, $addFields, $insertionPosition);
+                                                       // If there's not palette yet, create one:
+                                               } else {
+                                                       if ($generatedPalette) {
+                                                               $palette = $generatedPalette;
+                                                       } else {
+                                                               $palette = $generatedPalette = 'generatedFor-' . $field;
+                                                               self::addFieldsToPalette($table, $palette, $addFields, $insertionPosition);
                                                        }
+                                                       $fieldReference['details']['palette'] = $palette;
+                                                       $types[$type]['showitem'] =  self::generateItemList($structure['orderedItems']);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
 
-                                                       $TCA[$table]['types'][$k]['showitem'] = implode(', ', $showItem);
+       /**
+        * Adds new fields to a palette.
+        * If the palette does not exist yet, it's created automatically.
+        *
+        * @param       string          $table: Name of the table
+        * @param       string          $palette: Name of the palette to be extended
+        * @param       string          $addFields: List of fields to be added to the palette
+        * @param       string          $insertionPosition: Insert fields before (default) or after one
+        *                                              of this fields (commalist with "before:" or "after:" commands).
+        *                                              Example: "before:keywords,--palette--;;4,after:description".
+        *                                              Palettes must be passed like in the example no matter how the
+        *                                              palette definition looks like in TCA.
+        * @return      void
+        */
+       public static function addFieldsToPalette($table, $palette, $addFields, $insertionPosition = '') {
+               t3lib_div::loadTCA($table);
+
+               if (isset($GLOBALS['TCA'][$table])) {
+                       $paletteData =& $GLOBALS['TCA'][$table]['palettes'][$palette];
+                               // If palette already exists, merge the data:
+                       if (is_array($paletteData)) {
+                               $paletteData['showitem'] = self::executePositionedStringInsertion(
+                                       $paletteData['showitem'],
+                                       $addFields,
+                                       $insertionPosition
+                               );
+                               // If it's a new palette, just set the data:
+                       } else {
+                               $paletteData['showitem'] = $addFields;
+                       }
+               }
+       }
+
+       /**
+        * Inserts as list of data into an existing list.
+        * The insertion position can be defined accordant before of after existing list items.
+        *
+        * @param       string          $list: The list of items to be extended
+        * @param       string          $insertionList: The list of items to inserted
+        * @param       string          $insertionPosition: Insert fields before (default) or after one
+        *                                              of this fields (commalist with "before:" or "after:" commands).
+        *                                              Example: "before:keywords,--palette--;;4,after:description".
+        *                                              Palettes must be passed like in the example no matter how the
+        *                                              palette definition looks like in TCA.
+        * @return      string          The extended list
+        */
+       protected static function executePositionedStringInsertion($list, $insertionList, $insertionPosition = '') {
+               $list = trim($list);
+               $insertionList = self::removeDuplicatesForInsertion($list, $insertionList);
+
+                       // Append data to the end (default):
+               if ($insertionPosition === '') {
+                       $list.= ($list ? ', ' : '') . $insertionList;
+                       // Insert data before or after insertion points:
+               } else {
+                       $positions = t3lib_div::trimExplode(',', $insertionPosition, true);
+                       $structure = self::getStructureOfItemList($list);
+                       $isInserted = false;
+                               // Iterate through all fields an check whether it's possible to inserte there:
+                       foreach ($structure['orderedItems'] as &$itemDetails) {
+                               if ($itemDetails['details']['name']) {
+                                       $needles = self::getInsertionNeedles($itemDetails['details']);
+                                               // Insert data before:
+                                       foreach ($needles['before'] as $needle) {
+                                               if (in_array($needle, $positions)) {
+                                                       $itemDetails['rawData'] = $insertionList . ', '  . $itemDetails['rawData'];
+                                                       $isInserted = true;
+                                                       break;
                                                }
-                                               else {
-                                                       $TCA[$table]['types'][$k]['showitem'] .= ', ' . $str;
+                                       }
+                                               // Insert data after:
+                                       foreach ($needles['after'] as $needle) {
+                                               if (in_array($needle, $positions)) {
+                                                       $itemDetails['rawData'] .= ', ' . $insertionList;
+                                                       $isInserted = true;
+                                                       break;
                                                }
+                                       }
+                                               // Break if insertion was already done:
+                                       if ($isInserted) {
+                                               break;
+                                       }
+                               }
+                       }
+                               // If insertion point could not be determined, append the data:
+                       if (!$isInserted) {
+                               $list.= ($list ? ', ' : '') . $insertionList;
+                               // If data was correctly inserted before or after existing items, recreate the list:
+                       } else {
+                               $list = self::generateItemList($structure['orderedItems'], true);
+                       }
+               }
 
-                                       } else {
-                                               $TCA[$table]['types'][$k]['showitem'].=', ' . $str;
+               return $list;
+       }
+
+       /**
+        * Compares an existing list of items and a list of items to be inserted
+        * and returns a duplicate-free variant of that insertion list.
+        *
+        * Example:
+        *  + list: 'field_a, field_b;;;;2-2-2, field_c;;;;3-3-3'
+        *  + insertion: 'field_b, field_d, field_c;;;4-4-4'
+        * -> new insertion: 'field_d'
+        *
+        * @param       string          $list: The list of items to be extended
+        * @param       string          $insertionList: The list of items to inserted
+        * @return      string          Duplicate-free list of items to be inserted
+        */
+       protected static function removeDuplicatesForInsertion($list, $insertionList) {
+               $pattern = '/(^|,)\s*\b([^;,]+)\b[^,]*/';
+
+               if ($list && preg_match_all($pattern, $list, $listMatches)) {
+                       if ($insertionList && preg_match_all($pattern, $insertionList, $insertionListMatches)) {
+                               $duplicates = array_intersect($listMatches[2], $insertionListMatches[2]);
+                               if ($duplicates) {
+                                       foreach ($duplicates as &$duplicate) {
+                                               $duplicate = preg_quote($duplicate, '/');
                                        }
+                                       $insertionList = preg_replace(
+                                               array('/(^|,)\s*\b(' . implode('|', $duplicates) . ')\b[^,]*(,|$)/', '/,$/'),
+                                               array('\3', ''),
+                                               $insertionList
+                                       );
                                }
                        }
                }
+
+               return $insertionList;
        }
 
+       /**
+        * Generates search needles that are used for inserting fields/items into an existing list.
+        *
+        * @see         executePositionedStringInsertion
+        * @param       array           $itemDetails: Additional details of the field/item like e.g. palette information
+        *                                              (this gets created as sub-array by the function getStructureOfItemList())
+        * @return      array           The needled to be used for inserting content before or after existing fields/items
+        */
+       protected static function getInsertionNeedles(array $itemDetails) {
+               $itemName = $itemDetails['name'];
+
+               $needles = array(
+                       'before' => array($itemName, 'before:' . $itemName),
+                       'after' => array('after:' . $itemName),
+               );
+
+               if ($itemDetails['palette']) {
+                       $palette = $itemName . ';;' . $itemDetails['palette'];
+                       $needles['before'][] = $palette;
+                       $needles['before'][] = 'before:' . $palette;
+                       $needles['afer'][] = 'after:' . $palette;
+               }
+
+               return $needles;
+       }
+
+       /**
+        * Generates an array of fields/items with additional information such as e.g. the name of the palette.
+        *
+        * @param       string          $itemList: List of fields/items to be splitted up
+        *                                              (this mostly reflects the data in $TCA[<table>]['types'][<type>]['showitem'])
+        * @return      array           An array multidimensional array with the main keys 'orderedItems' and 'regularFields'
+        */
+       protected static function getStructureOfItemList($itemList) {
+               $structure = array(
+                       'orderedItems' => array(),
+                       'regularFields' => array(),
+               );
+
+               $itemParts = t3lib_div::trimExplode(',', $itemList, true);
+
+               foreach ($itemParts as $index => $itemPart) {
+                       $itemDetails = t3lib_div::trimExplode(';', $itemPart, false, 5);
+                               // Add definition of current item in the order it appeared in the field list:
+                       $structure['orderedItems'][$index] = array(
+                               'rawData' => $itemPart,
+                               'details' => array(
+                                       'name' => $itemDetails[0],
+                                       'label' => $itemDetails[1],
+                                       'palette' => $itemDetails[2],
+                                       'special' => $itemDetails[3],
+                                       'styles' => $itemDetails[4],
+                               ),
+                       );
+                               // Set reference for regular fields (no --div-- or --palette--):
+                       if (!isset($structure['regularFields'][$itemDetails[0]]) && strpos($itemDetails[0], '--') === false) {
+                               $structure['regularFields'][$itemDetails[0]] =& $structure['orderedItems'][$index]; 
+                       }
+               }
+
+               return $structure;
+       }
+
+       /**
+        * Generates a list of fields/items out of an array provided by the function getFieldsOfFieldList().
+        *
+        * @see         getStructureOfItemList
+        * @param       array           $orderedItems: The array of ordered items with optional additional information
+        * @param       boolean         $useRawData: Use raw data instead of building by using the details (default: false)
+        * @return      string          The list of fields/items which gets used for $TCA[<table>]['types'][<type>]['showitem']
+        *                                              or $TCA[<table>]['palettes'][<palette>]['showitem'] in most cases
+        */
+       protected static function generateItemList(array $orderedItems, $useRawData = false) {
+               $itemParts = array();
+
+               foreach ($orderedItems as $itemName => $itemDetails) {
+                       if ($useRawData) {
+                               $itemParts[] = $itemDetails['rawData'];
+                       } else {
+                               $itemParts[] = (count($itemDetails['details']) > 1 ? implode(';', $itemDetails['details']) : $itemName);
+                       }
+               }
+
+               return implode(', ', $itemParts);
+       }
 
        /**
         * Add tablename to default list of allowed tables on pages (in $PAGES_TYPES)
@@ -766,21 +993,12 @@ final class t3lib_extMgm {
                $cN = t3lib_extMgm::getCN($key);
 
                        // General plugin:
-               if ($cached) {
-                       $pluginContent = trim('
-includeLibs.'.$cN.$prefix.' = '.$TYPO3_LOADED_EXT[$key]['siteRelPath'].$classFile.'
-plugin.'.$cN.$prefix.' = USER
-plugin.'.$cN.$prefix.' {
-  userFunc = '.$cN.$prefix.'->main
-}');
-               } else {
-                       $pluginContent = trim('
-plugin.'.$cN.$prefix.' = USER_INT
+               $pluginContent = trim('
+plugin.'.$cN.$prefix.' = USER' . ($cached ? '' : '_INT') . '
 plugin.'.$cN.$prefix.' {
   includeLibs = '.$TYPO3_LOADED_EXT[$key]['siteRelPath'].$classFile.'
   userFunc = '.$cN.$prefix.'->main
 }');
-               }
                t3lib_extMgm::addTypoScript($key, 'setup', '
 # Setting '.$key.' plugin TypoScript
 '.$pluginContent);
@@ -1175,4 +1393,4 @@ $TYPO3_LOADED_EXT = unserialize(stripslashes(\''.addslashes(serialize($extension
        }
 }
 
-?>
+?>
\ No newline at end of file