[!!!][FEATURE] Refactor and streamline click menu / context menu
[Packages/TYPO3.CMS.git] / typo3 / sysext / filelist / Classes / FileList.php
1 <?php
2 namespace TYPO3\CMS\Filelist;
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\Clipboard\Clipboard;
18 use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
19 use TYPO3\CMS\Backend\RecordList\AbstractRecordList;
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
22 use TYPO3\CMS\Core\Imaging\Icon;
23 use TYPO3\CMS\Core\Imaging\IconFactory;
24 use TYPO3\CMS\Core\Messaging\FlashMessage;
25 use TYPO3\CMS\Core\Messaging\FlashMessageService;
26 use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
27 use TYPO3\CMS\Core\Resource\File;
28 use TYPO3\CMS\Core\Resource\Folder;
29 use TYPO3\CMS\Core\Resource\FolderInterface;
30 use TYPO3\CMS\Core\Resource\InaccessibleFolder;
31 use TYPO3\CMS\Core\Resource\ProcessedFile;
32 use TYPO3\CMS\Core\Resource\ResourceFactory;
33 use TYPO3\CMS\Core\Resource\Utility\ListUtility;
34 use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
35 use TYPO3\CMS\Core\Utility\GeneralUtility;
36 use TYPO3\CMS\Core\Utility\MathUtility;
37 use TYPO3\CMS\Filelist\Controller\FileListController;
38
39 /**
40 * Class for rendering of File>Filelist
41 */
42 class FileList extends AbstractRecordList
43 {
44 /**
45 * Default Max items shown
46 *
47 * @var int
48 */
49 public $iLimit = 40;
50
51 /**
52 * Thumbnails on records containing files (pictures)
53 *
54 * @var bool
55 */
56 public $thumbs = false;
57
58 /**
59 * Space icon used for alignment when no button is available
60 *
61 * @var string
62 */
63 public $spaceIcon;
64
65 /**
66 * Max length of strings
67 *
68 * @var int
69 */
70 public $fixedL = 30;
71
72 /**
73 * If TRUE click menus are generated on files and folders
74 *
75 * @var bool
76 */
77 public $clickMenus = 1;
78
79 /**
80 * The field to sort by
81 *
82 * @var string
83 */
84 public $sort = '';
85
86 /**
87 * Reverse sorting flag
88 *
89 * @var bool
90 */
91 public $sortRev = 1;
92
93 /**
94 * @var int
95 */
96 public $firstElementNumber = 0;
97
98 /**
99 * @var bool
100 */
101 public $clipBoard = 0;
102
103 /**
104 * @var bool
105 */
106 public $bigControlPanel = 0;
107
108 /**
109 * @var string
110 */
111 public $JScode = '';
112
113 /**
114 * @var string
115 */
116 public $HTMLcode = '';
117
118 /**
119 * @var int
120 */
121 public $totalbytes = 0;
122
123 /**
124 * @var array
125 */
126 public $dirs = [];
127
128 /**
129 * @var array
130 */
131 public $files = [];
132
133 /**
134 * @var string
135 */
136 public $path = '';
137
138 /**
139 * @var Folder
140 */
141 protected $folderObject;
142
143 /**
144 * Counting the elements no matter what
145 *
146 * @var int
147 */
148 public $eCounter = 0;
149
150 /**
151 * @var string
152 */
153 public $totalItems = '';
154
155 /**
156 * @var array
157 */
158 public $CBnames = [];
159
160 /**
161 * @var Clipboard $clipObj
162 */
163 public $clipObj;
164
165 /**
166 * @var ResourceFactory
167 */
168 protected $resourceFactory;
169
170 /**
171 * @param ResourceFactory $resourceFactory
172 */
173 public function injectResourceFactory(ResourceFactory $resourceFactory)
174 {
175 $this->resourceFactory = $resourceFactory;
176 }
177
178 /**
179 * @var IconFactory
180 */
181 protected $iconFactory;
182
183 /**
184 * @var FileListController
185 */
186 protected $fileListController;
187
188 /**
189 * Construct
190 *
191 * @param FileListController $fileListController
192 */
193 public function __construct(FileListController $fileListController)
194 {
195 parent::__construct();
196 $this->fileListController = $fileListController;
197 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
198
199 $modTSconfig = BackendUtility::getModTSconfig(0, 'options.file_list');
200 if (!empty($modTSconfig['properties']['filesPerPage'])) {
201 $this->iLimit = MathUtility::forceIntegerInRange($modTSconfig['properties']['filesPerPage'], 1);
202 }
203 }
204
205 /**
206 * Initialization of class
207 *
208 * @param Folder $folderObject The folder to work on
209 * @param int $pointer Pointer
210 * @param bool $sort Sorting column
211 * @param bool $sortRev Sorting direction
212 * @param bool $clipBoard
213 * @param bool $bigControlPanel Show clipboard flag
214 * @return void
215 */
216 public function start(Folder $folderObject, $pointer, $sort, $sortRev, $clipBoard = false, $bigControlPanel = false)
217 {
218 $this->folderObject = $folderObject;
219 $this->counter = 0;
220 $this->totalbytes = 0;
221 $this->JScode = '';
222 $this->HTMLcode = '';
223 $this->path = $folderObject->getReadablePath();
224 $this->sort = $sort;
225 $this->sortRev = $sortRev;
226 $this->firstElementNumber = $pointer;
227 $this->clipBoard = $clipBoard;
228 $this->bigControlPanel = $bigControlPanel;
229 // Setting the maximum length of the filenames to the user's settings or minimum 30 (= $this->fixedL)
230 $this->fixedL = max($this->fixedL, $this->getBackendUser()->uc['titleLen']);
231 $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_common.xlf');
232 $this->resourceFactory = ResourceFactory::getInstance();
233 }
234
235 /**
236 * Reading files and directories, counting elements and generating the list in ->HTMLcode
237 *
238 * @return void
239 */
240 public function generateList()
241 {
242 $this->HTMLcode .= $this->getTable('fileext,tstamp,size,rw,_REF_');
243 }
244
245 /**
246 * Wrapping input string in a link with clipboard command.
247 *
248 * @param string $string String to be linked - must be htmlspecialchar'ed / prepared before.
249 * @param string $_ unused
250 * @param string $cmd "cmd" value
251 * @param string $warning Warning for JS confirm message
252 * @return string Linked string
253 */
254 public function linkClipboardHeaderIcon($string, $_, $cmd, $warning = '')
255 {
256 $jsCode = 'document.dblistForm.cmd.value=' . GeneralUtility::quoteJSvalue($cmd)
257 . ';document.dblistForm.submit();';
258
259 $attributes = [];
260 if ($warning) {
261 $attributes['class'] = 'btn btn-default t3js-modal-trigger';
262 $attributes['data-href'] = 'javascript:' . $jsCode;
263 $attributes['data-severity'] = 'warning';
264 $attributes['data-content'] = $warning;
265 } else {
266 $attributes['class'] = 'btn btn-default';
267 $attributes['onclick'] = $jsCode . 'return false;';
268 }
269
270 $attributesString = '';
271 foreach ($attributes as $key => $value) {
272 $attributesString .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
273 }
274 return '<a href="#" ' . $attributesString . '>' . $string . '</a>';
275 }
276
277 /**
278 * Returns a table with directories and files listed.
279 *
280 * @param array $rowlist Array of files from path
281 * @return string HTML-table
282 */
283 public function getTable($rowlist)
284 {
285 // prepare space icon
286 $this->spaceIcon = '<span class="btn btn-default disabled">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
287
288 // @todo use folder methods directly when they support filters
289 $storage = $this->folderObject->getStorage();
290 $storage->resetFileAndFolderNameFiltersToDefault();
291
292 // Only render the contents of a browsable storage
293 if ($this->folderObject->getStorage()->isBrowsable()) {
294 try {
295 $foldersCount = $storage->countFoldersInFolder($this->folderObject);
296 $filesCount = $storage->countFilesInFolder($this->folderObject);
297 } catch (InsufficientFolderAccessPermissionsException $e) {
298 $foldersCount = 0;
299 $filesCount = 0;
300 }
301
302 if ($foldersCount <= $this->firstElementNumber) {
303 $foldersFrom = false;
304 $foldersNum = false;
305 } else {
306 $foldersFrom = $this->firstElementNumber;
307 if ($this->firstElementNumber + $this->iLimit > $foldersCount) {
308 $foldersNum = $foldersCount - $this->firstElementNumber;
309 } else {
310 $foldersNum = $this->iLimit;
311 }
312 }
313 if ($foldersCount >= $this->firstElementNumber + $this->iLimit) {
314 $filesFrom = false;
315 $filesNum = false;
316 } else {
317 if ($this->firstElementNumber <= $foldersCount) {
318 $filesFrom = 0;
319 $filesNum = $this->iLimit - $foldersNum;
320 } else {
321 $filesFrom = $this->firstElementNumber - $foldersCount;
322 if ($filesFrom + $this->iLimit > $filesCount) {
323 $filesNum = $filesCount - $filesFrom;
324 } else {
325 $filesNum = $this->iLimit;
326 }
327 }
328 }
329
330 $folders = $storage->getFoldersInFolder($this->folderObject, $foldersFrom, $foldersNum, true, false, trim($this->sort), (bool)$this->sortRev);
331 $files = $this->folderObject->getFiles($filesFrom, $filesNum, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, false, trim($this->sort), (bool)$this->sortRev);
332 $this->totalItems = $foldersCount + $filesCount;
333 // Adds the code of files/dirs
334 $out = '';
335 $titleCol = 'file';
336 // Cleaning rowlist for duplicates and place the $titleCol as the first column always!
337 $rowlist = '_LOCALIZATION_,' . $rowlist;
338 $rowlist = GeneralUtility::rmFromList($titleCol, $rowlist);
339 $rowlist = GeneralUtility::uniqueList($rowlist);
340 $rowlist = $rowlist ? $titleCol . ',' . $rowlist : $titleCol;
341 if ($this->clipBoard) {
342 $rowlist = str_replace('_LOCALIZATION_,', '_LOCALIZATION_,_CLIPBOARD_,', $rowlist);
343 $this->addElement_tdCssClass['_CLIPBOARD_'] = 'col-clipboard';
344 }
345 if ($this->bigControlPanel) {
346 $rowlist = str_replace('_LOCALIZATION_,', '_LOCALIZATION_,_CONTROL_,', $rowlist);
347 $this->addElement_tdCssClass['_CONTROL_'] = 'col-control';
348 }
349 $this->fieldArray = explode(',', $rowlist);
350
351 // Add classes to table cells
352 $this->addElement_tdCssClass[$titleCol] = 'col-title col-responsive';
353 $this->addElement_tdCssClass['_LOCALIZATION_'] = 'col-localizationa';
354
355 $folders = ListUtility::resolveSpecialFolderNames($folders);
356
357 $iOut = '';
358 // Directories are added
359 $this->eCounter = $this->firstElementNumber;
360 list(, $code) = $this->fwd_rwd_nav();
361 $iOut .= $code;
362
363 $iOut .= $this->formatDirList($folders);
364 // Files are added
365 $iOut .= $this->formatFileList($files);
366
367 $this->eCounter = $this->firstElementNumber + $this->iLimit < $this->totalItems
368 ? $this->firstElementNumber + $this->iLimit
369 : -1;
370 list(, $code) = $this->fwd_rwd_nav();
371 $iOut .= $code;
372
373 // Header line is drawn
374 $theData = [];
375 foreach ($this->fieldArray as $v) {
376 if ($v === '_CLIPBOARD_' && $this->clipBoard) {
377 $cells = [];
378 $table = '_FILE';
379 $elFromTable = $this->clipObj->elFromTable($table);
380 if (!empty($elFromTable) && $this->folderObject->checkActionPermission('write')) {
381 $addPasteButton = true;
382 $elToConfirm = [];
383 foreach ($elFromTable as $key => $element) {
384 $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
385 if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $this->folderObject)) {
386 $addPasteButton = false;
387 }
388 $elToConfirm[$key] = $clipBoardElement->getName();
389 }
390 if ($addPasteButton) {
391 $cells[] = '<a class="btn btn-default t3js-modal-trigger"' .
392 ' href="' . htmlspecialchars($this->clipObj->pasteUrl(
393 '_FILE',
394 $this->folderObject->getCombinedIdentifier()
395 )) . '"'
396 . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText(
397 '_FILE',
398 $this->path,
399 'into',
400 $elToConfirm
401 )) . '"'
402 . ' data-severity="warning"'
403 . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_paste')) . '"'
404 . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_paste')) . '">'
405 . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)
406 ->render()
407 . '</a>';
408 }
409 }
410 if ($this->clipObj->current !== 'normal' && $iOut) {
411 $cells[] = $this->linkClipboardHeaderIcon('<span title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_selectMarked')) . '">' . $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render() . '</span>', $table, 'setCB');
412 $cells[] = $this->linkClipboardHeaderIcon('<span title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_deleteMarked')) . '">' . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render(), $table, 'delete', $this->getLanguageService()->getLL('clip_deleteMarkedWarning'));
413 $onClick = 'checkOffCB(' . GeneralUtility::quoteJSvalue(implode(',', $this->CBnames)) . ', this); return false;';
414 $cells[] = '<a class="btn btn-default" rel="" href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_markRecords')) . '">' . $this->iconFactory->getIcon('actions-document-select', Icon::SIZE_SMALL)->render() . '</a>';
415 }
416 $theData[$v] = implode('', $cells);
417 } else {
418 // Normal row:
419 $theT = $this->linkWrapSort(htmlspecialchars($this->getLanguageService()->getLL('c_' . $v)), $this->folderObject->getCombinedIdentifier(), $v);
420 $theData[$v] = $theT;
421 }
422 }
423
424 $out .= '<thead>' . $this->addElement(1, '', $theData, '', '', '', 'th') . '</thead>';
425 $out .= '<tbody>' . $iOut . '</tbody>';
426 // half line is drawn
427 // finish
428 $out = '
429 <!--
430 Filelist table:
431 -->
432 <div class="table-fit">
433 <table class="table table-striped table-hover" id="typo3-filelist">
434 ' . $out . '
435 </table>
436 </div>';
437 } else {
438 /** @var $flashMessage FlashMessage */
439 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('storageNotBrowsableMessage'), $this->getLanguageService()->getLL('storageNotBrowsableTitle'), FlashMessage::INFO);
440 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
441 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
442 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
443 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
444 $defaultFlashMessageQueue->enqueue($flashMessage);
445 $out = '';
446 }
447 return $out;
448 }
449
450 /**
451 * If there is a parent folder and user has access to it, return an icon
452 * which is linked to the filelist of the parent folder.
453 *
454 * @param Folder $currentFolder
455 * @return string
456 */
457 protected function getLinkToParentFolder(Folder $currentFolder)
458 {
459 $levelUp = '';
460 try {
461 $currentStorage = $currentFolder->getStorage();
462 $parentFolder = $currentFolder->getParentFolder();
463 if ($parentFolder->getIdentifier() !== $currentFolder->getIdentifier() && $currentStorage->isWithinFileMountBoundaries($parentFolder)) {
464 $levelUp = $this->linkWrapDir(
465 '<span title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.upOneLevel')) . '">'
466 . $this->iconFactory->getIcon('actions-view-go-up', Icon::SIZE_SMALL)->render()
467 . '</span>',
468 $parentFolder
469 );
470 }
471 } catch (\Exception $e) {
472 }
473 return $levelUp;
474 }
475
476 /**
477 * Gets the number of files and total size of a folder
478 *
479 * @return string
480 */
481 public function getFolderInfo()
482 {
483 if ($this->counter == 1) {
484 $fileLabel = htmlspecialchars($this->getLanguageService()->getLL('file'));
485 } else {
486 $fileLabel = htmlspecialchars($this->getLanguageService()->getLL('files'));
487 }
488 return $this->counter . ' ' . $fileLabel . ', ' . GeneralUtility::formatSize($this->totalbytes, htmlspecialchars($this->getLanguageService()->getLL('byteSizeUnits')));
489 }
490
491 /**
492 * This returns tablerows for the directories in the array $items['sorting'].
493 *
494 * @param Folder[] $folders Folders of \TYPO3\CMS\Core\Resource\Folder
495 * @return string HTML table rows.
496 */
497 public function formatDirList(array $folders)
498 {
499 $out = '';
500 foreach ($folders as $folderName => $folderObject) {
501 $role = $folderObject->getRole();
502 if ($role === FolderInterface::ROLE_PROCESSING) {
503 // don't show processing-folder
504 continue;
505 }
506 if ($role !== FolderInterface::ROLE_DEFAULT) {
507 $displayName = '<strong>' . htmlspecialchars($folderName) . '</strong>';
508 } else {
509 $displayName = htmlspecialchars($folderName);
510 }
511
512 $isLocked = $folderObject instanceof InaccessibleFolder;
513 $isWritable = $folderObject->checkActionPermission('write');
514
515 // Initialization
516 $this->counter++;
517
518 // The icon with link
519 $theIcon = '<span title="' . htmlspecialchars($folderName) . '">' . $this->iconFactory->getIconForResource($folderObject, Icon::SIZE_SMALL)->render() . '</span>';
520 if (!$isLocked && $this->clickMenus) {
521 $theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, 'sys_file', $folderObject->getCombinedIdentifier());
522 }
523
524 // Preparing and getting the data-array
525 $theData = [];
526 if ($isLocked) {
527 foreach ($this->fieldArray as $field) {
528 $theData[$field] = '';
529 }
530 $theData['file'] = $displayName;
531 } else {
532 foreach ($this->fieldArray as $field) {
533 switch ($field) {
534 case 'size':
535 try {
536 $numFiles = $folderObject->getFileCount();
537 } catch (InsufficientFolderAccessPermissionsException $e) {
538 $numFiles = 0;
539 }
540 $theData[$field] = $numFiles . ' ' . htmlspecialchars($this->getLanguageService()->getLL(($numFiles === 1 ? 'file' : 'files')));
541 break;
542 case 'rw':
543 $theData[$field] = '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('read')) . '</strong>' . (!$isWritable ? '' : '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('write')) . '</strong>');
544 break;
545 case 'fileext':
546 $theData[$field] = htmlspecialchars($this->getLanguageService()->getLL('folder'));
547 break;
548 case 'tstamp':
549 $tstamp = $folderObject->getModificationTime();
550 $theData[$field] = $tstamp ? BackendUtility::date($tstamp) : '-';
551 break;
552 case 'file':
553 $theData[$field] = $this->linkWrapDir($displayName, $folderObject);
554 break;
555 case '_CONTROL_':
556 $theData[$field] = $this->makeEdit($folderObject);
557 break;
558 case '_CLIPBOARD_':
559 $theData[$field] = $this->makeClip($folderObject);
560 break;
561 case '_REF_':
562 $theData[$field] = $this->makeRef($folderObject);
563 break;
564 default:
565 $theData[$field] = GeneralUtility::fixed_lgd_cs($theData[$field], $this->fixedL);
566 }
567 }
568 }
569 $out .= $this->addElement(1, $theIcon, $theData);
570 }
571 return $out;
572 }
573
574 /**
575 * Wraps the directory-titles
576 *
577 * @param string $title String to be wrapped in links
578 * @param Folder $folderObject Folder to work on
579 * @return string HTML
580 */
581 public function linkWrapDir($title, Folder $folderObject)
582 {
583 $href = BackendUtility::getModuleUrl('file_FilelistList', ['id' => $folderObject->getCombinedIdentifier()]);
584 $onclick = ' onclick="' . htmlspecialchars(('top.document.getElementsByName("navigation")[0].contentWindow.Tree.highlightActiveItem("file","folder' . GeneralUtility::md5int($folderObject->getCombinedIdentifier()) . '_"+top.fsMod.currentBank)')) . '"';
585 // Sometimes $code contains plain HTML tags. In such a case the string should not be modified!
586 if ((string)$title === strip_tags($title)) {
587 return '<a href="' . htmlspecialchars($href) . '"' . $onclick . ' title="' . htmlspecialchars($title) . '">' . $title . '</a>';
588 } else {
589 return '<a href="' . htmlspecialchars($href) . '"' . $onclick . '>' . $title . '</a>';
590 }
591 }
592
593 /**
594 * Wraps filenames in links which opens the metadata editor.
595 *
596 * @param string $code String to be wrapped in links
597 * @param File $fileObject File to be linked
598 * @return string HTML
599 */
600 public function linkWrapFile($code, File $fileObject)
601 {
602 try {
603 if ($fileObject instanceof File && $fileObject->isIndexed() && $fileObject->checkActionPermission('write') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
604 $metaData = $fileObject->_getMetaData();
605 $urlParameters = [
606 'edit' => [
607 'sys_file_metadata' => [
608 $metaData['uid'] => 'edit'
609 ]
610 ],
611 'returnUrl' => $this->listURL()
612 ];
613 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
614 $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.editMetadata'));
615 $code = '<a class="responsive-title" href="' . htmlspecialchars($url) . '" title="' . $title . '">' . $code . '</a>';
616 }
617 } catch (\Exception $e) {
618 // intentional fall-through
619 }
620 return $code;
621 }
622
623 /**
624 * Returns list URL; This is the URL of the current script with id and imagemode parameters, that's all.
625 * The URL however is not relative, otherwise GeneralUtility::sanitizeLocalUrl() would say that
626 * the URL would be invalid
627 *
628 * @param string $altId
629 * @param string $table Table name to display. Enter "-1" for the current table.
630 * @param string $exclList Comma separated list of fields NOT to include ("sortField", "sortRev" or "firstElementNumber")
631 *
632 * @return string URL
633 */
634 public function listURL($altId = '', $table = '-1', $exclList = '')
635 {
636 return GeneralUtility::linkThisScript([
637 'target' => rawurlencode($this->folderObject->getCombinedIdentifier()),
638 'imagemode' => $this->thumbs
639 ]);
640 }
641
642 /**
643 * This returns tablerows for the files in the array $items['sorting'].
644 *
645 * @param File[] $files File items
646 * @return string HTML table rows.
647 */
648 public function formatFileList(array $files)
649 {
650 $out = '';
651 // first two keys are "0" (default) and "-1" (multiple), after that comes the "other languages"
652 $allSystemLanguages = GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages();
653 $systemLanguages = array_filter($allSystemLanguages, function ($languageRecord) {
654 if ($languageRecord['uid'] === -1 || $languageRecord['uid'] === 0 || !$this->getBackendUser()->checkLanguageAccess($languageRecord['uid'])) {
655 return false;
656 } else {
657 return true;
658 }
659 });
660
661 foreach ($files as $fileObject) {
662 // Initialization
663 $this->counter++;
664 $this->totalbytes += $fileObject->getSize();
665 $ext = $fileObject->getExtension();
666 $fileName = trim($fileObject->getName());
667 // The icon with link
668 $theIcon = '<span title="' . htmlspecialchars($fileName . ' [' . (int)$fileObject->getUid() . ']') . '">'
669 . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
670 if ($this->clickMenus) {
671 $theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, 'sys_file', $fileObject->getCombinedIdentifier());
672 }
673 // Preparing and getting the data-array
674 $theData = [];
675 foreach ($this->fieldArray as $field) {
676 switch ($field) {
677 case 'size':
678 $theData[$field] = GeneralUtility::formatSize($fileObject->getSize(), htmlspecialchars($this->getLanguageService()->getLL('byteSizeUnits')));
679 break;
680 case 'rw':
681 $theData[$field] = '' . (!$fileObject->checkActionPermission('read') ? ' ' : '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('read')) . '</strong>') . (!$fileObject->checkActionPermission('write') ? '' : '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('write')) . '</strong>');
682 break;
683 case 'fileext':
684 $theData[$field] = strtoupper($ext);
685 break;
686 case 'tstamp':
687 $theData[$field] = BackendUtility::date($fileObject->getModificationTime());
688 break;
689 case '_CONTROL_':
690 $theData[$field] = $this->makeEdit($fileObject);
691 break;
692 case '_CLIPBOARD_':
693 $theData[$field] = $this->makeClip($fileObject);
694 break;
695 case '_LOCALIZATION_':
696 if (!empty($systemLanguages) && $fileObject->isIndexed() && $fileObject->checkActionPermission('write') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
697 $metaDataRecord = $fileObject->_getMetaData();
698 $translations = $this->getTranslationsForMetaData($metaDataRecord);
699 $languageCode = '';
700
701 foreach ($systemLanguages as $language) {
702 $languageId = $language['uid'];
703 $flagIcon = $language['flagIcon'];
704 if (array_key_exists($languageId, $translations)) {
705 $title = htmlspecialchars(sprintf($this->getLanguageService()->getLL('editMetadataForLanguage'), $language['title']));
706 // @todo the overlay for the flag needs to be added ($flagIcon . '-overlay')
707 $urlParameters = [
708 'edit' => [
709 'sys_file_metadata' => [
710 $translations[$languageId]['uid'] => 'edit'
711 ]
712 ],
713 'returnUrl' => $this->listURL()
714 ];
715 $flagButtonIcon = $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-edit')->render();
716 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
717 $languageCode .= '<a href="' . htmlspecialchars($url) . '" class="btn btn-default" title="' . $title . '">'
718 . $flagButtonIcon . '</a>';
719 } else {
720 $parameters = [
721 'justLocalized' => 'sys_file_metadata:' . $metaDataRecord['uid'] . ':' . $languageId,
722 'returnUrl' => $this->listURL()
723 ];
724 $returnUrl = BackendUtility::getModuleUrl('record_edit', $parameters);
725 $href = BackendUtility::getLinkToDataHandlerAction(
726 '&cmd[sys_file_metadata][' . $metaDataRecord['uid'] . '][localize]=' . $languageId,
727 $returnUrl
728 );
729 $flagButtonIcon = '<span title="' . htmlspecialchars(sprintf($this->getLanguageService()->getLL('createMetadataForLanguage'), $language['title'])) . '">' . $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-new')->render() . '</span>';
730 $languageCode .= '<a href="' . htmlspecialchars($href) . '" class="btn btn-default">' . $flagButtonIcon . '</a> ';
731 }
732 }
733
734 // Hide flag button bar when not translated yet
735 $theData[$field] = ' <div class="localisationData btn-group" data-fileid="' . $fileObject->getUid() . '"' .
736 (empty($translations) ? ' style="display: none;"' : '') . '>' . $languageCode . '</div>';
737 $theData[$field] .= '<a class="btn btn-default filelist-translationToggler" data-fileid="' . $fileObject->getUid() . '">' .
738 '<span title="' . htmlspecialchars($this->getLanguageService()->getLL('translateMetadata')) . '">'
739 . $this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL)->render() . '</span>'
740 . '</a>';
741 }
742 break;
743 case '_REF_':
744 $theData[$field] = $this->makeRef($fileObject);
745 break;
746 case 'file':
747 // Edit metadata of file
748 $theData[$field] = $this->linkWrapFile(htmlspecialchars($fileName), $fileObject);
749
750 if ($fileObject->isMissing()) {
751 $theData[$field] .= '<span class="label label-danger label-space-left">'
752 . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.file_missing'))
753 . '</span>';
754 // Thumbnails?
755 } elseif ($this->thumbs && ($this->isImage($ext) || $this->isMediaFile($ext))) {
756 $processedFile = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, []);
757 if ($processedFile) {
758 $thumbUrl = $processedFile->getPublicUrl(true);
759 $theData[$field] .= '<br /><img src="' . $thumbUrl . '" ' .
760 'width="' . $processedFile->getProperty('width') . '" ' .
761 'height="' . $processedFile->getProperty('height') . '" ' .
762 'title="' . htmlspecialchars($fileName) . '" alt="" />';
763 }
764 }
765 break;
766 default:
767 $theData[$field] = '';
768 if ($fileObject->hasProperty($field)) {
769 $theData[$field] = htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileObject->getProperty($field), $this->fixedL));
770 }
771 }
772 }
773 $out .= $this->addElement(1, $theIcon, $theData);
774 }
775 return $out;
776 }
777
778 /**
779 * Fetch the translations for a sys_file_metadata record
780 *
781 * @param $metaDataRecord
782 * @return array keys are the sys_language uids, values are the $rows
783 */
784 protected function getTranslationsForMetaData($metaDataRecord)
785 {
786 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_metadata');
787 $queryBuilder->getRestrictions()->removeAll();
788 $translationRecords = $queryBuilder->select('*')
789 ->from('sys_file_metadata')
790 ->where(
791 $queryBuilder->expr()->eq(
792 $GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'],
793 $queryBuilder->createNamedParameter($metaDataRecord['uid'], \PDO::PARAM_INT)
794 ),
795 $queryBuilder->expr()->gt(
796 $GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'],
797 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
798 )
799 )
800 ->execute()
801 ->fetchAll();
802
803 $translations = [];
804 foreach ($translationRecords as $record) {
805 $translations[$record[$GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField']]] = $record;
806 }
807 return $translations;
808 }
809
810 /**
811 * Returns TRUE if $ext is an image-extension according to $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
812 *
813 * @param string $ext File extension
814 * @return bool
815 */
816 public function isImage($ext)
817 {
818 return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($ext));
819 }
820
821 /**
822 * Returns TRUE if $ext is an media-extension according to $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']
823 *
824 * @param string $ext File extension
825 * @return bool
826 */
827 public function isMediaFile($ext)
828 {
829 return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'], strtolower($ext));
830 }
831
832 /**
833 * Wraps the directory-titles ($code) in a link to filelist/Modules/Filelist/index.php (id=$path) and sorting commands...
834 *
835 * @param string $code String to be wrapped
836 * @param string $folderIdentifier ID (path)
837 * @param string $col Sorting column
838 * @return string HTML
839 */
840 public function linkWrapSort($code, $folderIdentifier, $col)
841 {
842 $params = ['id' => $folderIdentifier, 'SET' => [ 'sort' => $col ]];
843
844 if ($this->sort === $col) {
845 // Check reverse sorting
846 $params['SET']['reverse'] = ($this->sortRev ? '0' : '1');
847 $sortArrow = $this->iconFactory->getIcon('status-status-sorting-light-' . ($this->sortRev ? 'desc' : 'asc'), Icon::SIZE_SMALL)->render();
848 } else {
849 $params['SET']['reverse'] = 0;
850 $sortArrow = '';
851 }
852 $href = BackendUtility::getModuleUrl('file_FilelistList', $params);
853 return '<a href="' . htmlspecialchars($href) . '">' . $code . ' ' . $sortArrow . '</a>';
854 }
855
856 /**
857 * Creates the clipboard control pad
858 *
859 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.
860 * @return string HTML-table
861 */
862 public function makeClip($fileOrFolderObject)
863 {
864 if (!$fileOrFolderObject->checkActionPermission('read')) {
865 return '';
866 }
867 $cells = [];
868 $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
869 $fullName = $fileOrFolderObject->getName();
870 $md5 = GeneralUtility::shortMD5($fullIdentifier);
871 // For normal clipboard, add copy/cut buttons:
872 if ($this->clipObj->current === 'normal') {
873 $isSel = $this->clipObj->isSelected('_FILE', $md5);
874 $copyTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.copy'));
875 $cutTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.cut'));
876 $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render();
877 $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render();
878
879 if ($isSel === 'copy') {
880 $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render();
881 } elseif ($isSel === 'cut') {
882 $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render();
883 }
884
885 $cells[] = '<a class="btn btn-default"" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 1, ($isSel === 'copy'))) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';
886 // we can only cut if file can be moved
887 if ($fileOrFolderObject->checkActionPermission('move')) {
888 $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 0, ($isSel === 'cut'))) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';
889 } else {
890 $cells[] = $this->spaceIcon;
891 }
892 } else {
893 // For numeric pads, add select checkboxes:
894 $n = '_FILE|' . $md5;
895 $this->CBnames[] = $n;
896 $checked = $this->clipObj->isSelected('_FILE', $md5) ? ' checked="checked"' : '';
897 $cells[] = '<input type="hidden" name="CBH[' . $n . ']" value="0" /><label class="btn btn-default btn-checkbox"><input type="checkbox" name="CBC[' . $n . ']" value="' . htmlspecialchars($fullIdentifier) . '" ' . $checked . ' /><span class="t3-icon fa"></span></label>';
898 }
899 // Display PASTE button, if directory:
900 $elFromTable = $this->clipObj->elFromTable('_FILE');
901 if ($fileOrFolderObject instanceof Folder && !empty($elFromTable) && $fileOrFolderObject->checkActionPermission('write')) {
902 $addPasteButton = true;
903 $elToConfirm = [];
904 foreach ($elFromTable as $key => $element) {
905 $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
906 if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $fileOrFolderObject)) {
907 $addPasteButton = false;
908 }
909 $elToConfirm[$key] = $clipBoardElement->getName();
910 }
911 if ($addPasteButton) {
912 $cells[] = '<a class="btn btn-default t3js-modal-trigger" '
913 . ' href="' . htmlspecialchars($this->clipObj->pasteUrl('_FILE', $fullIdentifier)) . '"'
914 . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText('_FILE', $fullName, 'into', $elToConfirm)) . '"'
915 . ' data-severity="warning"'
916 . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"'
917 . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"'
918 . '>'
919 . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render()
920 . '</a>';
921 }
922 }
923 // Compile items into a DIV-element:
924 return ' <div class="btn-group" role="group">' . implode('', $cells) . '</div>';
925 }
926
927 /**
928 * Creates the edit control section
929 *
930 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the edit control section for the listing.
931 * @return string HTML-table
932 */
933 public function makeEdit($fileOrFolderObject)
934 {
935 $cells = [];
936 $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
937
938 // Edit file content (if editable)
939 if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('write') && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], $fileOrFolderObject->getExtension())) {
940 $url = BackendUtility::getModuleUrl('file_edit', ['target' => $fullIdentifier]);
941 $editOnClick = 'top.list_frame.location.href=' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.list_frame.document.location.pathname+top.list_frame.document.location.search);return false;';
942 $cells['edit'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($editOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.editcontent') . '">'
943 . $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render()
944 . '</a>';
945 } else {
946 $cells['edit'] = $this->spaceIcon;
947 }
948 if ($fileOrFolderObject instanceof File) {
949 $fileUrl = $fileOrFolderObject->getPublicUrl(true);
950 if ($fileUrl) {
951 $aOnClick = 'return top.openUrlInWindow(' . GeneralUtility::quoteJSvalue($fileUrl) . ', \'WebFile\');';
952 $cells['view'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($aOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.view') . '">' . $this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL)->render() . '</a>';
953 } else {
954 $cells['view'] = $this->spaceIcon;
955 }
956 } else {
957 $cells['view'] = $this->spaceIcon;
958 }
959
960 // replace file
961 if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('replace')) {
962 $url = BackendUtility::getModuleUrl('file_replace', ['target' => $fullIdentifier, 'uid' => $fileOrFolderObject->getUid()]);
963 $replaceOnClick = 'top.list_frame.location.href = ' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.list_frame.document.location.pathname+top.list_frame.document.location.search);return false;';
964 $cells['replace'] = '<a href="#" class="btn btn-default" onclick="' . $replaceOnClick . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.replace') . '">' . $this->iconFactory->getIcon('actions-edit-replace', Icon::SIZE_SMALL)->render() . '</a>';
965 }
966
967 // rename the file
968 if ($fileOrFolderObject->checkActionPermission('rename')) {
969 $url = BackendUtility::getModuleUrl('file_rename', ['target' => $fullIdentifier]);
970 $renameOnClick = 'top.list_frame.location.href = ' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.list_frame.document.location.pathname+top.list_frame.document.location.search);return false;';
971 $cells['rename'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($renameOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.rename') . '">' . $this->iconFactory->getIcon('actions-edit-rename', Icon::SIZE_SMALL)->render() . '</a>';
972 } else {
973 $cells['rename'] = $this->spaceIcon;
974 }
975 if ($fileOrFolderObject->checkActionPermission('read')) {
976 $infoOnClick = '';
977 if ($fileOrFolderObject instanceof Folder) {
978 $infoOnClick = 'top.launchView( \'_FOLDER\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
979 } elseif ($fileOrFolderObject instanceof File) {
980 $infoOnClick = 'top.launchView( \'_FILE\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
981 }
982 $cells['info'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($infoOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.info') . '">' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . '</a>';
983 } else {
984 $cells['info'] = $this->spaceIcon;
985 }
986
987 // delete the file
988 if ($fileOrFolderObject->checkActionPermission('delete')) {
989 $identifier = $fileOrFolderObject->getIdentifier();
990 if ($fileOrFolderObject instanceof Folder) {
991 $referenceCountText = BackendUtility::referenceCount('_FILE', $identifier, ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.referencesToFolder'));
992 $deleteType = 'delete_folder';
993 } else {
994 $referenceCountText = BackendUtility::referenceCount('sys_file', $fileOrFolderObject->getUid(), ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.referencesToFile'));
995 $deleteType = 'delete_file';
996 }
997
998 if ($this->getBackendUser()->jsConfirmation(JsConfirmation::DELETE)) {
999 $confirmationCheck = '1';
1000 } else {
1001 $confirmationCheck = '0';
1002 }
1003
1004 $deleteUrl = BackendUtility::getModuleUrl('tce_file');
1005 $confirmationMessage = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.delete'), $fileOrFolderObject->getName()) . $referenceCountText;
1006 $title = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.delete');
1007 $cells['delete'] = '<a href="#" class="btn btn-default t3js-filelist-delete" data-content="' . htmlspecialchars($confirmationMessage)
1008 . '" data-check="' . $confirmationCheck
1009 . '" data-delete-url="' . htmlspecialchars($deleteUrl)
1010 . '" data-title="' . htmlspecialchars($title)
1011 . '" data-identifier="' . htmlspecialchars($fileOrFolderObject->getCombinedIdentifier())
1012 . '" data-delete-type="' . $deleteType
1013 . '" title="' . htmlspecialchars($title) . '">'
1014 . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() . '</a>';
1015 } else {
1016 $cells['delete'] = $this->spaceIcon;
1017 }
1018
1019 // Hook for manipulating edit icons.
1020 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'])) {
1021 $cells['__fileOrFolderObject'] = $fileOrFolderObject;
1022 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'] as $classData) {
1023 $hookObject = GeneralUtility::getUserObj($classData);
1024 if (!$hookObject instanceof FileListEditIconHookInterface) {
1025 throw new \UnexpectedValueException(
1026 $classData . ' must implement interface ' . FileListEditIconHookInterface::class,
1027 1235225797
1028 );
1029 }
1030 $hookObject->manipulateEditIcons($cells, $this);
1031 }
1032 unset($cells['__fileOrFolderObject']);
1033 }
1034 // Compile items into a DIV-element:
1035 return '<div class="btn-group">' . implode('', $cells) . '</div>';
1036 }
1037
1038 /**
1039 * Make reference count
1040 *
1041 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.
1042 * @return string HTML
1043 */
1044 public function makeRef($fileOrFolderObject)
1045 {
1046 if ($fileOrFolderObject instanceof FolderInterface) {
1047 return '-';
1048 }
1049 // Look up the file in the sys_refindex.
1050 // Exclude sys_file_metadata records as these are no use references
1051 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
1052 $referenceCount = $queryBuilder->count('*')
1053 ->from('sys_refindex')
1054 ->where(
1055 $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
1056 $queryBuilder->expr()->eq(
1057 'ref_table',
1058 $queryBuilder->createNamedParameter('sys_file', \PDO::PARAM_STR)
1059 ),
1060 $queryBuilder->expr()->eq(
1061 'ref_uid',
1062 $queryBuilder->createNamedParameter($fileOrFolderObject->getUid(), \PDO::PARAM_INT)
1063 ),
1064 $queryBuilder->expr()->neq(
1065 'tablename',
1066 $queryBuilder->createNamedParameter('sys_file_metadata', \PDO::PARAM_STR)
1067 )
1068 )
1069 ->execute()
1070 ->fetchColumn();
1071
1072 return $this->generateReferenceToolTip($referenceCount, '\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileOrFolderObject->getCombinedIdentifier()));
1073 }
1074
1075 /**
1076 * Returns an instance of LanguageService
1077 *
1078 * @return \TYPO3\CMS\Lang\LanguageService
1079 */
1080 protected function getLanguageService()
1081 {
1082 return $GLOBALS['LANG'];
1083 }
1084
1085 /**
1086 * Returns the current BE user.
1087 *
1088 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1089 */
1090 protected function getBackendUser()
1091 {
1092 return $GLOBALS['BE_USER'];
1093 }
1094 }