[BUGFIX] Notice free InlineStackProcessor testing
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Utility / FormEngineUtility.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Utility;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
19 use TYPO3\CMS\Core\Imaging\Icon;
20 use TYPO3\CMS\Core\Imaging\IconFactory;
21 use TYPO3\CMS\Core\Utility\ArrayUtility;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Core\Utility\PathUtility;
24 use TYPO3\CMS\Core\Utility\StringUtility;
25
26 /**
27 * This is a static, internal and intermediate helper class for various
28 * FormEngine related tasks.
29 *
30 * This class was introduced to help disentangling FormEngine and
31 * its sub classes. It MUST NOT be used in other extensions and will
32 * change or vanish without further notice.
33 *
34 * @internal
35 * @todo: These helpers are target to be dropped if further FormEngine refactoring is done
36 */
37 class FormEngineUtility
38 {
39 /**
40 * Whitelist that allows TCA field configuration to be overridden by TSconfig
41 *
42 * @see overrideFieldConf()
43 * @var array
44 */
45 protected static $allowOverrideMatrix = [
46 'input' => ['size', 'max', 'readOnly'],
47 'text' => ['cols', 'rows', 'wrap', 'max', 'readOnly'],
48 'check' => ['cols', 'readOnly'],
49 'select' => ['size', 'autoSizeMax', 'maxitems', 'minitems', 'readOnly', 'treeConfig'],
50 'group' => ['size', 'autoSizeMax', 'max_size', 'maxitems', 'minitems', 'readOnly'],
51 'inline' => ['appearance', 'behaviour', 'foreign_label', 'foreign_selector', 'foreign_unique', 'maxitems', 'minitems', 'size', 'autoSizeMax', 'symmetric_label', 'readOnly'],
52 'imageManipulation' => ['ratios', 'cropVariants']
53 ];
54
55 /**
56 * Overrides the TCA field configuration by TSconfig settings.
57 *
58 * Example TSconfig: TCEform.<table>.<field>.config.appearance.useSortable = 1
59 * This overrides the setting in $GLOBALS['TCA'][<table>]['columns'][<field>]['config']['appearance']['useSortable'].
60 *
61 * @param array $fieldConfig $GLOBALS['TCA'] field configuration
62 * @param array $TSconfig TSconfig
63 * @return array Changed TCA field configuration
64 * @internal
65 */
66 public static function overrideFieldConf($fieldConfig, $TSconfig)
67 {
68 if (is_array($TSconfig)) {
69 $TSconfig = GeneralUtility::removeDotsFromTS($TSconfig);
70 $type = $fieldConfig['type'] ?? '';
71 if (isset($TSconfig['config']) && is_array($TSconfig['config']) && is_array(static::$allowOverrideMatrix[$type])) {
72 // Check if the keys in TSconfig['config'] are allowed to override TCA field config:
73 foreach ($TSconfig['config'] as $key => $_) {
74 if (!in_array($key, static::$allowOverrideMatrix[$type], true)) {
75 unset($TSconfig['config'][$key]);
76 }
77 }
78 // Override $GLOBALS['TCA'] field config by remaining TSconfig['config']:
79 if (!empty($TSconfig['config'])) {
80 ArrayUtility::mergeRecursiveWithOverrule($fieldConfig, $TSconfig['config']);
81 }
82 }
83 }
84 return $fieldConfig;
85 }
86
87 /**
88 * Returns TSconfig for given table and row
89 *
90 * @param string $table The table name
91 * @param array $row The table row - Must at least contain the "uid" value, even if "NEW..." string.
92 * The "pid" field is important as well, negative values will be interpreted as pointing to a record from the same table.
93 * @param string $field Optionally specify the field name as well. In that case the TSconfig for this field is returned.
94 * @return mixed The TSconfig values - probably in an array
95 * @internal
96 */
97 public static function getTSconfigForTableRow($table, $row, $field = '')
98 {
99 static $cache;
100 if (is_null($cache)) {
101 $cache = [];
102 }
103 $cacheIdentifier = $table . ':' . $row['uid'];
104 if (!isset($cache[$cacheIdentifier])) {
105 $cache[$cacheIdentifier] = BackendUtility::getTCEFORM_TSconfig($table, $row);
106 }
107 if ($field && isset($cache[$cacheIdentifier][$field])) {
108 return $cache[$cacheIdentifier][$field];
109 }
110 return $cache[$cacheIdentifier];
111 }
112
113 /**
114 * Renders the $icon, supports a filename for skinImg or sprite-icon-name
115 *
116 * @param string $icon The icon passed, could be a file-reference or a sprite Icon name
117 * @param string $alt Alt attribute of the icon returned
118 * @param string $title Title attribute of the icon return
119 * @return string A tag representing to show the asked icon
120 * @internal
121 */
122 public static function getIconHtml($icon, $alt = '', $title = '')
123 {
124 $icon = (string)$icon;
125 $absoluteFilePath = GeneralUtility::getFileAbsFileName($icon);
126 if (!empty($absoluteFilePath) && is_file($absoluteFilePath)) {
127 $iconInfo = StringUtility::endsWith($absoluteFilePath, '.svg')
128 ? true
129 : getimagesize($absoluteFilePath);
130
131 if ($iconInfo !== false) {
132 return '<img'
133 . ' src="' . htmlspecialchars(PathUtility::getAbsoluteWebPath($absoluteFilePath)) . '"'
134 . ' alt="' . htmlspecialchars($alt) . '" '
135 . ($title ? 'title="' . htmlspecialchars($title) . '"' : '')
136 . ' />';
137 }
138 }
139
140 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
141 return '<span alt="' . htmlspecialchars($alt) . '" title="' . htmlspecialchars($title) . '">'
142 . $iconFactory->getIcon($icon, Icon::SIZE_SMALL)->render()
143 . '</span>';
144 }
145
146 /**
147 * Update expanded/collapsed states on new inline records if any.
148 *
149 * @param array $uc The uc array to be processed and saved (by reference)
150 * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tce Instance of FormEngine that saved data before
151 * @internal
152 */
153 public static function updateInlineView(&$uc, $tce)
154 {
155 $backendUser = static::getBackendUserAuthentication();
156 if (isset($uc['inlineView']) && is_array($uc['inlineView'])) {
157 $inlineView = (array)unserialize($backendUser->uc['inlineView']);
158 foreach ($uc['inlineView'] as $topTable => $topRecords) {
159 foreach ($topRecords as $topUid => $childElements) {
160 foreach ($childElements as $childTable => $childRecords) {
161 $uids = array_keys($tce->substNEWwithIDs_table, $childTable);
162 if (!empty($uids)) {
163 $newExpandedChildren = [];
164 foreach ($childRecords as $childUid => $state) {
165 if ($state && in_array($childUid, $uids)) {
166 $newChildUid = $tce->substNEWwithIDs[$childUid];
167 $newExpandedChildren[] = $newChildUid;
168 }
169 }
170 // Add new expanded child records to UC (if any):
171 if (!empty($newExpandedChildren)) {
172 $inlineViewCurrent = &$inlineView[$topTable][$topUid][$childTable];
173 if (is_array($inlineViewCurrent)) {
174 $inlineViewCurrent = array_unique(array_merge($inlineViewCurrent, $newExpandedChildren));
175 } else {
176 $inlineViewCurrent = $newExpandedChildren;
177 }
178 }
179 }
180 }
181 }
182 }
183 $backendUser->uc['inlineView'] = serialize($inlineView);
184 $backendUser->writeUC();
185 }
186 }
187
188 /**
189 * Compatibility layer for methods not in FormEngine scope.
190 *
191 * databaseRow was a flat array with single elements in select and group fields as comma separated list.
192 * With new data handling in FormEngine, this is now an array of element values. There are however "old"
193 * methods that still expect the flat array.
194 * This method implodes the array again to fake the old behavior of a database row before it is given
195 * to those methods.
196 *
197 * @param array $row Incoming array
198 * @return array Flat array
199 * @internal
200 */
201 public static function databaseRowCompatibility(array $row)
202 {
203 $newRow = [];
204 foreach ($row as $fieldName => $fieldValue) {
205 if (!is_array($fieldValue)) {
206 $newRow[$fieldName] = $fieldValue;
207 } else {
208 $newElementValue = [];
209 foreach ($fieldValue as $itemNumber => $itemValue) {
210 if (is_array($itemValue) && array_key_exists(1, $itemValue)) {
211 $newElementValue[] = $itemValue[1];
212 } else {
213 $newElementValue[] = $itemValue;
214 }
215 }
216 $newRow[$fieldName] = implode(',', $newElementValue);
217 }
218 }
219 return $newRow;
220 }
221
222 /**
223 * @return BackendUserAuthentication
224 */
225 protected static function getBackendUserAuthentication()
226 {
227 return $GLOBALS['BE_USER'];
228 }
229 }