| Total Complexity | 179 | 
| Total Lines | 1220 | 
| Duplicated Lines | 0 % | 
| Changes | 2 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like FileList often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use FileList, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 50 | class FileList  | 
            ||
| 51 | { | 
            ||
| 52 | /**  | 
            ||
| 53 | * Default Max items shown  | 
            ||
| 54 | *  | 
            ||
| 55 | * @var int  | 
            ||
| 56 | */  | 
            ||
| 57 | public $iLimit = 40;  | 
            ||
| 58 | |||
| 59 | /**  | 
            ||
| 60 | * Thumbnails on records containing files (pictures)  | 
            ||
| 61 | *  | 
            ||
| 62 | * @var bool  | 
            ||
| 63 | */  | 
            ||
| 64 | public $thumbs = false;  | 
            ||
| 65 | |||
| 66 | /**  | 
            ||
| 67 | * Space icon used for alignment when no button is available  | 
            ||
| 68 | *  | 
            ||
| 69 | * @var string  | 
            ||
| 70 | */  | 
            ||
| 71 | public $spaceIcon;  | 
            ||
| 72 | |||
| 73 | /**  | 
            ||
| 74 | * Max length of strings  | 
            ||
| 75 | *  | 
            ||
| 76 | * @var int  | 
            ||
| 77 | */  | 
            ||
| 78 | public $fixedL = 30;  | 
            ||
| 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 = true;  | 
            ||
| 93 | |||
| 94 | /**  | 
            ||
| 95 | * @var int  | 
            ||
| 96 | */  | 
            ||
| 97 | public $firstElementNumber = 0;  | 
            ||
| 98 | |||
| 99 | /**  | 
            ||
| 100 | * @var int  | 
            ||
| 101 | */  | 
            ||
| 102 | public $totalbytes = 0;  | 
            ||
| 103 | |||
| 104 | /**  | 
            ||
| 105 | * This could be set to the total number of items. Used by the fwd_rew_navigation...  | 
            ||
| 106 | *  | 
            ||
| 107 | * @var int  | 
            ||
| 108 | */  | 
            ||
| 109 | public $totalItems = 0;  | 
            ||
| 110 | |||
| 111 | /**  | 
            ||
| 112 | * Decides the columns shown. Filled with values that refers to the keys of the data-array. $this->fieldArray[0] is the title column.  | 
            ||
| 113 | *  | 
            ||
| 114 | * @var array  | 
            ||
| 115 | */  | 
            ||
| 116 | public $fieldArray = [];  | 
            ||
| 117 | |||
| 118 | /**  | 
            ||
| 119 | * Counter increased for each element. Used to index elements for the JavaScript-code that transfers to the clipboard  | 
            ||
| 120 | *  | 
            ||
| 121 | * @var int  | 
            ||
| 122 | */  | 
            ||
| 123 | public $counter = 0;  | 
            ||
| 124 | |||
| 125 | /**  | 
            ||
| 126 | * @var TranslationConfigurationProvider  | 
            ||
| 127 | */  | 
            ||
| 128 | public $translateTools;  | 
            ||
| 129 | |||
| 130 | /**  | 
            ||
| 131 | * Keys are fieldnames and values are td-css-classes to add in addElement();  | 
            ||
| 132 | *  | 
            ||
| 133 | * @var array  | 
            ||
| 134 | */  | 
            ||
| 135 | public $addElement_tdCssClass = [  | 
            ||
| 136 | '_CONTROL_' => 'col-control',  | 
            ||
| 137 | '_CLIPBOARD_' => 'col-clipboard',  | 
            ||
| 138 | 'file' => 'col-title col-responsive',  | 
            ||
| 139 | '_LOCALIZATION_' => 'col-localizationa',  | 
            ||
| 140 | ];  | 
            ||
| 141 | |||
| 142 | /**  | 
            ||
| 143 | * @var Folder  | 
            ||
| 144 | */  | 
            ||
| 145 | protected $folderObject;  | 
            ||
| 146 | |||
| 147 | /**  | 
            ||
| 148 | * @var array  | 
            ||
| 149 | */  | 
            ||
| 150 | public $CBnames = [];  | 
            ||
| 151 | |||
| 152 | /**  | 
            ||
| 153 | * @var Clipboard $clipObj  | 
            ||
| 154 | */  | 
            ||
| 155 | public $clipObj;  | 
            ||
| 156 | |||
| 157 | /**  | 
            ||
| 158 | * @var ResourceFactory  | 
            ||
| 159 | */  | 
            ||
| 160 | protected $resourceFactory;  | 
            ||
| 161 | |||
| 162 | /**  | 
            ||
| 163 | * @var IconFactory  | 
            ||
| 164 | */  | 
            ||
| 165 | protected $iconFactory;  | 
            ||
| 166 | |||
| 167 | /**  | 
            ||
| 168 | * @var int  | 
            ||
| 169 | */  | 
            ||
| 170 | protected $id = 0;  | 
            ||
| 171 | |||
| 172 | /**  | 
            ||
| 173 | * @var UriBuilder  | 
            ||
| 174 | */  | 
            ||
| 175 | protected $uriBuilder;  | 
            ||
| 176 | |||
| 177 | protected ?FileSearchDemand $searchDemand = null;  | 
            ||
| 178 | |||
| 179 | public function __construct()  | 
            ||
| 180 |     { | 
            ||
| 181 | // Setting the maximum length of the filenames to the user's settings or minimum 30 (= $this->fixedL)  | 
            ||
| 182 | $this->fixedL = max($this->fixedL, $this->getBackendUser()->uc['titleLen'] ?? 1);  | 
            ||
| 183 | $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);  | 
            ||
| 184 | $this->translateTools = GeneralUtility::makeInstance(TranslationConfigurationProvider::class);  | 
            ||
| 185 | $this->iLimit = MathUtility::forceIntegerInRange(  | 
            ||
| 186 | $this->getBackendUser()->getTSConfig()['options.']['file_list.']['filesPerPage'] ?? $this->iLimit,  | 
            ||
| 187 | 1  | 
            ||
| 188 | );  | 
            ||
| 189 | // Create clipboard object and initialize that  | 
            ||
| 190 | $this->clipObj = GeneralUtility::makeInstance(Clipboard::class);  | 
            ||
| 191 | $this->clipObj->fileMode = true;  | 
            ||
| 192 | $this->clipObj->initializeClipboard();  | 
            ||
| 193 | $this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);  | 
            ||
| 194 |         $this->getLanguageService()->includeLLFile('EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf'); | 
            ||
| 195 |         $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_common.xlf'); | 
            ||
| 196 | $this->uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);  | 
            ||
| 197 |         $this->spaceIcon = '<span class="btn btn-default disabled">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>'; | 
            ||
| 198 | }  | 
            ||
| 199 | |||
| 200 | /**  | 
            ||
| 201 | * Initialization of class  | 
            ||
| 202 | *  | 
            ||
| 203 | * @param Folder $folderObject The folder to work on  | 
            ||
| 204 | * @param int $pointer Pointer  | 
            ||
| 205 | * @param string $sort Sorting column  | 
            ||
| 206 | * @param bool $sortRev Sorting direction  | 
            ||
| 207 | * @param bool $clipBoard  | 
            ||
| 208 | */  | 
            ||
| 209 | public function start(Folder $folderObject, $pointer, $sort, $sortRev, $clipBoard = false)  | 
            ||
| 210 |     { | 
            ||
| 211 | $this->folderObject = $folderObject;  | 
            ||
| 212 | $this->counter = 0;  | 
            ||
| 213 | $this->totalbytes = 0;  | 
            ||
| 214 | $this->sort = $sort;  | 
            ||
| 215 | $this->sortRev = $sortRev;  | 
            ||
| 216 | $this->firstElementNumber = $pointer;  | 
            ||
| 217 | // Cleaning rowlist for duplicates and place the $titleCol as the first column always!  | 
            ||
| 218 | $rowlist = 'file,_LOCALIZATION_,_CONTROL_,fileext,tstamp,size,rw,_REF_';  | 
            ||
| 219 |         if ($clipBoard) { | 
            ||
| 220 |             $rowlist = str_replace('_CONTROL_,', '_CONTROL_,_CLIPBOARD_,', $rowlist); | 
            ||
| 221 | }  | 
            ||
| 222 |         $this->fieldArray = explode(',', $rowlist); | 
            ||
| 223 | }  | 
            ||
| 224 | |||
| 225 | /**  | 
            ||
| 226 | * Wrapping input string in a link with clipboard command.  | 
            ||
| 227 | *  | 
            ||
| 228 | * @param string $string String to be linked - must be htmlspecialchar'ed / prepared before.  | 
            ||
| 229 | * @param string $cmd "cmd" value  | 
            ||
| 230 | * @param string $warning Warning for JS confirm message  | 
            ||
| 231 | * @return string Linked string  | 
            ||
| 232 | */  | 
            ||
| 233 | public function linkClipboardHeaderIcon($string, $cmd, $warning = '')  | 
            ||
| 234 |     { | 
            ||
| 235 |         if ($warning) { | 
            ||
| 236 | $attributes['class'] = 'btn btn-default t3js-modal-trigger';  | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 237 | $attributes['data-severity'] = 'warning';  | 
            ||
| 238 | $attributes['data-bs-content'] = $warning;  | 
            ||
| 239 | $attributes['data-event-name'] = 'filelist:clipboard:cmd';  | 
            ||
| 240 | $attributes['data-event-payload'] = $cmd;  | 
            ||
| 241 |         } else { | 
            ||
| 242 | $attributes['class'] = 'btn btn-default';  | 
            ||
| 243 | $attributes['data-filelist-clipboard-cmd'] = $cmd;  | 
            ||
| 244 | }  | 
            ||
| 245 | |||
| 246 | return '<button type="button" ' . GeneralUtility::implodeAttributes($attributes, true) . '>' . $string . '</button>';  | 
            ||
| 247 | }  | 
            ||
| 248 | |||
| 249 | /**  | 
            ||
| 250 | * Returns a table with directories and files listed.  | 
            ||
| 251 | *  | 
            ||
| 252 | * @param FileSearchDemand|null $searchDemand  | 
            ||
| 253 | * @return string HTML-table  | 
            ||
| 254 | */  | 
            ||
| 255 | public function getTable(?FileSearchDemand $searchDemand = null): string  | 
            ||
| 256 |     { | 
            ||
| 257 |         if ($searchDemand !== null) { | 
            ||
| 258 | // Store given search demand  | 
            ||
| 259 | $this->searchDemand = $searchDemand;  | 
            ||
| 260 | // Search currently only works for files  | 
            ||
| 261 | $folders = [];  | 
            ||
| 262 | // Find files by the given search demand  | 
            ||
| 263 | $files = iterator_to_array($this->folderObject->searchFiles($this->searchDemand));  | 
            ||
| 264 | // @todo Currently files, which got deleted in the file system, are still found.  | 
            ||
| 265 | // Therefore we have to ask their parent folder if it still contains the file.  | 
            ||
| 266 |             $files = array_filter($files, static function (FileInterface $file): bool { | 
            ||
| 267 |                 try { | 
            ||
| 268 |                     if ($file->getParentFolder()->hasFile($file->getName())) { | 
            ||
| 269 | return true;  | 
            ||
| 270 | }  | 
            ||
| 271 |                 } catch (ResourceDoesNotExistException $e) { | 
            ||
| 272 | // Nothing to do, file does not longer exist in folder  | 
            ||
| 273 | }  | 
            ||
| 274 | return false;  | 
            ||
| 275 | });  | 
            ||
| 276 | |||
| 277 | // @todo We have to manually slice the search result, since it may  | 
            ||
| 278 | // contain invalid files, which were manually filtered out above.  | 
            ||
| 279 | // This should be fixed, so we can use the $firstResult and $maxResults  | 
            ||
| 280 | // properties of the search demand directly.  | 
            ||
| 281 | $this->totalItems = count($files);  | 
            ||
| 282 | $filesNum = $this->firstElementNumber + $this->iLimit > $this->totalItems  | 
            ||
| 283 | ? $this->totalItems - $this->firstElementNumber  | 
            ||
| 284 | : $this->iLimit;  | 
            ||
| 285 | $files = array_slice($files, $this->firstElementNumber, $filesNum);  | 
            ||
| 286 | |||
| 287 | // Add special "Path" field for the search result  | 
            ||
| 288 | array_unshift($this->fieldArray, '_PATH_');  | 
            ||
| 289 |         } else { | 
            ||
| 290 | // @todo use folder methods directly when they support filters  | 
            ||
| 291 | $storage = $this->folderObject->getStorage();  | 
            ||
| 292 | $storage->resetFileAndFolderNameFiltersToDefault();  | 
            ||
| 293 | |||
| 294 | // Only render the contents of a browsable storage  | 
            ||
| 295 |             if (!$this->folderObject->getStorage()->isBrowsable()) { | 
            ||
| 296 | return '';  | 
            ||
| 297 | }  | 
            ||
| 298 |             try { | 
            ||
| 299 | $foldersCount = $storage->countFoldersInFolder($this->folderObject);  | 
            ||
| 300 | $filesCount = $storage->countFilesInFolder($this->folderObject);  | 
            ||
| 301 |             } catch (InsufficientFolderAccessPermissionsException $e) { | 
            ||
| 302 | $foldersCount = 0;  | 
            ||
| 303 | $filesCount = 0;  | 
            ||
| 304 | }  | 
            ||
| 305 | |||
| 306 |             if ($foldersCount <= $this->firstElementNumber) { | 
            ||
| 307 | $foldersFrom = false;  | 
            ||
| 308 | $foldersNum = false;  | 
            ||
| 309 |             } else { | 
            ||
| 310 | $foldersFrom = $this->firstElementNumber;  | 
            ||
| 311 |                 if ($this->firstElementNumber + $this->iLimit > $foldersCount) { | 
            ||
| 312 | $foldersNum = $foldersCount - $this->firstElementNumber;  | 
            ||
| 313 |                 } else { | 
            ||
| 314 | $foldersNum = $this->iLimit;  | 
            ||
| 315 | }  | 
            ||
| 316 | }  | 
            ||
| 317 |             if ($foldersCount >= $this->firstElementNumber + $this->iLimit) { | 
            ||
| 318 | $filesFrom = false;  | 
            ||
| 319 | $filesNum = false;  | 
            ||
| 320 |             } elseif ($this->firstElementNumber <= $foldersCount) { | 
            ||
| 321 | $filesFrom = 0;  | 
            ||
| 322 | $filesNum = $this->iLimit - $foldersNum;  | 
            ||
| 323 |             } else { | 
            ||
| 324 | $filesFrom = $this->firstElementNumber - $foldersCount;  | 
            ||
| 325 |                 if ($filesFrom + $this->iLimit > $filesCount) { | 
            ||
| 326 | $filesNum = $filesCount - $filesFrom;  | 
            ||
| 327 |                 } else { | 
            ||
| 328 | $filesNum = $this->iLimit;  | 
            ||
| 329 | }  | 
            ||
| 330 | }  | 
            ||
| 331 | |||
| 332 | $folders = $storage->getFoldersInFolder($this->folderObject, $foldersFrom, $foldersNum, true, false, trim($this->sort), (bool)$this->sortRev);  | 
            ||
| 333 | $files = $this->folderObject->getFiles($filesFrom, $filesNum, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, false, trim($this->sort), (bool)$this->sortRev);  | 
            ||
| 334 | $this->totalItems = $foldersCount + $filesCount;  | 
            ||
| 335 | |||
| 336 | // Adds the code of files/dirs  | 
            ||
| 337 | $folders = ListUtility::resolveSpecialFolderNames($folders);  | 
            ||
| 338 | }  | 
            ||
| 339 | |||
| 340 | $iOut = '';  | 
            ||
| 341 | // Directories are added  | 
            ||
| 342 | $iOut .= $this->fwd_rwd_nav($this->firstElementNumber);  | 
            ||
| 343 | |||
| 344 | $iOut .= $this->formatDirList($folders);  | 
            ||
| 345 | // Files are added  | 
            ||
| 346 | $iOut .= $this->formatFileList($files);  | 
            ||
| 347 | |||
| 348 | $amountOfItemsShownOnCurrentPage = $this->firstElementNumber + $this->iLimit < $this->totalItems  | 
            ||
| 349 | ? $this->firstElementNumber + $this->iLimit  | 
            ||
| 350 | : -1;  | 
            ||
| 351 | $iOut .= $this->fwd_rwd_nav($amountOfItemsShownOnCurrentPage);  | 
            ||
| 352 | |||
| 353 | // Header line is drawn  | 
            ||
| 354 | $theData = [];  | 
            ||
| 355 |         foreach ($this->fieldArray as $v) { | 
            ||
| 356 |             if ($v === '_CLIPBOARD_') { | 
            ||
| 357 | $theData[$v] = $this->renderClipboardHeaderRow(!empty($iOut));  | 
            ||
| 358 |             } elseif ($v === '_REF_') { | 
            ||
| 359 |                 $theData[$v] = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels._REF_')); | 
            ||
| 360 |             } elseif ($v === '_PATH_') { | 
            ||
| 361 |                 $theData[$v] = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels._PATH_')); | 
            ||
| 362 |             } else { | 
            ||
| 363 | // Normal row  | 
            ||
| 364 | $theData[$v] = $this->linkWrapSort($v);  | 
            ||
| 365 | }  | 
            ||
| 366 | }  | 
            ||
| 367 | |||
| 368 | return '  | 
            ||
| 369 | <div class="mb-4 mt-2">  | 
            ||
| 370 | <div class="table-fit mb-0">  | 
            ||
| 371 | <table class="table table-striped table-hover" id="typo3-filelist">  | 
            ||
| 372 |                         <thead>' . $this->addElement('', $theData, true) . '</thead> | 
            ||
| 373 | <tbody>' . $iOut . '</tbody>  | 
            ||
| 374 | </table>  | 
            ||
| 375 | </div>  | 
            ||
| 376 | </div>';  | 
            ||
| 377 | }  | 
            ||
| 378 | |||
| 379 | protected function renderClipboardHeaderRow(bool $hasContent): string  | 
            ||
| 380 |     { | 
            ||
| 381 | $cells = [];  | 
            ||
| 382 |         $elFromTable = $this->clipObj->elFromTable('_FILE'); | 
            ||
| 383 |         if (!empty($elFromTable) && $this->folderObject->checkActionPermission('write')) { | 
            ||
| 384 | $clipboardMode = $this->clipObj->clipData[$this->clipObj->current]['mode'] ?? '';  | 
            ||
| 385 | $permission = $clipboardMode === 'copy' ? 'copy' : 'move';  | 
            ||
| 386 | $addPasteButton = $this->folderObject->checkActionPermission($permission);  | 
            ||
| 387 | $elToConfirm = [];  | 
            ||
| 388 |             foreach ($elFromTable as $key => $element) { | 
            ||
| 389 | $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);  | 
            ||
| 390 |                 if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $this->folderObject)) { | 
            ||
| 391 | $addPasteButton = false;  | 
            ||
| 392 | }  | 
            ||
| 393 | $elToConfirm[$key] = $clipBoardElement->getName();  | 
            ||
| 394 | }  | 
            ||
| 395 |             if ($addPasteButton) { | 
            ||
| 396 | $cells[] = '<a class="btn btn-default t3js-modal-trigger"' .  | 
            ||
| 397 | ' href="' . htmlspecialchars($this->clipObj->pasteUrl(  | 
            ||
| 398 | '_FILE',  | 
            ||
| 399 | $this->folderObject->getCombinedIdentifier()  | 
            ||
| 400 | )) . '"'  | 
            ||
| 401 | . ' data-bs-content="' . htmlspecialchars($this->clipObj->confirmMsgText(  | 
            ||
| 402 | '_FILE',  | 
            ||
| 403 | $this->folderObject->getReadablePath(),  | 
            ||
| 404 | 'into',  | 
            ||
| 405 | $elToConfirm  | 
            ||
| 406 | )) . '"'  | 
            ||
| 407 | . ' data-severity="warning"'  | 
            ||
| 408 |                     . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_paste')) . '"' | 
            ||
| 409 |                     . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_paste')) . '">' | 
            ||
| 410 |                     . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL) | 
            ||
| 411 | ->render()  | 
            ||
| 412 | . '</a>';  | 
            ||
| 413 |             } else { | 
            ||
| 414 | $cells[] = $this->spaceIcon;  | 
            ||
| 415 | }  | 
            ||
| 416 | }  | 
            ||
| 417 |         if ($this->clipObj->current !== 'normal' && $hasContent) { | 
            ||
| 418 |             $cells[] = $this->linkClipboardHeaderIcon('<span title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_selectMarked')) . '">' . $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render() . '</span>', 'setCB'); | 
            ||
| 419 |             $cells[] = $this->linkClipboardHeaderIcon('<span title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_deleteMarked')) . '">' . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() . '</span>', 'delete', $this->getLanguageService()->getLL('clip_deleteMarkedWarning')); | 
            ||
| 420 |             $cells[] = '<a class="btn btn-default t3js-toggle-all-checkboxes" data-checkboxes-names="' . htmlspecialchars(implode(',', $this->CBnames)) . '" rel="" href="#" title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_markRecords')) . '">' . $this->iconFactory->getIcon('actions-document-select', Icon::SIZE_SMALL)->render() . '</a>'; | 
            ||
| 421 | }  | 
            ||
| 422 |         if (!empty($cells)) { | 
            ||
| 423 |             return '<div class="btn-group">' . implode('', $cells) . '</div>'; | 
            ||
| 424 | }  | 
            ||
| 425 | return '';  | 
            ||
| 426 | }  | 
            ||
| 427 | |||
| 428 | /**  | 
            ||
| 429 | * Returns a table-row with the content from the fields in the input data array.  | 
            ||
| 430 | * OBS: $this->fieldArray MUST be set! (represents the list of fields to display)  | 
            ||
| 431 | *  | 
            ||
| 432 | * @param string $icon Is the <img>+<a> of the record. If not supplied the first 'join'-icon will be a 'line' instead  | 
            ||
| 433 | * @param array $data Is the data array, record with the fields. Notice: These fields are (currently) NOT htmlspecialchar'ed before being wrapped in <td>-tags  | 
            ||
| 434 | * @param bool $isTableHeader Whether the element to be added is a table header  | 
            ||
| 435 | *  | 
            ||
| 436 | * @return string HTML content for the table row  | 
            ||
| 437 | */  | 
            ||
| 438 | public function addElement(string $icon, array $data, bool $isTableHeader = false): string  | 
            ||
| 439 |     { | 
            ||
| 440 | // Initialize additional data attributes for the row  | 
            ||
| 441 | // Note: To be consistent with the other $data values, the additional data attributes  | 
            ||
| 442 | // are not htmlspecialchar'ed before being added to the table row. Therefore it  | 
            ||
| 443 | // has to be ensured they are properly escaped when applied to the $data array!  | 
            ||
| 444 | $dataAttributes = [];  | 
            ||
| 445 |         foreach (['type', 'file-uid', 'metadata-uid', 'folder-identifier', 'combined-identifier'] as $dataAttribute) { | 
            ||
| 446 |             if (isset($data[$dataAttribute])) { | 
            ||
| 447 | $dataAttributes['data-' . $dataAttribute] = $data[$dataAttribute];  | 
            ||
| 448 | // Unset as we don't need them anymore, when building the table cells  | 
            ||
| 449 | unset($data[$dataAttribute]);  | 
            ||
| 450 | }  | 
            ||
| 451 | }  | 
            ||
| 452 | |||
| 453 | // Initialize rendering.  | 
            ||
| 454 | $cols = [];  | 
            ||
| 455 | $colType = $isTableHeader ? 'th' : 'td';  | 
            ||
| 456 | $colspan = '';  | 
            ||
| 457 | $colspanCounter = 0;  | 
            ||
| 458 | $lastField = '';  | 
            ||
| 459 | // Traverse field array which contains the data to present:  | 
            ||
| 460 |         foreach ($this->fieldArray as $fieldName) { | 
            ||
| 461 |             if (isset($data[$fieldName])) { | 
            ||
| 462 |                 if ($lastField && isset($data[$lastField])) { | 
            ||
| 463 | $cssClass = $this->addElement_tdCssClass[$lastField] ?? '';  | 
            ||
| 464 | $cols[] = '<' . $colType . ' class="' . $cssClass . '"' . $colspan . '>' . $data[$lastField] . '</' . $colType . '>';  | 
            ||
| 465 | }  | 
            ||
| 466 | $lastField = $fieldName;  | 
            ||
| 467 | $colspanCounter = 1;  | 
            ||
| 468 |             } else { | 
            ||
| 469 |                 if (!$lastField) { | 
            ||
| 470 | $lastField = $fieldName;  | 
            ||
| 471 | }  | 
            ||
| 472 | $colspanCounter++;  | 
            ||
| 473 | }  | 
            ||
| 474 | $colspan = ($colspanCounter > 1) ? ' colspan="' . $colspanCounter . '"' : '';  | 
            ||
| 475 | }  | 
            ||
| 476 |         if ($lastField) { | 
            ||
| 477 | $cssClass = $this->addElement_tdCssClass[$lastField] ?? '';  | 
            ||
| 478 | $cols[] = '<' . $colType . ' class="' . $cssClass . '"' . $colspan . '>' . $data[$lastField] . '</' . $colType . '>';  | 
            ||
| 479 | }  | 
            ||
| 480 | |||
| 481 | // Add the the table row  | 
            ||
| 482 | return '  | 
            ||
| 483 | <tr ' . GeneralUtility::implodeAttributes($dataAttributes) . '>  | 
            ||
| 484 | <' . $colType . ' class="col-icon nowrap">' . ($icon ?: '') . '</' . $colType . '>'  | 
            ||
| 485 | . implode(PHP_EOL, $cols) .  | 
            ||
| 486 | '</tr>';  | 
            ||
| 487 | }  | 
            ||
| 488 | |||
| 489 | /**  | 
            ||
| 490 | * Creates a forward/reverse button based on the status of ->eCounter, ->firstElementNumber, ->iLimit  | 
            ||
| 491 | *  | 
            ||
| 492 | * @return string the table-row code for the element  | 
            ||
| 493 | */  | 
            ||
| 494 | public function fwd_rwd_nav(int $currentItemCount): string  | 
            ||
| 495 |     { | 
            ||
| 496 | $code = '';  | 
            ||
| 497 |         if ($currentItemCount >= $this->firstElementNumber && $currentItemCount < $this->firstElementNumber + $this->iLimit) { | 
            ||
| 498 |             if ($this->firstElementNumber && $currentItemCount == $this->firstElementNumber) { | 
            ||
| 499 | // Reverse  | 
            ||
| 500 | $theData = [];  | 
            ||
| 501 | $href = $this->listURL(['pointer' => ($currentItemCount - $this->iLimit)]);  | 
            ||
| 502 | $theData['file'] = '<a href="' . htmlspecialchars($href) . '">' . $this->iconFactory->getIcon(  | 
            ||
| 503 | 'actions-move-up',  | 
            ||
| 504 | Icon::SIZE_SMALL  | 
            ||
| 505 | )->render() . ' <i>[' . (max(0, $currentItemCount - $this->iLimit) + 1) . ' - ' . $currentItemCount . ']</i></a>';  | 
            ||
| 506 |                 $code = $this->addElement('', $theData); | 
            ||
| 507 | }  | 
            ||
| 508 | return $code;  | 
            ||
| 509 | }  | 
            ||
| 510 |         if ($currentItemCount === $this->firstElementNumber + $this->iLimit) { | 
            ||
| 511 | // Forward  | 
            ||
| 512 | $theData = [];  | 
            ||
| 513 | $href = $this->listURL(['pointer' => $currentItemCount]);  | 
            ||
| 514 | $theData['file'] = '<a href="' . htmlspecialchars($href) . '">' . $this->iconFactory->getIcon(  | 
            ||
| 515 | 'actions-move-down',  | 
            ||
| 516 | Icon::SIZE_SMALL  | 
            ||
| 517 | )->render() . ' <i>[' . ($currentItemCount + 1) . ' - ' . $this->totalItems . ']</i></a>';  | 
            ||
| 518 |             $code = $this->addElement('', $theData); | 
            ||
| 519 | }  | 
            ||
| 520 | return $code;  | 
            ||
| 521 | }  | 
            ||
| 522 | |||
| 523 | /**  | 
            ||
| 524 | * Gets the number of files and total size of a folder  | 
            ||
| 525 | *  | 
            ||
| 526 | * @return string  | 
            ||
| 527 | */  | 
            ||
| 528 | public function getFolderInfo()  | 
            ||
| 536 | }  | 
            ||
| 537 | |||
| 538 | /**  | 
            ||
| 539 | * This returns tablerows for the directories in the array $items['sorting'].  | 
            ||
| 540 | *  | 
            ||
| 541 | * @param Folder[] $folders Folders of \TYPO3\CMS\Core\Resource\Folder  | 
            ||
| 542 | * @return string HTML table rows.  | 
            ||
| 543 | */  | 
            ||
| 544 | public function formatDirList(array $folders)  | 
            ||
| 545 |     { | 
            ||
| 546 | $out = '';  | 
            ||
| 547 |         foreach ($folders as $folderName => $folderObject) { | 
            ||
| 548 | $role = $folderObject->getRole();  | 
            ||
| 549 |             if ($role === FolderInterface::ROLE_PROCESSING) { | 
            ||
| 550 | // don't show processing-folder  | 
            ||
| 551 | continue;  | 
            ||
| 552 | }  | 
            ||
| 553 |             if ($role !== FolderInterface::ROLE_DEFAULT) { | 
            ||
| 554 | $displayName = '<strong>' . htmlspecialchars($folderName) . '</strong>';  | 
            ||
| 555 |             } else { | 
            ||
| 556 | $displayName = htmlspecialchars($folderName);  | 
            ||
| 557 | }  | 
            ||
| 558 | |||
| 559 | $isLocked = $folderObject instanceof InaccessibleFolder;  | 
            ||
| 560 |             $isWritable = $folderObject->checkActionPermission('write'); | 
            ||
| 561 | |||
| 562 | // Initialization  | 
            ||
| 563 | $this->counter++;  | 
            ||
| 564 | |||
| 565 | // The icon with link  | 
            ||
| 566 | $theIcon = '<span title="' . htmlspecialchars($folderName) . '">' . $this->iconFactory->getIconForResource($folderObject, Icon::SIZE_SMALL)->render() . '</span>';  | 
            ||
| 567 |             if (!$isLocked) { | 
            ||
| 568 | $theIcon = (string)BackendUtility::wrapClickMenuOnIcon($theIcon, 'sys_file', $folderObject->getCombinedIdentifier());  | 
            ||
| 569 | }  | 
            ||
| 570 | |||
| 571 | // Preparing and getting the data-array  | 
            ||
| 572 | $theData = [  | 
            ||
| 573 | 'type' => 'folder',  | 
            ||
| 574 | 'folder-identifier' => htmlspecialchars($folderObject->getIdentifier()),  | 
            ||
| 575 | 'combined-identifier' => htmlspecialchars($folderObject->getCombinedIdentifier()),  | 
            ||
| 576 | ];  | 
            ||
| 577 |             if ($isLocked) { | 
            ||
| 578 |                 foreach ($this->fieldArray as $field) { | 
            ||
| 579 | $theData[$field] = '';  | 
            ||
| 580 | }  | 
            ||
| 581 | $theData['file'] = $displayName;  | 
            ||
| 582 |             } else { | 
            ||
| 583 |                 foreach ($this->fieldArray as $field) { | 
            ||
| 584 |                     switch ($field) { | 
            ||
| 585 | case 'size':  | 
            ||
| 586 |                             try { | 
            ||
| 587 | $numFiles = $folderObject->getFileCount();  | 
            ||
| 588 |                             } catch (InsufficientFolderAccessPermissionsException $e) { | 
            ||
| 589 | $numFiles = 0;  | 
            ||
| 590 | }  | 
            ||
| 591 | $theData[$field] = $numFiles . ' ' . htmlspecialchars($this->getLanguageService()->getLL(($numFiles === 1 ? 'file' : 'files')));  | 
            ||
| 592 | break;  | 
            ||
| 593 | case 'rw':  | 
            ||
| 594 |                             $theData[$field] = '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('read')) . '</strong>' . (!$isWritable ? '' : '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('write')) . '</strong>'); | 
            ||
| 595 | break;  | 
            ||
| 596 | case 'fileext':  | 
            ||
| 597 |                             $theData[$field] = htmlspecialchars($this->getLanguageService()->getLL('folder')); | 
            ||
| 598 | break;  | 
            ||
| 599 | case 'tstamp':  | 
            ||
| 600 | $tstamp = $folderObject->getModificationTime();  | 
            ||
| 601 | $theData[$field] = $tstamp ? BackendUtility::date($tstamp) : '-';  | 
            ||
| 602 | break;  | 
            ||
| 603 | case 'file':  | 
            ||
| 604 | $theData[$field] = $this->linkWrapDir($displayName, $folderObject);  | 
            ||
| 605 | break;  | 
            ||
| 606 | case '_CONTROL_':  | 
            ||
| 607 | $theData[$field] = $this->makeEdit($folderObject);  | 
            ||
| 608 | break;  | 
            ||
| 609 | case '_CLIPBOARD_':  | 
            ||
| 610 | $theData[$field] = $this->makeClip($folderObject);  | 
            ||
| 611 | break;  | 
            ||
| 612 | case '_REF_':  | 
            ||
| 613 | $theData[$field] = $this->makeRef($folderObject);  | 
            ||
| 614 | break;  | 
            ||
| 615 | case '_PATH_':  | 
            ||
| 616 | $theData[$field] = $this->makePath($folderObject);  | 
            ||
| 617 | break;  | 
            ||
| 618 | default:  | 
            ||
| 619 | $theData[$field] = GeneralUtility::fixed_lgd_cs($theData[$field] ?? '', $this->fixedL);  | 
            ||
| 620 | }  | 
            ||
| 621 | }  | 
            ||
| 622 | }  | 
            ||
| 623 | $out .= $this->addElement($theIcon, $theData);  | 
            ||
| 624 | }  | 
            ||
| 625 | return $out;  | 
            ||
| 626 | }  | 
            ||
| 627 | |||
| 628 | /**  | 
            ||
| 629 | * Wraps the directory-titles  | 
            ||
| 630 | *  | 
            ||
| 631 | * @param string $title String to be wrapped in links  | 
            ||
| 632 | * @param Folder $folderObject Folder to work on  | 
            ||
| 633 | * @return string HTML  | 
            ||
| 634 | */  | 
            ||
| 635 | public function linkWrapDir($title, Folder $folderObject)  | 
            ||
| 636 |     { | 
            ||
| 637 | $href = $this->listURL(['id' => $folderObject->getCombinedIdentifier(), 'searchTerm' => '', 'pointer' => 0]);  | 
            ||
| 638 | $triggerTreeUpdateAttribute = sprintf(  | 
            ||
| 639 | ' data-tree-update-request="%s"',  | 
            ||
| 640 | htmlspecialchars($folderObject->getCombinedIdentifier())  | 
            ||
| 641 | );  | 
            ||
| 642 | // Sometimes $code contains plain HTML tags. In such a case the string should not be modified!  | 
            ||
| 643 |         if ((string)$title === strip_tags($title)) { | 
            ||
| 644 | return '<a href="' . htmlspecialchars($href) . '"' . $triggerTreeUpdateAttribute . ' title="' . htmlspecialchars($title) . '">' . $title . '</a>';  | 
            ||
| 645 | }  | 
            ||
| 646 | return '<a href="' . htmlspecialchars($href) . '"' . $triggerTreeUpdateAttribute . '>' . $title . '</a>';  | 
            ||
| 647 | }  | 
            ||
| 648 | |||
| 649 | /**  | 
            ||
| 650 | * Wraps filenames in links which opens the metadata editor.  | 
            ||
| 651 | *  | 
            ||
| 652 | * @param string $code String to be wrapped in links  | 
            ||
| 653 | * @param File $fileObject File to be linked  | 
            ||
| 654 | * @return string HTML  | 
            ||
| 655 | */  | 
            ||
| 656 | public function linkWrapFile($code, File $fileObject)  | 
            ||
| 657 |     { | 
            ||
| 658 |         try { | 
            ||
| 659 |             if ($this->isEditMetadataAllowed($fileObject)) { | 
            ||
| 660 | $metaData = $fileObject->getMetaData()->get();  | 
            ||
| 661 | $urlParameters = [  | 
            ||
| 662 | 'edit' => [  | 
            ||
| 663 | 'sys_file_metadata' => [  | 
            ||
| 664 | $metaData['uid'] => 'edit'  | 
            ||
| 665 | ]  | 
            ||
| 666 | ],  | 
            ||
| 667 | 'returnUrl' => $this->listURL()  | 
            ||
| 668 | ];  | 
            ||
| 669 |                 $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters); | 
            ||
| 670 |                 $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.editMetadata')); | 
            ||
| 671 | $code = '<a class="responsive-title" href="' . htmlspecialchars($url) . '" title="' . $title . '">' . $code . '</a>';  | 
            ||
| 672 | }  | 
            ||
| 673 |         } catch (\Exception $e) { | 
            ||
| 674 | // intentional fall-through  | 
            ||
| 675 | }  | 
            ||
| 676 | return $code;  | 
            ||
| 677 | }  | 
            ||
| 678 | |||
| 679 | /**  | 
            ||
| 680 | * Returns list URL; This is the URL of the current script with id and imagemode parameters, that's all.  | 
            ||
| 681 | *  | 
            ||
| 682 | * @return string URL  | 
            ||
| 683 | */  | 
            ||
| 684 | public function listURL(array $params = []): string  | 
            ||
| 685 |     { | 
            ||
| 686 | $params = array_replace_recursive([  | 
            ||
| 687 | 'pointer' => $this->firstElementNumber,  | 
            ||
| 688 | 'id' => $this->folderObject->getCombinedIdentifier(),  | 
            ||
| 689 | 'searchTerm' => $this->searchDemand ? $this->searchDemand->getSearchTerm() : ''  | 
            ||
| 690 | ], $params);  | 
            ||
| 691 | $params = array_filter($params);  | 
            ||
| 692 |         return (string)$this->uriBuilder->buildUriFromRoute('file_FilelistList', $params); | 
            ||
| 693 | }  | 
            ||
| 694 | |||
| 695 | protected function getAvailableSystemLanguages(): array  | 
            ||
| 696 |     { | 
            ||
| 697 | // first two keys are "0" (default) and "-1" (multiple), after that comes the "other languages"  | 
            ||
| 698 | $allSystemLanguages = $this->translateTools->getSystemLanguages();  | 
            ||
| 699 |         return array_filter($allSystemLanguages, function ($languageRecord) { | 
            ||
| 700 |             if ($languageRecord['uid'] === -1 || $languageRecord['uid'] === 0 || !$this->getBackendUser()->checkLanguageAccess($languageRecord['uid'])) { | 
            ||
| 701 | return false;  | 
            ||
| 702 | }  | 
            ||
| 703 | return true;  | 
            ||
| 704 | });  | 
            ||
| 705 | }  | 
            ||
| 706 | /**  | 
            ||
| 707 | * This returns tablerows for the files in the array $items['sorting'].  | 
            ||
| 708 | *  | 
            ||
| 709 | * @param File[] $files File items  | 
            ||
| 710 | * @return string HTML table rows.  | 
            ||
| 711 | */  | 
            ||
| 712 | public function formatFileList(array $files)  | 
            ||
| 713 |     { | 
            ||
| 714 | $out = '';  | 
            ||
| 715 | $systemLanguages = $this->getAvailableSystemLanguages();  | 
            ||
| 716 |         foreach ($files as $fileObject) { | 
            ||
| 717 | // Initialization  | 
            ||
| 718 | $this->counter++;  | 
            ||
| 719 | $this->totalbytes += $fileObject->getSize();  | 
            ||
| 720 | $ext = $fileObject->getExtension();  | 
            ||
| 721 | $fileUid = $fileObject->getUid();  | 
            ||
| 722 | $fileName = trim($fileObject->getName());  | 
            ||
| 723 | // The icon with link  | 
            ||
| 724 | $theIcon = '<span title="' . htmlspecialchars($fileName . ' [' . $fileUid . ']') . '">'  | 
            ||
| 725 | . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';  | 
            ||
| 726 | $theIcon = (string)BackendUtility::wrapClickMenuOnIcon($theIcon, 'sys_file', $fileObject->getCombinedIdentifier());  | 
            ||
| 727 | // Preparing and getting the data-array  | 
            ||
| 728 | $theData = [  | 
            ||
| 729 | 'type' => 'file',  | 
            ||
| 730 | 'file-uid' => $fileUid  | 
            ||
| 731 | ];  | 
            ||
| 732 |             if ($this->isEditMetadataAllowed($fileObject) && $fileObject->getMetaData()->offsetExists('uid')) { | 
            ||
| 733 |                 $theData['metadata-uid'] = htmlspecialchars((string)$fileObject->getMetaData()->offsetGet('uid')); | 
            ||
| 734 | }  | 
            ||
| 735 |             foreach ($this->fieldArray as $field) { | 
            ||
| 736 |                 switch ($field) { | 
            ||
| 737 | case 'size':  | 
            ||
| 738 |                         $theData[$field] = GeneralUtility::formatSize((int)$fileObject->getSize(), htmlspecialchars($this->getLanguageService()->getLL('byteSizeUnits'))); | 
            ||
| 739 | break;  | 
            ||
| 740 | case 'rw':  | 
            ||
| 741 |                         $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>'); | 
            ||
| 742 | break;  | 
            ||
| 743 | case 'fileext':  | 
            ||
| 744 | $theData[$field] = htmlspecialchars(strtoupper($ext));  | 
            ||
| 745 | break;  | 
            ||
| 746 | case 'tstamp':  | 
            ||
| 747 | $theData[$field] = BackendUtility::date($fileObject->getModificationTime());  | 
            ||
| 748 | break;  | 
            ||
| 749 | case '_CONTROL_':  | 
            ||
| 750 | $theData[$field] = $this->makeEdit($fileObject);  | 
            ||
| 751 | break;  | 
            ||
| 752 | case '_CLIPBOARD_':  | 
            ||
| 753 | $theData[$field] = $this->makeClip($fileObject);  | 
            ||
| 754 | break;  | 
            ||
| 755 | case '_LOCALIZATION_':  | 
            ||
| 756 |                         if (!empty($systemLanguages) && $fileObject->isIndexed() && $fileObject->checkActionPermission('editMeta') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata') && !empty($GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'] ?? null)) { | 
            ||
| 757 | $metaDataRecord = $fileObject->getMetaData()->get();  | 
            ||
| 758 | $translations = $this->getTranslationsForMetaData($metaDataRecord);  | 
            ||
| 759 | $languageCode = '';  | 
            ||
| 760 | |||
| 761 |                             foreach ($systemLanguages as $language) { | 
            ||
| 762 | $languageId = $language['uid'];  | 
            ||
| 763 | $flagIcon = $language['flagIcon'];  | 
            ||
| 764 |                                 if (array_key_exists($languageId, $translations)) { | 
            ||
| 765 |                                     $title = htmlspecialchars(sprintf($this->getLanguageService()->getLL('editMetadataForLanguage'), $language['title'])); | 
            ||
| 766 | $urlParameters = [  | 
            ||
| 767 | 'edit' => [  | 
            ||
| 768 | 'sys_file_metadata' => [  | 
            ||
| 769 | $translations[$languageId]['uid'] => 'edit'  | 
            ||
| 770 | ]  | 
            ||
| 771 | ],  | 
            ||
| 772 | 'returnUrl' => $this->listURL()  | 
            ||
| 773 | ];  | 
            ||
| 774 | $flagButtonIcon = $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-edit')->render();  | 
            ||
| 775 |                                     $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters); | 
            ||
| 776 | $languageCode .= '<a href="' . htmlspecialchars($url) . '" class="btn btn-default" title="' . $title . '">'  | 
            ||
| 777 | . $flagButtonIcon . '</a>';  | 
            ||
| 778 |                                 } else { | 
            ||
| 779 | $parameters = [  | 
            ||
| 780 | 'justLocalized' => 'sys_file_metadata:' . $metaDataRecord['uid'] . ':' . $languageId,  | 
            ||
| 781 | 'returnUrl' => $this->listURL()  | 
            ||
| 782 | ];  | 
            ||
| 783 |                                     $returnUrl = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $parameters); | 
            ||
| 784 | $href = BackendUtility::getLinkToDataHandlerAction(  | 
            ||
| 785 | '&cmd[sys_file_metadata][' . $metaDataRecord['uid'] . '][localize]=' . $languageId,  | 
            ||
| 786 | $returnUrl  | 
            ||
| 787 | );  | 
            ||
| 788 |                                     $flagButtonIcon = '<span title="' . htmlspecialchars(sprintf($this->getLanguageService()->getLL('createMetadataForLanguage'), $language['title'])) . '">' . $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-new')->render() . '</span>'; | 
            ||
| 789 | $languageCode .= '<a href="' . htmlspecialchars($href) . '" class="btn btn-default">' . $flagButtonIcon . '</a> ';  | 
            ||
| 790 | }  | 
            ||
| 791 | }  | 
            ||
| 792 | |||
| 793 | // Hide flag button bar when not translated yet  | 
            ||
| 794 | $theData[$field] = ' <div class="localisationData btn-group' . (empty($translations) ? ' hidden' : '') . '" data-fileid="' . $fileUid . '">'  | 
            ||
| 795 | . $languageCode . '</div>';  | 
            ||
| 796 | $theData[$field] .= '<a class="btn btn-default filelist-translationToggler" data-fileid="' . $fileUid . '">' .  | 
            ||
| 797 |                                 '<span title="' . htmlspecialchars($this->getLanguageService()->getLL('translateMetadata')) . '">' | 
            ||
| 798 |                                 . $this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL)->render() . '</span>' | 
            ||
| 799 | . '</a>';  | 
            ||
| 800 | }  | 
            ||
| 801 | break;  | 
            ||
| 802 | case '_REF_':  | 
            ||
| 803 | $theData[$field] = $this->makeRef($fileObject);  | 
            ||
| 804 | break;  | 
            ||
| 805 | case '_PATH_':  | 
            ||
| 806 | $theData[$field] = $this->makePath($fileObject);  | 
            ||
| 807 | break;  | 
            ||
| 808 | case 'file':  | 
            ||
| 809 | // Edit metadata of file  | 
            ||
| 810 | $theData[$field] = $this->linkWrapFile(htmlspecialchars($fileName), $fileObject);  | 
            ||
| 811 | |||
| 812 |                         if ($fileObject->isMissing()) { | 
            ||
| 813 | $theData[$field] .= '<span class="label label-danger label-space-left">'  | 
            ||
| 814 |                                 . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.file_missing')) | 
            ||
| 815 | . '</span>';  | 
            ||
| 816 | // Thumbnails?  | 
            ||
| 817 |                         } elseif ($this->thumbs && ($fileObject->isImage() || $fileObject->isMediaFile())) { | 
            ||
| 818 | $processedFile = $fileObject->process(  | 
            ||
| 819 | ProcessedFile::CONTEXT_IMAGEPREVIEW,  | 
            ||
| 820 | [  | 
            ||
| 821 | 'width' => (int)($this->getBackendUser()->getTSConfig()['options.']['file_list.']['thumbnail.']['width'] ?? 64),  | 
            ||
| 822 | 'height' => (int)($this->getBackendUser()->getTSConfig()['options.']['file_list.']['thumbnail.']['height'] ?? 64),  | 
            ||
| 823 | ]  | 
            ||
| 824 | );  | 
            ||
| 825 | $theData[$field] .= '<br /><img src="' . htmlspecialchars(PathUtility::getAbsoluteWebPath($processedFile->getPublicUrl() ?? '')) . '" ' .  | 
            ||
| 826 |                                 'width="' . htmlspecialchars($processedFile->getProperty('width')) . '" ' . | 
            ||
| 827 |                                 'height="' . htmlspecialchars($processedFile->getProperty('height')) . '" ' . | 
            ||
| 828 | 'title="' . htmlspecialchars($fileName) . '" alt="" />';  | 
            ||
| 829 | }  | 
            ||
| 830 | break;  | 
            ||
| 831 | default:  | 
            ||
| 832 | $theData[$field] = '';  | 
            ||
| 833 |                         if ($fileObject->hasProperty($field)) { | 
            ||
| 834 | $theData[$field] = htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileObject->getProperty($field), $this->fixedL));  | 
            ||
| 835 | }  | 
            ||
| 836 | }  | 
            ||
| 837 | }  | 
            ||
| 838 | $out .= $this->addElement($theIcon, $theData);  | 
            ||
| 839 | }  | 
            ||
| 840 | return $out;  | 
            ||
| 841 | }  | 
            ||
| 842 | |||
| 843 | /**  | 
            ||
| 844 | * Fetch the translations for a sys_file_metadata record  | 
            ||
| 845 | *  | 
            ||
| 846 | * @param array $metaDataRecord  | 
            ||
| 847 | * @return array keys are the sys_language uids, values are the $rows  | 
            ||
| 848 | */  | 
            ||
| 849 | protected function getTranslationsForMetaData($metaDataRecord)  | 
            ||
| 850 |     { | 
            ||
| 851 |         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_metadata'); | 
            ||
| 852 | $queryBuilder->getRestrictions()->removeAll();  | 
            ||
| 853 |         $translationRecords = $queryBuilder->select('*') | 
            ||
| 854 |             ->from('sys_file_metadata') | 
            ||
| 855 | ->where(  | 
            ||
| 856 | $queryBuilder->expr()->eq(  | 
            ||
| 857 | $GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'],  | 
            ||
| 858 | $queryBuilder->createNamedParameter($metaDataRecord['uid'], \PDO::PARAM_INT)  | 
            ||
| 859 | ),  | 
            ||
| 860 | $queryBuilder->expr()->gt(  | 
            ||
| 861 | $GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'],  | 
            ||
| 862 | $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)  | 
            ||
| 863 | )  | 
            ||
| 864 | )  | 
            ||
| 865 | ->execute()  | 
            ||
| 866 | ->fetchAll();  | 
            ||
| 867 | |||
| 868 | $translations = [];  | 
            ||
| 869 |         foreach ($translationRecords as $record) { | 
            ||
| 870 | $translations[$record[$GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField']]] = $record;  | 
            ||
| 871 | }  | 
            ||
| 872 | return $translations;  | 
            ||
| 873 | }  | 
            ||
| 874 | |||
| 875 | /**  | 
            ||
| 876 | * Wraps the directory-titles ($code) in a link to filelist/Modules/Filelist/index.php (id=$path) and sorting commands...  | 
            ||
| 877 | *  | 
            ||
| 878 | * @param string $col Sorting column  | 
            ||
| 879 | * @return string HTML  | 
            ||
| 880 | */  | 
            ||
| 881 | public function linkWrapSort($col)  | 
            ||
| 882 |     { | 
            ||
| 883 |         $code = htmlspecialchars($this->getLanguageService()->getLL('c_' . $col)); | 
            ||
| 884 | $params = ['SET' => ['sort' => $col], 'pointer' => 0];  | 
            ||
| 885 | |||
| 886 |         if ($this->sort === $col) { | 
            ||
| 887 | // Check reverse sorting  | 
            ||
| 888 | $params['SET']['reverse'] = ($this->sortRev ? '0' : '1');  | 
            ||
| 889 |             $sortArrow = $this->iconFactory->getIcon('status-status-sorting-' . ($this->sortRev ? 'desc' : 'asc'), Icon::SIZE_SMALL)->render(); | 
            ||
| 890 |         } else { | 
            ||
| 891 | $params['SET']['reverse'] = 0;  | 
            ||
| 892 | $sortArrow = '';  | 
            ||
| 893 | }  | 
            ||
| 894 | $href = $this->listURL($params);  | 
            ||
| 895 | return '<a href="' . htmlspecialchars($href) . '">' . $code . ' ' . $sortArrow . '</a>';  | 
            ||
| 896 | }  | 
            ||
| 897 | |||
| 898 | /**  | 
            ||
| 899 | * Creates the clipboard control pad  | 
            ||
| 900 | *  | 
            ||
| 901 | * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.  | 
            ||
| 902 | * @return string HTML-table  | 
            ||
| 903 | */  | 
            ||
| 904 | public function makeClip($fileOrFolderObject)  | 
            ||
| 905 |     { | 
            ||
| 906 |         if (!$fileOrFolderObject->checkActionPermission('read')) { | 
            ||
| 907 | return '';  | 
            ||
| 908 | }  | 
            ||
| 909 | $cells = [];  | 
            ||
| 910 | $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();  | 
            ||
| 911 | $fullName = $fileOrFolderObject->getName();  | 
            ||
| 912 | $md5 = GeneralUtility::shortMD5($fullIdentifier);  | 
            ||
| 913 | // For normal clipboard, add copy/cut buttons:  | 
            ||
| 914 |         if ($this->clipObj->current === 'normal') { | 
            ||
| 915 |             $isSel = $this->clipObj->isSelected('_FILE', $md5); | 
            ||
| 916 |             $copyTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.copy')); | 
            ||
| 917 |             $cutTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.cut')); | 
            ||
| 918 |             $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render(); | 
            ||
| 919 |             $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render(); | 
            ||
| 920 | |||
| 921 |             if ($isSel === 'copy') { | 
            ||
| 922 |                 $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render(); | 
            ||
| 923 |             } elseif ($isSel === 'cut') { | 
            ||
| 924 |                 $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render(); | 
            ||
| 925 | }  | 
            ||
| 926 | |||
| 927 |             if ($fileOrFolderObject->checkActionPermission('copy')) { | 
            ||
| 928 | $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile(  | 
            ||
| 929 | $fullIdentifier,  | 
            ||
| 930 | 1,  | 
            ||
| 931 | $isSel === 'copy'  | 
            ||
| 932 | )) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';  | 
            ||
| 933 |             } else { | 
            ||
| 934 | $cells[] = $this->spaceIcon;  | 
            ||
| 935 | }  | 
            ||
| 936 | // we can only cut if file can be moved  | 
            ||
| 937 |             if ($fileOrFolderObject->checkActionPermission('move')) { | 
            ||
| 938 | $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile(  | 
            ||
| 939 | $fullIdentifier,  | 
            ||
| 940 | 0,  | 
            ||
| 941 | $isSel === 'cut'  | 
            ||
| 942 | )) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';  | 
            ||
| 943 |             } else { | 
            ||
| 944 | $cells[] = $this->spaceIcon;  | 
            ||
| 945 | }  | 
            ||
| 946 |         } else { | 
            ||
| 947 | // For numeric pads, add select checkboxes:  | 
            ||
| 948 | $n = '_FILE|' . $md5;  | 
            ||
| 949 | $this->CBnames[] = $n;  | 
            ||
| 950 |             $checked = $this->clipObj->isSelected('_FILE', $md5) ? ' checked="checked"' : ''; | 
            ||
| 951 | $cells[] = '<label class="btn btn-default btn-checkbox"><input type="checkbox" name="CBC[' . $n . ']" value="' . htmlspecialchars($fullIdentifier) . '" ' . $checked . ' /><span class="t3-icon fa"></span><input type="hidden" name="CBH[' . $n . ']" value="0" /></label>';  | 
            ||
| 952 | }  | 
            ||
| 953 | // Display PASTE button, if directory:  | 
            ||
| 954 |         $elFromTable = $this->clipObj->elFromTable('_FILE'); | 
            ||
| 955 |         if ($fileOrFolderObject instanceof Folder && !empty($elFromTable) && $fileOrFolderObject->checkActionPermission('write')) { | 
            ||
| 956 | $clipboardMode = $this->clipObj->clipData[$this->clipObj->current]['mode'] ?? '';  | 
            ||
| 957 | $permission = $clipboardMode === 'copy' ? 'copy' : 'move';  | 
            ||
| 958 | $addPasteButton = $this->folderObject->checkActionPermission($permission);  | 
            ||
| 959 | $elToConfirm = [];  | 
            ||
| 960 |             foreach ($elFromTable as $key => $element) { | 
            ||
| 961 | $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);  | 
            ||
| 962 |                 if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $fileOrFolderObject)) { | 
            ||
| 963 | $addPasteButton = false;  | 
            ||
| 964 | }  | 
            ||
| 965 | $elToConfirm[$key] = $clipBoardElement->getName();  | 
            ||
| 966 | }  | 
            ||
| 967 |             if ($addPasteButton) { | 
            ||
| 968 | $cells[] = '<a class="btn btn-default t3js-modal-trigger" '  | 
            ||
| 969 |                     . ' href="' . htmlspecialchars($this->clipObj->pasteUrl('_FILE', $fullIdentifier)) . '"' | 
            ||
| 970 |                     . ' data-bs-content="' . htmlspecialchars($this->clipObj->confirmMsgText('_FILE', $fullName, 'into', $elToConfirm)) . '"' | 
            ||
| 971 | . ' data-severity="warning"'  | 
            ||
| 972 |                     . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"' | 
            ||
| 973 |                     . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"' | 
            ||
| 974 | . '>'  | 
            ||
| 975 |                     . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render() | 
            ||
| 976 | . '</a>';  | 
            ||
| 977 |             } else { | 
            ||
| 978 | $cells[] = $this->spaceIcon;  | 
            ||
| 979 | }  | 
            ||
| 980 | }  | 
            ||
| 981 | // Compile items into a DIV-element:  | 
            ||
| 982 |         return ' <div class="btn-group" role="group">' . implode('', $cells) . '</div>'; | 
            ||
| 983 | }  | 
            ||
| 984 | |||
| 985 | /**  | 
            ||
| 986 | * Creates the edit control section  | 
            ||
| 987 | *  | 
            ||
| 988 | * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the edit control section for the listing.  | 
            ||
| 989 | * @return string HTML-table  | 
            ||
| 990 | */  | 
            ||
| 991 | public function makeEdit($fileOrFolderObject)  | 
            ||
| 992 |     { | 
            ||
| 993 | $cells = [];  | 
            ||
| 994 | $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();  | 
            ||
| 995 | |||
| 996 | // Edit file content (if editable)  | 
            ||
| 997 |         if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('write') && $fileOrFolderObject->isTextFile()) { | 
            ||
| 998 | $attributes = [  | 
            ||
| 999 |                 'href' => (string)$this->uriBuilder->buildUriFromRoute('file_edit', ['target' => $fullIdentifier, 'returnUrl' => $this->listURL()]), | 
            ||
| 1000 |                 'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.editcontent'), | 
            ||
| 1001 | ];  | 
            ||
| 1002 | $cells['edit'] = '<a class="btn btn-default" ' . GeneralUtility::implodeAttributes($attributes, true) . '>'  | 
            ||
| 1003 |                 . $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render() | 
            ||
| 1004 | . '</a>';  | 
            ||
| 1005 |         } else { | 
            ||
| 1006 | $cells['edit'] = $this->spaceIcon;  | 
            ||
| 1007 | }  | 
            ||
| 1008 | |||
| 1009 | // Edit metadata of file  | 
            ||
| 1010 |         if ($fileOrFolderObject instanceof File && $this->isEditMetadataAllowed($fileOrFolderObject)) { | 
            ||
| 1011 | $metaData = $fileOrFolderObject->getMetaData()->get();  | 
            ||
| 1012 | $urlParameters = [  | 
            ||
| 1013 | 'edit' => [  | 
            ||
| 1014 | 'sys_file_metadata' => [  | 
            ||
| 1015 | $metaData['uid'] => 'edit'  | 
            ||
| 1016 | ]  | 
            ||
| 1017 | ],  | 
            ||
| 1018 | 'returnUrl' => $this->listURL()  | 
            ||
| 1019 | ];  | 
            ||
| 1020 |             $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters); | 
            ||
| 1021 |             $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.editMetadata')); | 
            ||
| 1022 |             $cells['metadata'] = '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" title="' . $title . '">' . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>'; | 
            ||
| 1023 | }  | 
            ||
| 1024 | |||
| 1025 | // document view  | 
            ||
| 1026 |         if ($fileOrFolderObject instanceof File) { | 
            ||
| 1027 | $fileUrl = $fileOrFolderObject->getPublicUrl();  | 
            ||
| 1028 |             if ($fileUrl) { | 
            ||
| 1029 |                 $cells['view'] = '<a href="' . htmlspecialchars(PathUtility::getAbsoluteWebPath($fileUrl)) . '" target="_blank" class="btn btn-default" title="' . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.view') . '">' . $this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL)->render() . '</a>'; | 
            ||
| 1030 |             } else { | 
            ||
| 1031 | $cells['view'] = $this->spaceIcon;  | 
            ||
| 1032 | }  | 
            ||
| 1033 |         } else { | 
            ||
| 1034 | $cells['view'] = $this->spaceIcon;  | 
            ||
| 1035 | }  | 
            ||
| 1036 | |||
| 1037 | // replace file  | 
            ||
| 1038 |         if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('replace')) { | 
            ||
| 1039 | $attributes = [  | 
            ||
| 1040 |                 'href' => (string)$this->uriBuilder->buildUriFromRoute('file_replace', ['target' => $fullIdentifier, 'uid' => $fileOrFolderObject->getUid(), 'returnUrl' => $this->listURL()]), | 
            ||
| 1041 |                 'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.replace'), | 
            ||
| 1042 | ];  | 
            ||
| 1043 |             $cells['replace'] = '<a class="btn btn-default" ' . GeneralUtility::implodeAttributes($attributes, true) . '>' . $this->iconFactory->getIcon('actions-edit-replace', Icon::SIZE_SMALL)->render() . '</a>'; | 
            ||
| 1044 | }  | 
            ||
| 1045 | |||
| 1046 | // rename the file  | 
            ||
| 1047 |         if ($fileOrFolderObject->checkActionPermission('rename')) { | 
            ||
| 1048 | $attributes = [  | 
            ||
| 1049 |                 'href' => (string)$this->uriBuilder->buildUriFromRoute('file_rename', ['target' => $fullIdentifier, 'returnUrl' => $this->listURL()]), | 
            ||
| 1050 |                 'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.rename'), | 
            ||
| 1051 | ];  | 
            ||
| 1052 |             $cells['rename'] = '<a class="btn btn-default" ' . GeneralUtility::implodeAttributes($attributes, true) . '>' . $this->iconFactory->getIcon('actions-edit-rename', Icon::SIZE_SMALL)->render() . '</a>'; | 
            ||
| 1053 |         } else { | 
            ||
| 1054 | $cells['rename'] = $this->spaceIcon;  | 
            ||
| 1055 | }  | 
            ||
| 1056 | |||
| 1057 | // upload files  | 
            ||
| 1058 |         if ($fileOrFolderObject->getStorage()->checkUserActionPermission('add', 'File') && $fileOrFolderObject->checkActionPermission('write')) { | 
            ||
| 1059 |             if ($fileOrFolderObject instanceof Folder) { | 
            ||
| 1060 | $attributes = [  | 
            ||
| 1061 |                     'href' => (string)$this->uriBuilder->buildUriFromRoute('file_upload', ['target' => $fullIdentifier, 'returnUrl' => $this->listURL()]), | 
            ||
| 1062 |                     'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.upload'), | 
            ||
| 1063 | ];  | 
            ||
| 1064 |                 $cells['upload'] = '<a class="btn btn-default" ' . GeneralUtility::implodeAttributes($attributes, true) . '>' . $this->iconFactory->getIcon('actions-edit-upload', Icon::SIZE_SMALL)->render() . '</a>'; | 
            ||
| 1065 | }  | 
            ||
| 1066 | }  | 
            ||
| 1067 | |||
| 1068 |         if ($fileOrFolderObject->checkActionPermission('read')) { | 
            ||
| 1069 | $attributes = [  | 
            ||
| 1070 |                 'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.info'), | 
            ||
| 1071 | ];  | 
            ||
| 1072 |             if ($fileOrFolderObject instanceof Folder || $fileOrFolderObject instanceof File) { | 
            ||
| 1073 | $attributes['data-filelist-show-item-type'] = $fileOrFolderObject instanceof File ? '_FILE' : '_FOLDER';  | 
            ||
| 1074 | $attributes['data-filelist-show-item-identifier'] = $fullIdentifier;  | 
            ||
| 1075 | }  | 
            ||
| 1076 | $cells['info'] = '<a href="#" class="btn btn-default" ' . GeneralUtility::implodeAttributes($attributes, true) . '>'  | 
            ||
| 1077 |                 . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . '</a>'; | 
            ||
| 1078 |         } else { | 
            ||
| 1079 | $cells['info'] = $this->spaceIcon;  | 
            ||
| 1080 | }  | 
            ||
| 1081 | |||
| 1082 | // delete the file  | 
            ||
| 1083 |         if ($fileOrFolderObject->checkActionPermission('delete')) { | 
            ||
| 1084 | $identifier = $fileOrFolderObject->getIdentifier();  | 
            ||
| 1085 |             if ($fileOrFolderObject instanceof Folder) { | 
            ||
| 1086 |                 $referenceCountText = BackendUtility::referenceCount('_FILE', $identifier, ' ' . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.referencesToFolder')); | 
            ||
| 1087 | $deleteType = 'delete_folder';  | 
            ||
| 1088 |             } else { | 
            ||
| 1089 |                 $referenceCountText = BackendUtility::referenceCount('sys_file', (string)$fileOrFolderObject->getUid(), ' ' . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.referencesToFile')); | 
            ||
| 1090 | $deleteType = 'delete_file';  | 
            ||
| 1091 | }  | 
            ||
| 1092 | |||
| 1093 |             if ($this->getBackendUser()->jsConfirmation(JsConfirmation::DELETE)) { | 
            ||
| 1094 | $confirmationCheck = '1';  | 
            ||
| 1095 |             } else { | 
            ||
| 1096 | $confirmationCheck = '0';  | 
            ||
| 1097 | }  | 
            ||
| 1098 | |||
| 1099 |             $deleteUrl = (string)$this->uriBuilder->buildUriFromRoute('tce_file'); | 
            ||
| 1100 |             $confirmationMessage = sprintf($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:mess.delete'), $fileOrFolderObject->getName()) . $referenceCountText; | 
            ||
| 1101 |             $title = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'); | 
            ||
| 1102 | $cells['delete'] = '<a href="#" class="btn btn-default t3js-filelist-delete" data-bs-content="' . htmlspecialchars($confirmationMessage)  | 
            ||
| 1103 | . '" data-check="' . $confirmationCheck  | 
            ||
| 1104 | . '" data-delete-url="' . htmlspecialchars($deleteUrl)  | 
            ||
| 1105 | . '" data-title="' . htmlspecialchars($title)  | 
            ||
| 1106 | . '" data-identifier="' . htmlspecialchars($fileOrFolderObject->getCombinedIdentifier())  | 
            ||
| 1107 | . '" data-delete-type="' . $deleteType  | 
            ||
| 1108 | . '" title="' . htmlspecialchars($title) . '">'  | 
            ||
| 1109 |                 . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() . '</a>'; | 
            ||
| 1110 |         } else { | 
            ||
| 1111 | $cells['delete'] = $this->spaceIcon;  | 
            ||
| 1112 | }  | 
            ||
| 1113 | |||
| 1114 | // Hook for manipulating edit icons.  | 
            ||
| 1115 | $cells['__fileOrFolderObject'] = $fileOrFolderObject;  | 
            ||
| 1116 |         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'] ?? [] as $className) { | 
            ||
| 1117 | $hookObject = GeneralUtility::makeInstance($className);  | 
            ||
| 1118 |             if (!$hookObject instanceof FileListEditIconHookInterface) { | 
            ||
| 1119 | throw new \UnexpectedValueException(  | 
            ||
| 1120 | $className . ' must implement interface ' . FileListEditIconHookInterface::class,  | 
            ||
| 1121 | 1235225797  | 
            ||
| 1122 | );  | 
            ||
| 1123 | }  | 
            ||
| 1124 | $hookObject->manipulateEditIcons($cells, $this);  | 
            ||
| 1125 | }  | 
            ||
| 1126 | unset($cells['__fileOrFolderObject']);  | 
            ||
| 1127 | // Compile items into a dropdown  | 
            ||
| 1128 | $cellOutput = '';  | 
            ||
| 1129 | $output = '';  | 
            ||
| 1130 |         foreach ($cells as $key => $action) { | 
            ||
| 1131 |             if (in_array($key, ['view', 'metadata', 'delete'])) { | 
            ||
| 1132 | $output .= $action;  | 
            ||
| 1133 | continue;  | 
            ||
| 1134 | }  | 
            ||
| 1135 |             if ($action === $this->spaceIcon) { | 
            ||
| 1136 | continue;  | 
            ||
| 1137 | }  | 
            ||
| 1138 | // This is a backwards-compat layer for the existing hook items, which will be removed in TYPO3 v12.  | 
            ||
| 1139 |             $action = str_replace('btn btn-default', 'dropdown-item', $action); | 
            ||
| 1140 | $title = [];  | 
            ||
| 1141 |             preg_match('/title="([^"]*)"/', $action, $title); | 
            ||
| 1142 |             if (empty($title)) { | 
            ||
| 1143 |                 preg_match('/aria-label="([^"]*)"/', $action, $title); | 
            ||
| 1144 | }  | 
            ||
| 1145 |             if (!empty($title[1] ?? '')) { | 
            ||
| 1146 |                 $action = str_replace('</a>', ' ' . $title[1] . '</a>', $action); | 
            ||
| 1147 |                 $action = str_replace('</button>', ' ' . $title[1] . '</button>', $action); | 
            ||
| 1148 | }  | 
            ||
| 1149 | $cellOutput .= '<li>' . $action . '</li>';  | 
            ||
| 1150 | }  | 
            ||
| 1151 |         $icon = $this->iconFactory->getIcon('actions-menu-alternative', Icon::SIZE_SMALL); | 
            ||
| 1152 | $output .= '<div class="btn-group dropdown position-static">' .  | 
            ||
| 1153 | '<a href="#actions_' . $fileOrFolderObject->getHashedIdentifier() . '" class="btn btn-default dropdown-toggle dropdown-toggle-no-chevron" data-bs-toggle="dropdown" data-bs-boundary="window" aria-expanded="false">' . $icon->render() . '</a>' .  | 
            ||
| 1154 | '<ul id="actions_' . $fileOrFolderObject->getHashedIdentifier() . '" class="dropdown-menu dropdown-list">' . $cellOutput . '</ul>' .  | 
            ||
| 1155 | '</div>';  | 
            ||
| 1156 | return '<div class="btn-group position-static">' . $output . '</div>';  | 
            ||
| 1157 | }  | 
            ||
| 1158 | |||
| 1159 | /**  | 
            ||
| 1160 | * Make reference count  | 
            ||
| 1161 | *  | 
            ||
| 1162 | * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.  | 
            ||
| 1163 | * @return string HTML  | 
            ||
| 1164 | */  | 
            ||
| 1165 | public function makeRef($fileOrFolderObject)  | 
            ||
| 1166 |     { | 
            ||
| 1167 |         if ($fileOrFolderObject instanceof FolderInterface) { | 
            ||
| 1168 | return '-';  | 
            ||
| 1169 | }  | 
            ||
| 1170 | // Look up the file in the sys_refindex.  | 
            ||
| 1171 | // Exclude sys_file_metadata records as these are no use references  | 
            ||
| 1172 |         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex'); | 
            ||
| 1173 |         $referenceCount = $queryBuilder->count('*') | 
            ||
| 1174 |             ->from('sys_refindex') | 
            ||
| 1175 | ->where(  | 
            ||
| 1176 | $queryBuilder->expr()->eq(  | 
            ||
| 1177 | 'ref_table',  | 
            ||
| 1178 |                     $queryBuilder->createNamedParameter('sys_file', \PDO::PARAM_STR) | 
            ||
| 1179 | ),  | 
            ||
| 1180 | $queryBuilder->expr()->eq(  | 
            ||
| 1181 | 'ref_uid',  | 
            ||
| 1182 | $queryBuilder->createNamedParameter($fileOrFolderObject->getUid(), \PDO::PARAM_INT)  | 
            ||
| 1183 | ),  | 
            ||
| 1184 | $queryBuilder->expr()->neq(  | 
            ||
| 1185 | 'tablename',  | 
            ||
| 1186 |                     $queryBuilder->createNamedParameter('sys_file_metadata', \PDO::PARAM_STR) | 
            ||
| 1187 | )  | 
            ||
| 1188 | )  | 
            ||
| 1189 | ->execute()  | 
            ||
| 1190 | ->fetchColumn();  | 
            ||
| 1191 | |||
| 1192 | return $this->generateReferenceToolTip($referenceCount, $fileOrFolderObject);  | 
            ||
| 1193 | }  | 
            ||
| 1194 | |||
| 1195 | /**  | 
            ||
| 1196 | * Generate readable path  | 
            ||
| 1197 | *  | 
            ||
| 1198 | * @param ResourceInterface $resource  | 
            ||
| 1199 | * @return string  | 
            ||
| 1200 | */  | 
            ||
| 1201 | protected function makePath(ResourceInterface $resource): string  | 
            ||
| 1202 |     { | 
            ||
| 1203 | $folder = null;  | 
            ||
| 1204 | $method = 'getReadablePath';  | 
            ||
| 1205 | |||
| 1206 |         if ($resource instanceof FileInterface) { | 
            ||
| 1207 | $folder = $resource->getParentFolder();  | 
            ||
| 1208 |         } elseif ($resource instanceof FolderInterface) { | 
            ||
| 1209 | $folder = $resource;  | 
            ||
| 1210 | }  | 
            ||
| 1211 | |||
| 1212 |         if ($folder === null || !is_callable([$folder, $method])) { | 
            ||
| 1213 | return '';  | 
            ||
| 1214 | }  | 
            ||
| 1215 | |||
| 1216 | return htmlspecialchars($folder->$method());  | 
            ||
| 1217 | }  | 
            ||
| 1218 | |||
| 1219 | /**  | 
            ||
| 1220 | * Returns an instance of LanguageService  | 
            ||
| 1221 | *  | 
            ||
| 1222 | * @return LanguageService  | 
            ||
| 1223 | */  | 
            ||
| 1224 | protected function getLanguageService()  | 
            ||
| 1225 |     { | 
            ||
| 1226 | return $GLOBALS['LANG'];  | 
            ||
| 1227 | }  | 
            ||
| 1228 | |||
| 1229 | /**  | 
            ||
| 1230 | * Returns the current BE user.  | 
            ||
| 1231 | *  | 
            ||
| 1232 | * @return BackendUserAuthentication  | 
            ||
| 1233 | */  | 
            ||
| 1234 | protected function getBackendUser()  | 
            ||
| 1237 | }  | 
            ||
| 1238 | |||
| 1239 | /**  | 
            ||
| 1240 | * Generates HTML code for a Reference tooltip out of  | 
            ||
| 1241 | * sys_refindex records you hand over  | 
            ||
| 1242 | *  | 
            ||
| 1243 | * @param int $references number of records from sys_refindex table  | 
            ||
| 1244 | * @param AbstractFile $fileObject  | 
            ||
| 1245 | * @return string  | 
            ||
| 1246 | */  | 
            ||
| 1247 | protected function generateReferenceToolTip($references, $fileObject)  | 
            ||
| 1248 |     { | 
            ||
| 1249 |         if (!$references) { | 
            ||
| 1250 | return '-';  | 
            ||
| 1251 | }  | 
            ||
| 1252 | $attributes = [  | 
            ||
| 1253 | 'data-filelist-show-item-type' => '_FILE',  | 
            ||
| 1254 | 'data-filelist-show-item-identifier' => $fileObject->getCombinedIdentifier(),  | 
            ||
| 1255 | 'title' => $this->getLanguageService()  | 
            ||
| 1256 |                 ->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:show_references') | 
            ||
| 1257 |                 . ' (' . $references . ')' | 
            ||
| 1258 | ];  | 
            ||
| 1259 | $htmlCode = '<a href="#" ' . GeneralUtility::implodeAttributes($attributes, true) . '">';  | 
            ||
| 1260 | $htmlCode .= $references;  | 
            ||
| 1261 | $htmlCode .= '</a>';  | 
            ||
| 1262 | return $htmlCode;  | 
            ||
| 1263 | }  | 
            ||
| 1264 | |||
| 1265 | protected function isEditMetadataAllowed(File $file): bool  | 
            ||
| 1270 | }  | 
            ||
| 1271 | }  | 
            ||
| 1272 |