[TASK] Move rendering of formattedLabel_userFunc to DataProvider
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormDataProvider / TcaRecordTitle.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\FormDataProvider;
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\Form\FormDataProviderInterface;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Database\DatabaseConnection;
20 use TYPO3\CMS\Core\Database\RelationHandler;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Lang\LanguageService;
23
24 /**
25 * Determine the title of a record and write it to $result['recordTitle'].
26 *
27 * TCA ctrl fields like label and label_alt are evaluated and their
28 * current values from databaseRow used to create the title.
29 */
30 class TcaRecordTitle implements FormDataProviderInterface
31 {
32 /**
33 * Enrich the processed record information with the resolved title
34 *
35 * @param array $result Incoming result array
36 * @return array Modified array
37 */
38 public function addData(array $result)
39 {
40 if (!isset($result['processedTca']['ctrl']['label'])) {
41 throw new \UnexpectedValueException(
42 'TCA of table ' . $result['tableName'] . ' misses required [\'ctrl\'][\'label\'] definition.',
43 1443706103
44 );
45 }
46
47 if ($result['isInlineChild'] && isset($result['processedTca']['ctrl']['formattedLabel_userFunc'])) {
48 $parameters = [
49 'table' => $result['tableName'],
50 'row' => $result['databaseRow'],
51 'title' => '',
52 'isOnSymmetricSide' => $result['isOnSymmetricSide'],
53 'options' => isset($result['processedTca']['ctrl']['formattedLabel_userFunc_options'])
54 ? $result['processedTca']['ctrl']['formattedLabel_userFunc_options']
55 : [],
56 'parent' => [
57 'uid' => $result['databaseRow']['uid'],
58 'config' => $result['inlineParentConfig']
59 ]
60 ];
61 // callUserFunction requires a third parameter, but we don't want to give $this as reference!
62 $null = null;
63 GeneralUtility::callUserFunction($result['processedTca']['ctrl']['formattedLabel_userFunc'], $parameters, $null);
64 $result['recordTitle'] = $parameters['title'];
65 } elseif (isset($result['processedTca']['ctrl']['label_userFunc'])) {
66 // userFunc takes precedence over everything
67 $parameters = [
68 'table' => $result['tableName'],
69 'row' => $result['databaseRow'],
70 'title' => '',
71 'options' => isset($result['processedTca']['ctrl']['label_userFunc_options'])
72 ? $result['processedTca']['ctrl']['label_userFunc_options']
73 : [],
74 ];
75 $null = null;
76 GeneralUtility::callUserFunction($result['processedTca']['ctrl']['label_userFunc'], $parameters, $null);
77 $result['recordTitle'] = $parameters['title'];
78 } else {
79 $result = $this->getRecordTitleByLabelProperties($result);
80 }
81
82 return $result;
83 }
84
85 /**
86 * Build the record title from label, label_alt and label_alt_force properties
87 *
88 * @param array $result Incoming result array
89 * @return array Modified result array
90 */
91 protected function getRecordTitleByLabelProperties(array $result)
92 {
93 $titles = [];
94 $titleByLabel = $this->getRecordTitleForField($result['processedTca']['ctrl']['label'], $result);
95 if (!empty($titleByLabel)) {
96 $titles[] = $titleByLabel;
97 }
98
99 $labelAltForce = isset($result['processedTca']['ctrl']['label_alt_force'])
100 ? (bool)$result['processedTca']['ctrl']['label_alt_force']
101 : false;
102 if (!empty($result['processedTca']['ctrl']['label_alt']) && ($labelAltForce || empty($titleByLabel))) {
103 // Dive into label_alt evaluation if label_alt_force is set or if label did not came up with a title yet
104 $labelAltFields = GeneralUtility::trimExplode(',', $result['processedTca']['ctrl']['label_alt'], true);
105 foreach ($labelAltFields as $fieldName) {
106 $titleByLabelAlt = $this->getRecordTitleForField($fieldName, $result);
107 if (!empty($titleByLabelAlt)) {
108 $titles[] = $titleByLabelAlt;
109 }
110 if (!$labelAltForce && !empty($titleByLabelAlt)) {
111 // label_alt_force creates a comma separated list of multiple fields.
112 // If not set, one found field with content is enough
113 break;
114 }
115 }
116 }
117
118 $result['recordTitle'] = implode(', ', $titles);
119 return $result;
120 }
121
122 /**
123 * Record title of a single field
124 *
125 * @param string $fieldName Field to handle
126 * @param array $result Incoming result array
127 * @return string
128 */
129 protected function getRecordTitleForField($fieldName, $result)
130 {
131 if ($fieldName === 'uid') {
132 // uid return field content directly since it usually has not TCA definition
133 return $result['databaseRow']['uid'];
134 }
135
136 if (!isset($result['processedTca']['columns'][$fieldName]['config']['type'])
137 || !is_string($result['processedTca']['columns'][$fieldName]['config']['type'])
138 ) {
139 return '';
140 }
141
142 $recordTitle = '';
143 $rawValue = null;
144 if (array_key_exists($fieldName, $result['databaseRow'])) {
145 $rawValue = $result['databaseRow'][$fieldName];
146 }
147 $fieldConfig = $result['processedTca']['columns'][$fieldName]['config'];
148 switch ($fieldConfig['type']) {
149 case 'radio':
150 $recordTitle = $this->getRecordTitleForRadioType($rawValue, $fieldConfig);
151 break;
152 case 'inline':
153 // intentional fall-through
154 case 'select':
155 $recordTitle = $this->getRecordTitleForSelectType($rawValue, $fieldConfig);
156 break;
157 case 'group':
158 $recordTitle = $this->getRecordTitleForGroupType($rawValue, $fieldConfig);
159 break;
160 case 'check':
161 $recordTitle = $this->getRecordTitleForCheckboxType($rawValue, $fieldConfig);
162 break;
163 case 'input':
164 $recordTitle = $this->getRecordTitleForInputType($rawValue, $fieldConfig);
165 break;
166 case 'text':
167 $recordTitle = $this->getRecordTitleForTextType($rawValue);
168 case 'flex':
169 // TODO: Check if and how a label could be generated from flex field data
170 default:
171
172 }
173
174 return $recordTitle;
175 }
176
177 /**
178 * Return the record title for radio fields
179 *
180 * @param mixed $value Current database value of this field
181 * @param array $fieldConfig TCA field configuration
182 * @return string
183 */
184 protected function getRecordTitleForRadioType($value, $fieldConfig)
185 {
186 if (!isset($fieldConfig['items']) || !is_array($fieldConfig['items'])) {
187 return '';
188 }
189 foreach ($fieldConfig['items'] as $item) {
190 list($itemLabel, $itemValue) = $item;
191 if ((string)$value === (string)$itemValue) {
192 return $itemLabel;
193 }
194 }
195 return '';
196 }
197
198 /**
199 * Return the record title for database records
200 *
201 * @param mixed $value Current database value of this field
202 * @param array $fieldConfig TCA field configuration
203 * @return string
204 */
205 protected function getRecordTitleForSelectType($value, $fieldConfig)
206 {
207 if (!is_array($value)) {
208 return '';
209 }
210 $labelParts = [];
211 foreach ($value as $itemValue) {
212 $itemKey = array_search($itemValue, array_column($fieldConfig['items'], 1));
213 if ($itemKey !== false) {
214 $labelParts[] = $fieldConfig['items'][$itemKey][0];
215 }
216 }
217 $title = implode(', ', $labelParts);
218 if (empty($title) && !empty($value)) {
219 $title = implode(', ', $value);
220 }
221 return $title;
222 }
223
224 /**
225 * Return the record title for database records
226 *
227 * @param mixed $value Current database value of this field
228 * @param array $fieldConfig TCA field configuration
229 * @return string
230 */
231 protected function getRecordTitleForGroupType($value, $fieldConfig)
232 {
233 if ($fieldConfig['internal_type'] !== 'db') {
234 return implode(', ', GeneralUtility::trimExplode(',', $value, true));
235 }
236 $labelParts = array_map(
237 function ($rawLabelItem) {
238 return array_pop(GeneralUtility::trimExplode('|', $rawLabelItem, true, 2));
239 },
240 GeneralUtility::trimExplode(',', $value, true)
241 );
242 if (!empty($labelParts)) {
243 sort($labelParts);
244 return implode(', ', $labelParts);
245 }
246 return '';
247 }
248
249 /**
250 * Returns the record title for checkbox fields
251 *
252 * @param mixed $value Current database value of this field
253 * @param array $fieldConfig TCA field configuration
254 * @return string
255 */
256 protected function getRecordTitleForCheckboxType($value, $fieldConfig)
257 {
258 $languageService = $this->getLanguageService();
259 if (empty($fieldConfig['items']) || !is_array($fieldConfig['items'])) {
260 $title = (bool)$value
261 ? $languageService->sL('LLL:EXT:lang/locallang_common.xlf:yes')
262 : $languageService->sL('LLL:EXT:lang/locallang_common.xlf:no');
263 } else {
264 $labelParts = [];
265 foreach ($fieldConfig['items'] as $key => $val) {
266 if ($value & pow(2, $key)) {
267 $labelParts[] = $val[0];
268 }
269 }
270 $title = implode(', ', $labelParts);
271 }
272 return $title;
273 }
274
275 /**
276 * Returns the record title for input fields
277 *
278 * @param mixed $value Current database value of this field
279 * @param array $fieldConfig TCA field configuration
280 * @return string
281 */
282 protected function getRecordTitleForInputType($value, $fieldConfig)
283 {
284 if (!isset($value)) {
285 return '';
286 }
287 $title = $value;
288 if (GeneralUtility::inList($fieldConfig['eval'], 'date')) {
289 if (isset($fieldConfig['dbType']) && $fieldConfig['dbType'] === 'date') {
290 $value = $value === '0000-00-00' ? 0 : (int)strtotime($value);
291 } else {
292 $value = (int)$value;
293 }
294 if (!empty($value)) {
295 $ageSuffix = '';
296 // Generate age suffix as long as not explicitly suppressed
297 if (!isset($fieldConfig['disableAgeDisplay']) || (bool)$fieldConfig['disableAgeDisplay'] === false) {
298 $ageDelta = $GLOBALS['EXEC_TIME'] - $value;
299 $calculatedAge = BackendUtility::calcAge(
300 abs($ageDelta),
301 $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')
302 );
303 $ageSuffix = ' (' . ($ageDelta > 0 ? '-' : '') . $calculatedAge . ')';
304 }
305 $title = BackendUtility::date($value) . $ageSuffix;
306 }
307 } elseif (GeneralUtility::inList($fieldConfig['eval'], 'time')) {
308 if (!empty($value)) {
309 $title = BackendUtility::time((int)$value, false);
310 }
311 } elseif (GeneralUtility::inList($fieldConfig['eval'], 'timesec')) {
312 if (!empty($value)) {
313 $title = BackendUtility::time((int)$value);
314 }
315 } elseif (GeneralUtility::inList($fieldConfig['eval'], 'datetime')) {
316 // Handle native date/time field
317 if (isset($fieldConfig['dbType']) && $fieldConfig['dbType'] === 'datetime') {
318 $value = $value === '0000-00-00 00:00:00' ? 0 : (int)strtotime($value);
319 } else {
320 $value = (int)$value;
321 }
322 if (!empty($value)) {
323 $title = BackendUtility::datetime($value);
324 }
325 }
326 return $title;
327 }
328
329 /**
330 * Returns the record title for text fields
331 *
332 * @param mixed $value Current database value of this field
333 * @return string
334 */
335 protected function getRecordTitleForTextType($value)
336 {
337 return trim(strip_tags($value));
338 }
339
340 /**
341 * @return DatabaseConnection
342 */
343 protected function getDatabaseConnection()
344 {
345 return $GLOBALS['TYPO3_DB'];
346 }
347
348 /**
349 * @return LanguageService
350 */
351 protected function getLanguageService()
352 {
353 return $GLOBALS['LANG'];
354 }
355 }