[TASK] Move LanguageService to EXT:core
[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\Localization\LanguageService;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
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(
162 $rawValue,
163 $result['processedTca']['columns'][$fieldName]['children']
164 );
165 break;
166 case 'select':
167 $recordTitle = $this->getRecordTitleForSelectType($rawValue, $fieldConfig);
168 break;
169 case 'group':
170 $recordTitle = $this->getRecordTitleForGroupType($rawValue, $fieldConfig);
171 break;
172 case 'check':
173 $recordTitle = $this->getRecordTitleForCheckboxType($rawValue, $fieldConfig);
174 break;
175 case 'input':
176 $recordTitle = $this->getRecordTitleForInputType($rawValue, $fieldConfig);
177 break;
178 case 'text':
179 $recordTitle = $this->getRecordTitleForTextType($rawValue);
180 break;
181 case 'flex':
182 // @todo: Check if and how a label could be generated from flex field data
183 default:
184
185 }
186
187 return $recordTitle;
188 }
189
190 /**
191 * Return the record title for radio fields
192 *
193 * @param mixed $value Current database value of this field
194 * @param array $fieldConfig TCA field configuration
195 * @return string
196 */
197 protected function getRecordTitleForRadioType($value, $fieldConfig)
198 {
199 if (!isset($fieldConfig['items']) || !is_array($fieldConfig['items'])) {
200 return '';
201 }
202 foreach ($fieldConfig['items'] as $item) {
203 list($itemLabel, $itemValue) = $item;
204 if ((string)$value === (string)$itemValue) {
205 return $itemLabel;
206 }
207 }
208 return '';
209 }
210
211 /**
212 * @param int $value
213 * @param array $children
214 * @return string
215 */
216 protected function getRecordTitleForInlineType($value, array $children)
217 {
218 foreach ($children as $child) {
219 if ((int)$value === $child['vanillaUid']) {
220 return $child['recordTitle'];
221 }
222 }
223
224 return '';
225 }
226
227 /**
228 * Return the record title for database records
229 *
230 * @param mixed $value Current database value of this field
231 * @param array $fieldConfig TCA field configuration
232 * @return string
233 */
234 protected function getRecordTitleForSelectType($value, $fieldConfig)
235 {
236 if (!is_array($value)) {
237 return '';
238 }
239 $labelParts = [];
240 foreach ($value as $itemValue) {
241 $itemKey = array_search($itemValue, array_column($fieldConfig['items'], 1));
242 if ($itemKey !== false) {
243 $labelParts[] = $fieldConfig['items'][$itemKey][0];
244 }
245 }
246 $title = implode(', ', $labelParts);
247 if (empty($title) && !empty($value)) {
248 $title = implode(', ', $value);
249 }
250 return $title;
251 }
252
253 /**
254 * Return the record title for database records
255 *
256 * @param mixed $value Current database value of this field
257 * @param array $fieldConfig TCA field configuration
258 * @return string
259 */
260 protected function getRecordTitleForGroupType($value, $fieldConfig)
261 {
262 $labelParts = [];
263 foreach ($value as $singleValue) {
264 if (isset($singleValue['uidOrPath'])) {
265 $labelParts[] = $singleValue['uidOrPath'];
266 } elseif (isset($singleValue['folder'])) {
267 $labelParts[] = $singleValue['folder'];
268 } else {
269 $labelParts[] = $singleValue['title'];
270 }
271 }
272 return implode(', ', $labelParts);
273 }
274
275 /**
276 * Returns the record title for checkbox 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 getRecordTitleForCheckboxType($value, $fieldConfig)
283 {
284 $languageService = $this->getLanguageService();
285 if (empty($fieldConfig['items']) || !is_array($fieldConfig['items'])) {
286 $title = (bool)$value
287 ? $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:yes')
288 : $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:no');
289 } else {
290 $labelParts = [];
291 foreach ($fieldConfig['items'] as $key => $val) {
292 if ($value & pow(2, $key)) {
293 $labelParts[] = $val[0];
294 }
295 }
296 $title = implode(', ', $labelParts);
297 }
298 return $title;
299 }
300
301 /**
302 * Returns the record title for input fields
303 *
304 * @param mixed $value Current database value of this field
305 * @param array $fieldConfig TCA field configuration
306 * @return string
307 */
308 protected function getRecordTitleForInputType($value, $fieldConfig)
309 {
310 if (!isset($value)) {
311 return '';
312 }
313 $title = $value;
314 if (GeneralUtility::inList($fieldConfig['eval'], 'date')) {
315 if (isset($fieldConfig['dbType']) && $fieldConfig['dbType'] === 'date') {
316 $value = $value === '0000-00-00' ? 0 : (int)strtotime($value);
317 } else {
318 $value = (int)$value;
319 }
320 if (!empty($value)) {
321 $ageSuffix = '';
322 // Generate age suffix as long as not explicitly suppressed
323 if (!isset($fieldConfig['disableAgeDisplay']) || (bool)$fieldConfig['disableAgeDisplay'] === false) {
324 $ageDelta = $GLOBALS['EXEC_TIME'] - $value;
325 $calculatedAge = BackendUtility::calcAge(
326 abs($ageDelta),
327 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
328 );
329 $ageSuffix = ' (' . ($ageDelta > 0 ? '-' : '') . $calculatedAge . ')';
330 }
331 $title = BackendUtility::date($value) . $ageSuffix;
332 }
333 } elseif (GeneralUtility::inList($fieldConfig['eval'], 'time')) {
334 if (!empty($value)) {
335 $title = BackendUtility::time((int)$value, false);
336 }
337 } elseif (GeneralUtility::inList($fieldConfig['eval'], 'timesec')) {
338 if (!empty($value)) {
339 $title = BackendUtility::time((int)$value);
340 }
341 } elseif (GeneralUtility::inList($fieldConfig['eval'], 'datetime')) {
342 // Handle native date/time field
343 if (isset($fieldConfig['dbType']) && $fieldConfig['dbType'] === 'datetime') {
344 $value = $value === '0000-00-00 00:00:00' ? 0 : (int)strtotime($value);
345 } else {
346 $value = (int)$value;
347 }
348 if (!empty($value)) {
349 $title = BackendUtility::datetime($value);
350 }
351 }
352 return $title;
353 }
354
355 /**
356 * Returns the record title for text fields
357 *
358 * @param mixed $value Current database value of this field
359 * @return string
360 */
361 protected function getRecordTitleForTextType($value)
362 {
363 return trim(strip_tags($value));
364 }
365
366 /**
367 * @return LanguageService
368 */
369 protected function getLanguageService()
370 {
371 return $GLOBALS['LANG'];
372 }
373 }