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