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