[BUGFIX] Streamline iframe namings for new backend
[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/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/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, $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/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, $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/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($GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'], (int)$metaDataRecord['uid']),
792 $queryBuilder->expr()->gt($GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'], 0)
793 )
794 ->execute()
795 ->fetchAll();
796
797 $translations = [];
798 foreach ($translationRecords as $record) {
799 $translations[$record[$GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField']]] = $record;
800 }
801 return $translations;
802 }
803
804 /**
805 * Returns TRUE if $ext is an image-extension according to $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
806 *
807 * @param string $ext File extension
808 * @return bool
809 */
810 public function isImage($ext)
811 {
812 return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($ext));
813 }
814
815 /**
816 * Returns TRUE if $ext is an media-extension according to $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']
817 *
818 * @param string $ext File extension
819 * @return bool
820 */
821 public function isMediaFile($ext)
822 {
823 return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'], strtolower($ext));
824 }
825
826 /**
827 * Wraps the directory-titles ($code) in a link to filelist/Modules/Filelist/index.php (id=$path) and sorting commands...
828 *
829 * @param string $code String to be wrapped
830 * @param string $folderIdentifier ID (path)
831 * @param string $col Sorting column
832 * @return string HTML
833 */
834 public function linkWrapSort($code, $folderIdentifier, $col)
835 {
836 $params = ['id' => $folderIdentifier, 'SET' => [ 'sort' => $col ]];
837
838 if ($this->sort === $col) {
839 // Check reverse sorting
840 $params['SET']['reverse'] = ($this->sortRev ? '0' : '1');
841 $sortArrow = $this->iconFactory->getIcon('status-status-sorting-light-' . ($this->sortRev ? 'desc' : 'asc'), Icon::SIZE_SMALL)->render();
842 } else {
843 $params['SET']['reverse'] = 0;
844 $sortArrow = '';
845 }
846 $href = BackendUtility::getModuleUrl('file_FilelistList', $params);
847 return '<a href="' . htmlspecialchars($href) . '">' . $code . ' ' . $sortArrow . '</a>';
848 }
849
850 /**
851 * Creates the clipboard control pad
852 *
853 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.
854 * @return string HTML-table
855 */
856 public function makeClip($fileOrFolderObject)
857 {
858 if (!$fileOrFolderObject->checkActionPermission('read')) {
859 return '';
860 }
861 $cells = [];
862 $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
863 $fullName = $fileOrFolderObject->getName();
864 $md5 = GeneralUtility::shortMD5($fullIdentifier);
865 // For normal clipboard, add copy/cut buttons:
866 if ($this->clipObj->current === 'normal') {
867 $isSel = $this->clipObj->isSelected('_FILE', $md5);
868 $copyTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.copy'));
869 $cutTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.cut'));
870 $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render();
871 $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render();
872
873 if ($isSel === 'copy') {
874 $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render();
875 } elseif ($isSel === 'cut') {
876 $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render();
877 }
878
879 $cells[] = '<a class="btn btn-default"" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 1, ($isSel === 'copy'))) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';
880 // we can only cut if file can be moved
881 if ($fileOrFolderObject->checkActionPermission('move')) {
882 $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 0, ($isSel === 'cut'))) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';
883 } else {
884 $cells[] = $this->spaceIcon;
885 }
886 } else {
887 // For numeric pads, add select checkboxes:
888 $n = '_FILE|' . $md5;
889 $this->CBnames[] = $n;
890 $checked = $this->clipObj->isSelected('_FILE', $md5) ? ' checked="checked"' : '';
891 $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>';
892 }
893 // Display PASTE button, if directory:
894 $elFromTable = $this->clipObj->elFromTable('_FILE');
895 if ($fileOrFolderObject instanceof Folder && !empty($elFromTable) && $fileOrFolderObject->checkActionPermission('write')) {
896 $addPasteButton = true;
897 $elToConfirm = [];
898 foreach ($elFromTable as $key => $element) {
899 $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
900 if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $fileOrFolderObject)) {
901 $addPasteButton = false;
902 }
903 $elToConfirm[$key] = $clipBoardElement->getName();
904 }
905 if ($addPasteButton) {
906 $cells[] = '<a class="btn btn-default t3js-modal-trigger" '
907 . ' href="' . htmlspecialchars($this->clipObj->pasteUrl('_FILE', $fullIdentifier)) . '"'
908 . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText('_FILE', $fullName, 'into', $elToConfirm)) . '"'
909 . ' data-severity="warning"'
910 . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"'
911 . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"'
912 . '>'
913 . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render()
914 . '</a>';
915 }
916 }
917 // Compile items into a DIV-element:
918 return ' <div class="btn-group" role="group">' . implode('', $cells) . '</div>';
919 }
920
921 /**
922 * Creates the edit control section
923 *
924 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the edit control section for the listing.
925 * @return string HTML-table
926 */
927 public function makeEdit($fileOrFolderObject)
928 {
929 $cells = [];
930 $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
931
932 // Edit file content (if editable)
933 if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('write') && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], $fileOrFolderObject->getExtension())) {
934 $url = BackendUtility::getModuleUrl('file_edit', ['target' => $fullIdentifier]);
935 $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;';
936 $cells['edit'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($editOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.editcontent') . '">'
937 . $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render()
938 . '</a>';
939 } else {
940 $cells['edit'] = $this->spaceIcon;
941 }
942 if ($fileOrFolderObject instanceof File) {
943 $fileUrl = $fileOrFolderObject->getPublicUrl(true);
944 if ($fileUrl) {
945 $aOnClick = 'return top.openUrlInWindow(' . GeneralUtility::quoteJSvalue($fileUrl) . ', \'WebFile\');';
946 $cells['view'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($aOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.view') . '">' . $this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL)->render() . '</a>';
947 } else {
948 $cells['view'] = $this->spaceIcon;
949 }
950 } else {
951 $cells['view'] = $this->spaceIcon;
952 }
953
954 // replace file
955 if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('replace')) {
956 $url = BackendUtility::getModuleUrl('file_replace', ['target' => $fullIdentifier, 'uid' => $fileOrFolderObject->getUid()]);
957 $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;';
958 $cells['replace'] = '<a href="#" class="btn btn-default" onclick="' . $replaceOnClick . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.replace') . '">' . $this->iconFactory->getIcon('actions-edit-replace', Icon::SIZE_SMALL)->render() . '</a>';
959 }
960
961 // rename the file
962 if ($fileOrFolderObject->checkActionPermission('rename')) {
963 $url = BackendUtility::getModuleUrl('file_rename', ['target' => $fullIdentifier]);
964 $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;';
965 $cells['rename'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($renameOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.rename') . '">' . $this->iconFactory->getIcon('actions-edit-rename', Icon::SIZE_SMALL)->render() . '</a>';
966 } else {
967 $cells['rename'] = $this->spaceIcon;
968 }
969 if ($fileOrFolderObject->checkActionPermission('read')) {
970 $infoOnClick = '';
971 if ($fileOrFolderObject instanceof Folder) {
972 $infoOnClick = 'top.launchView( \'_FOLDER\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
973 } elseif ($fileOrFolderObject instanceof File) {
974 $infoOnClick = 'top.launchView( \'_FILE\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
975 }
976 $cells['info'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($infoOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.info') . '">' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . '</a>';
977 } else {
978 $cells['info'] = $this->spaceIcon;
979 }
980
981 // delete the file
982 if ($fileOrFolderObject->checkActionPermission('delete')) {
983 $identifier = $fileOrFolderObject->getIdentifier();
984 if ($fileOrFolderObject instanceof Folder) {
985 $referenceCountText = BackendUtility::referenceCount('_FILE', $identifier, ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.referencesToFolder'));
986 $deleteType = 'delete_folder';
987 } else {
988 $referenceCountText = BackendUtility::referenceCount('sys_file', $fileOrFolderObject->getUid(), ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.referencesToFile'));
989 $deleteType = 'delete_file';
990 }
991
992 if ($this->getBackendUser()->jsConfirmation(JsConfirmation::DELETE)) {
993 $confirmationCheck = '1';
994 } else {
995 $confirmationCheck = '0';
996 }
997
998 $deleteUrl = BackendUtility::getModuleUrl('tce_file');
999 $confirmationMessage = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:mess.delete'), $fileOrFolderObject->getName()) . $referenceCountText;
1000 $title = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.delete');
1001 $cells['delete'] = '<a href="#" class="btn btn-default t3js-filelist-delete" data-content="' . htmlspecialchars($confirmationMessage)
1002 . '" data-check="' . $confirmationCheck
1003 . '" data-delete-url="' . htmlspecialchars($deleteUrl)
1004 . '" data-title="' . htmlspecialchars($title)
1005 . '" data-identifier="' . htmlspecialchars($fileOrFolderObject->getCombinedIdentifier())
1006 . '" data-veri-code="' . $this->getBackendUser()->veriCode()
1007 . '" data-delete-type="' . $deleteType
1008 . '" title="' . htmlspecialchars($title) . '">'
1009 . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() . '</a>';
1010 } else {
1011 $cells['delete'] = $this->spaceIcon;
1012 }
1013
1014 // Hook for manipulating edit icons.
1015 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'])) {
1016 $cells['__fileOrFolderObject'] = $fileOrFolderObject;
1017 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'] as $classData) {
1018 $hookObject = GeneralUtility::getUserObj($classData);
1019 if (!$hookObject instanceof FileListEditIconHookInterface) {
1020 throw new \UnexpectedValueException(
1021 $classData . ' must implement interface ' . FileListEditIconHookInterface::class,
1022 1235225797
1023 );
1024 }
1025 $hookObject->manipulateEditIcons($cells, $this);
1026 }
1027 unset($cells['__fileOrFolderObject']);
1028 }
1029 // Compile items into a DIV-element:
1030 return '<div class="btn-group">' . implode('', $cells) . '</div>';
1031 }
1032
1033 /**
1034 * Make reference count
1035 *
1036 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.
1037 * @return string HTML
1038 */
1039 public function makeRef($fileOrFolderObject)
1040 {
1041 if ($fileOrFolderObject instanceof FolderInterface) {
1042 return '-';
1043 }
1044 // Look up the file in the sys_refindex.
1045 // Exclude sys_file_metadata records as these are no use references
1046 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
1047 $referenceCount = $queryBuilder->count('*')
1048 ->from('sys_refindex')
1049 ->where(
1050 $queryBuilder->expr()->eq('deleted', 0),
1051 $queryBuilder->expr()->eq('ref_table', $queryBuilder->quote('sys_file')),
1052 $queryBuilder->expr()->eq('ref_uid', (int)$fileOrFolderObject->getUid()),
1053 $queryBuilder->expr()->neq('tablename', $queryBuilder->quote('sys_file_metadata'))
1054 )
1055 ->execute()
1056 ->fetchColumn();
1057
1058 return $this->generateReferenceToolTip($referenceCount, '\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileOrFolderObject->getCombinedIdentifier()));
1059 }
1060
1061 /**
1062 * Returns an instance of LanguageService
1063 *
1064 * @return \TYPO3\CMS\Lang\LanguageService
1065 */
1066 protected function getLanguageService()
1067 {
1068 return $GLOBALS['LANG'];
1069 }
1070
1071 /**
1072 * Returns the current BE user.
1073 *
1074 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1075 */
1076 protected function getBackendUser()
1077 {
1078 return $GLOBALS['BE_USER'];
1079 }
1080 }