[TASK] Use correct icons for TCA type select with renderType selectTree
[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\Database\Query\QueryContextType;
23 use TYPO3\CMS\Core\Imaging\Icon;
24 use TYPO3\CMS\Core\Imaging\IconFactory;
25 use TYPO3\CMS\Core\Messaging\FlashMessage;
26 use TYPO3\CMS\Core\Messaging\FlashMessageService;
27 use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
28 use TYPO3\CMS\Core\Resource\File;
29 use TYPO3\CMS\Core\Resource\Folder;
30 use TYPO3\CMS\Core\Resource\FolderInterface;
31 use TYPO3\CMS\Core\Resource\InaccessibleFolder;
32 use TYPO3\CMS\Core\Resource\ProcessedFile;
33 use TYPO3\CMS\Core\Resource\ResourceFactory;
34 use TYPO3\CMS\Core\Resource\Utility\ListUtility;
35 use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
36 use TYPO3\CMS\Core\Utility\GeneralUtility;
37 use TYPO3\CMS\Core\Utility\MathUtility;
38 use TYPO3\CMS\Filelist\Controller\FileListController;
39
40 /**
41 * Class for rendering of File>Filelist
42 */
43 class FileList extends AbstractRecordList
44 {
45 /**
46 * Default Max items shown
47 *
48 * @var int
49 */
50 public $iLimit = 40;
51
52 /**
53 * Thumbnails on records containing files (pictures)
54 *
55 * @var bool
56 */
57 public $thumbs = false;
58
59 /**
60 * Space icon used for alignment when no button is available
61 *
62 * @var string
63 */
64 public $spaceIcon;
65
66 /**
67 * Max length of strings
68 *
69 * @var int
70 */
71 public $fixedL = 30;
72
73 /**
74 * If TRUE click menus are generated on files and folders
75 *
76 * @var bool
77 */
78 public $clickMenus = 1;
79
80 /**
81 * The field to sort by
82 *
83 * @var string
84 */
85 public $sort = '';
86
87 /**
88 * Reverse sorting flag
89 *
90 * @var bool
91 */
92 public $sortRev = 1;
93
94 /**
95 * @var int
96 */
97 public $firstElementNumber = 0;
98
99 /**
100 * @var bool
101 */
102 public $clipBoard = 0;
103
104 /**
105 * @var bool
106 */
107 public $bigControlPanel = 0;
108
109 /**
110 * @var string
111 */
112 public $JScode = '';
113
114 /**
115 * @var string
116 */
117 public $HTMLcode = '';
118
119 /**
120 * @var int
121 */
122 public $totalbytes = 0;
123
124 /**
125 * @var array
126 */
127 public $dirs = array();
128
129 /**
130 * @var array
131 */
132 public $files = array();
133
134 /**
135 * @var string
136 */
137 public $path = '';
138
139 /**
140 * @var Folder
141 */
142 protected $folderObject;
143
144 /**
145 * Counting the elements no matter what
146 *
147 * @var int
148 */
149 public $eCounter = 0;
150
151 /**
152 * @var string
153 */
154 public $totalItems = '';
155
156 /**
157 * @var array
158 */
159 public $CBnames = array();
160
161 /**
162 * @var Clipboard $clipObj
163 */
164 public $clipObj;
165
166 /**
167 * @var ResourceFactory
168 */
169 protected $resourceFactory;
170
171 /**
172 * @param ResourceFactory $resourceFactory
173 */
174 public function injectResourceFactory(ResourceFactory $resourceFactory)
175 {
176 $this->resourceFactory = $resourceFactory;
177 }
178
179 /**
180 * @var IconFactory
181 */
182 protected $iconFactory;
183
184 /**
185 * @var FileListController
186 */
187 protected $fileListController;
188
189 /**
190 * Construct
191 *
192 * @param FileListController $fileListController
193 */
194 public function __construct(FileListController $fileListController)
195 {
196 parent::__construct();
197 $this->fileListController = $fileListController;
198 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
199
200 $modTSconfig = BackendUtility::getModTSconfig(0, 'mod.file_list');
201 if (!empty($modTSconfig['properties']['filesPerPage'])) {
202 $this->iLimit = MathUtility::forceIntegerInRange($modTSconfig['properties']['filesPerPage'], 1);
203 }
204 }
205
206 /**
207 * Initialization of class
208 *
209 * @param Folder $folderObject The folder to work on
210 * @param int $pointer Pointer
211 * @param bool $sort Sorting column
212 * @param bool $sortRev Sorting direction
213 * @param bool $clipBoard
214 * @param bool $bigControlPanel Show clipboard flag
215 * @return void
216 */
217 public function start(Folder $folderObject, $pointer, $sort, $sortRev, $clipBoard = false, $bigControlPanel = false)
218 {
219 $this->folderObject = $folderObject;
220 $this->counter = 0;
221 $this->totalbytes = 0;
222 $this->JScode = '';
223 $this->HTMLcode = '';
224 $this->path = $folderObject->getReadablePath();
225 $this->sort = $sort;
226 $this->sortRev = $sortRev;
227 $this->firstElementNumber = $pointer;
228 $this->clipBoard = $clipBoard;
229 $this->bigControlPanel = $bigControlPanel;
230 // Setting the maximum length of the filenames to the user's settings or minimum 30 (= $this->fixedL)
231 $this->fixedL = max($this->fixedL, $this->getBackendUser()->uc['titleLen']);
232 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_common.xlf');
233 $this->resourceFactory = ResourceFactory::getInstance();
234 }
235
236 /**
237 * Reading files and directories, counting elements and generating the list in ->HTMLcode
238 *
239 * @return void
240 */
241 public function generateList()
242 {
243 $this->HTMLcode .= $this->getTable('fileext,tstamp,size,rw,_REF_');
244 }
245
246 /**
247 * Wrapping input string in a link with clipboard command.
248 *
249 * @param string $string String to be linked - must be htmlspecialchar'ed / prepared before.
250 * @param string $_ unused
251 * @param string $cmd "cmd" value
252 * @param string $warning Warning for JS confirm message
253 * @return string Linked string
254 */
255 public function linkClipboardHeaderIcon($string, $_, $cmd, $warning = '')
256 {
257 $jsCode = 'document.dblistForm.cmd.value=' . GeneralUtility::quoteJSvalue($cmd)
258 . ';document.dblistForm.submit();';
259
260 $attributes = [];
261 if ($warning) {
262 $attributes['class'] = 'btn btn-default t3js-modal-trigger';
263 $attributes['data-href'] = 'javascript:' . $jsCode;
264 $attributes['data-severity'] = 'warning';
265 $attributes['data-content'] = $warning;
266 } else {
267 $attributes['class'] = 'btn btn-default';
268 $attributes['onclick'] = $jsCode . 'return false;';
269 }
270
271 $attributesString = '';
272 foreach ($attributes as $key => $value) {
273 $attributesString .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
274 }
275 return '<a href="#" ' . $attributesString . '>' . $string . '</a>';
276 }
277
278 /**
279 * Returns a table with directories and files listed.
280 *
281 * @param array $rowlist Array of files from path
282 * @return string HTML-table
283 */
284 public function getTable($rowlist)
285 {
286 // prepare space icon
287 $this->spaceIcon = '<span class="btn btn-default disabled">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
288
289 // @todo use folder methods directly when they support filters
290 $storage = $this->folderObject->getStorage();
291 $storage->resetFileAndFolderNameFiltersToDefault();
292
293 // Only render the contents of a browsable storage
294 if ($this->folderObject->getStorage()->isBrowsable()) {
295 try {
296 $foldersCount = $storage->countFoldersInFolder($this->folderObject);
297 $filesCount = $storage->countFilesInFolder($this->folderObject);
298 } catch (InsufficientFolderAccessPermissionsException $e) {
299 $foldersCount = 0;
300 $filesCount = 0;
301 }
302
303 if ($foldersCount <= $this->firstElementNumber) {
304 $foldersFrom = false;
305 $foldersNum = false;
306 } else {
307 $foldersFrom = $this->firstElementNumber;
308 if ($this->firstElementNumber + $this->iLimit > $foldersCount) {
309 $foldersNum = $foldersCount - $this->firstElementNumber;
310 } else {
311 $foldersNum = $this->iLimit;
312 }
313 }
314 if ($foldersCount >= $this->firstElementNumber + $this->iLimit) {
315 $filesFrom = false;
316 $filesNum = false;
317 } else {
318 if ($this->firstElementNumber <= $foldersCount) {
319 $filesFrom = 0;
320 $filesNum = $this->iLimit - $foldersNum;
321 } else {
322 $filesFrom = $this->firstElementNumber - $foldersCount;
323 if ($filesFrom + $this->iLimit > $filesCount) {
324 $filesNum = $filesCount - $filesFrom;
325 } else {
326 $filesNum = $this->iLimit;
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';
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 : $this->totalItems;
370 list(, $code) = $this->fwd_rwd_nav();
371 $iOut .= $code;
372
373 // Header line is drawn
374 $theData = array();
375 foreach ($this->fieldArray as $v) {
376 if ($v == '_CLIPBOARD_' && $this->clipBoard) {
377 $cells = array();
378 $table = '_FILE';
379 $elFromTable = $this->clipObj->elFromTable($table);
380 if (!empty($elFromTable) && $this->folderObject->checkActionPermission('write')) {
381 $addPasteButton = true;
382 $elToConfirm = array();
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="' . $this->getLanguageService()->getLL('clip_paste', true) . '"'
404 . ' title="' . $this->getLanguageService()->getLL('clip_paste', true) . '">'
405 . $this->iconFactory->getIcon('actions-document-paste-after', Icon::SIZE_SMALL)
406 ->render()
407 . '</a>';
408 }
409 }
410 if ($this->clipObj->current !== 'normal' && $iOut) {
411 $cells[] = $this->linkClipboardHeaderIcon('<span title="' . $this->getLanguageService()->getLL('clip_selectMarked', true) . '">' . $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render() . '</span>', $table, 'setCB');
412 $cells[] = $this->linkClipboardHeaderIcon('<span title="' . $this->getLanguageService()->getLL('clip_deleteMarked', true) . '">' . $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="' . $this->getLanguageService()->getLL('clip_markRecords', true) . '">' . $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($this->getLanguageService()->getLL('c_' . $v, true), $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="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.upOneLevel', true) . '">'
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 = $this->getLanguageService()->getLL('file', true);
485 } else {
486 $fileLabel = $this->getLanguageService()->getLL('files', true);
487 }
488 return $this->counter . ' ' . $fileLabel . ', ' . GeneralUtility::formatSize($this->totalbytes, $this->getLanguageService()->getLL('byteSizeUnits', true));
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 = array();
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 . ' ' . $this->getLanguageService()->getLL(($numFiles === 1 ? 'file' : 'files'), true);
541 break;
542 case 'rw':
543 $theData[$field] = '<strong class="text-danger">' . $this->getLanguageService()->getLL('read', true) . '</strong>' . (!$isWritable ? '' : '<strong class="text-danger">' . $this->getLanguageService()->getLL('write', true) . '</strong>');
544 break;
545 case 'fileext':
546 $theData[$field] = $this->getLanguageService()->getLL('folder', true);
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) . '">' . GeneralUtility::fixed_lgd_cs($title, $this->fixedL) . '</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 href="' . htmlspecialchars($url) . '" title="' . $title . '">' . GeneralUtility::fixed_lgd_cs($code, $this->fixedL) . '</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(array(
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 = array();
675 foreach ($this->fieldArray as $field) {
676 switch ($field) {
677 case 'size':
678 $theData[$field] = GeneralUtility::formatSize($fileObject->getSize(), $this->getLanguageService()->getLL('byteSizeUnits', true));
679 break;
680 case 'rw':
681 $theData[$field] = '' . (!$fileObject->checkActionPermission('read') ? ' ' : '<strong class="text-danger">' . $this->getLanguageService()->getLL('read', true) . '</strong>') . (!$fileObject->checkActionPermission('write') ? '' : '<strong class="text-danger">' . $this->getLanguageService()->getLL('write', true) . '</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="' . $this->getLanguageService()->getLL('translateMetadata', true) . '">'
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, array());
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->getQueryContext()->setContext(QueryContextType::UNRESTRICTED);
788 $translationRecords = $queryBuilder->select('*')
789 ->from('sys_file_metadata')
790 ->where($queryBuilder->expr()->eq($GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'], (int)$metaDataRecord['uid']))
791 ->andWhere($queryBuilder->expr()->gt($GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'], 0))
792 ->execute()
793 ->fetchAll();
794
795 $translations = array();
796 foreach ($translationRecords as $record) {
797 $translations[$record[$GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField']]] = $record;
798 }
799 return $translations;
800 }
801
802 /**
803 * Returns TRUE if $ext is an image-extension according to $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
804 *
805 * @param string $ext File extension
806 * @return bool
807 */
808 public function isImage($ext)
809 {
810 return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($ext));
811 }
812
813 /**
814 * Returns TRUE if $ext is an media-extension according to $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']
815 *
816 * @param string $ext File extension
817 * @return bool
818 */
819 public function isMediaFile($ext)
820 {
821 return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'], strtolower($ext));
822 }
823
824 /**
825 * Wraps the directory-titles ($code) in a link to filelist/Modules/Filelist/index.php (id=$path) and sorting commands...
826 *
827 * @param string $code String to be wrapped
828 * @param string $folderIdentifier ID (path)
829 * @param string $col Sorting column
830 * @return string HTML
831 */
832 public function linkWrapSort($code, $folderIdentifier, $col)
833 {
834 $params = ['id' => $folderIdentifier, 'SET' => [ 'sort' => $col ]];
835
836 if ($this->sort === $col) {
837 // Check reverse sorting
838 $params['SET']['reverse'] = ($this->sortRev ? '0' : '1');
839 $sortArrow = $this->iconFactory->getIcon('status-status-sorting-light-' . ($this->sortRev ? 'desc' : 'asc'), Icon::SIZE_SMALL)->render();
840 } else {
841 $params['SET']['reverse'] = 0;
842 $sortArrow = '';
843 }
844 $href = BackendUtility::getModuleUrl('file_FilelistList', $params);
845 return '<a href="' . htmlspecialchars($href) . '">' . $code . ' ' . $sortArrow . '</a>';
846 }
847
848 /**
849 * Creates the clipboard control pad
850 *
851 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.
852 * @return string HTML-table
853 */
854 public function makeClip($fileOrFolderObject)
855 {
856 if (!$fileOrFolderObject->checkActionPermission('read')) {
857 return '';
858 }
859 $cells = array();
860 $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
861 $fullName = $fileOrFolderObject->getName();
862 $md5 = GeneralUtility::shortMD5($fullIdentifier);
863 // For normal clipboard, add copy/cut buttons:
864 if ($this->clipObj->current === 'normal') {
865 $isSel = $this->clipObj->isSelected('_FILE', $md5);
866 $copyTitle = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.copy', true);
867 $cutTitle = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.cut', true);
868 $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render();
869 $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render();
870
871 if ($isSel === 'copy') {
872 $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render();
873 } elseif ($isSel === 'cut') {
874 $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render();
875 }
876
877 $cells[] = '<a class="btn btn-default"" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 1, ($isSel === 'copy'))) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';
878 // we can only cut if file can be moved
879 if ($fileOrFolderObject->checkActionPermission('move')) {
880 $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 0, ($isSel === 'cut'))) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';
881 } else {
882 $cells[] = $this->spaceIcon;
883 }
884 } else {
885 // For numeric pads, add select checkboxes:
886 $n = '_FILE|' . $md5;
887 $this->CBnames[] = $n;
888 $checked = $this->clipObj->isSelected('_FILE', $md5) ? ' checked="checked"' : '';
889 $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>';
890 }
891 // Display PASTE button, if directory:
892 $elFromTable = $this->clipObj->elFromTable('_FILE');
893 if ($fileOrFolderObject instanceof Folder && !empty($elFromTable) && $fileOrFolderObject->checkActionPermission('write')) {
894 $addPasteButton = true;
895 $elToConfirm = array();
896 foreach ($elFromTable as $key => $element) {
897 $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
898 if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $fileOrFolderObject)) {
899 $addPasteButton = false;
900 }
901 $elToConfirm[$key] = $clipBoardElement->getName();
902 }
903 if ($addPasteButton) {
904 $cells[] = '<a class="btn btn-default t3js-modal-trigger" '
905 . ' href="' . htmlspecialchars($this->clipObj->pasteUrl('_FILE', $fullIdentifier)) . '"'
906 . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText('_FILE', $fullName, 'into', $elToConfirm)) . '"'
907 . ' data-severity="warning"'
908 . ' data-title="' . $this->getLanguageService()->getLL('clip_pasteInto', true) . '"'
909 . ' title="' . $this->getLanguageService()->getLL('clip_pasteInto', true) . '"'
910 . '>'
911 . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render()
912 . '</a>';
913 }
914 }
915 // Compile items into a DIV-element:
916 return ' <div class="btn-group" role="group">' . implode('', $cells) . '</div>';
917 }
918
919 /**
920 * Creates the edit control section
921 *
922 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the edit control section for the listing.
923 * @return string HTML-table
924 */
925 public function makeEdit($fileOrFolderObject)
926 {
927 $cells = array();
928 $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
929
930 // Edit file content (if editable)
931 if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('write') && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], $fileOrFolderObject->getExtension())) {
932 $url = BackendUtility::getModuleUrl('file_edit', array('target' => $fullIdentifier));
933 $editOnClick = 'top.content.list_frame.location.href=' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.content.list_frame.document.location.pathname+top.content.list_frame.document.location.search);return false;';
934 $cells['edit'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($editOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.editcontent') . '">'
935 . $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render()
936 . '</a>';
937 } else {
938 $cells['edit'] = $this->spaceIcon;
939 }
940 if ($fileOrFolderObject instanceof File) {
941 $fileUrl = $fileOrFolderObject->getPublicUrl(true);
942 if ($fileUrl) {
943 $aOnClick = 'return top.openUrlInWindow(' . GeneralUtility::quoteJSvalue($fileUrl) . ', \'WebFile\');';
944 $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>';
945 } else {
946 $cells['view'] = $this->spaceIcon;
947 }
948 } else {
949 $cells['view'] = $this->spaceIcon;
950 }
951
952 // replace file
953 if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('replace')) {
954 $url = BackendUtility::getModuleUrl('file_replace', array('target' => $fullIdentifier, 'uid' => $fileOrFolderObject->getUid()));
955 $replaceOnClick = 'top.content.list_frame.location.href = ' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.content.list_frame.document.location.pathname+top.content.list_frame.document.location.search);return false;';
956 $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>';
957 }
958
959 // rename the file
960 if ($fileOrFolderObject->checkActionPermission('rename')) {
961 $url = BackendUtility::getModuleUrl('file_rename', array('target' => $fullIdentifier));
962 $renameOnClick = 'top.content.list_frame.location.href = ' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.content.list_frame.document.location.pathname+top.content.list_frame.document.location.search);return false;';
963 $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>';
964 } else {
965 $cells['rename'] = $this->spaceIcon;
966 }
967 if ($fileOrFolderObject->checkActionPermission('read')) {
968 $infoOnClick = '';
969 if ($fileOrFolderObject instanceof Folder) {
970 $infoOnClick = 'top.launchView( \'_FOLDER\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
971 } elseif ($fileOrFolderObject instanceof File) {
972 $infoOnClick = 'top.launchView( \'_FILE\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
973 }
974 $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>';
975 } else {
976 $cells['info'] = $this->spaceIcon;
977 }
978
979 // delete the file
980 if ($fileOrFolderObject->checkActionPermission('delete')) {
981 $identifier = $fileOrFolderObject->getIdentifier();
982 if ($fileOrFolderObject instanceof Folder) {
983 $referenceCountText = BackendUtility::referenceCount('_FILE', $identifier, ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.referencesToFolder'));
984 $deleteType = 'delete_folder';
985 } else {
986 $referenceCountText = BackendUtility::referenceCount('sys_file', $fileOrFolderObject->getUid(), ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.referencesToFile'));
987 $deleteType = 'delete_file';
988 }
989
990 if ($this->getBackendUser()->jsConfirmation(JsConfirmation::DELETE)) {
991 $confirmationCheck = '1';
992 } else {
993 $confirmationCheck = '0';
994 }
995
996 $deleteUrl = BackendUtility::getModuleUrl('tce_file');
997 $confirmationMessage = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:mess.delete'), $fileOrFolderObject->getName()) . $referenceCountText;
998 $title = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.delete');
999 $cells['delete'] = '<a href="#" class="btn btn-default t3js-filelist-delete" data-content="' . htmlspecialchars($confirmationMessage)
1000 . '" data-check="' . $confirmationCheck
1001 . '" data-delete-url="' . htmlspecialchars($deleteUrl)
1002 . '" data-title="' . htmlspecialchars($title)
1003 . '" data-identifier="' . htmlspecialchars($fileOrFolderObject->getCombinedIdentifier())
1004 . '" data-veri-code="' . $this->getBackendUser()->veriCode()
1005 . '" data-delete-type="' . $deleteType
1006 . '" title="' . htmlspecialchars($title) . '">'
1007 . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() . '</a>';
1008 } else {
1009 $cells['delete'] = $this->spaceIcon;
1010 }
1011
1012 // Hook for manipulating edit icons.
1013 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'])) {
1014 $cells['__fileOrFolderObject'] = $fileOrFolderObject;
1015 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'] as $classData) {
1016 $hookObject = GeneralUtility::getUserObj($classData);
1017 if (!$hookObject instanceof FileListEditIconHookInterface) {
1018 throw new \UnexpectedValueException(
1019 '$hookObject must implement interface \\TYPO3\\CMS\\Filelist\\FileListEditIconHookInterface',
1020 1235225797
1021 );
1022 }
1023 $hookObject->manipulateEditIcons($cells, $this);
1024 }
1025 unset($cells['__fileOrFolderObject']);
1026 }
1027 // Compile items into a DIV-element:
1028 return '<div class="btn-group">' . implode('', $cells) . '</div>';
1029 }
1030
1031 /**
1032 * Make reference count
1033 *
1034 * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.
1035 * @return string HTML
1036 */
1037 public function makeRef($fileOrFolderObject)
1038 {
1039 if ($fileOrFolderObject instanceof FolderInterface) {
1040 return '-';
1041 }
1042 // Look up the file in the sys_refindex.
1043 // Exclude sys_file_metadata records as these are no use references
1044 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
1045 $referenceCount = $queryBuilder->count('*')
1046 ->from('sys_refindex')
1047 ->where($queryBuilder->expr()->eq('ref_table', $queryBuilder->quote('sys_file')))
1048 ->andWhere($queryBuilder->expr()->eq('ref_uid', (int)$fileOrFolderObject->getUid()))
1049 ->andWhere($queryBuilder->expr()->neq('tablename', $queryBuilder->quote('sys_file_metadata')))
1050 ->execute()
1051 ->fetchColumn();
1052
1053 return $this->generateReferenceToolTip($referenceCount, '\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileOrFolderObject->getCombinedIdentifier()));
1054 }
1055
1056 /**
1057 * Returns an instance of LanguageService
1058 *
1059 * @return \TYPO3\CMS\Lang\LanguageService
1060 */
1061 protected function getLanguageService()
1062 {
1063 return $GLOBALS['LANG'];
1064 }
1065
1066 /**
1067 * Returns the current BE user.
1068 *
1069 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1070 */
1071 protected function getBackendUser()
1072 {
1073 return $GLOBALS['BE_USER'];
1074 }
1075 }