TYPO3 CMS  TYPO3_8-7
FileList.php
Go to the documentation of this file.
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 
38 
43 {
49  public $iLimit = 40;
50 
56  public $thumbs = false;
57 
63  public $spaceIcon;
64 
70  public $fixedL = 30;
71 
77  public $sort = '';
78 
84  public $sortRev = 1;
85 
89  public $firstElementNumber = 0;
90 
94  public $clipBoard = 0;
95 
99  public $bigControlPanel = 0;
100 
104  public $JScode = '';
105 
109  public $HTMLcode = '';
110 
114  public $totalbytes = 0;
115 
119  public $dirs = [];
120 
124  public $files = [];
125 
129  public $path = '';
130 
134  protected $folderObject;
135 
141  public $eCounter = 0;
142 
146  public $totalItems = '';
147 
151  public $CBnames = [];
152 
156  public $clipObj;
157 
161  protected $resourceFactory;
162 
167  {
168  $this->resourceFactory = $resourceFactory;
169  }
170 
174  protected $iconFactory;
175 
180 
187  {
188  parent::__construct();
189  $this->fileListController = $fileListController;
190  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
191 
192  $modTSconfig = BackendUtility::getModTSconfig(0, 'options.file_list');
193  if (!empty($modTSconfig['properties']['filesPerPage'])) {
194  $this->iLimit = MathUtility::forceIntegerInRange($modTSconfig['properties']['filesPerPage'], 1);
195  }
196  }
197 
208  public function start(Folder $folderObject, $pointer, $sort, $sortRev, $clipBoard = false, $bigControlPanel = false)
209  {
210  $this->folderObject = $folderObject;
211  $this->counter = 0;
212  $this->totalbytes = 0;
213  $this->JScode = '';
214  $this->HTMLcode = '';
215  $this->path = $folderObject->getReadablePath();
216  $this->sort = $sort;
217  $this->sortRev = $sortRev;
218  $this->firstElementNumber = $pointer;
219  $this->clipBoard = $clipBoard;
220  $this->bigControlPanel = $bigControlPanel;
221  // Setting the maximum length of the filenames to the user's settings or minimum 30 (= $this->fixedL)
222  $this->fixedL = max($this->fixedL, $this->getBackendUser()->uc['titleLen']);
223  $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_common.xlf');
224  $this->resourceFactory = ResourceFactory::getInstance();
225  }
226 
230  public function generateList()
231  {
232  $this->HTMLcode .= $this->getTable('fileext,tstamp,size,rw,_REF_');
233  }
234 
244  public function linkClipboardHeaderIcon($string, $_, $cmd, $warning = '')
245  {
246  $jsCode = 'document.dblistForm.cmd.value=' . GeneralUtility::quoteJSvalue($cmd)
247  . ';document.dblistForm.submit();';
248 
249  $attributes = [];
250  if ($warning) {
251  $attributes['class'] = 'btn btn-default t3js-modal-trigger';
252  $attributes['data-href'] = 'javascript:' . $jsCode;
253  $attributes['data-severity'] = 'warning';
254  $attributes['data-content'] = $warning;
255  } else {
256  $attributes['class'] = 'btn btn-default';
257  $attributes['onclick'] = $jsCode . 'return false;';
258  }
259 
260  $attributesString = '';
261  foreach ($attributes as $key => $value) {
262  $attributesString .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
263  }
264  return '<a href="#" ' . $attributesString . '>' . $string . '</a>';
265  }
266 
273  public function getTable($rowlist)
274  {
275  // prepare space icon
276  $this->spaceIcon = '<span class="btn btn-default disabled">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
277 
278  // @todo use folder methods directly when they support filters
279  $storage = $this->folderObject->getStorage();
280  $storage->resetFileAndFolderNameFiltersToDefault();
281 
282  // Only render the contents of a browsable storage
283  if ($this->folderObject->getStorage()->isBrowsable()) {
284  try {
285  $foldersCount = $storage->countFoldersInFolder($this->folderObject);
286  $filesCount = $storage->countFilesInFolder($this->folderObject);
288  $foldersCount = 0;
289  $filesCount = 0;
290  }
291 
292  if ($foldersCount <= $this->firstElementNumber) {
293  $foldersFrom = false;
294  $foldersNum = false;
295  } else {
296  $foldersFrom = $this->firstElementNumber;
297  if ($this->firstElementNumber + $this->iLimit > $foldersCount) {
298  $foldersNum = $foldersCount - $this->firstElementNumber;
299  } else {
300  $foldersNum = $this->iLimit;
301  }
302  }
303  if ($foldersCount >= $this->firstElementNumber + $this->iLimit) {
304  $filesFrom = false;
305  $filesNum = false;
306  } else {
307  if ($this->firstElementNumber <= $foldersCount) {
308  $filesFrom = 0;
309  $filesNum = $this->iLimit - $foldersNum;
310  } else {
311  $filesFrom = $this->firstElementNumber - $foldersCount;
312  if ($filesFrom + $this->iLimit > $filesCount) {
313  $filesNum = $filesCount - $filesFrom;
314  } else {
315  $filesNum = $this->iLimit;
316  }
317  }
318  }
319 
320  $folders = $storage->getFoldersInFolder($this->folderObject, $foldersFrom, $foldersNum, true, false, trim($this->sort), (bool)$this->sortRev);
321  $files = $this->folderObject->getFiles($filesFrom, $filesNum, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, false, trim($this->sort), (bool)$this->sortRev);
322  $this->totalItems = $foldersCount + $filesCount;
323  // Adds the code of files/dirs
324  $out = '';
325  $titleCol = 'file';
326  // Cleaning rowlist for duplicates and place the $titleCol as the first column always!
327  $rowlist = '_LOCALIZATION_,' . $rowlist;
328  $rowlist = GeneralUtility::rmFromList($titleCol, $rowlist);
329  $rowlist = GeneralUtility::uniqueList($rowlist);
330  $rowlist = $rowlist ? $titleCol . ',' . $rowlist : $titleCol;
331  if ($this->clipBoard) {
332  $rowlist = str_replace('_LOCALIZATION_,', '_LOCALIZATION_,_CLIPBOARD_,', $rowlist);
333  $this->addElement_tdCssClass['_CLIPBOARD_'] = 'col-clipboard';
334  }
335  if ($this->bigControlPanel) {
336  $rowlist = str_replace('_LOCALIZATION_,', '_LOCALIZATION_,_CONTROL_,', $rowlist);
337  $this->addElement_tdCssClass['_CONTROL_'] = 'col-control';
338  }
339  $this->fieldArray = explode(',', $rowlist);
340 
341  // Add classes to table cells
342  $this->addElement_tdCssClass[$titleCol] = 'col-title col-responsive';
343  $this->addElement_tdCssClass['_LOCALIZATION_'] = 'col-localizationa';
344 
345  $folders = ListUtility::resolveSpecialFolderNames($folders);
346 
347  $iOut = '';
348  // Directories are added
349  $this->eCounter = $this->firstElementNumber;
350  list(, $code) = $this->fwd_rwd_nav();
351  $iOut .= $code;
352 
353  $iOut .= $this->formatDirList($folders);
354  // Files are added
355  $iOut .= $this->formatFileList($files);
356 
357  $this->eCounter = $this->firstElementNumber + $this->iLimit < $this->totalItems
358  ? $this->firstElementNumber + $this->iLimit
359  : -1;
360  list(, $code) = $this->fwd_rwd_nav();
361  $iOut .= $code;
362 
363  // Header line is drawn
364  $theData = [];
365  foreach ($this->fieldArray as $v) {
366  if ($v === '_CLIPBOARD_' && $this->clipBoard) {
367  $cells = [];
368  $table = '_FILE';
369  $elFromTable = $this->clipObj->elFromTable($table);
370  if (!empty($elFromTable) && $this->folderObject->checkActionPermission('write')) {
371  $addPasteButton = true;
372  $elToConfirm = [];
373  foreach ($elFromTable as $key => $element) {
374  $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
375  if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $this->folderObject)) {
376  $addPasteButton = false;
377  }
378  $elToConfirm[$key] = $clipBoardElement->getName();
379  }
380  if ($addPasteButton) {
381  $cells[] = '<a class="btn btn-default t3js-modal-trigger"' .
382  ' href="' . htmlspecialchars($this->clipObj->pasteUrl(
383  '_FILE',
384  $this->folderObject->getCombinedIdentifier()
385  )) . '"'
386  . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText(
387  '_FILE',
388  $this->path,
389  'into',
390  $elToConfirm
391  )) . '"'
392  . ' data-severity="warning"'
393  . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_paste')) . '"'
394  . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_paste')) . '">'
395  . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)
396  ->render()
397  . '</a>';
398  }
399  }
400  if ($this->clipObj->current !== 'normal' && $iOut) {
401  $cells[] = $this->linkClipboardHeaderIcon('<span title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_selectMarked')) . '">' . $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render() . '</span>', $table, 'setCB');
402  $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'));
403  $onClick = 'checkOffCB(' . GeneralUtility::quoteJSvalue(implode(',', $this->CBnames)) . ', this); return false;';
404  $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>';
405  }
406  $theData[$v] = implode('', $cells);
407  } else {
408  // Normal row:
409  $theT = $this->linkWrapSort(htmlspecialchars($this->getLanguageService()->getLL('c_' . $v)), $this->folderObject->getCombinedIdentifier(), $v);
410  $theData[$v] = $theT;
411  }
412  }
413 
414  $out .= '<thead>' . $this->addElement(1, '', $theData, '', '', '', 'th') . '</thead>';
415  $out .= '<tbody>' . $iOut . '</tbody>';
416  // half line is drawn
417  // finish
418  $out = '
419  <!--
420  Filelist table:
421  -->
422  <div class="table-fit">
423  <table class="table table-striped table-hover" id="typo3-filelist">
424  ' . $out . '
425  </table>
426  </div>';
427  } else {
429  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('storageNotBrowsableMessage'), $this->getLanguageService()->getLL('storageNotBrowsableTitle'), FlashMessage::INFO);
431  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
433  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
434  $defaultFlashMessageQueue->enqueue($flashMessage);
435  $out = '';
436  }
437  return $out;
438  }
439 
447  protected function getLinkToParentFolder(Folder $currentFolder)
448  {
449  $levelUp = '';
450  try {
451  $currentStorage = $currentFolder->getStorage();
452  $parentFolder = $currentFolder->getParentFolder();
453  if ($parentFolder->getIdentifier() !== $currentFolder->getIdentifier() && $currentStorage->isWithinFileMountBoundaries($parentFolder)) {
454  $levelUp = $this->linkWrapDir(
455  '<span title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.upOneLevel')) . '">'
456  . $this->iconFactory->getIcon('actions-view-go-up', Icon::SIZE_SMALL)->render()
457  . '</span>',
458  $parentFolder
459  );
460  }
461  } catch (\Exception $e) {
462  }
463  return $levelUp;
464  }
465 
471  public function getFolderInfo()
472  {
473  if ($this->counter == 1) {
474  $fileLabel = htmlspecialchars($this->getLanguageService()->getLL('file'));
475  } else {
476  $fileLabel = htmlspecialchars($this->getLanguageService()->getLL('files'));
477  }
478  return $this->counter . ' ' . $fileLabel . ', ' . GeneralUtility::formatSize($this->totalbytes, htmlspecialchars($this->getLanguageService()->getLL('byteSizeUnits')));
479  }
480 
487  public function formatDirList(array $folders)
488  {
489  $out = '';
490  foreach ($folders as $folderName => $folderObject) {
491  $role = $folderObject->getRole();
492  if ($role === FolderInterface::ROLE_PROCESSING) {
493  // don't show processing-folder
494  continue;
495  }
496  if ($role !== FolderInterface::ROLE_DEFAULT) {
497  $displayName = '<strong>' . htmlspecialchars($folderName) . '</strong>';
498  } else {
499  $displayName = htmlspecialchars($folderName);
500  }
501 
502  $isLocked = $folderObject instanceof InaccessibleFolder;
503  $isWritable = $folderObject->checkActionPermission('write');
504 
505  // Initialization
506  $this->counter++;
507 
508  // The icon with link
509  $theIcon = '<span title="' . htmlspecialchars($folderName) . '">' . $this->iconFactory->getIconForResource($folderObject, Icon::SIZE_SMALL)->render() . '</span>';
510  if (!$isLocked) {
511  $theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, 'sys_file', $folderObject->getCombinedIdentifier());
512  }
513 
514  // Preparing and getting the data-array
515  $theData = [];
516  if ($isLocked) {
517  foreach ($this->fieldArray as $field) {
518  $theData[$field] = '';
519  }
520  $theData['file'] = $displayName;
521  } else {
522  foreach ($this->fieldArray as $field) {
523  switch ($field) {
524  case 'size':
525  try {
526  $numFiles = $folderObject->getFileCount();
528  $numFiles = 0;
529  }
530  $theData[$field] = $numFiles . ' ' . htmlspecialchars($this->getLanguageService()->getLL(($numFiles === 1 ? 'file' : 'files')));
531  break;
532  case 'rw':
533  $theData[$field] = '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('read')) . '</strong>' . (!$isWritable ? '' : '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('write')) . '</strong>');
534  break;
535  case 'fileext':
536  $theData[$field] = htmlspecialchars($this->getLanguageService()->getLL('folder'));
537  break;
538  case 'tstamp':
539  $tstamp = $folderObject->getModificationTime();
540  $theData[$field] = $tstamp ? BackendUtility::date($tstamp) : '-';
541  break;
542  case 'file':
543  $theData[$field] = $this->linkWrapDir($displayName, $folderObject);
544  break;
545  case '_CONTROL_':
546  $theData[$field] = $this->makeEdit($folderObject);
547  break;
548  case '_CLIPBOARD_':
549  $theData[$field] = $this->makeClip($folderObject);
550  break;
551  case '_REF_':
552  $theData[$field] = $this->makeRef($folderObject);
553  break;
554  default:
555  $theData[$field] = GeneralUtility::fixed_lgd_cs($theData[$field], $this->fixedL);
556  }
557  }
558  }
559  $out .= $this->addElement(1, $theIcon, $theData);
560  }
561  return $out;
562  }
563 
571  public function linkWrapDir($title, Folder $folderObject)
572  {
573  $href = BackendUtility::getModuleUrl('file_FilelistList', ['id' => $folderObject->getCombinedIdentifier()]);
574  $onclick = ' onclick="' . htmlspecialchars(('top.document.getElementsByName("nav_frame")[0].contentWindow.Tree.highlightActiveItem("file","folder' . GeneralUtility::md5int($folderObject->getCombinedIdentifier()) . '_"+top.fsMod.currentBank)')) . '"';
575  // Sometimes $code contains plain HTML tags. In such a case the string should not be modified!
576  if ((string)$title === strip_tags($title)) {
577  return '<a href="' . htmlspecialchars($href) . '"' . $onclick . ' title="' . htmlspecialchars($title) . '">' . $title . '</a>';
578  }
579  return '<a href="' . htmlspecialchars($href) . '"' . $onclick . '>' . $title . '</a>';
580  }
581 
589  public function linkWrapFile($code, File $fileObject)
590  {
591  try {
592  if ($fileObject instanceof File && $fileObject->isIndexed() && $fileObject->checkActionPermission('editMeta') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
593  $metaData = $fileObject->_getMetaData();
594  $urlParameters = [
595  'edit' => [
596  'sys_file_metadata' => [
597  $metaData['uid'] => 'edit'
598  ]
599  ],
600  'returnUrl' => $this->listURL()
601  ];
602  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
603  $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.editMetadata'));
604  $code = '<a class="responsive-title" href="' . htmlspecialchars($url) . '" title="' . $title . '">' . $code . '</a>';
605  }
606  } catch (\Exception $e) {
607  // intentional fall-through
608  }
609  return $code;
610  }
611 
623  public function listURL($altId = '', $table = '-1', $exclList = '')
624  {
626  'target' => rawurlencode($this->folderObject->getCombinedIdentifier()),
627  'imagemode' => $this->thumbs
628  ]);
629  }
630 
637  public function formatFileList(array $files)
638  {
639  $out = '';
640  // first two keys are "0" (default) and "-1" (multiple), after that comes the "other languages"
641  $allSystemLanguages = GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages();
642  $systemLanguages = array_filter($allSystemLanguages, function ($languageRecord) {
643  if ($languageRecord['uid'] === -1 || $languageRecord['uid'] === 0 || !$this->getBackendUser()->checkLanguageAccess($languageRecord['uid'])) {
644  return false;
645  }
646  return true;
647  });
648 
649  foreach ($files as $fileObject) {
650  // Initialization
651  $this->counter++;
652  $this->totalbytes += $fileObject->getSize();
653  $ext = $fileObject->getExtension();
654  $fileName = trim($fileObject->getName());
655  // The icon with link
656  $theIcon = '<span title="' . htmlspecialchars($fileName . ' [' . (int)$fileObject->getUid() . ']') . '">'
657  . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
658  $theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, 'sys_file', $fileObject->getCombinedIdentifier());
659  // Preparing and getting the data-array
660  $theData = [];
661  foreach ($this->fieldArray as $field) {
662  switch ($field) {
663  case 'size':
664  $theData[$field] = GeneralUtility::formatSize($fileObject->getSize(), htmlspecialchars($this->getLanguageService()->getLL('byteSizeUnits')));
665  break;
666  case 'rw':
667  $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>');
668  break;
669  case 'fileext':
670  $theData[$field] = htmlspecialchars(strtoupper($ext));
671  break;
672  case 'tstamp':
673  $theData[$field] = BackendUtility::date($fileObject->getModificationTime());
674  break;
675  case '_CONTROL_':
676  $theData[$field] = $this->makeEdit($fileObject);
677  break;
678  case '_CLIPBOARD_':
679  $theData[$field] = $this->makeClip($fileObject);
680  break;
681  case '_LOCALIZATION_':
682  if (!empty($systemLanguages) && $fileObject->isIndexed() && $fileObject->checkActionPermission('editMeta') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
683  $metaDataRecord = $fileObject->_getMetaData();
684  $translations = $this->getTranslationsForMetaData($metaDataRecord);
685  $languageCode = '';
686 
687  foreach ($systemLanguages as $language) {
688  $languageId = $language['uid'];
689  $flagIcon = $language['flagIcon'];
690  if (array_key_exists($languageId, $translations)) {
691  $title = htmlspecialchars(sprintf($this->getLanguageService()->getLL('editMetadataForLanguage'), $language['title']));
692  // @todo the overlay for the flag needs to be added ($flagIcon . '-overlay')
693  $urlParameters = [
694  'edit' => [
695  'sys_file_metadata' => [
696  $translations[$languageId]['uid'] => 'edit'
697  ]
698  ],
699  'returnUrl' => $this->listURL()
700  ];
701  $flagButtonIcon = $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-edit')->render();
702  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
703  $languageCode .= '<a href="' . htmlspecialchars($url) . '" class="btn btn-default" title="' . $title . '">'
704  . $flagButtonIcon . '</a>';
705  } else {
706  $parameters = [
707  'justLocalized' => 'sys_file_metadata:' . $metaDataRecord['uid'] . ':' . $languageId,
708  'returnUrl' => $this->listURL()
709  ];
710  $returnUrl = BackendUtility::getModuleUrl('record_edit', $parameters);
712  '&cmd[sys_file_metadata][' . $metaDataRecord['uid'] . '][localize]=' . $languageId,
713  $returnUrl
714  );
715  $flagButtonIcon = '<span title="' . htmlspecialchars(sprintf($this->getLanguageService()->getLL('createMetadataForLanguage'), $language['title'])) . '">' . $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-new')->render() . '</span>';
716  $languageCode .= '<a href="' . htmlspecialchars($href) . '" class="btn btn-default">' . $flagButtonIcon . '</a> ';
717  }
718  }
719 
720  // Hide flag button bar when not translated yet
721  $theData[$field] = ' <div class="localisationData btn-group" data-fileid="' . $fileObject->getUid() . '"' .
722  (empty($translations) ? ' style="display: none;"' : '') . '>' . $languageCode . '</div>';
723  $theData[$field] .= '<a class="btn btn-default filelist-translationToggler" data-fileid="' . $fileObject->getUid() . '">' .
724  '<span title="' . htmlspecialchars($this->getLanguageService()->getLL('translateMetadata')) . '">'
725  . $this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL)->render() . '</span>'
726  . '</a>';
727  }
728  break;
729  case '_REF_':
730  $theData[$field] = $this->makeRef($fileObject);
731  break;
732  case 'file':
733  // Edit metadata of file
734  $theData[$field] = $this->linkWrapFile(htmlspecialchars($fileName), $fileObject);
735 
736  if ($fileObject->isMissing()) {
737  $theData[$field] .= '<span class="label label-danger label-space-left">'
738  . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.file_missing'))
739  . '</span>';
740  // Thumbnails?
741  } elseif ($this->thumbs && ($this->isImage($ext) || $this->isMediaFile($ext))) {
742  $processedFile = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, []);
743  if ($processedFile) {
744  $thumbUrl = $processedFile->getPublicUrl(true);
745  $theData[$field] .= '<br /><img src="' . htmlspecialchars($thumbUrl) . '" ' .
746  'width="' . $processedFile->getProperty('width') . '" ' .
747  'height="' . $processedFile->getProperty('height') . '" ' .
748  'title="' . htmlspecialchars($fileName) . '" alt="" />';
749  }
750  }
751  break;
752  default:
753  $theData[$field] = '';
754  if ($fileObject->hasProperty($field)) {
755  $theData[$field] = htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileObject->getProperty($field), $this->fixedL));
756  }
757  }
758  }
759  $out .= $this->addElement(1, $theIcon, $theData);
760  }
761  return $out;
762  }
763 
770  protected function getTranslationsForMetaData($metaDataRecord)
771  {
772  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_metadata');
773  $queryBuilder->getRestrictions()->removeAll();
774  $translationRecords = $queryBuilder->select('*')
775  ->from('sys_file_metadata')
776  ->where(
777  $queryBuilder->expr()->eq(
778  $GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'],
779  $queryBuilder->createNamedParameter($metaDataRecord['uid'], \PDO::PARAM_INT)
780  ),
781  $queryBuilder->expr()->gt(
782  $GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'],
783  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
784  )
785  )
786  ->execute()
787  ->fetchAll();
788 
789  $translations = [];
790  foreach ($translationRecords as $record) {
791  $translations[$record[$GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField']]] = $record;
792  }
793  return $translations;
794  }
795 
802  public function isImage($ext)
803  {
804  return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($ext));
805  }
806 
813  public function isMediaFile($ext)
814  {
815  return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'], strtolower($ext));
816  }
817 
826  public function linkWrapSort($code, $folderIdentifier, $col)
827  {
828  $params = ['id' => $folderIdentifier, 'SET' => [ 'sort' => $col ]];
829 
830  if ($this->sort === $col) {
831  // Check reverse sorting
832  $params['SET']['reverse'] = ($this->sortRev ? '0' : '1');
833  $sortArrow = $this->iconFactory->getIcon('status-status-sorting-' . ($this->sortRev ? 'desc' : 'asc'), Icon::SIZE_SMALL)->render();
834  } else {
835  $params['SET']['reverse'] = 0;
836  $sortArrow = '';
837  }
838  $href = BackendUtility::getModuleUrl('file_FilelistList', $params);
839  return '<a href="' . htmlspecialchars($href) . '">' . $code . ' ' . $sortArrow . '</a>';
840  }
841 
848  public function makeClip($fileOrFolderObject)
849  {
850  if (!$fileOrFolderObject->checkActionPermission('read')) {
851  return '';
852  }
853  $cells = [];
854  $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
855  $fullName = $fileOrFolderObject->getName();
856  $md5 = GeneralUtility::shortMD5($fullIdentifier);
857  // For normal clipboard, add copy/cut buttons:
858  if ($this->clipObj->current === 'normal') {
859  $isSel = $this->clipObj->isSelected('_FILE', $md5);
860  $copyTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.copy'));
861  $cutTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.cut'));
862  $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render();
863  $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render();
864 
865  if ($isSel === 'copy') {
866  $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render();
867  } elseif ($isSel === 'cut') {
868  $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render();
869  }
870 
871  $cells[] = '<a class="btn btn-default"" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 1, ($isSel === 'copy'))) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';
872  // we can only cut if file can be moved
873  if ($fileOrFolderObject->checkActionPermission('move')) {
874  $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 0, ($isSel === 'cut'))) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';
875  } else {
876  $cells[] = $this->spaceIcon;
877  }
878  } else {
879  // For numeric pads, add select checkboxes:
880  $n = '_FILE|' . $md5;
881  $this->CBnames[] = $n;
882  $checked = $this->clipObj->isSelected('_FILE', $md5) ? ' checked="checked"' : '';
883  $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>';
884  }
885  // Display PASTE button, if directory:
886  $elFromTable = $this->clipObj->elFromTable('_FILE');
887  if ($fileOrFolderObject instanceof Folder && !empty($elFromTable) && $fileOrFolderObject->checkActionPermission('write')) {
888  $addPasteButton = true;
889  $elToConfirm = [];
890  foreach ($elFromTable as $key => $element) {
891  $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
892  if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $fileOrFolderObject)) {
893  $addPasteButton = false;
894  }
895  $elToConfirm[$key] = $clipBoardElement->getName();
896  }
897  if ($addPasteButton) {
898  $cells[] = '<a class="btn btn-default t3js-modal-trigger" '
899  . ' href="' . htmlspecialchars($this->clipObj->pasteUrl('_FILE', $fullIdentifier)) . '"'
900  . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText('_FILE', $fullName, 'into', $elToConfirm)) . '"'
901  . ' data-severity="warning"'
902  . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"'
903  . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"'
904  . '>'
905  . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render()
906  . '</a>';
907  }
908  }
909  // Compile items into a DIV-element:
910  return ' <div class="btn-group" role="group">' . implode('', $cells) . '</div>';
911  }
912 
919  public function makeEdit($fileOrFolderObject)
920  {
921  $cells = [];
922  $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
923  $md5 = GeneralUtility::shortMD5($fullIdentifier);
924  $isSel = $this->clipObj->isSelected('_FILE', $md5);
925 
926  // Edit file content (if editable)
927  if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('write') && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], $fileOrFolderObject->getExtension())) {
928  $url = BackendUtility::getModuleUrl('file_edit', ['target' => $fullIdentifier]);
929  $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;';
930  $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') . '">'
931  . $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render()
932  . '</a>';
933  } else {
934  $cells['edit'] = $this->spaceIcon;
935  }
936 
937  // Edit metadata of file
938  if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('editMeta') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
939  $metaData = $fileOrFolderObject->_getMetaData();
940  $urlParameters = [
941  'edit' => [
942  'sys_file_metadata' => [
943  $metaData['uid'] => 'edit'
944  ]
945  ],
946  'returnUrl' => $this->listURL()
947  ];
948  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
949  $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.editMetadata'));
950  $cells['metadata'] = '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" title="' . $title . '">' . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>';
951  }
952 
953  // document view
954  if ($fileOrFolderObject instanceof File) {
955  $fileUrl = $fileOrFolderObject->getPublicUrl(true);
956  if ($fileUrl) {
957  $aOnClick = 'return top.openUrlInWindow(' . GeneralUtility::quoteJSvalue($fileUrl) . ', \'WebFile\');';
958  $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>';
959  } else {
960  $cells['view'] = $this->spaceIcon;
961  }
962  } else {
963  $cells['view'] = $this->spaceIcon;
964  }
965 
966  // replace file
967  if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('replace')) {
968  $url = BackendUtility::getModuleUrl('file_replace', ['target' => $fullIdentifier, 'uid' => $fileOrFolderObject->getUid()]);
969  $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;';
970  $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>';
971  }
972 
973  // rename the file
974  if ($fileOrFolderObject->checkActionPermission('rename')) {
975  $url = BackendUtility::getModuleUrl('file_rename', ['target' => $fullIdentifier]);
976  $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;';
977  $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>';
978  } else {
979  $cells['rename'] = $this->spaceIcon;
980  }
981 
982  // upload files
983  if ($fileOrFolderObject->getStorage()->checkUserActionPermission('add', 'File') && $fileOrFolderObject->checkActionPermission('write')) {
984  if ($fileOrFolderObject instanceof Folder) {
985  $url = BackendUtility::getModuleUrl('file_upload', ['target' => $fullIdentifier]);
986  $uploadOnClick = '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;';
987  $cells['upload'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($uploadOnClick) . '" title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.upload')) . '">' . $this->iconFactory->getIcon('actions-edit-upload', Icon::SIZE_SMALL)->render() . '</a>';
988  }
989  }
990 
991  if ($fileOrFolderObject->checkActionPermission('read')) {
992  $infoOnClick = '';
993  if ($fileOrFolderObject instanceof Folder) {
994  $infoOnClick = 'top.launchView( \'_FOLDER\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
995  } elseif ($fileOrFolderObject instanceof File) {
996  $infoOnClick = 'top.launchView( \'_FILE\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
997  }
998  $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>';
999  } else {
1000  $cells['info'] = $this->spaceIcon;
1001  }
1002 
1003  // delete the file
1004  if ($fileOrFolderObject->checkActionPermission('delete')) {
1005  $identifier = $fileOrFolderObject->getIdentifier();
1006  if ($fileOrFolderObject instanceof Folder) {
1007  $referenceCountText = BackendUtility::referenceCount('_FILE', $identifier, ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.referencesToFolder'));
1008  $deleteType = 'delete_folder';
1009  } else {
1010  $referenceCountText = BackendUtility::referenceCount('sys_file', $fileOrFolderObject->getUid(), ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.referencesToFile'));
1011  $deleteType = 'delete_file';
1012  }
1013 
1014  if ($this->getBackendUser()->jsConfirmation(JsConfirmation::DELETE)) {
1015  $confirmationCheck = '1';
1016  } else {
1017  $confirmationCheck = '0';
1018  }
1019 
1020  $deleteUrl = BackendUtility::getModuleUrl('tce_file');
1021  $confirmationMessage = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.delete'), $fileOrFolderObject->getName()) . $referenceCountText;
1022  $title = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.delete');
1023  $cells['delete'] = '<a href="#" class="btn btn-default t3js-filelist-delete" data-content="' . htmlspecialchars($confirmationMessage)
1024  . '" data-check="' . $confirmationCheck
1025  . '" data-delete-url="' . htmlspecialchars($deleteUrl)
1026  . '" data-title="' . htmlspecialchars($title)
1027  . '" data-identifier="' . htmlspecialchars($fileOrFolderObject->getCombinedIdentifier())
1028  . '" data-delete-type="' . $deleteType
1029  . '" title="' . htmlspecialchars($title) . '">'
1030  . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() . '</a>';
1031  } else {
1032  $cells['delete'] = $this->spaceIcon;
1033  }
1034 
1035  // Hook for manipulating edit icons.
1036  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'])) {
1037  $cells['__fileOrFolderObject'] = $fileOrFolderObject;
1038  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'] as $classData) {
1039  $hookObject = GeneralUtility::getUserObj($classData);
1040  if (!$hookObject instanceof FileListEditIconHookInterface) {
1041  throw new \UnexpectedValueException(
1042  $classData . ' must implement interface ' . FileListEditIconHookInterface::class,
1043  1235225797
1044  );
1045  }
1046  $hookObject->manipulateEditIcons($cells, $this);
1047  }
1048  unset($cells['__fileOrFolderObject']);
1049  }
1050  // Compile items into a DIV-element:
1051  return '<div class="btn-group">' . implode('', $cells) . '</div>';
1052  }
1053 
1060  public function makeRef($fileOrFolderObject)
1061  {
1062  if ($fileOrFolderObject instanceof FolderInterface) {
1063  return '-';
1064  }
1065  // Look up the file in the sys_refindex.
1066  // Exclude sys_file_metadata records as these are no use references
1067  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
1068  $referenceCount = $queryBuilder->count('*')
1069  ->from('sys_refindex')
1070  ->where(
1071  $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
1072  $queryBuilder->expr()->eq(
1073  'ref_table',
1074  $queryBuilder->createNamedParameter('sys_file', \PDO::PARAM_STR)
1075  ),
1076  $queryBuilder->expr()->eq(
1077  'ref_uid',
1078  $queryBuilder->createNamedParameter($fileOrFolderObject->getUid(), \PDO::PARAM_INT)
1079  ),
1080  $queryBuilder->expr()->neq(
1081  'tablename',
1082  $queryBuilder->createNamedParameter('sys_file_metadata', \PDO::PARAM_STR)
1083  )
1084  )
1085  ->execute()
1086  ->fetchColumn();
1087 
1088  return $this->generateReferenceToolTip($referenceCount, '\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileOrFolderObject->getCombinedIdentifier()));
1089  }
1090 
1096  protected function getLanguageService()
1097  {
1098  return $GLOBALS['LANG'];
1099  }
1100 
1106  protected function getBackendUser()
1107  {
1108  return $GLOBALS['BE_USER'];
1109  }
1110 }
linkWrapFile($code, File $fileObject)
Definition: FileList.php:589
makeClip($fileOrFolderObject)
Definition: FileList.php:848
static wrapClickMenuOnIcon( $content, $table, $uid=0, $context='', $_addParams='', $_enDisItems='', $returnTagParameters=false)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
makeRef($fileOrFolderObject)
Definition: FileList.php:1060
getLinkToParentFolder(Folder $currentFolder)
Definition: FileList.php:447
linkClipboardHeaderIcon($string, $_, $cmd, $warning='')
Definition: FileList.php:244
static linkThisScript(array $getParams=[])
static makeInstance($className,... $constructorArguments)
getReadablePath($rootId=null)
Definition: Folder.php:105
start(Folder $folderObject, $pointer, $sort, $sortRev, $clipBoard=false, $bigControlPanel=false)
Definition: FileList.php:208
__construct(FileListController $fileListController)
Definition: FileList.php:186
listURL($altId='', $table='-1', $exclList='')
Definition: FileList.php:623
formatDirList(array $folders)
Definition: FileList.php:487
linkWrapDir($title, Folder $folderObject)
Definition: FileList.php:571
addElement($h, $icon, $data, $rowParams='', $_='', $_2='', $colType='td')
static referenceCount($table, $ref, $msg='', $count=null)
const FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS
Definition: Folder.php:68
makeEdit($fileOrFolderObject)
Definition: FileList.php:919
injectResourceFactory(ResourceFactory $resourceFactory)
Definition: FileList.php:166
checkActionPermission($action)
Definition: File.php:266
getTranslationsForMetaData($metaDataRecord)
Definition: FileList.php:770
static fixed_lgd_cs($string, $chars, $appendString='...')
formatFileList(array $files)
Definition: FileList.php:637
static formatSize($sizeInBytes, $labels='', $base=0)
linkWrapSort($code, $folderIdentifier, $col)
Definition: FileList.php:826
generateReferenceToolTip($references, $launchViewParameter='')
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static getLinkToDataHandlerAction($parameters, $redirectUrl='')
static uniqueList($in_list, $secondParameter=null)