TYPO3 CMS  TYPO3_7-6
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 
37 
42 {
48  public $iLimit = 40;
49 
55  public $thumbs = false;
56 
62  public $spaceIcon;
63 
69  public $fixedL = 30;
70 
76  public $clickMenus = 1;
77 
83  public $sort = '';
84 
90  public $sortRev = 1;
91 
95  public $firstElementNumber = 0;
96 
100  public $clipBoard = 0;
101 
105  public $bigControlPanel = 0;
106 
110  public $JScode = '';
111 
115  public $HTMLcode = '';
116 
120  public $totalbytes = 0;
121 
125  public $dirs = [];
126 
130  public $files = [];
131 
135  public $path = '';
136 
140  protected $folderObject;
141 
147  public $eCounter = 0;
148 
152  public $totalItems = '';
153 
157  public $CBnames = [];
158 
162  public $clipObj;
163 
167  protected $resourceFactory;
168 
173  {
174  $this->resourceFactory = $resourceFactory;
175  }
176 
180  protected $iconFactory;
181 
186 
193  {
194  parent::__construct();
195  $this->fileListController = $fileListController;
196  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
197  }
198 
210  public function start(Folder $folderObject, $pointer, $sort, $sortRev, $clipBoard = false, $bigControlPanel = false)
211  {
212  $this->folderObject = $folderObject;
213  $this->counter = 0;
214  $this->totalbytes = 0;
215  $this->JScode = '';
216  $this->HTMLcode = '';
217  $this->path = $folderObject->getReadablePath();
218  $this->sort = $sort;
219  $this->sortRev = $sortRev;
220  $this->firstElementNumber = $pointer;
221  $this->clipBoard = $clipBoard;
222  $this->bigControlPanel = $bigControlPanel;
223  // Setting the maximum length of the filenames to the user's settings or minimum 30 (= $this->fixedL)
224  $this->fixedL = max($this->fixedL, $this->getBackendUser()->uc['titleLen']);
225  $this->getLanguageService()->includeLLFile('EXT:lang/locallang_common.xlf');
226  $this->resourceFactory = ResourceFactory::getInstance();
227  }
228 
234  public function generateList()
235  {
236  $this->HTMLcode .= $this->getTable('fileext,tstamp,size,rw,_REF_');
237  }
238 
247  {
249  $otherMarkers = [
250  'PAGE_ICON' => '',
251  'TITLE' => ''
252  ];
253  $buttons = [
254  'level_up' => $this->getLinkToParentFolder($folderObject),
255  'refresh' => '',
256  'title' => '',
257  'page_icon' => '',
258  'PASTE' => ''
259  ];
260  // Makes the code for the folder icon in the top
261  if ($folderObject) {
262  $title = $folderObject->getReadablePath();
263  // Start compiling the HTML
264  // If this is some subFolder under the mount root....
265  if ($folderObject->getStorage()->isWithinFileMountBoundaries($folderObject)) {
266  // The icon with link
267  $otherMarkers['PAGE_ICON'] = '<span title="' . htmlspecialchars($title) . '">' . $this->iconFactory->getIconForResource($folderObject, Icon::SIZE_SMALL)->render() . '</span>';
268  } else {
269  // This is the root folder
270  $otherMarkers['PAGE_ICON'] = '<span title="' . htmlspecialchars($title) . '">' . $this->iconFactory->getIconForResource($folderObject, Icon::SIZE_SMALL, null, ['mount-root' => true])->render() . '</span>';
271  }
272  $otherMarkers['TITLE'] .= htmlspecialchars(GeneralUtility::fixed_lgd_cs($title, -($this->fixedL + 20)));
273 
274  if ($this->clickMenus) {
275  $otherMarkers['PAGE_ICON'] = BackendUtility::wrapClickMenuOnIcon($otherMarkers['PAGE_ICON'], $folderObject->getCombinedIdentifier());
276  }
277  // Add paste button if clipboard is initialized
278  if ($this->clipObj instanceof Clipboard && $folderObject->checkActionPermission('write')) {
279  $elFromTable = $this->clipObj->elFromTable('_FILE');
280  if (!empty($elFromTable)) {
281  $addPasteButton = true;
282  $elToConfirm = [];
283  foreach ($elFromTable as $key => $element) {
284  $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
285  if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $folderObject)) {
286  $addPasteButton = false;
287  }
288  $elToConfirm[$key] = $clipBoardElement->getName();
289  }
290  if ($addPasteButton) {
291  $buttons['PASTE'] = '<a href="' . htmlspecialchars($this->clipObj->pasteUrl('_FILE', $folderObject->getCombinedIdentifier())) . '" onclick="return ' . htmlspecialchars($this->clipObj->confirmMsg('_FILE', $this->path, 'into', $elToConfirm)) . '" title="' . $this->getLanguageService()->getLL('clip_paste', true) . '">' . $this->iconFactory->getIcon('actions-document-paste-after', Icon::SIZE_SMALL)->render() . '</a>';
292  }
293  }
294  }
295  }
296  $buttons['refresh'] = '<a href="' . htmlspecialchars($this->listURL()) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.reload', true) . '">' . $this->iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL)->render() . '</a>';
297  return [$buttons, $otherMarkers];
298  }
299 
309  public function linkClipboardHeaderIcon($string, $_, $cmd, $warning = '')
310  {
311  $jsCode = 'document.dblistForm.cmd.value=' . GeneralUtility::quoteJSvalue($cmd)
312  . ';document.dblistForm.submit();';
313 
314  $attributes = [];
315  if ($warning) {
316  $attributes['class'] = 'btn btn-default t3js-modal-trigger';
317  $attributes['data-href'] = 'javascript:' . $jsCode;
318  $attributes['data-severity'] = 'warning';
319  $attributes['data-content'] = $warning;
320  } else {
321  $attributes['class'] = 'btn btn-default';
322  $attributes['onclick'] = $jsCode . 'return false;';
323  }
324 
325  $attributesString = '';
326  foreach ($attributes as $key => $value) {
327  $attributesString .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
328  }
329  return '<a href="#" ' . $attributesString . '>' . $string . '</a>';
330  }
331 
338  public function getTable($rowlist)
339  {
340  // prepare space icon
341  $this->spaceIcon = '<span class="btn btn-default disabled">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
342 
343  // @todo use folder methods directly when they support filters
344  $storage = $this->folderObject->getStorage();
345  $storage->resetFileAndFolderNameFiltersToDefault();
346 
347  // Only render the contents of a browsable storage
348  if ($this->folderObject->getStorage()->isBrowsable()) {
349  try {
350  $foldersCount = $storage->countFoldersInFolder($this->folderObject);
351  $filesCount = $storage->countFilesInFolder($this->folderObject);
353  $foldersCount = 0;
354  $filesCount = 0;
355  }
356 
357  if ($foldersCount <= $this->firstElementNumber) {
358  $foldersFrom = false;
359  $foldersNum = false;
360  } else {
361  $foldersFrom = $this->firstElementNumber;
362  if ($this->firstElementNumber + $this->iLimit > $foldersCount) {
363  $foldersNum = $foldersCount - $this->firstElementNumber;
364  } else {
365  $foldersNum = $this->iLimit;
366  }
367  }
368  if ($foldersCount >= $this->firstElementNumber + $this->iLimit) {
369  $filesFrom = false;
370  $filesNum = false;
371  } else {
372  if ($this->firstElementNumber <= $foldersCount) {
373  $filesFrom = 0;
374  $filesNum = $this->iLimit - $foldersNum;
375  } else {
376  $filesFrom = $this->firstElementNumber - $foldersCount;
377  if ($filesFrom + $this->iLimit > $filesCount) {
378  $filesNum = $filesCount - $filesFrom;
379  } else {
380  $filesNum = $this->iLimit;
381  }
382  }
383  }
384 
385  $folders = $storage->getFoldersInFolder($this->folderObject, $foldersFrom, $foldersNum, true, false, trim($this->sort), (bool)$this->sortRev);
386  $files = $this->folderObject->getFiles($filesFrom, $filesNum, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, false, trim($this->sort), (bool)$this->sortRev);
387  $this->totalItems = $foldersCount + $filesCount;
388  // Adds the code of files/dirs
389  $out = '';
390  $titleCol = 'file';
391  // Cleaning rowlist for duplicates and place the $titleCol as the first column always!
392  $rowlist = '_LOCALIZATION_,' . $rowlist;
393  $rowlist = GeneralUtility::rmFromList($titleCol, $rowlist);
394  $rowlist = GeneralUtility::uniqueList($rowlist);
395  $rowlist = $rowlist ? $titleCol . ',' . $rowlist : $titleCol;
396  if ($this->clipBoard) {
397  $rowlist = str_replace('_LOCALIZATION_,', '_LOCALIZATION_,_CLIPBOARD_,', $rowlist);
398  $this->addElement_tdCssClass['_CLIPBOARD_'] = 'col-clipboard';
399  }
400  if ($this->bigControlPanel) {
401  $rowlist = str_replace('_LOCALIZATION_,', '_LOCALIZATION_,_CONTROL_,', $rowlist);
402  $this->addElement_tdCssClass['_CONTROL_'] = 'col-control';
403  }
404  $this->fieldArray = explode(',', $rowlist);
405 
406  // Add classes to table cells
407  $this->addElement_tdCssClass[$titleCol] = 'col-title';
408  $this->addElement_tdCssClass['_LOCALIZATION_'] = 'col-localizationa';
409 
410  $folders = ListUtility::resolveSpecialFolderNames($folders);
411 
412  $iOut = '';
413  // Directories are added
414  $this->eCounter = $this->firstElementNumber;
415  list(, $code) = $this->fwd_rwd_nav();
416  $iOut .= $code;
417 
418  $iOut .= $this->formatDirList($folders);
419  // Files are added
420  $iOut .= $this->formatFileList($files);
421 
422  $this->eCounter = $this->firstElementNumber + $this->iLimit < $this->totalItems
423  ? $this->firstElementNumber + $this->iLimit
424  : -1;
425  list(, $code) = $this->fwd_rwd_nav();
426  $iOut .= $code;
427 
428  // Header line is drawn
429  $theData = [];
430  foreach ($this->fieldArray as $v) {
431  if ($v == '_CLIPBOARD_' && $this->clipBoard) {
432  $cells = [];
433  $table = '_FILE';
434  $elFromTable = $this->clipObj->elFromTable($table);
435  if (!empty($elFromTable) && $this->folderObject->checkActionPermission('write')) {
436  $addPasteButton = true;
437  $elToConfirm = [];
438  foreach ($elFromTable as $key => $element) {
439  $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
440  if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $this->folderObject)) {
441  $addPasteButton = false;
442  }
443  $elToConfirm[$key] = $clipBoardElement->getName();
444  }
445  if ($addPasteButton) {
446  $cells[] = '<a class="btn btn-default t3js-modal-trigger"' .
447  ' href="' . htmlspecialchars($this->clipObj->pasteUrl(
448  '_FILE',
449  $this->folderObject->getCombinedIdentifier()
450  )) . '"'
451  . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText(
452  '_FILE',
453  $this->path,
454  'into',
455  $elToConfirm
456  )) . '"'
457  . ' data-severity="warning"'
458  . ' data-title="' . $this->getLanguageService()->getLL('clip_paste', true) . '"'
459  . ' title="' . $this->getLanguageService()->getLL('clip_paste', true) . '">'
460  . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)
461  ->render()
462  . '</a>';
463  }
464  }
465  if ($this->clipObj->current !== 'normal' && $iOut) {
466  $cells[] = $this->linkClipboardHeaderIcon('<span title="' . $this->getLanguageService()->getLL('clip_selectMarked', true) . '">' . $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render() . '</span>', $table, 'setCB');
467  $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'));
468  $onClick = 'checkOffCB(' . GeneralUtility::quoteJSvalue(implode(',', $this->CBnames)) . ', this); return false;';
469  $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>';
470  }
471  $theData[$v] = implode('', $cells);
472  } else {
473  // Normal row:
474  $theT = $this->linkWrapSort($this->getLanguageService()->getLL('c_' . $v, true), $this->folderObject->getCombinedIdentifier(), $v);
475  $theData[$v] = $theT;
476  }
477  }
478 
479  $out .= '<thead>' . $this->addelement(1, '', $theData, '', '', '', 'th') . '</thead>';
480  $out .= '<tbody>' . $iOut . '</tbody>';
481  // half line is drawn
482  // finish
483  $out = '
484  <!--
485  Filelist table:
486  -->
487  <div class="table-fit">
488  <table class="table table-striped table-hover" id="typo3-filelist">
489  ' . $out . '
490  </table>
491  </div>';
492  } else {
494  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('storageNotBrowsableMessage'), $this->getLanguageService()->getLL('storageNotBrowsableTitle'), FlashMessage::INFO);
496  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
498  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
499  $defaultFlashMessageQueue->enqueue($flashMessage);
500  $out = '';
501  }
502  return $out;
503  }
504 
512  protected function getLinkToParentFolder(Folder $currentFolder)
513  {
514  $levelUp = '';
515  try {
516  $currentStorage = $currentFolder->getStorage();
517  $parentFolder = $currentFolder->getParentFolder();
518  if ($parentFolder->getIdentifier() !== $currentFolder->getIdentifier() && $currentStorage->isWithinFileMountBoundaries($parentFolder)) {
519  $levelUp = $this->linkWrapDir(
520  '<span title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.upOneLevel', true) . '">'
521  . $this->iconFactory->getIcon('actions-view-go-up', Icon::SIZE_SMALL)->render()
522  . '</span>',
523  $parentFolder
524  );
525  }
526  } catch (\Exception $e) {
527  }
528  return $levelUp;
529  }
530 
536  public function getFolderInfo()
537  {
538  if ($this->counter == 1) {
539  $fileLabel = $this->getLanguageService()->getLL('file', true);
540  } else {
541  $fileLabel = $this->getLanguageService()->getLL('files', true);
542  }
543  return $this->counter . ' ' . $fileLabel . ', ' . GeneralUtility::formatSize($this->totalbytes, $this->getLanguageService()->getLL('byteSizeUnits', true));
544  }
545 
552  public function formatDirList(array $folders)
553  {
554  $out = '';
555  foreach ($folders as $folderName => $folderObject) {
556  $role = $folderObject->getRole();
557  if ($role === FolderInterface::ROLE_PROCESSING) {
558  // don't show processing-folder
559  continue;
560  }
561  if ($role !== FolderInterface::ROLE_DEFAULT) {
562  $displayName = '<strong>' . htmlspecialchars($folderName) . '</strong>';
563  } else {
564  $displayName = htmlspecialchars($folderName);
565  }
566 
567  $isLocked = $folderObject instanceof InaccessibleFolder;
568  $isWritable = $folderObject->checkActionPermission('write');
569 
570  // Initialization
571  $this->counter++;
572 
573  // The icon with link
574  $theIcon = '<span title="' . htmlspecialchars($folderName) . '">' . $this->iconFactory->getIconForResource($folderObject, Icon::SIZE_SMALL)->render() . '</span>';
575  if (!$isLocked && $this->clickMenus) {
576  $theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, $folderObject->getCombinedIdentifier());
577  }
578 
579  // Preparing and getting the data-array
580  $theData = [];
581  if ($isLocked) {
582  foreach ($this->fieldArray as $field) {
583  $theData[$field] = '';
584  }
585  $theData['file'] = $displayName;
586  } else {
587  foreach ($this->fieldArray as $field) {
588  switch ($field) {
589  case 'size':
590  try {
591  $numFiles = $folderObject->getFileCount();
593  $numFiles = 0;
594  }
595  $theData[$field] = $numFiles . ' ' . $this->getLanguageService()->getLL(($numFiles === 1 ? 'file' : 'files'), true);
596  break;
597  case 'rw':
598  $theData[$field] = '<strong class="text-danger">' . $this->getLanguageService()->getLL('read', true) . '</strong>' . (!$isWritable ? '' : '<strong class="text-danger">' . $this->getLanguageService()->getLL('write', true) . '</strong>');
599  break;
600  case 'fileext':
601  $theData[$field] = $this->getLanguageService()->getLL('folder', true);
602  break;
603  case 'tstamp':
604  // @todo: FAL: how to get the mtime info -- $theData[$field] = \TYPO3\CMS\Backend\Utility\BackendUtility::date($theFile['tstamp']);
605  $theData[$field] = '-';
606  break;
607  case 'file':
608  $theData[$field] = $this->linkWrapDir($displayName, $folderObject);
609  break;
610  case '_CONTROL_':
611  $theData[$field] = $this->makeEdit($folderObject);
612  break;
613  case '_CLIPBOARD_':
614  $theData[$field] = $this->makeClip($folderObject);
615  break;
616  case '_REF_':
617  $theData[$field] = $this->makeRef($folderObject);
618  break;
619  default:
620  $theData[$field] = GeneralUtility::fixed_lgd_cs($theData[$field], $this->fixedL);
621  }
622  }
623  }
624  $out .= $this->addelement(1, $theIcon, $theData);
625  }
626  return $out;
627  }
628 
636  public function linkWrapDir($title, Folder $folderObject)
637  {
638  $href = BackendUtility::getModuleUrl('file_FilelistList', ['id' => $folderObject->getCombinedIdentifier()]);
639  $onclick = ' onclick="' . htmlspecialchars(('top.document.getElementsByName("navigation")[0].contentWindow.Tree.highlightActiveItem("file","folder' . GeneralUtility::md5int($folderObject->getCombinedIdentifier()) . '_"+top.fsMod.currentBank)')) . '"';
640  // Sometimes $code contains plain HTML tags. In such a case the string should not be modified!
641  if ((string)$title === strip_tags($title)) {
642  return '<a href="' . htmlspecialchars($href) . '"' . $onclick . ' title="' . htmlspecialchars($title) . '">' . GeneralUtility::fixed_lgd_cs($title, $this->fixedL) . '</a>';
643  } else {
644  return '<a href="' . htmlspecialchars($href) . '"' . $onclick . '>' . $title . '</a>';
645  }
646  }
647 
655  public function linkWrapFile($code, File $fileObject)
656  {
657  try {
658  if ($fileObject instanceof File && $fileObject->isIndexed() && $fileObject->checkActionPermission('write') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
659  $metaData = $fileObject->_getMetaData();
660  $urlParameters = [
661  'edit' => [
662  'sys_file_metadata' => [
663  $metaData['uid'] => 'edit'
664  ]
665  ],
666  'returnUrl' => $this->listURL()
667  ];
668  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
669  $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.editMetadata'));
670  $code = '<a href="' . htmlspecialchars($url) . '" title="' . $title . '">' . GeneralUtility::fixed_lgd_cs($code, $this->fixedL) . '</a>';
671  }
672  } catch (\Exception $e) {
673  // intentional fall-through
674  }
675  return $code;
676  }
677 
689  public function listURL($altId = '', $table = '-1', $exclList = '')
690  {
692  'target' => rawurlencode($this->folderObject->getCombinedIdentifier()),
693  'imagemode' => $this->thumbs
694  ]);
695  }
696 
703  public function formatFileList(array $files)
704  {
705  $out = '';
706  // first two keys are "0" (default) and "-1" (multiple), after that comes the "other languages"
707  $allSystemLanguages = GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages();
708  $systemLanguages = array_filter($allSystemLanguages, function ($languageRecord) {
709  if ($languageRecord['uid'] === -1 || $languageRecord['uid'] === 0 || !$this->getBackendUser()->checkLanguageAccess($languageRecord['uid'])) {
710  return false;
711  } else {
712  return true;
713  }
714  });
715 
716  foreach ($files as $fileObject) {
717  // Initialization
718  $this->counter++;
719  $this->totalbytes += $fileObject->getSize();
720  $ext = $fileObject->getExtension();
721  $fileName = trim($fileObject->getName());
722  // The icon with link
723  $theIcon = '<span title="' . htmlspecialchars($fileName . ' [' . (int)$fileObject->getUid() . ']') . '">'
724  . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
725  if ($this->clickMenus) {
726  $theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, $fileObject->getCombinedIdentifier());
727  }
728  // Preparing and getting the data-array
729  $theData = [];
730  foreach ($this->fieldArray as $field) {
731  switch ($field) {
732  case 'size':
733  $theData[$field] = GeneralUtility::formatSize($fileObject->getSize(), $this->getLanguageService()->getLL('byteSizeUnits', true));
734  break;
735  case 'rw':
736  $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>');
737  break;
738  case 'fileext':
739  $theData[$field] = strtoupper($ext);
740  break;
741  case 'tstamp':
742  $theData[$field] = BackendUtility::date($fileObject->getModificationTime());
743  break;
744  case '_CONTROL_':
745  $theData[$field] = $this->makeEdit($fileObject);
746  break;
747  case '_CLIPBOARD_':
748  $theData[$field] = $this->makeClip($fileObject);
749  break;
750  case '_LOCALIZATION_':
751  if (!empty($systemLanguages) && $fileObject->isIndexed() && $fileObject->checkActionPermission('write') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
752  $metaDataRecord = $fileObject->_getMetaData();
753  $translations = $this->getTranslationsForMetaData($metaDataRecord);
754  $languageCode = '';
755 
756  foreach ($systemLanguages as $language) {
757  $languageId = $language['uid'];
758  $flagIcon = $language['flagIcon'];
759  if (array_key_exists($languageId, $translations)) {
760  $title = htmlspecialchars(sprintf($this->getLanguageService()->getLL('editMetadataForLanguage'), $language['title']));
761  // @todo the overlay for the flag needs to be added ($flagIcon . '-overlay')
762  $urlParameters = [
763  'edit' => [
764  'sys_file_metadata' => [
765  $translations[$languageId]['uid'] => 'edit'
766  ]
767  ],
768  'returnUrl' => $this->listURL()
769  ];
770  $flagButtonIcon = $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-edit')->render();
771  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
772  $languageCode .= '<a href="' . htmlspecialchars($url) . '" class="btn btn-default" title="' . $title . '">'
773  . $flagButtonIcon . '</a>';
774  } else {
775  $parameters = [
776  'justLocalized' => 'sys_file_metadata:' . $metaDataRecord['uid'] . ':' . $languageId,
777  'returnUrl' => $this->listURL()
778  ];
779  $returnUrl = BackendUtility::getModuleUrl('record_edit', $parameters);
781  '&cmd[sys_file_metadata][' . $metaDataRecord['uid'] . '][localize]=' . $languageId,
782  $returnUrl
783  );
784  $flagButtonIcon = '<span title="' . htmlspecialchars(sprintf($this->getLanguageService()->getLL('createMetadataForLanguage'), $language['title'])) . '">' . $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-new')->render() . '</span>';
785  $languageCode .= '<a href="' . htmlspecialchars($href) . '" class="btn btn-default">' . $flagButtonIcon . '</a> ';
786  }
787  }
788 
789  // Hide flag button bar when not translated yet
790  $theData[$field] = ' <div class="localisationData btn-group" data-fileid="' . $fileObject->getUid() . '"' .
791  (empty($translations) ? ' style="display: none;"' : '') . '>' . $languageCode . '</div>';
792  $theData[$field] .= '<a class="btn btn-default filelist-translationToggler" data-fileid="' . $fileObject->getUid() . '">' .
793  '<span title="' . $this->getLanguageService()->getLL('translateMetadata', true) . '">'
794  . $this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL)->render() . '</span>'
795  . '</a>';
796  }
797  break;
798  case '_REF_':
799  $theData[$field] = $this->makeRef($fileObject);
800  break;
801  case 'file':
802  // Edit metadata of file
803  $theData[$field] = $this->linkWrapFile(htmlspecialchars($fileName), $fileObject);
804 
805  if ($fileObject->isMissing()) {
806  $theData[$field] .= '<span class="label label-danger label-space-left">'
807  . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_missing'))
808  . '</span>';
809  // Thumbnails?
810  } elseif ($this->thumbs && ($this->isImage($ext) || $this->isMediaFile($ext))) {
811  $processedFile = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, []);
812  if ($processedFile) {
813  $thumbUrl = $processedFile->getPublicUrl(true);
814  $theData[$field] .= '<br /><img src="' . $thumbUrl . '" ' .
815  'width="' . $processedFile->getProperty('width') . '" ' .
816  'height="' . $processedFile->getProperty('height') . '" ' .
817  'title="' . htmlspecialchars($fileName) . '" alt="" />';
818  }
819  }
820  break;
821  default:
822  $theData[$field] = '';
823  if ($fileObject->hasProperty($field)) {
824  $theData[$field] = htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileObject->getProperty($field), $this->fixedL));
825  }
826  }
827  }
828  $out .= $this->addelement(1, $theIcon, $theData);
829  }
830  return $out;
831  }
832 
839  protected function getTranslationsForMetaData($metaDataRecord)
840  {
841  $where = $GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'] . '=' . (int)$metaDataRecord['uid'] .
842  ' AND ' . $GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'] . '>0';
843  $translationRecords = $this->getDatabaseConnection()->exec_SELECTgetRows('*', 'sys_file_metadata', $where);
844  $translations = [];
845  foreach ($translationRecords as $record) {
846  $translations[$record[$GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField']]] = $record;
847  }
848  return $translations;
849  }
850 
857  public function isImage($ext)
858  {
859  return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($ext));
860  }
861 
868  public function isMediaFile($ext)
869  {
870  return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'], strtolower($ext));
871  }
872 
881  public function linkWrapSort($code, $folderIdentifier, $col)
882  {
883  $params = ['id' => $folderIdentifier, 'SET' => [ 'sort' => $col ]];
884 
885  if ($this->sort === $col) {
886  // Check reverse sorting
887  $params['SET']['reverse'] = ($this->sortRev ? '0' : '1');
888  $sortArrow = $this->iconFactory->getIcon('status-status-sorting-light-' . ($this->sortRev ? 'desc' : 'asc'), Icon::SIZE_SMALL)->render();
889  } else {
890  $params['SET']['reverse'] = 0;
891  $sortArrow = '';
892  }
893  $href = BackendUtility::getModuleUrl('file_FilelistList', $params);
894  return '<a href="' . htmlspecialchars($href) . '">' . $code . ' ' . $sortArrow . '</a>';
895  }
896 
903  public function makeClip($fileOrFolderObject)
904  {
905  if (!$fileOrFolderObject->checkActionPermission('read')) {
906  return '';
907  }
908  $cells = [];
909  $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
910  $fullName = $fileOrFolderObject->getName();
911  $md5 = GeneralUtility::shortmd5($fullIdentifier);
912  // For normal clipboard, add copy/cut buttons:
913  if ($this->clipObj->current === 'normal') {
914  $isSel = $this->clipObj->isSelected('_FILE', $md5);
915  $copyTitle = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.copy', true);
916  $cutTitle = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.cut', true);
917  $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render();
918  $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render();
919 
920  if ($isSel === 'copy') {
921  $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render();
922  } elseif ($isSel === 'cut') {
923  $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render();
924  }
925 
926  $cells[] = '<a class="btn btn-default"" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 1, ($isSel === 'copy'))) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';
927  // we can only cut if file can be moved
928  if ($fileOrFolderObject->checkActionPermission('move')) {
929  $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 0, ($isSel === 'cut'))) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';
930  } else {
931  $cells[] = $this->spaceIcon;
932  }
933  } else {
934  // For numeric pads, add select checkboxes:
935  $n = '_FILE|' . $md5;
936  $this->CBnames[] = $n;
937  $checked = $this->clipObj->isSelected('_FILE', $md5) ? ' checked="checked"' : '';
938  $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>';
939  }
940  // Display PASTE button, if directory:
941  $elFromTable = $this->clipObj->elFromTable('_FILE');
942  if ($fileOrFolderObject instanceof Folder && !empty($elFromTable) && $fileOrFolderObject->checkActionPermission('write')) {
943  $addPasteButton = true;
944  $elToConfirm = [];
945  foreach ($elFromTable as $key => $element) {
946  $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
947  if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $fileOrFolderObject)) {
948  $addPasteButton = false;
949  }
950  $elToConfirm[$key] = $clipBoardElement->getName();
951  }
952  if ($addPasteButton) {
953  $cells[] = '<a class="btn btn-default t3js-modal-trigger" '
954  . ' href="' . htmlspecialchars($this->clipObj->pasteUrl('_FILE', $fullIdentifier)) . '"'
955  . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText('_FILE', $fullName, 'into', $elToConfirm)) . '"'
956  . ' data-severity="warning"'
957  . ' data-title="' . $this->getLanguageService()->getLL('clip_pasteInto', true) . '"'
958  . ' title="' . $this->getLanguageService()->getLL('clip_pasteInto', true) . '"'
959  . '>'
960  . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render()
961  . '</a>';
962  }
963  }
964  // Compile items into a DIV-element:
965  return ' <div class="btn-group" role="group">' . implode('', $cells) . '</div>';
966  }
967 
974  public function makeEdit($fileOrFolderObject)
975  {
976  $cells = [];
977  $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
978  $md5 = GeneralUtility::shortMD5($fullIdentifier);
979  $isSel = $this->clipObj->isSelected('_FILE', $md5);
980 
981  // Edit file content (if editable)
982  if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('write') && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], $fileOrFolderObject->getExtension())) {
983  $url = BackendUtility::getModuleUrl('file_edit', ['target' => $fullIdentifier]);
984  $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;';
985  $cells['edit'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($editOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.editcontent') . '">'
986  . $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render()
987  . '</a>';
988  } else {
989  $cells['edit'] = $this->spaceIcon;
990  }
991  if ($fileOrFolderObject instanceof File) {
992  $fileUrl = $fileOrFolderObject->getPublicUrl(true);
993  if ($fileUrl) {
994  $aOnClick = 'return top.openUrlInWindow(' . GeneralUtility::quoteJSvalue($fileUrl) . ', \'WebFile\');';
995  $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>';
996  } else {
997  $cells['view'] = $this->spaceIcon;
998  }
999  } else {
1000  $cells['view'] = $this->spaceIcon;
1001  }
1002 
1003  // replace file
1004  if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('replace')) {
1005  $url = BackendUtility::getModuleUrl('file_replace', ['target' => $fullIdentifier, 'uid' => $fileOrFolderObject->getUid()]);
1006  $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;';
1007  $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>';
1008  }
1009 
1010  // rename the file
1011  if ($fileOrFolderObject->checkActionPermission('rename')) {
1012  $url = BackendUtility::getModuleUrl('file_rename', ['target' => $fullIdentifier]);
1013  $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;';
1014  $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>';
1015  } else {
1016  $cells['rename'] = $this->spaceIcon;
1017  }
1018  if ($fileOrFolderObject->checkActionPermission('read')) {
1019  $infoOnClick = '';
1020  if ($fileOrFolderObject instanceof Folder) {
1021  $infoOnClick = 'top.launchView( \'_FOLDER\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
1022  } elseif ($fileOrFolderObject instanceof File) {
1023  $infoOnClick = 'top.launchView( \'_FILE\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
1024  }
1025  $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>';
1026  } else {
1027  $cells['info'] = $this->spaceIcon;
1028  }
1029 
1030  // copy the file
1031  if ($fileOrFolderObject->checkActionPermission('copy') && $this->clipObj->current === 'normal') {
1032  $copyTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.copy'));
1033  $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render();
1034 
1035  if ($isSel === 'copy') {
1036  $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render();
1037  }
1038 
1039  $cells['copy'] = '<a class="btn btn-default"" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 1, ($isSel === 'copy'))) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';
1040  }
1041 
1042  // cut the file
1043  if ($fileOrFolderObject->checkActionPermission('move') && $this->clipObj->current === 'normal') {
1044  $cutTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.cut'));
1045  $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render();
1046 
1047  if ($isSel === 'cut') {
1048  $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render();
1049  }
1050 
1051  $cells['cut'] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 0, ($isSel === 'cut'))) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';
1052  }
1053 
1054  // delete the file
1055  if ($fileOrFolderObject->checkActionPermission('delete')) {
1056  $identifier = $fileOrFolderObject->getIdentifier();
1057  if ($fileOrFolderObject instanceof Folder) {
1058  $referenceCountText = BackendUtility::referenceCount('_FILE', $identifier, ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.referencesToFolder'));
1059  } else {
1060  $referenceCountText = BackendUtility::referenceCount('sys_file', $fileOrFolderObject->getUid(), ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.referencesToFile'));
1061  }
1062 
1063  if ($this->getBackendUser()->jsConfirmation(JsConfirmation::DELETE)) {
1064  $confirmationCheck = '1';
1065  } else {
1066  $confirmationCheck = '0';
1067  }
1068 
1069  $deleteUrl = BackendUtility::getModuleUrl('tce_file');
1070  $confirmationMessage = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:mess.delete'), $fileOrFolderObject->getName()) . $referenceCountText;
1071  $title = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:cm.delete');
1072  $cells['delete'] = '<a href="#" class="btn btn-default t3js-filelist-delete" data-content="' . htmlspecialchars($confirmationMessage)
1073  . '" data-check="' . $confirmationCheck
1074  . '" data-delete-url="' . htmlspecialchars($deleteUrl)
1075  . '" data-title="' . htmlspecialchars($title)
1076  . '" data-identifier="' . htmlspecialchars($fileOrFolderObject->getCombinedIdentifier())
1077  . '" data-veri-code="' . $this->getBackendUser()->veriCode()
1078  . '" title="' . htmlspecialchars($title) . '">'
1079  . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() . '</a>';
1080  } else {
1081  $cells['delete'] = $this->spaceIcon;
1082  }
1083 
1084  // Hook for manipulating edit icons.
1085  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'])) {
1086  $cells['__fileOrFolderObject'] = $fileOrFolderObject;
1087  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'] as $classData) {
1088  $hookObject = GeneralUtility::getUserObj($classData);
1089  if (!$hookObject instanceof FileListEditIconHookInterface) {
1090  throw new \UnexpectedValueException(
1091  $classData . ' must implement interface ' . FileListEditIconHookInterface::class,
1092  1235225797
1093  );
1094  }
1095  $hookObject->manipulateEditIcons($cells, $this);
1096  }
1097  unset($cells['__fileOrFolderObject']);
1098  }
1099  // Compile items into a DIV-element:
1100  return '<div class="btn-group">' . implode('', $cells) . '</div>';
1101  }
1102 
1109  public function makeRef($fileOrFolderObject)
1110  {
1111  if ($fileOrFolderObject instanceof FolderInterface) {
1112  return '-';
1113  }
1114  // Look up the file in the sys_refindex.
1115  // Exclude sys_file_metadata records as these are no use references
1116  $databaseConnection = $this->getDatabaseConnection();
1117  $table = 'sys_refindex';
1118  $referenceCount = $databaseConnection->exec_SELECTcountRows(
1119  '*',
1120  $table,
1121  'ref_table=' . $databaseConnection->fullQuoteStr('sys_file', $table)
1122  . ' AND ref_uid=' . (int)$fileOrFolderObject->getUid()
1123  . ' AND deleted=0'
1124  . ' AND tablename != ' . $databaseConnection->fullQuoteStr('sys_file_metadata', $table)
1125  );
1126  return $this->generateReferenceToolTip($referenceCount, '\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileOrFolderObject->getCombinedIdentifier()));
1127  }
1128 
1134  protected function getDatabaseConnection()
1135  {
1136  return $GLOBALS['TYPO3_DB'];
1137  }
1138 
1144  protected function getLanguageService()
1145  {
1146  return $GLOBALS['LANG'];
1147  }
1148 
1154  protected function getBackendUser()
1155  {
1156  return $GLOBALS['BE_USER'];
1157  }
1158 }
linkWrapFile($code, File $fileObject)
Definition: FileList.php:655
makeClip($fileOrFolderObject)
Definition: FileList.php:903
makeRef($fileOrFolderObject)
Definition: FileList.php:1109
getLinkToParentFolder(Folder $currentFolder)
Definition: FileList.php:512
linkClipboardHeaderIcon($string, $_, $cmd, $warning='')
Definition: FileList.php:309
getButtonsAndOtherMarkers(Folder $folderObject)
Definition: FileList.php:246
static linkThisScript(array $getParams=[])
getReadablePath($rootId=null)
Definition: Folder.php:105
start(Folder $folderObject, $pointer, $sort, $sortRev, $clipBoard=false, $bigControlPanel=false)
Definition: FileList.php:210
__construct(FileListController $fileListController)
Definition: FileList.php:192
listURL($altId='', $table='-1', $exclList='')
Definition: FileList.php:689
formatDirList(array $folders)
Definition: FileList.php:552
linkWrapDir($title, Folder $folderObject)
Definition: FileList.php:636
static referenceCount($table, $ref, $msg='', $count=null)
const FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS
Definition: Folder.php:68
makeEdit($fileOrFolderObject)
Definition: FileList.php:974
injectResourceFactory(ResourceFactory $resourceFactory)
Definition: FileList.php:172
checkActionPermission($action)
Definition: File.php:271
getTranslationsForMetaData($metaDataRecord)
Definition: FileList.php:839
static fixed_lgd_cs($string, $chars, $appendString='...')
formatFileList(array $files)
Definition: FileList.php:703
static formatSize($sizeInBytes, $labels='', $base=0)
linkWrapSort($code, $folderIdentifier, $col)
Definition: FileList.php:881
static wrapClickMenuOnIcon( $content, $table, $uid=0, $listFrame=true, $addParams='', $enDisItems='', $returnTagParameters=false)
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)