‪TYPO3CMS  ‪main
FileReferenceContainer.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
25 use TYPO3\CMS\Backend\Utility\BackendUtility;
29 use TYPO3\CMS\Core\Imaging\IconSize;
39 
50 {
51  public const ‪NODE_TYPE_IDENTIFIER = 'fileReferenceContainer';
52 
53  private const ‪FILE_REFERENCE_TABLE = 'sys_file_reference';
54  private const ‪FOREIGN_SELECTOR = 'uid_local';
55 
59  protected array ‪$fileReferenceData = [];
60 
61  public function ‪__construct(
62  private readonly ‪IconFactory $iconFactory,
63  private readonly ‪InlineStackProcessor $inlineStackProcessor,
64  private readonly EventDispatcherInterface $eventDispatcher,
65  private readonly ‪ResourceFactory $resourceFactory,
66  private readonly ‪ConnectionPool $connectionPool,
67  private readonly ‪UriBuilder $uriBuilder,
68  private readonly ‪MetaDataRepository $metaDataRepository,
69  ) {}
70 
71  public function ‪render(): array
72  {
73  $this->inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
74 
75  // Send a mapping information to the browser via JSON:
76  // e.g. data[<curTable>][<curId>][<curField>] => data-<pid>-<parentTable>-<parentId>-<parentField>-<curTable>-<curId>-<curField>
77  $formPrefix = $this->inlineStackProcessor->getCurrentStructureFormPrefix();
78  $domObjectId = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
79 
80  $this->fileReferenceData = $this->data['inlineData'];
81  $this->fileReferenceData['map'][$formPrefix] = $domObjectId;
82 
83  $resultArray = $this->‪initializeResultArray();
84  $resultArray['inlineData'] = ‪$this->fileReferenceData;
85 
86  $html = '';
87  $classes = [];
88  $combinationHtml = '';
89  ‪$record = $this->data['databaseRow'];
90  ‪$uid = ‪$record['uid'] ?? 0;
91  $appendFormFieldNames = '[' . self::FILE_REFERENCE_TABLE . '][' . ‪$uid . ']';
92  $objectId = $domObjectId . '-' . self::FILE_REFERENCE_TABLE . '-' . ‪$uid;
93  $isNewRecord = $this->data['command'] === 'new';
94  $hiddenFieldName = (string)($this->data['processedTca']['ctrl']['enablecolumns']['disabled'] ?? '');
95  if (!$this->data['isInlineDefaultLanguageRecordInLocalizedParentContext']) {
96  if ($isNewRecord || $this->data['isInlineChildExpanded']) {
97  ‪$fileReferenceData = $this->‪renderFileReference($this->data);
98  $html = ‪$fileReferenceData['html'];
99  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, ‪$fileReferenceData, false);
100  } else {
101  // This class is the marker for the JS-function to check if the full content has already been loaded
102  $classes[] = 't3js-not-loaded';
103  }
104  if ($isNewRecord) {
105  // Add pid of file reference as hidden field
106  $html .= '<input type="hidden" name="data' . htmlspecialchars($appendFormFieldNames)
107  . '[pid]" value="' . (int)‪$record['pid'] . '"/>';
108  // Tell DataHandler this file reference is expanded
109  $ucFieldName = 'uc[inlineView]'
110  . '[' . $this->data['inlineTopMostParentTableName'] . ']'
111  . '[' . $this->data['inlineTopMostParentUid'] . ']'
112  . htmlspecialchars($appendFormFieldNames);
113  $html .= '<input type="hidden" name="' . htmlspecialchars($ucFieldName)
114  . '" value="' . (int)$this->data['isInlineChildExpanded'] . '" />';
115  } else {
116  // Set additional field for processing for saving
117  $html .= '<input type="hidden" name="cmd' . htmlspecialchars($appendFormFieldNames)
118  . '[delete]" value="1" disabled="disabled" />';
119  if ($hiddenFieldName !== ''
120  && (!$this->data['isInlineChildExpanded']
121  || !in_array($hiddenFieldName, $this->data['columnsToProcess'], true))
122  ) {
123  $isHidden = (bool)(‪$record[$hiddenFieldName] ?? false);
124  $html .= '<input type="checkbox" class="d-none" data-formengine-input-name="data'
125  . htmlspecialchars($appendFormFieldNames)
126  . '[' . htmlspecialchars($hiddenFieldName) . ']" value="1"'
127  . ($isHidden ? ' checked="checked"' : '') . ' />';
128  $html .= '<input type="input" class="d-none" name="data' . htmlspecialchars($appendFormFieldNames)
129  . '[' . htmlspecialchars($hiddenFieldName) . ']" value="' . (int)$isHidden . '" />';
130  }
131  }
132  // If this file reference should be shown collapsed
133  $classes[] = $this->data['isInlineChildExpanded'] ? 'panel-visible' : 'panel-collapsed';
134  }
135  $hiddenFieldHtml = implode("\n", $resultArray['additionalHiddenFields'] ?? []);
136 
137  if ($this->data['inlineParentConfig']['renderFieldsOnly'] ?? false) {
138  // Render "body" part only
139  $resultArray['html'] = $html . $hiddenFieldHtml . $combinationHtml;
140  return $resultArray;
141  }
142 
143  // Render header row and content (if expanded)
144  if ($this->data['isInlineDefaultLanguageRecordInLocalizedParentContext']) {
145  $classes[] = 't3-form-field-container-inline-placeHolder';
146  }
147  if (‪$record[$hiddenFieldName] ?? false) {
148  $classes[] = 't3-form-field-container-inline-hidden';
149  }
150  if ($isNewRecord) {
151  $classes[] = 'isNewFileReference';
152  }
153 
154  // The hashed object id needs a non-numeric prefix, the value is used as ID selector in JavaScript
155  $hashedObjectId = 'hash-' . md5($objectId);
156  $containerAttributes = [
157  'id' => $objectId . '_div',
158  'class' => 'form-irre-object panel panel-default panel-condensed ' . trim(implode(' ', $classes)),
159  'data-object-uid' => ‪$record['uid'] ?? 0,
160  'data-object-id' => $objectId,
161  'data-object-id-hash' => $hashedObjectId,
162  'data-object-parent-group' => $domObjectId . '-' . ‪self::FILE_REFERENCE_TABLE,
163  'data-field-name' => $appendFormFieldNames,
164  'data-topmost-parent-table' => $this->data['inlineTopMostParentTableName'],
165  'data-topmost-parent-uid' => $this->data['inlineTopMostParentUid'],
166  'data-placeholder-record' => $this->data['isInlineDefaultLanguageRecordInLocalizedParentContext'] ? '1' : '0',
167  ];
168 
169  $ariaControls = htmlspecialchars($objectId . '_fields', ENT_QUOTES | ENT_HTML5);
170  $resultArray['html'] = '
171  <div ' . GeneralUtility::implodeAttributes($containerAttributes, true) . '>
172  <div class="panel-heading" data-bs-toggle="formengine-file" id="' . htmlspecialchars($hashedObjectId, ENT_QUOTES | ENT_HTML5) . '_header" data-expandSingle="' . (($this->data['inlineParentConfig']['appearance']['expandSingle'] ?? false) ? 1 : 0) . '">
173  <div class="form-irre-header">
174  <div class="form-irre-header-cell form-irre-header-icon">
175  <span class="caret"></span>
176  </div>
177  ' . $this->‪renderFileHeader('aria-expanded="' . (($this->data['isInlineChildExpanded'] ?? false) ? 'true' : 'false') . '" aria-controls="' . $ariaControls . '"') . '
178  </div>
179  </div>
180  <div class="panel-collapse" id="' . $ariaControls . '">' . $html . $hiddenFieldHtml . $combinationHtml . '</div>
181  </div>';
182 
183  return $resultArray;
184  }
185 
186  protected function ‪renderFileReference(array ‪$data): array
187  {
188  ‪$data['tabAndInlineStack'][] = [
189  'inline',
190  $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix(‪$data['inlineFirstPid'])
191  . '-'
192  . ‪$data['tableName']
193  . '-'
194  . ‪$data['databaseRow']['uid'],
195  ];
196 
197  return $this->nodeFactory->create(array_replace_recursive(‪$data, [
198  'inlineData' => $this->fileReferenceData,
199  'renderType' => 'fullRecordContainer',
200  ]))->render();
201  }
202 
207  protected function ‪renderFileHeader(string $ariaAttributesString): string
208  {
209  $languageService = $this->‪getLanguageService();
210 
211  $databaseRow = $this->data['databaseRow'];
212  $recordTitle = $this->‪getRecordTitle();
213 
214  if (empty($recordTitle)) {
215  $recordTitle = '<em>[' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title')) . ']</em>';
216  }
217 
218  $objectId = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid'])
219  . '-' . self::FILE_REFERENCE_TABLE
220  . '-' . ($databaseRow['uid'] ?? 0);
221 
222  $altText = BackendUtility::getRecordIconAltText($databaseRow, self::FILE_REFERENCE_TABLE, false);
223 
224  // Renders a thumbnail for the header
225  $thumbnail = '';
226  if (‪$GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails'] ?? false) {
227  $fileUid = $databaseRow[‪self::FOREIGN_SELECTOR][0]['uid'] ?? null;
228  if (!empty($fileUid)) {
229  try {
230  $fileObject = $this->resourceFactory->getFileObject($fileUid);
231  if ($fileObject->isMissing()) {
232  $thumbnail = '
233  <span class="badge badge-danger">'
234  . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.file_missing')) . '
235  </span>&nbsp;
236  ' . htmlspecialchars($fileObject->getName()) . '
237  <br />';
238  } elseif ($fileObject->isImage() || $fileObject->isMediaFile()) {
239  $imageSetup = $this->data['inlineParentConfig']['appearance']['headerThumbnail'] ?? [];
240  $cropVariantCollection = ‪CropVariantCollection::create($databaseRow['crop'] ?? '');
241  if (!$cropVariantCollection->getCropArea()->isEmpty()) {
242  $imageSetup['crop'] = $cropVariantCollection->getCropArea()->makeAbsoluteBasedOnFile($fileObject);
243  }
244  $processedImage = $fileObject->process(
246  array_merge(['maxWidth' => '145', 'maxHeight' => '45'], $imageSetup)
247  );
248  // Only use a thumbnail if the processing process was successful by checking if image width is set
249  if ($processedImage->getProperty('width')) {
250  $imageUrl = $processedImage->getPublicUrl() ?? '';
251  $thumbnail = '<img src="' . htmlspecialchars($imageUrl) . '" ' .
252  'width="' . $processedImage->getProperty('width') . '" ' .
253  'height="' . $processedImage->getProperty('height') . '" ' .
254  'alt="" ' .
255  'title="' . htmlspecialchars($altText) . '" ' .
256  'loading="lazy">';
257  }
258  }
259  } catch (\InvalidArgumentException $e) {
260  $fileObject = null;
261  }
262  }
263  }
264 
265  if ($thumbnail !== '') {
266  $headerImage = '
267  <div class="form-irre-header-thumbnail" id="' . $objectId . '_thumbnailcontainer">
268  ' . $thumbnail . '
269  </div>';
270  } else {
271  $headerImage = '
272  <div class="form-irre-header-icon" id="' . $objectId . '_iconcontainer">
273  ' . $this->iconFactory
274  ->getIconForRecord(self::FILE_REFERENCE_TABLE, $databaseRow, IconSize::SMALL)
275  ->setTitle($altText)
276  ->render() . '
277  </div>';
278  }
279 
280  // @todo check classes and change to dedicated file related ones if possible
281  return '
282  <button class="form-irre-header-cell form-irre-header-button" ' . $ariaAttributesString . '>
283  ' . $headerImage . '
284  <div class="form-irre-header-body">
285  <span id="' . $objectId . '_label">' . $recordTitle . '</span>
286  </div>
287  </button>
288  <div class="form-irre-header-cell form-irre-header-control t3js-formengine-file-header-control">
289  ' . $this->‪renderFileReferenceHeaderControl() . '
290  </div>';
291  }
292 
296  protected function ‪renderFileReferenceHeaderControl(): string
297  {
298  $controls = [];
299  $databaseRow = $this->data['databaseRow'];
300  $databaseRow += [
301  'uid' => 0,
302  ];
303  $parentConfig = $this->data['inlineParentConfig'];
304  $languageService = $this->‪getLanguageService();
305  $backendUser = $this->‪getBackendUserAuthentication();
306  $isNewItem = str_starts_with((string)$databaseRow['uid'], 'NEW');
307  $fileReferenceTableTca = ‪$GLOBALS['TCA'][‪self::FILE_REFERENCE_TABLE];
308  $calcPerms = new ‪Permission(
309  $backendUser->calcPerms(BackendUtility::readPageAccess(
310  (int)($this->data['parentPageRow']['uid'] ?? 0),
311  $backendUser->getPagePermsClause(‪Permission::PAGE_SHOW)
312  ))
313  );
314  $event = $this->eventDispatcher->dispatch(
315  new ‪ModifyFileReferenceEnabledControlsEvent($this->data, $databaseRow)
316  );
317  if ($this->data['isInlineDefaultLanguageRecordInLocalizedParentContext']) {
318  $controls['localize'] = $this->iconFactory
319  ->getIcon('actions-edit-localize-status-low', IconSize::SMALL)
320  ->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf:localize.isLocalizable'))
321  ->render();
322  }
323  if ($event->isControlEnabled('info')) {
324  if ($isNewItem) {
325  $controls['info'] = '
326  <span class="btn btn-default disabled">
327  ' . $this->iconFactory->getIcon('empty-empty', IconSize::SMALL)->render() . '
328  </span>';
329  } else {
330  $controls['info'] = '
331  <button type="button" class="btn btn-default" data-action="infowindow" data-info-table="' . htmlspecialchars('_FILE') . '" data-info-uid="' . (int)$databaseRow['uid_local'][0]['uid'] . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:showInfo')) . '">
332  ' . $this->iconFactory->getIcon('actions-document-info', IconSize::SMALL)->render() . '
333  </button>';
334  }
335  }
336  // If the table is NOT a read-only table, then show these links:
337  if (!($parentConfig['readOnly'] ?? false)
338  && !($fileReferenceTableTca['ctrl']['readOnly'] ?? false)
339  && !($this->data['isInlineDefaultLanguageRecordInLocalizedParentContext'] ?? false)
340  ) {
341  if ($event->isControlEnabled('sort')) {
342  $icon = 'actions-move-up';
343  $class = '';
344  if ((int)$parentConfig['inline']['first'] === (int)$databaseRow['uid']) {
345  $class = ' disabled';
346  $icon = 'empty-empty';
347  }
348  $controls['sort.up'] = '
349  <button type="button" class="btn btn-default' . $class . '" data-action="sort" data-direction="up" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:moveUp')) . '">
350  ' . $this->iconFactory->getIcon($icon, IconSize::SMALL)->render() . '
351  </button>';
352 
353  $icon = 'actions-move-down';
354  $class = '';
355  if ((int)$parentConfig['inline']['last'] === (int)$databaseRow['uid']) {
356  $class = ' disabled';
357  $icon = 'empty-empty';
358  }
359  $controls['sort.down'] = '
360  <button type="button" class="btn btn-default' . $class . '" data-action="sort" data-direction="down" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:moveDown')) . '">
361  ' . $this->iconFactory->getIcon($icon, IconSize::SMALL)->render() . '
362  </button>';
363  }
364  if (!$isNewItem
365  && ($languageField = (‪$GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'] ?? false))
366  && $backendUser->check('tables_modify', 'sys_file_metadata')
367  && $event->isControlEnabled('edit')
368  ) {
369  $languageId = (int)(is_array($databaseRow[$languageField] ?? null)
370  ? ($databaseRow[$languageField][0] ?? 0)
371  : ($databaseRow[$languageField] ?? 0));
372  $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_file_metadata');
373  $metadataRecord = $queryBuilder
374  ->select('uid')
375  ->from('sys_file_metadata')
376  ->where(
377  $queryBuilder->expr()->eq(
378  'file',
379  $queryBuilder->createNamedParameter((int)$databaseRow['uid_local'][0]['uid'], ‪Connection::PARAM_INT)
380  ),
381  $queryBuilder->expr()->eq(
382  $languageField,
383  $queryBuilder->createNamedParameter($languageId, ‪Connection::PARAM_INT)
384  )
385  )
386  ->setMaxResults(1)
387  ->executeQuery()
388  ->fetchAssociative();
389  if (!empty($metadataRecord)) {
390  ‪$url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', [
391  'edit[sys_file_metadata][' . (int)$metadataRecord['uid'] . ']' => 'edit',
392  'returnUrl' => $this->data['returnUrl'],
393  ]);
394  $controls['edit'] = '
395  <a class="btn btn-default" href="' . htmlspecialchars(‪$url) . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.editMetadata')) . '">
396  ' . $this->iconFactory->getIcon('actions-open', IconSize::SMALL)->render() . '
397  </a>';
398  }
399  }
400  if ($event->isControlEnabled('delete') && $calcPerms->editContentPermissionIsGranted()) {
401  $recordInfo = $this->data['databaseRow']['uid_local'][0]['title'] ?? $this->data['recordTitle'] ?? '';
402  if ($this->‪getBackendUserAuthentication()->shallDisplayDebugInformation()) {
403  $recordInfo .= ' [' . $this->data['tableName'] . ':' . $this->data['vanillaUid'] . ']';
404  }
405  $controls['delete'] = '
406  <button type="button" class="btn btn-default t3js-editform-delete-file-reference" data-record-info="' . htmlspecialchars(trim($recordInfo)) . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:delete')) . '">
407  ' . $this->iconFactory->getIcon('actions-edit-delete', IconSize::SMALL)->render() . '
408  </button>';
409  }
410  if (($hiddenField = (string)($fileReferenceTableTca['ctrl']['enablecolumns']['disabled'] ?? '')) !== ''
411  && ($fileReferenceTableTca['columns'][$hiddenField] ?? false)
412  && $event->isControlEnabled('hide')
413  && (
414  !($fileReferenceTableTca['columns'][$hiddenField]['exclude'] ?? false)
415  || $backendUser->check('non_exclude_fields', self::FILE_REFERENCE_TABLE . ':' . $hiddenField)
416  )
417  ) {
418  if ($databaseRow[$hiddenField] ?? false) {
419  $controls['hide'] = '
420  <button type="button" class="btn btn-default t3js-toggle-visibility-button" data-hidden-field="' . htmlspecialchars($hiddenField) . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:unHide')) . '">
421  ' . $this->iconFactory->getIcon('actions-edit-unhide', IconSize::SMALL)->render() . '
422  </button>';
423  } else {
424  $controls['hide'] = '
425  <button type="button" class="btn btn-default t3js-toggle-visibility-button" data-hidden-field="' . htmlspecialchars($hiddenField) . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:hide')) . '">
426  ' . $this->iconFactory->getIcon('actions-edit-hide', IconSize::SMALL)->render() . '
427  </button>';
428  }
429  }
430  if (($parentConfig['appearance']['useSortable'] ?? false) && $event->isControlEnabled('dragdrop')) {
431  $controls['dragdrop'] = '
432  <span class="btn btn-default sortableHandle" data-id="' . (int)$databaseRow['uid'] . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move')) . '">
433  ' . $this->iconFactory->getIcon('actions-move-move', IconSize::SMALL)->render() . '
434  </span>';
435  }
436  } elseif (($this->data['isInlineDefaultLanguageRecordInLocalizedParentContext'] ?? false)
437  && ‪MathUtility::canBeInterpretedAsInteger($this->data['inlineParentUid'])
438  && $event->isControlEnabled('localize')
439  ) {
440  $controls['localize'] = '
441  <button type="button" class="btn btn-default t3js-synchronizelocalize-button" data-type="' . htmlspecialchars((string)$databaseRow['uid']) . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf:localize')) . '">
442  ' . $this->iconFactory->getIcon('actions-document-localize', IconSize::SMALL)->render() . '
443  </button>';
444  }
445  if ($lockInfo = BackendUtility::isRecordLocked(self::FILE_REFERENCE_TABLE, $databaseRow['uid'])) {
446  $controls['locked'] = '
447  <button type="button" class="btn btn-default" title="' . htmlspecialchars($lockInfo['msg']) . '">
448  ' . $this->iconFactory->getIcon('status-user-backend', IconSize::SMALL, 'overlay-edit')->render() . '
449  </button>';
450  }
451 
452  // Get modified controls. This means their markup was modified, new controls were added or controls got removed.
453  $controls = $this->eventDispatcher->dispatch(
454  new ‪ModifyFileReferenceControlsEvent($controls, $this->data, $databaseRow)
455  )->getControls();
456 
457  $out = '';
458  if (($controls['edit'] ?? false) || ($controls['hide'] ?? false) || ($controls['delete'] ?? false)) {
459  $out .= '
460  <div class="btn-group btn-group-sm" role="group">
461  ' . ($controls['edit'] ?? '') . ($controls['hide'] ?? '') . ($controls['delete'] ?? '') . '
462  </div>';
463  unset($controls['edit'], $controls['hide'], $controls['delete']);
464  }
465  if (($controls['info'] ?? false) || ($controls['new'] ?? false) || ($controls['sort.up'] ?? false) || ($controls['sort.down'] ?? false) || ($controls['dragdrop'] ?? false)) {
466  $out .= '
467  <div class="btn-group btn-group-sm" role="group">
468  ' . ($controls['info'] ?? '') . ($controls['new'] ?? '') . ($controls['sort.up'] ?? '') . ($controls['sort.down'] ?? '') . ($controls['dragdrop'] ?? '') . '
469  </div>';
470  unset($controls['info'], $controls['new'], $controls['sort.up'], $controls['sort.down'], $controls['dragdrop']);
471  }
472  if ($controls['localize'] ?? false) {
473  $out .= '<div class="btn-group btn-group-sm" role="group">' . $controls['localize'] . '</div>';
474  unset($controls['localize']);
475  }
476  if ($controls !== [] && ($remainingControls = trim(implode('', $controls))) !== '') {
477  $out .= '<div class="btn-group btn-group-sm" role="group">' . $remainingControls . '</div>';
478  }
479  return $out;
480  }
481 
482  protected function ‪getRecordTitle(): string
483  {
484  $databaseRow = $this->data['databaseRow'];
485  $fileRecord = $databaseRow['uid_local'][0]['row'] ?? null;
486 
487  if ($fileRecord === null) {
488  return $this->data['recordTitle'] ?: (string)$databaseRow['uid'];
489  }
490 
491  $value = '';
492 
493  $recordTitle = $this->‪getTitleForRecord($databaseRow, $fileRecord);
494  $recordName = $this->‪getLabelFieldForRecord($databaseRow, $fileRecord, 'name');
495 
496  $labelField = !empty($recordTitle) ? 'title' : 'name';
497 
498  if (!empty($recordTitle)) {
499  $value .= $recordTitle . ' (' . $recordName . ')';
500  } else {
501  $value .= $recordName;
502  }
503 
504  $title = '
505  <dt class="col">
506  ' . htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file.' . $labelField)) . '
507  </dt>
508  <dd class="col text-truncate">
509  ' . $value . '
510  </dd>';
511 
512  // In debug mode, add the table name to the record title
513  if ($this->‪getBackendUserAuthentication()->shallDisplayDebugInformation()) {
514  $title .= '<div class="col"><code class="m-0">[' . self::FILE_REFERENCE_TABLE . ']</code></div>';
515  }
516 
517  return '<dl class="row row-cols-auto gx-2">' . $title . '</dl>';
518  }
519 
520  protected function ‪getTitleForRecord(array $databaseRow, array $fileRecord): string
521  {
522  $fullTitle = '';
523  if (isset($databaseRow['title'])) {
524  $fullTitle = $databaseRow['title'];
525  } elseif ($fileRecord['uid'] ?? false) {
526  try {
527  $metaData = $this->metaDataRepository->findByFileUid($fileRecord['uid']);
528  $fullTitle = $metaData['title'] ?? '';
529  } catch (‪InvalidUidException $e) {
530  }
531  }
532 
533  if ($fullTitle === '') {
534  return '';
535  }
536 
537  return BackendUtility::getRecordTitlePrep($fullTitle);
538  }
539 
540  protected function ‪getLabelFieldForRecord(array $databaseRow, array $fileRecord, string $field): string
541  {
542  $value = '';
543 
544  if (isset($databaseRow[$field])) {
545  $value = htmlspecialchars((string)$databaseRow[$field]);
546  } elseif (isset($fileRecord[$field])) {
547  $value = BackendUtility::getRecordTitlePrep($fileRecord[$field]);
548  }
549 
550  return $value;
551  }
552 
554  {
555  return ‪$GLOBALS['LANG'];
556  }
557 }
‪TYPO3\CMS\Core\Resource\Index\MetaDataRepository
Definition: MetaDataRepository.php:40
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:104
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\FILE_REFERENCE_TABLE
‪const FILE_REFERENCE_TABLE
Definition: FileReferenceContainer.php:53
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection\create
‪static create(string $jsonString, array $tcaConfig=[])
Definition: CropVariantCollection.php:37
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\$fileReferenceData
‪array $fileReferenceData
Definition: FileReferenceContainer.php:59
‪TYPO3\CMS\Core\Resource\ProcessedFile\CONTEXT_IMAGECROPSCALEMASK
‪const CONTEXT_IMAGECROPSCALEMASK
Definition: ProcessedFile.php:61
‪TYPO3\CMS\Backend\Form\Container
Definition: AbstractContainer.php:16
‪TYPO3\CMS\Backend\Form\Event\ModifyFileReferenceControlsEvent
Definition: ModifyFileReferenceControlsEvent.php:25
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\getLabelFieldForRecord
‪getLabelFieldForRecord(array $databaseRow, array $fileRecord, string $field)
Definition: FileReferenceContainer.php:540
‪TYPO3\CMS\Core\Resource\Exception\InvalidUidException
Definition: InvalidUidException.php:23
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\FOREIGN_SELECTOR
‪const FOREIGN_SELECTOR
Definition: FileReferenceContainer.php:54
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer
Definition: FileReferenceContainer.php:50
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\renderFileHeader
‪renderFileHeader(string $ariaAttributesString)
Definition: FileReferenceContainer.php:207
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\renderFileReferenceHeaderControl
‪renderFileReferenceHeaderControl()
Definition: FileReferenceContainer.php:296
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\getTitleForRecord
‪getTitleForRecord(array $databaseRow, array $fileRecord)
Definition: FileReferenceContainer.php:520
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\render
‪render()
Definition: FileReferenceContainer.php:71
‪TYPO3\CMS\Backend\Form\Container\AbstractContainer\getBackendUserAuthentication
‪getBackendUserAuthentication()
Definition: AbstractContainer.php:149
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Backend\Form\AbstractNode\$data
‪array $data
Definition: AbstractNode.php:35
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:42
‪TYPO3\CMS\Backend\Form\Container\AbstractContainer
Definition: AbstractContainer.php:29
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:47
‪TYPO3\CMS\Backend\Form\Event\ModifyFileReferenceEnabledControlsEvent
Definition: ModifyFileReferenceEnabledControlsEvent.php:24
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\NODE_TYPE_IDENTIFIER
‪const NODE_TYPE_IDENTIFIER
Definition: FileReferenceContainer.php:51
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection
Definition: CropVariantCollection.php:23
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Backend\Form\InlineStackProcessor
Definition: InlineStackProcessor.php:32
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\renderFileReference
‪renderFileReference(array $data)
Definition: FileReferenceContainer.php:186
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\getRecordTitle
‪getRecordTitle()
Definition: FileReferenceContainer.php:482
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\getLanguageService
‪getLanguageService()
Definition: FileReferenceContainer.php:553
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪initializeResultArray()
Definition: AbstractNode.php:77
‪TYPO3\CMS\Backend\Form\Container\FileReferenceContainer\__construct
‪__construct(private readonly IconFactory $iconFactory, private readonly InlineStackProcessor $inlineStackProcessor, private readonly EventDispatcherInterface $eventDispatcher, private readonly ResourceFactory $resourceFactory, private readonly ConnectionPool $connectionPool, private readonly UriBuilder $uriBuilder, private readonly MetaDataRepository $metaDataRepository,)
Definition: FileReferenceContainer.php:61