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