Passed
Branch master (6c65a4)
by Christian
16:31
created

FileList::start()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 6
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace TYPO3\CMS\Filelist;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use TYPO3\CMS\Backend\Clipboard\Clipboard;
18
use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
19
use TYPO3\CMS\Backend\Routing\UriBuilder;
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
23
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
24
use TYPO3\CMS\Core\Imaging\Icon;
25
use TYPO3\CMS\Core\Imaging\IconFactory;
26
use TYPO3\CMS\Core\Messaging\FlashMessage;
27
use TYPO3\CMS\Core\Messaging\FlashMessageService;
28
use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
29
use TYPO3\CMS\Core\Resource\File;
30
use TYPO3\CMS\Core\Resource\Folder;
31
use TYPO3\CMS\Core\Resource\FolderInterface;
32
use TYPO3\CMS\Core\Resource\InaccessibleFolder;
33
use TYPO3\CMS\Core\Resource\ProcessedFile;
34
use TYPO3\CMS\Core\Resource\ResourceFactory;
35
use TYPO3\CMS\Core\Resource\Utility\ListUtility;
36
use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
37
use TYPO3\CMS\Core\Utility\GeneralUtility;
38
use TYPO3\CMS\Core\Utility\MathUtility;
39
use TYPO3\CMS\Filelist\Configuration\ThumbnailConfiguration;
40
use TYPO3\CMS\Filelist\Controller\FileListController;
41
42
/**
43
 * Class for rendering of File>Filelist
44
 */
45
class FileList
46
{
47
    /**
48
     * Default Max items shown
49
     *
50
     * @var int
51
     */
52
    public $iLimit = 40;
53
54
    /**
55
     * Thumbnails on records containing files (pictures)
56
     *
57
     * @var bool
58
     */
59
    public $thumbs = false;
60
61
    /**
62
     * Space icon used for alignment when no button is available
63
     *
64
     * @var string
65
     */
66
    public $spaceIcon;
67
68
    /**
69
     * Max length of strings
70
     *
71
     * @var int
72
     */
73
    public $fixedL = 30;
74
75
    /**
76
     * The field to sort by
77
     *
78
     * @var string
79
     */
80
    public $sort = '';
81
82
    /**
83
     * Reverse sorting flag
84
     *
85
     * @var bool
86
     */
87
    public $sortRev = 1;
88
89
    /**
90
     * @var int
91
     */
92
    public $firstElementNumber = 0;
93
94
    /**
95
     * @var bool
96
     */
97
    public $clipBoard = 0;
98
99
    /**
100
     * @var bool
101
     */
102
    public $bigControlPanel = 0;
103
104
    /**
105
     * @var string
106
     */
107
    public $JScode = '';
108
109
    /**
110
     * String with accumulated HTML content
111
     *
112
     * @var string
113
     */
114
    public $HTMLcode = '';
115
116
    /**
117
     * @var int
118
     */
119
    public $totalbytes = 0;
120
121
    /**
122
     * @var array
123
     */
124
    public $dirs = [];
125
126
    /**
127
     * @var array
128
     */
129
    public $files = [];
130
131
    /**
132
     * @var string
133
     */
134
    public $path = '';
135
136
    /**
137
     * OBSOLETE - NOT USED ANYMORE. leftMargin
138
     *
139
     * @var int
140
     */
141
    public $leftMargin = 0;
142
143
    /**
144
     * This could be set to the total number of items. Used by the fwd_rew_navigation...
145
     *
146
     * @var string
147
     */
148
    public $totalItems = '';
149
150
    /**
151
     * Decides the columns shown. Filled with values that refers to the keys of the data-array. $this->fieldArray[0] is the title column.
152
     *
153
     * @var array
154
     */
155
    public $fieldArray = [];
156
157
    /**
158
     * Set to zero, if you don't want a left-margin with addElement function
159
     *
160
     * @var int
161
     */
162
    public $setLMargin = 1;
163
164
    /**
165
     * Contains page translation languages
166
     *
167
     * @var array
168
     */
169
    public $pageOverlays = [];
170
171
    /**
172
     * Counter increased for each element. Used to index elements for the JavaScript-code that transfers to the clipboard
173
     *
174
     * @var int
175
     */
176
    public $counter = 0;
177
178
    /**
179
     * Contains sys language icons and titles
180
     *
181
     * @var array
182
     */
183
    public $languageIconTitles = [];
184
185
    /**
186
     * Script URL
187
     *
188
     * @var string
189
     */
190
    public $thisScript = '';
191
192
    /**
193
     * If set this is <td> CSS-classname for odd columns in addElement. Used with db_layout / pages section
194
     *
195
     * @var string
196
     */
197
    public $oddColumnsCssClass = '';
198
199
    /**
200
     * Counting the elements no matter what
201
     *
202
     * @var int
203
     */
204
    public $eCounter = 0;
205
206
    /**
207
     * @var TranslationConfigurationProvider
208
     */
209
    public $translateTools;
210
211
    /**
212
     * Keys are fieldnames and values are td-parameters to add in addElement(), please use $addElement_tdCSSClass for CSS-classes;
213
     *
214
     * @var array
215
     */
216
    public $addElement_tdParams = [];
217
218
    /**
219
     * @var int
220
     */
221
    public $no_noWrap = 0;
222
223
    /**
224
     * @var int
225
     */
226
    public $showIcon = 1;
227
228
    /**
229
     * Keys are fieldnames and values are td-css-classes to add in addElement();
230
     *
231
     * @var array
232
     */
233
    public $addElement_tdCssClass = [];
234
235
    /**
236
     * @var Folder
237
     */
238
    protected $folderObject;
239
240
    /**
241
     * @var array
242
     */
243
    public $CBnames = [];
244
245
    /**
246
     * @var Clipboard $clipObj
247
     */
248
    public $clipObj;
249
250
    /**
251
     * @var ResourceFactory
252
     */
253
    protected $resourceFactory;
254
255
    /**
256
     * @var IconFactory
257
     */
258
    protected $iconFactory;
259
260
    /**
261
     * @var int
262
     */
263
    protected $id = 0;
264
265
    /**
266
     * @var FileListController
267
     */
268
    protected $fileListController;
269
270
    /**
271
     * @var ThumbnailConfiguration
272
     */
273
    protected $thumbnailConfiguration;
274
275
    /**
276
     * Construct
277
     *
278
     * @param FileListController $fileListController
279
     */
280
    public function __construct(FileListController $fileListController)
281
    {
282
        if (isset($GLOBALS['BE_USER']->uc['titleLen']) && $GLOBALS['BE_USER']->uc['titleLen'] > 0) {
283
            $this->fixedL = $GLOBALS['BE_USER']->uc['titleLen'];
284
        }
285
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
286
        $this->getTranslateTools();
287
        $this->determineScriptUrl();
288
        $this->fileListController = $fileListController;
289
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
290
        $this->thumbnailConfiguration = GeneralUtility::makeInstance(ThumbnailConfiguration::class);
291
292
        $modTSconfig = BackendUtility::getModTSconfig(0, 'options.file_list');
293
        if (!empty($modTSconfig['properties']['filesPerPage'])) {
294
            $this->iLimit = MathUtility::forceIntegerInRange($modTSconfig['properties']['filesPerPage'], 1);
295
        }
296
    }
297
298
    /**
299
     * @param ResourceFactory $resourceFactory
300
     */
301
    public function injectResourceFactory(ResourceFactory $resourceFactory)
302
    {
303
        $this->resourceFactory = $resourceFactory;
304
    }
305
306
    /**
307
     * Initialization of class
308
     *
309
     * @param Folder $folderObject The folder to work on
310
     * @param int $pointer Pointer
311
     * @param bool $sort Sorting column
312
     * @param bool $sortRev Sorting direction
313
     * @param bool $clipBoard
314
     * @param bool $bigControlPanel Show clipboard flag
315
     */
316
    public function start(Folder $folderObject, $pointer, $sort, $sortRev, $clipBoard = false, $bigControlPanel = false)
317
    {
318
        $this->folderObject = $folderObject;
319
        $this->counter = 0;
320
        $this->totalbytes = 0;
321
        $this->JScode = '';
322
        $this->HTMLcode = '';
323
        $this->path = $folderObject->getReadablePath();
324
        $this->sort = $sort;
0 ignored issues
show
Documentation Bug introduced by
The property $sort was declared of type string, but $sort is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
325
        $this->sortRev = $sortRev;
326
        $this->firstElementNumber = $pointer;
327
        $this->clipBoard = $clipBoard;
328
        $this->bigControlPanel = $bigControlPanel;
329
        // Setting the maximum length of the filenames to the user's settings or minimum 30 (= $this->fixedL)
330
        $this->fixedL = max($this->fixedL, $this->getBackendUser()->uc['titleLen']);
331
        $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_common.xlf');
332
        $this->resourceFactory = ResourceFactory::getInstance();
333
    }
334
335
    /**
336
     * Reading files and directories, counting elements and generating the list in ->HTMLcode
337
     */
338
    public function generateList()
339
    {
340
        $this->HTMLcode .= $this->getTable('fileext,tstamp,size,rw,_REF_');
0 ignored issues
show
Bug introduced by
'fileext,tstamp,size,rw,_REF_' of type string is incompatible with the type array expected by parameter $rowlist of TYPO3\CMS\Filelist\FileList::getTable(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

340
        $this->HTMLcode .= $this->getTable(/** @scrutinizer ignore-type */ 'fileext,tstamp,size,rw,_REF_');
Loading history...
341
    }
342
343
    /**
344
     * Wrapping input string in a link with clipboard command.
345
     *
346
     * @param string $string String to be linked - must be htmlspecialchar'ed / prepared before.
347
     * @param string $_ unused
348
     * @param string $cmd "cmd" value
349
     * @param string $warning Warning for JS confirm message
350
     * @return string Linked string
351
     */
352
    public function linkClipboardHeaderIcon($string, $_, $cmd, $warning = '')
353
    {
354
        $jsCode = 'document.dblistForm.cmd.value=' . GeneralUtility::quoteJSvalue($cmd)
355
            . ';document.dblistForm.submit();';
356
357
        $attributes = [];
358
        if ($warning) {
359
            $attributes['class'] = 'btn btn-default t3js-modal-trigger';
360
            $attributes['data-href'] = 'javascript:' . $jsCode;
361
            $attributes['data-severity'] = 'warning';
362
            $attributes['data-content'] = $warning;
363
        } else {
364
            $attributes['class'] = 'btn btn-default';
365
            $attributes['onclick'] = $jsCode . 'return false;';
366
        }
367
368
        $attributesString = '';
369
        foreach ($attributes as $key => $value) {
370
            $attributesString .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
371
        }
372
        return '<a href="#" ' . $attributesString . '>' . $string . '</a>';
373
    }
374
375
    /**
376
     * Returns a table with directories and files listed.
377
     *
378
     * @param array $rowlist Array of files from path
379
     * @return string HTML-table
380
     */
381
    public function getTable($rowlist)
382
    {
383
        // prepare space icon
384
        $this->spaceIcon = '<span class="btn btn-default disabled">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
385
386
        // @todo use folder methods directly when they support filters
387
        $storage = $this->folderObject->getStorage();
388
        $storage->resetFileAndFolderNameFiltersToDefault();
389
390
        // Only render the contents of a browsable storage
391
        if ($this->folderObject->getStorage()->isBrowsable()) {
392
            try {
393
                $foldersCount = $storage->countFoldersInFolder($this->folderObject);
394
                $filesCount = $storage->countFilesInFolder($this->folderObject);
395
            } catch (InsufficientFolderAccessPermissionsException $e) {
396
                $foldersCount = 0;
397
                $filesCount = 0;
398
            }
399
400
            if ($foldersCount <= $this->firstElementNumber) {
401
                $foldersFrom = false;
402
                $foldersNum = false;
403
            } else {
404
                $foldersFrom = $this->firstElementNumber;
405
                if ($this->firstElementNumber + $this->iLimit > $foldersCount) {
406
                    $foldersNum = $foldersCount - $this->firstElementNumber;
407
                } else {
408
                    $foldersNum = $this->iLimit;
409
                }
410
            }
411
            if ($foldersCount >= $this->firstElementNumber + $this->iLimit) {
412
                $filesFrom = false;
413
                $filesNum  = false;
414
            } else {
415
                if ($this->firstElementNumber <= $foldersCount) {
416
                    $filesFrom = 0;
417
                    $filesNum  = $this->iLimit - $foldersNum;
418
                } else {
419
                    $filesFrom = $this->firstElementNumber - $foldersCount;
420
                    if ($filesFrom + $this->iLimit > $filesCount) {
421
                        $filesNum = $filesCount - $filesFrom;
422
                    } else {
423
                        $filesNum  = $this->iLimit;
424
                    }
425
                }
426
            }
427
428
            $folders = $storage->getFoldersInFolder($this->folderObject, $foldersFrom, $foldersNum, true, false, trim($this->sort), (bool)$this->sortRev);
0 ignored issues
show
Bug introduced by
It seems like $foldersFrom can also be of type false; however, parameter $start of TYPO3\CMS\Core\Resource\...e::getFoldersInFolder() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

428
            $folders = $storage->getFoldersInFolder($this->folderObject, /** @scrutinizer ignore-type */ $foldersFrom, $foldersNum, true, false, trim($this->sort), (bool)$this->sortRev);
Loading history...
Bug introduced by
It seems like $foldersNum can also be of type false; however, parameter $maxNumberOfItems of TYPO3\CMS\Core\Resource\...e::getFoldersInFolder() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

428
            $folders = $storage->getFoldersInFolder($this->folderObject, $foldersFrom, /** @scrutinizer ignore-type */ $foldersNum, true, false, trim($this->sort), (bool)$this->sortRev);
Loading history...
429
            $files = $this->folderObject->getFiles($filesFrom, $filesNum, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, false, trim($this->sort), (bool)$this->sortRev);
0 ignored issues
show
Bug introduced by
It seems like $filesNum can also be of type false; however, parameter $numberOfItems of TYPO3\CMS\Core\Resource\Folder::getFiles() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

429
            $files = $this->folderObject->getFiles($filesFrom, /** @scrutinizer ignore-type */ $filesNum, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, false, trim($this->sort), (bool)$this->sortRev);
Loading history...
Bug introduced by
It seems like $filesFrom can also be of type false; however, parameter $start of TYPO3\CMS\Core\Resource\Folder::getFiles() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

429
            $files = $this->folderObject->getFiles(/** @scrutinizer ignore-type */ $filesFrom, $filesNum, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, false, trim($this->sort), (bool)$this->sortRev);
Loading history...
430
            $this->totalItems = $foldersCount + $filesCount;
431
            // Adds the code of files/dirs
432
            $out = '';
433
            $titleCol = 'file';
434
            // Cleaning rowlist for duplicates and place the $titleCol as the first column always!
435
            $rowlist = '_LOCALIZATION_,' . $rowlist;
0 ignored issues
show
Bug introduced by
Are you sure $rowlist of type array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

435
            $rowlist = '_LOCALIZATION_,' . /** @scrutinizer ignore-type */ $rowlist;
Loading history...
436
            $rowlist = GeneralUtility::rmFromList($titleCol, $rowlist);
437
            $rowlist = GeneralUtility::uniqueList($rowlist);
438
            $rowlist = $rowlist ? $titleCol . ',' . $rowlist : $titleCol;
439
            if ($this->clipBoard) {
440
                $rowlist = str_replace('_LOCALIZATION_,', '_LOCALIZATION_,_CLIPBOARD_,', $rowlist);
441
                $this->addElement_tdCssClass['_CLIPBOARD_'] = 'col-clipboard';
442
            }
443
            if ($this->bigControlPanel) {
444
                $rowlist = str_replace('_LOCALIZATION_,', '_LOCALIZATION_,_CONTROL_,', $rowlist);
445
                $this->addElement_tdCssClass['_CONTROL_'] = 'col-control';
446
            }
447
            $this->fieldArray = explode(',', $rowlist);
448
449
            // Add classes to table cells
450
            $this->addElement_tdCssClass[$titleCol] = 'col-title col-responsive';
451
            $this->addElement_tdCssClass['_LOCALIZATION_'] = 'col-localizationa';
452
453
            $folders = ListUtility::resolveSpecialFolderNames($folders);
454
455
            $iOut = '';
456
            // Directories are added
457
            $this->eCounter = $this->firstElementNumber;
458
            list(, $code) = $this->fwd_rwd_nav();
459
            $iOut .= $code;
460
461
            $iOut .= $this->formatDirList($folders);
462
            // Files are added
463
            $iOut .= $this->formatFileList($files);
464
465
            $this->eCounter = $this->firstElementNumber + $this->iLimit < $this->totalItems
466
                ? $this->firstElementNumber + $this->iLimit
467
                : -1;
468
            list(, $code) = $this->fwd_rwd_nav();
469
            $iOut .= $code;
470
471
            // Header line is drawn
472
            $theData = [];
473
            foreach ($this->fieldArray as $v) {
474
                if ($v === '_CLIPBOARD_' && $this->clipBoard) {
475
                    $cells = [];
476
                    $table = '_FILE';
477
                    $elFromTable = $this->clipObj->elFromTable($table);
478
                    if (!empty($elFromTable) && $this->folderObject->checkActionPermission('write')) {
479
                        $addPasteButton = true;
480
                        $elToConfirm = [];
481
                        foreach ($elFromTable as $key => $element) {
482
                            $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
483
                            if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $this->folderObject)) {
484
                                $addPasteButton = false;
485
                            }
486
                            $elToConfirm[$key] = $clipBoardElement->getName();
487
                        }
488
                        if ($addPasteButton) {
489
                            $cells[] = '<a class="btn btn-default t3js-modal-trigger"' .
490
                                ' href="' . htmlspecialchars($this->clipObj->pasteUrl(
491
                                    '_FILE',
492
                                    $this->folderObject->getCombinedIdentifier()
493
                                )) . '"'
494
                                . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText(
495
                                    '_FILE',
496
                                    $this->path,
497
                                    'into',
498
                                    $elToConfirm
499
                                )) . '"'
500
                                . ' data-severity="warning"'
501
                                . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_paste')) . '"'
502
                                . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_paste')) . '">'
503
                                . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)
504
                                    ->render()
505
                                . '</a>';
506
                        }
507
                    }
508
                    if ($this->clipObj->current !== 'normal' && $iOut) {
509
                        $cells[] = $this->linkClipboardHeaderIcon('<span title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_selectMarked')) . '">' . $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render() . '</span>', $table, 'setCB');
510
                        $cells[] = $this->linkClipboardHeaderIcon('<span title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_deleteMarked')) . '">' . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render(), $table, 'delete', $this->getLanguageService()->getLL('clip_deleteMarkedWarning'));
511
                        $onClick = 'checkOffCB(' . GeneralUtility::quoteJSvalue(implode(',', $this->CBnames)) . ', this); return false;';
512
                        $cells[] = '<a class="btn btn-default" rel="" href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_markRecords')) . '">' . $this->iconFactory->getIcon('actions-document-select', Icon::SIZE_SMALL)->render() . '</a>';
513
                    }
514
                    $theData[$v] = implode('', $cells);
515
                } else {
516
                    // Normal row:
517
                    $theT = $this->linkWrapSort(htmlspecialchars($this->getLanguageService()->getLL('c_' . $v)), $this->folderObject->getCombinedIdentifier(), $v);
518
                    $theData[$v] = $theT;
519
                }
520
            }
521
522
            $out .= '<thead>' . $this->addElement(1, '', $theData, '', '', '', 'th') . '</thead>';
523
            $out .= '<tbody>' . $iOut . '</tbody>';
524
            // half line is drawn
525
            // finish
526
            $out = '
527
		<!--
528
			Filelist table:
529
		-->
530
			<div class="table-fit">
531
				<table class="table table-striped table-hover" id="typo3-filelist">
532
					' . $out . '
533
				</table>
534
			</div>';
535
        } else {
536
            /** @var $flashMessage FlashMessage */
537
            $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('storageNotBrowsableMessage'), $this->getLanguageService()->getLL('storageNotBrowsableTitle'), FlashMessage::INFO);
0 ignored issues
show
Bug introduced by
$this->getLanguageServic...geNotBrowsableMessage') of type string is incompatible with the type array<integer,mixed> expected by parameter $constructorArguments of TYPO3\CMS\Core\Utility\G...Utility::makeInstance(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

537
            $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, /** @scrutinizer ignore-type */ $this->getLanguageService()->getLL('storageNotBrowsableMessage'), $this->getLanguageService()->getLL('storageNotBrowsableTitle'), FlashMessage::INFO);
Loading history...
538
            /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
539
            $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
540
            /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
541
            $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
542
            $defaultFlashMessageQueue->enqueue($flashMessage);
543
            $out = '';
544
        }
545
        return $out;
546
    }
547
548
    /**
549
     * Returns a table-row with the content from the fields in the input data array.
550
     * OBS: $this->fieldArray MUST be set! (represents the list of fields to display)
551
     *
552
     * @param int $h Is an integer >=0 and denotes how tall an element is. Set to '0' makes a halv line, -1 = full line, set to 1 makes a 'join' and above makes 'line'
553
     * @param string $icon Is the <img>+<a> of the record. If not supplied the first 'join'-icon will be a 'line' instead
554
     * @param array $data Is the dataarray, record with the fields. Notice: These fields are (currently) NOT htmlspecialchar'ed before being wrapped in <td>-tags
555
     * @param string $rowParams Is insert in the <tr>-tags. Must carry a ' ' as first character
556
     * @param string $_ OBSOLETE - NOT USED ANYMORE. $lMargin is the leftMargin (int)
557
     * @param string $_2 OBSOLETE - NOT USED ANYMORE. Is the HTML <img>-tag for an alternative 'gfx/ol/line.gif'-icon (used in the top)
558
     * @param string $colType Defines the tag being used for the columns. Default is td.
559
     *
560
     * @return string HTML content for the table row
561
     */
562
    public function addElement($h, $icon, $data, $rowParams = '', $_ = '', $_2 = '', $colType = 'td')
563
    {
564
        $colType = ($colType === 'th') ? 'th' : 'td';
565
        $noWrap = $this->no_noWrap ? '' : ' nowrap';
566
        // Start up:
567
        $l10nParent = (int)($data['_l10nparent_'] ?? 0);
568
        $out = '
569
		<!-- Element, begin: -->
570
		<tr ' . $rowParams . ' data-uid="' . (int)($data['uid'] ?? 0) . '" data-l10nparent="' . $l10nParent . '">';
571
        // Show icon and lines
572
        if ($this->showIcon) {
573
            $out .= '
574
			<' . $colType . ' class="col-icon nowrap">';
575
            if (!$h) {
576
                $out .= '&nbsp;';
577
            } else {
578
                for ($a = 0; $a < $h; $a++) {
579
                    if (!$a) {
580
                        if ($icon) {
581
                            $out .= $icon;
582
                        }
583
                    }
584
                }
585
            }
586
            $out .= '</' . $colType . '>
587
			';
588
        }
589
        // Init rendering.
590
        $colsp = '';
591
        $lastKey = '';
592
        $c = 0;
593
        $ccount = 0;
594
        // __label is used as the label key to circumvent problems with uid used as label (see #67756)
595
        // as it was introduced later on, check if it really exists before using it
596
        $fields = $this->fieldArray;
597
        if ($colType === 'td' && array_key_exists('__label', $data)) {
598
            $fields[0] = '__label';
599
        }
600
        // Traverse field array which contains the data to present:
601
        foreach ($fields as $vKey) {
602
            if (isset($data[$vKey])) {
603
                if ($lastKey) {
604
                    $cssClass = $this->addElement_tdCssClass[$lastKey] ?? '';
605
                    if ($this->oddColumnsCssClass && $ccount % 2 == 0) {
606
                        $cssClass = implode(' ', [$cssClass, $this->oddColumnsCssClass]);
607
                    }
608
                    $out .= '
609
						<' . $colType . ' class="' . $cssClass . $noWrap . '"' . $colsp . ($this->addElement_tdParams[$lastKey] ?? '') . '>' . $data[$lastKey] . '</' . $colType . '>';
610
                }
611
                $lastKey = $vKey;
612
                $c = 1;
613
                $ccount++;
614
            } else {
615
                if (!$lastKey) {
616
                    $lastKey = $vKey;
617
                }
618
                $c++;
619
            }
620
            if ($c > 1) {
621
                $colsp = ' colspan="' . $c . '"';
622
            } else {
623
                $colsp = '';
624
            }
625
        }
626
        if ($lastKey) {
627
            $cssClass = $this->addElement_tdCssClass[$lastKey] ?? '';
628
            if ($this->oddColumnsCssClass) {
629
                $cssClass = implode(' ', [$cssClass, $this->oddColumnsCssClass]);
630
            }
631
            $out .= '
632
				<' . $colType . ' class="' . $cssClass . $noWrap . '"' . $colsp . ($this->addElement_tdParams[$lastKey] ?? '') . '>' . $data[$lastKey] . '</' . $colType . '>';
633
        }
634
        // End row
635
        $out .= '
636
		</tr>';
637
        // Return row.
638
        return $out;
639
    }
640
641
    /**
642
     * Dummy function, used to write the top of a table listing.
643
     */
644
    public function writeTop()
645
    {
646
    }
647
648
    /**
649
     * Creates a forward/reverse button based on the status of ->eCounter, ->firstElementNumber, ->iLimit
650
     *
651
     * @param string $table Table name
652
     * @return array array([boolean], [HTML]) where [boolean] is 1 for reverse element, [HTML] is the table-row code for the element
653
     */
654
    public function fwd_rwd_nav($table = '')
655
    {
656
        $code = '';
657
        if ($this->eCounter >= $this->firstElementNumber && $this->eCounter < $this->firstElementNumber + $this->iLimit) {
658
            if ($this->firstElementNumber && $this->eCounter == $this->firstElementNumber) {
659
                // 	Reverse
660
                $theData = [];
661
                $titleCol = $this->fieldArray[0];
662
                $theData[$titleCol] = $this->fwd_rwd_HTML('fwd', $this->eCounter, $table);
663
                $code = $this->addElement(1, '', $theData, 'class="fwd_rwd_nav"');
664
            }
665
            return [1, $code];
666
        }
667
        if ($this->eCounter == $this->firstElementNumber + $this->iLimit) {
668
            // 	Forward
669
            $theData = [];
670
            $titleCol = $this->fieldArray[0];
671
            $theData[$titleCol] = $this->fwd_rwd_HTML('rwd', $this->eCounter, $table);
672
            $code = $this->addElement(1, '', $theData, 'class="fwd_rwd_nav"');
673
        }
674
        return [0, $code];
675
    }
676
677
    /**
678
     * Creates the button with link to either forward or reverse
679
     *
680
     * @param string $type Type: "fwd" or "rwd
681
     * @param int $pointer Pointer
682
     * @param string $table Table name
683
     * @return string
684
     * @access private
685
     */
686
    public function fwd_rwd_HTML($type, $pointer, $table = '')
687
    {
688
        $content = '';
689
        $tParam = $table ? '&table=' . rawurlencode($table) : '';
690
        switch ($type) {
691
            case 'fwd':
692
                $href = $this->listURL() . '&pointer=' . ($pointer - $this->iLimit) . $tParam;
693
                $content = '<a href="' . htmlspecialchars($href) . '">' . $this->iconFactory->getIcon(
694
                        'actions-move-up',
695
                        Icon::SIZE_SMALL
696
                    )->render() . '</a> <i>[1 - ' . $pointer . ']</i>';
697
                break;
698
            case 'rwd':
699
                $href = $this->listURL() . '&pointer=' . $pointer . $tParam;
700
                $content = '<a href="' . htmlspecialchars($href) . '">' . $this->iconFactory->getIcon(
701
                        'actions-move-down',
702
                        Icon::SIZE_SMALL
703
                    )->render() . '</a> <i>[' . ($pointer + 1) . ' - ' . $this->totalItems . ']</i>';
704
                break;
705
        }
706
        return $content;
707
    }
708
709
    /**
710
     * Returning JavaScript for ClipBoard functionality.
711
     *
712
     * @return string
713
     */
714
    public function CBfunctions()
715
    {
716
        return '
717
		// checkOffCB()
718
	function checkOffCB(listOfCBnames, link) {	//
719
		var checkBoxes, flag, i;
720
		var checkBoxes = listOfCBnames.split(",");
721
		if (link.rel === "") {
722
			link.rel = "allChecked";
723
			flag = true;
724
		} else {
725
			link.rel = "";
726
			flag = false;
727
		}
728
		for (i = 0; i < checkBoxes.length; i++) {
729
			setcbValue(checkBoxes[i], flag);
730
		}
731
	}
732
		// cbValue()
733
	function cbValue(CBname) {	//
734
		var CBfullName = "CBC["+CBname+"]";
735
		return (document.dblistForm[CBfullName] && document.dblistForm[CBfullName].checked ? 1 : 0);
736
	}
737
		// setcbValue()
738
	function setcbValue(CBname,flag) {	//
739
		CBfullName = "CBC["+CBname+"]";
740
		if(document.dblistForm[CBfullName]) {
741
			document.dblistForm[CBfullName].checked = flag ? "on" : 0;
742
		}
743
	}
744
745
		';
746
    }
747
748
    /**
749
     * Initializes page languages and icons
750
     */
751
    public function initializeLanguages()
752
    {
753
        // Look up page overlays:
754
        $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
755
        $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
756
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
757
            ->getQueryBuilderForTable('pages');
758
        $queryBuilder->getRestrictions()
759
            ->removeAll()
760
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
761
            ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
762
        $result = $queryBuilder
763
            ->select('*')
764
            ->from('pages')
765
            ->where(
766
                $queryBuilder->expr()->andX(
767
                    $queryBuilder->expr()->eq(
768
                        $localizationParentField,
769
                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
770
                    ),
771
                    $queryBuilder->expr()->gt(
772
                        $languageField,
773
                        $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
774
                    )
775
                )
776
            )
777
            ->execute();
778
779
        $this->pageOverlays = [];
780
        while ($row = $result->fetch()) {
781
            $this->pageOverlays[$row[$languageField]] = $row;
782
        }
783
784
        $this->languageIconTitles = $this->getTranslateTools()->getSystemLanguages($this->id);
785
    }
786
787
    /**
788
     * Return the icon for the language
789
     *
790
     * @param int $sys_language_uid Sys language uid
791
     * @param bool $addAsAdditionalText If set to true, only the flag is returned
792
     * @return string Language icon
793
     */
794
    public function languageFlag($sys_language_uid, $addAsAdditionalText = true)
795
    {
796
        $out = '';
797
        $title = htmlspecialchars($this->languageIconTitles[$sys_language_uid]['title']);
798
        if ($this->languageIconTitles[$sys_language_uid]['flagIcon']) {
799
            $out .= '<span title="' . $title . '">' . $this->iconFactory->getIcon(
800
                    $this->languageIconTitles[$sys_language_uid]['flagIcon'],
801
                    Icon::SIZE_SMALL
802
                )->render() . '</span>';
803
            if (!$addAsAdditionalText) {
804
                return $out;
805
            }
806
            $out .= '&nbsp;';
807
        }
808
        $out .= $title;
809
        return $out;
810
    }
811
812
    /**
813
     * If there is a parent folder and user has access to it, return an icon
814
     * which is linked to the filelist of the parent folder.
815
     *
816
     * @param Folder $currentFolder
817
     * @return string
818
     */
819
    protected function getLinkToParentFolder(Folder $currentFolder)
820
    {
821
        $levelUp = '';
822
        try {
823
            $currentStorage = $currentFolder->getStorage();
824
            $parentFolder = $currentFolder->getParentFolder();
825
            if ($parentFolder->getIdentifier() !== $currentFolder->getIdentifier() && $currentStorage->isWithinFileMountBoundaries($parentFolder)) {
826
                $levelUp = $this->linkWrapDir(
827
                    '<span title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.upOneLevel')) . '">'
828
                    . $this->iconFactory->getIcon('actions-view-go-up', Icon::SIZE_SMALL)->render()
829
                    . '</span>',
830
                    $parentFolder
831
                );
832
            }
833
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
834
        }
835
        return $levelUp;
836
    }
837
838
    /**
839
     * Gets the number of files and total size of a folder
840
     *
841
     * @return string
842
     */
843
    public function getFolderInfo()
844
    {
845
        if ($this->counter == 1) {
846
            $fileLabel = htmlspecialchars($this->getLanguageService()->getLL('file'));
847
        } else {
848
            $fileLabel = htmlspecialchars($this->getLanguageService()->getLL('files'));
849
        }
850
        return $this->counter . ' ' . $fileLabel . ', ' . GeneralUtility::formatSize($this->totalbytes, htmlspecialchars($this->getLanguageService()->getLL('byteSizeUnits')));
851
    }
852
853
    /**
854
     * This returns tablerows for the directories in the array $items['sorting'].
855
     *
856
     * @param Folder[] $folders Folders of \TYPO3\CMS\Core\Resource\Folder
857
     * @return string HTML table rows.
858
     */
859
    public function formatDirList(array $folders)
860
    {
861
        $out = '';
862
        foreach ($folders as $folderName => $folderObject) {
863
            $role = $folderObject->getRole();
864
            if ($role === FolderInterface::ROLE_PROCESSING) {
865
                // don't show processing-folder
866
                continue;
867
            }
868
            if ($role !== FolderInterface::ROLE_DEFAULT) {
869
                $displayName = '<strong>' . htmlspecialchars($folderName) . '</strong>';
870
            } else {
871
                $displayName = htmlspecialchars($folderName);
872
            }
873
874
            $isLocked = $folderObject instanceof InaccessibleFolder;
875
            $isWritable = $folderObject->checkActionPermission('write');
876
877
            // Initialization
878
            $this->counter++;
879
880
            // The icon with link
881
            $theIcon = '<span title="' . htmlspecialchars($folderName) . '">' . $this->iconFactory->getIconForResource($folderObject, Icon::SIZE_SMALL)->render() . '</span>';
882
            if (!$isLocked) {
883
                $theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, 'sys_file', $folderObject->getCombinedIdentifier());
884
            }
885
886
            // Preparing and getting the data-array
887
            $theData = [];
888
            if ($isLocked) {
889
                foreach ($this->fieldArray as $field) {
890
                    $theData[$field] = '';
891
                }
892
                $theData['file'] = $displayName;
893
            } else {
894
                foreach ($this->fieldArray as $field) {
895
                    switch ($field) {
896
                        case 'size':
897
                            try {
898
                                $numFiles = $folderObject->getFileCount();
899
                            } catch (InsufficientFolderAccessPermissionsException $e) {
900
                                $numFiles = 0;
901
                            }
902
                            $theData[$field] = $numFiles . ' ' . htmlspecialchars($this->getLanguageService()->getLL(($numFiles === 1 ? 'file' : 'files')));
903
                            break;
904
                        case 'rw':
905
                            $theData[$field] = '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('read')) . '</strong>' . (!$isWritable ? '' : '<strong class="text-danger">' . htmlspecialchars($this->getLanguageService()->getLL('write')) . '</strong>');
906
                            break;
907
                        case 'fileext':
908
                            $theData[$field] = htmlspecialchars($this->getLanguageService()->getLL('folder'));
909
                            break;
910
                        case 'tstamp':
911
                            $tstamp = $folderObject->getModificationTime();
912
                            $theData[$field] = $tstamp ? BackendUtility::date($tstamp) : '-';
913
                            break;
914
                        case 'file':
915
                            $theData[$field] = $this->linkWrapDir($displayName, $folderObject);
916
                            break;
917
                        case '_CONTROL_':
918
                            $theData[$field] = $this->makeEdit($folderObject);
919
                            break;
920
                        case '_CLIPBOARD_':
921
                            $theData[$field] = $this->makeClip($folderObject);
922
                            break;
923
                        case '_REF_':
924
                            $theData[$field] = $this->makeRef($folderObject);
925
                            break;
926
                        default:
927
                            $theData[$field] = GeneralUtility::fixed_lgd_cs($theData[$field] ?? '', $this->fixedL);
928
                    }
929
                }
930
            }
931
            $out .= $this->addElement(1, $theIcon, $theData);
932
        }
933
        return $out;
934
    }
935
936
    /**
937
     * Wraps the directory-titles
938
     *
939
     * @param string $title String to be wrapped in links
940
     * @param Folder $folderObject Folder to work on
941
     * @return string HTML
942
     */
943
    public function linkWrapDir($title, Folder $folderObject)
944
    {
945
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
946
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
947
        $href = (string)$uriBuilder->buildUriFromRoute('file_FilelistList', ['id' => $folderObject->getCombinedIdentifier()]);
948
        $onclick = ' onclick="' . htmlspecialchars(('top.document.getElementsByName("navigation")[0].contentWindow.Tree.highlightActiveItem("file","folder' . GeneralUtility::md5int($folderObject->getCombinedIdentifier()) . '_"+top.fsMod.currentBank)')) . '"';
949
        // Sometimes $code contains plain HTML tags. In such a case the string should not be modified!
950
        if ((string)$title === strip_tags($title)) {
951
            return '<a href="' . htmlspecialchars($href) . '"' . $onclick . ' title="' . htmlspecialchars($title) . '">' . $title . '</a>';
952
        }
953
        return '<a href="' . htmlspecialchars($href) . '"' . $onclick . '>' . $title . '</a>';
954
    }
955
956
    /**
957
     * Wraps filenames in links which opens the metadata editor.
958
     *
959
     * @param string $code String to be wrapped in links
960
     * @param File $fileObject File to be linked
961
     * @return string HTML
962
     */
963
    public function linkWrapFile($code, File $fileObject)
964
    {
965
        try {
966
            if ($fileObject instanceof File && $fileObject->isIndexed() && $fileObject->checkActionPermission('write') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
967
                $metaData = $fileObject->_getMetaData();
968
                $urlParameters = [
969
                    'edit' => [
970
                        'sys_file_metadata' => [
971
                            $metaData['uid'] => 'edit'
972
                        ]
973
                    ],
974
                    'returnUrl' => $this->listURL()
975
                ];
976
                /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
977
                $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
978
                $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
979
                $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.editMetadata'));
980
                $code = '<a class="responsive-title" href="' . htmlspecialchars($url) . '" title="' . $title . '">' . $code . '</a>';
981
            }
982
        } catch (\Exception $e) {
983
            // intentional fall-through
984
        }
985
        return $code;
986
    }
987
988
    /**
989
     * Returns list URL; This is the URL of the current script with id and imagemode parameters, that's all.
990
     * The URL however is not relative, otherwise GeneralUtility::sanitizeLocalUrl() would say that
991
     * the URL would be invalid
992
     *
993
     * @param string $altId
994
     * @param string $table Table name to display. Enter "-1" for the current table.
995
     * @param string $exclList Comma separated list of fields NOT to include ("sortField", "sortRev" or "firstElementNumber")
996
     *
997
     * @return string URL
998
     */
999
    public function listURL($altId = '', $table = '-1', $exclList = '')
1000
    {
1001
        return GeneralUtility::linkThisScript([
1002
            'target' => rawurlencode($this->folderObject->getCombinedIdentifier()),
1003
            'imagemode' => $this->thumbs
1004
        ]);
1005
    }
1006
1007
    /**
1008
     * This returns tablerows for the files in the array $items['sorting'].
1009
     *
1010
     * @param File[] $files File items
1011
     * @return string HTML table rows.
1012
     */
1013
    public function formatFileList(array $files)
1014
    {
1015
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1016
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1017
        $out = '';
1018
        // first two keys are "0" (default) and "-1" (multiple), after that comes the "other languages"
1019
        $allSystemLanguages = GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages();
1020
        $systemLanguages = array_filter($allSystemLanguages, function ($languageRecord) {
1021
            if ($languageRecord['uid'] === -1 || $languageRecord['uid'] === 0 || !$this->getBackendUser()->checkLanguageAccess($languageRecord['uid'])) {
1022
                return false;
1023
            }
1024
            return true;
1025
        });
1026
1027
        foreach ($files as $fileObject) {
1028
            // Initialization
1029
            $this->counter++;
1030
            $this->totalbytes += $fileObject->getSize();
1031
            $ext = $fileObject->getExtension();
1032
            $fileName = trim($fileObject->getName());
1033
            // The icon with link
1034
            $theIcon = '<span title="' . htmlspecialchars($fileName . ' [' . (int)$fileObject->getUid() . ']') . '">'
1035
                . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
1036
            $theIcon = BackendUtility::wrapClickMenuOnIcon($theIcon, 'sys_file', $fileObject->getCombinedIdentifier());
1037
            // Preparing and getting the data-array
1038
            $theData = [];
1039
            foreach ($this->fieldArray as $field) {
1040
                switch ($field) {
1041
                    case 'size':
1042
                        $theData[$field] = GeneralUtility::formatSize($fileObject->getSize(), htmlspecialchars($this->getLanguageService()->getLL('byteSizeUnits')));
1043
                        break;
1044
                    case 'rw':
1045
                        $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>');
1046
                        break;
1047
                    case 'fileext':
1048
                        $theData[$field] = strtoupper($ext);
1049
                        break;
1050
                    case 'tstamp':
1051
                        $theData[$field] = BackendUtility::date($fileObject->getModificationTime());
1052
                        break;
1053
                    case '_CONTROL_':
1054
                        $theData[$field] = $this->makeEdit($fileObject);
1055
                        break;
1056
                    case '_CLIPBOARD_':
1057
                        $theData[$field] = $this->makeClip($fileObject);
1058
                        break;
1059
                    case '_LOCALIZATION_':
1060
                        if (!empty($systemLanguages) && $fileObject->isIndexed() && $fileObject->checkActionPermission('write') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
1061
                            $metaDataRecord = $fileObject->_getMetaData();
1062
                            $translations = $this->getTranslationsForMetaData($metaDataRecord);
1063
                            $languageCode = '';
1064
1065
                            foreach ($systemLanguages as $language) {
1066
                                $languageId = $language['uid'];
1067
                                $flagIcon = $language['flagIcon'];
1068
                                if (array_key_exists($languageId, $translations)) {
1069
                                    $title = htmlspecialchars(sprintf($this->getLanguageService()->getLL('editMetadataForLanguage'), $language['title']));
1070
                                    // @todo the overlay for the flag needs to be added ($flagIcon . '-overlay')
1071
                                    $urlParameters = [
1072
                                        'edit' => [
1073
                                            'sys_file_metadata' => [
1074
                                                $translations[$languageId]['uid'] => 'edit'
1075
                                            ]
1076
                                        ],
1077
                                        'returnUrl' => $this->listURL()
1078
                                    ];
1079
                                    $flagButtonIcon = $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-edit')->render();
1080
                                    $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
1081
                                    $languageCode .= '<a href="' . htmlspecialchars($url) . '" class="btn btn-default" title="' . $title . '">'
1082
                                        . $flagButtonIcon . '</a>';
1083
                                } else {
1084
                                    $parameters = [
1085
                                        'justLocalized' => 'sys_file_metadata:' . $metaDataRecord['uid'] . ':' . $languageId,
1086
                                        'returnUrl' => $this->listURL()
1087
                                    ];
1088
                                    $returnUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', $parameters);
1089
                                    $href = BackendUtility::getLinkToDataHandlerAction(
1090
                                        '&cmd[sys_file_metadata][' . $metaDataRecord['uid'] . '][localize]=' . $languageId,
1091
                                        $returnUrl
1092
                                    );
1093
                                    $flagButtonIcon = '<span title="' . htmlspecialchars(sprintf($this->getLanguageService()->getLL('createMetadataForLanguage'), $language['title'])) . '">' . $this->iconFactory->getIcon($flagIcon, Icon::SIZE_SMALL, 'overlay-new')->render() . '</span>';
1094
                                    $languageCode .= '<a href="' . htmlspecialchars($href) . '" class="btn btn-default">' . $flagButtonIcon . '</a> ';
1095
                                }
1096
                            }
1097
1098
                            // Hide flag button bar when not translated yet
1099
                            $theData[$field] = ' <div class="localisationData btn-group" data-fileid="' . $fileObject->getUid() . '"' .
1100
                                (empty($translations) ? ' style="display: none;"' : '') . '>' . $languageCode . '</div>';
1101
                            $theData[$field] .= '<a class="btn btn-default filelist-translationToggler" data-fileid="' . $fileObject->getUid() . '">' .
1102
                                '<span title="' . htmlspecialchars($this->getLanguageService()->getLL('translateMetadata')) . '">'
1103
                                . $this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL)->render() . '</span>'
1104
                                . '</a>';
1105
                        }
1106
                        break;
1107
                    case '_REF_':
1108
                        $theData[$field] = $this->makeRef($fileObject);
1109
                        break;
1110
                    case 'file':
1111
                        // Edit metadata of file
1112
                        $theData[$field] = $this->linkWrapFile(htmlspecialchars($fileName), $fileObject);
1113
1114
                        if ($fileObject->isMissing()) {
1115
                            $theData[$field] .= '<span class="label label-danger label-space-left">'
1116
                                . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.file_missing'))
1117
                                . '</span>';
1118
                            // Thumbnails?
1119
                        } elseif ($this->thumbs && ($this->isImage($ext) || $this->isMediaFile($ext))) {
1120
                            $processedFile = $fileObject->process(
1121
                                ProcessedFile::CONTEXT_IMAGEPREVIEW,
1122
                                [
1123
                                    'width' => $this->thumbnailConfiguration->getWidth(),
1124
                                    'height' => $this->thumbnailConfiguration->getHeight()
1125
                                ]
1126
                            );
1127
                            if ($processedFile) {
1128
                                $thumbUrl = $processedFile->getPublicUrl(true);
1129
                                $theData[$field] .= '<br /><img src="' . $thumbUrl . '" ' .
1130
                                    'width="' . $processedFile->getProperty('width') . '" ' .
1131
                                    'height="' . $processedFile->getProperty('height') . '" ' .
1132
                                    'title="' . htmlspecialchars($fileName) . '" alt="" />';
1133
                            }
1134
                        }
1135
                        break;
1136
                    default:
1137
                        $theData[$field] = '';
1138
                        if ($fileObject->hasProperty($field)) {
1139
                            $theData[$field] = htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileObject->getProperty($field), $this->fixedL));
1140
                        }
1141
                }
1142
            }
1143
            $out .= $this->addElement(1, $theIcon, $theData);
1144
        }
1145
        return $out;
1146
    }
1147
1148
    /**
1149
     * Fetch the translations for a sys_file_metadata record
1150
     *
1151
     * @param $metaDataRecord
1152
     * @return array keys are the sys_language uids, values are the $rows
1153
     */
1154
    protected function getTranslationsForMetaData($metaDataRecord)
1155
    {
1156
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_metadata');
1157
        $queryBuilder->getRestrictions()->removeAll();
1158
        $translationRecords = $queryBuilder->select('*')
1159
            ->from('sys_file_metadata')
1160
            ->where(
1161
                $queryBuilder->expr()->eq(
1162
                    $GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'],
1163
                    $queryBuilder->createNamedParameter($metaDataRecord['uid'], \PDO::PARAM_INT)
1164
                ),
1165
                $queryBuilder->expr()->gt(
1166
                    $GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'],
1167
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1168
                )
1169
            )
1170
            ->execute()
1171
            ->fetchAll();
1172
1173
        $translations = [];
1174
        foreach ($translationRecords as $record) {
1175
            $translations[$record[$GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField']]] = $record;
1176
        }
1177
        return $translations;
1178
    }
1179
1180
    /**
1181
     * Returns TRUE if $ext is an image-extension according to $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
1182
     *
1183
     * @param string $ext File extension
1184
     * @return bool
1185
     */
1186
    public function isImage($ext)
1187
    {
1188
        return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($ext));
1189
    }
1190
1191
    /**
1192
     * Returns TRUE if $ext is an media-extension according to $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']
1193
     *
1194
     * @param string $ext File extension
1195
     * @return bool
1196
     */
1197
    public function isMediaFile($ext)
1198
    {
1199
        return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'], strtolower($ext));
1200
    }
1201
1202
    /**
1203
     * Wraps the directory-titles ($code) in a link to filelist/Modules/Filelist/index.php (id=$path) and sorting commands...
1204
     *
1205
     * @param string $code String to be wrapped
1206
     * @param string $folderIdentifier ID (path)
1207
     * @param string $col Sorting column
1208
     * @return string HTML
1209
     */
1210
    public function linkWrapSort($code, $folderIdentifier, $col)
1211
    {
1212
        $params = ['id' => $folderIdentifier, 'SET' => [ 'sort' => $col ]];
1213
1214
        if ($this->sort === $col) {
1215
            // Check reverse sorting
1216
            $params['SET']['reverse'] = ($this->sortRev ? '0' : '1');
1217
            $sortArrow = $this->iconFactory->getIcon('status-status-sorting-light-' . ($this->sortRev ? 'desc' : 'asc'), Icon::SIZE_SMALL)->render();
1218
        } else {
1219
            $params['SET']['reverse'] = 0;
1220
            $sortArrow = '';
1221
        }
1222
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1223
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1224
        $href = (string)$uriBuilder->buildUriFromRoute('file_FilelistList', $params);
1225
        return '<a href="' . htmlspecialchars($href) . '">' . $code . ' ' . $sortArrow . '</a>';
1226
    }
1227
1228
    /**
1229
     * Creates the clipboard control pad
1230
     *
1231
     * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.
1232
     * @return string HTML-table
1233
     */
1234
    public function makeClip($fileOrFolderObject)
1235
    {
1236
        if (!$fileOrFolderObject->checkActionPermission('read')) {
1237
            return '';
1238
        }
1239
        $cells = [];
1240
        $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
1241
        $fullName = $fileOrFolderObject->getName();
1242
        $md5 = GeneralUtility::shortMD5($fullIdentifier);
1243
        // For normal clipboard, add copy/cut buttons:
1244
        if ($this->clipObj->current === 'normal') {
1245
            $isSel = $this->clipObj->isSelected('_FILE', $md5);
0 ignored issues
show
Bug introduced by
$md5 of type string is incompatible with the type integer expected by parameter $uid of TYPO3\CMS\Backend\Clipbo...Clipboard::isSelected(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1245
            $isSel = $this->clipObj->isSelected('_FILE', /** @scrutinizer ignore-type */ $md5);
Loading history...
1246
            $copyTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.copy'));
1247
            $cutTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.cut'));
1248
            $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render();
1249
            $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render();
1250
1251
            if ($isSel === 'copy') {
1252
                $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render();
1253
            } elseif ($isSel === 'cut') {
1254
                $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render();
1255
            }
1256
1257
            $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 1, ($isSel === 'copy'))) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';
1258
            // we can only cut if file can be moved
1259
            if ($fileOrFolderObject->checkActionPermission('move')) {
1260
                $cells[] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 0, ($isSel === 'cut'))) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';
1261
            } else {
1262
                $cells[] = $this->spaceIcon;
1263
            }
1264
        } else {
1265
            // For numeric pads, add select checkboxes:
1266
            $n = '_FILE|' . $md5;
1267
            $this->CBnames[] = $n;
1268
            $checked = $this->clipObj->isSelected('_FILE', $md5) ? ' checked="checked"' : '';
1269
            $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>';
1270
        }
1271
        // Display PASTE button, if directory:
1272
        $elFromTable = $this->clipObj->elFromTable('_FILE');
1273
        if ($fileOrFolderObject instanceof Folder && !empty($elFromTable) && $fileOrFolderObject->checkActionPermission('write')) {
1274
            $addPasteButton = true;
1275
            $elToConfirm = [];
1276
            foreach ($elFromTable as $key => $element) {
1277
                $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
1278
                if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder($clipBoardElement, $fileOrFolderObject)) {
1279
                    $addPasteButton = false;
1280
                }
1281
                $elToConfirm[$key] = $clipBoardElement->getName();
1282
            }
1283
            if ($addPasteButton) {
1284
                $cells[] = '<a class="btn btn-default t3js-modal-trigger" '
1285
                    . ' href="' . htmlspecialchars($this->clipObj->pasteUrl('_FILE', $fullIdentifier)) . '"'
1286
                    . ' data-content="' . htmlspecialchars($this->clipObj->confirmMsgText('_FILE', $fullName, 'into', $elToConfirm)) . '"'
1287
                    . ' data-severity="warning"'
1288
                    . ' data-title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"'
1289
                    . ' title="' . htmlspecialchars($this->getLanguageService()->getLL('clip_pasteInto')) . '"'
1290
                    . '>'
1291
                    . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render()
1292
                    . '</a>';
1293
            }
1294
        }
1295
        // Compile items into a DIV-element:
1296
        return ' <div class="btn-group" role="group">' . implode('', $cells) . '</div>';
1297
    }
1298
1299
    /**
1300
     * Creates the edit control section
1301
     *
1302
     * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the edit control section for the listing.
1303
     * @return string HTML-table
1304
     */
1305
    public function makeEdit($fileOrFolderObject)
1306
    {
1307
        $cells = [];
1308
        $fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
1309
        $md5 = GeneralUtility::shortMD5($fullIdentifier);
1310
        $isSel = $this->clipObj->isSelected('_FILE', $md5);
0 ignored issues
show
Bug introduced by
$md5 of type string is incompatible with the type integer expected by parameter $uid of TYPO3\CMS\Backend\Clipbo...Clipboard::isSelected(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1310
        $isSel = $this->clipObj->isSelected('_FILE', /** @scrutinizer ignore-type */ $md5);
Loading history...
1311
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1312
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1313
1314
        // Edit file content (if editable)
1315
        if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('write') && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], $fileOrFolderObject->getExtension())) {
1316
            $url = (string)$uriBuilder->buildUriFromRoute('file_edit', ['target' => $fullIdentifier]);
1317
            $editOnClick = 'top.list_frame.location.href=' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.list_frame.document.location.pathname+top.list_frame.document.location.search);return false;';
1318
            $cells['edit'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($editOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.editcontent') . '">'
1319
                . $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render()
1320
                . '</a>';
1321
        } else {
1322
            $cells['edit'] = $this->spaceIcon;
1323
        }
1324
1325
        // Edit metadata of file
1326
        if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('write') && $this->getBackendUser()->check('tables_modify', 'sys_file_metadata')) {
1327
            $metaData = $fileOrFolderObject->_getMetaData();
1328
            $urlParameters = [
1329
                'edit' => [
1330
                    'sys_file_metadata' => [
1331
                        $metaData['uid'] => 'edit'
1332
                    ]
1333
                ],
1334
                'returnUrl' => $this->listURL()
1335
            ];
1336
            $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
1337
            $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.editMetadata'));
1338
            $cells['metadata'] = '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" title="' . $title . '">' . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>';
1339
        }
1340
1341
        // document view
1342
        if ($fileOrFolderObject instanceof File) {
1343
            $fileUrl = $fileOrFolderObject->getPublicUrl(true);
1344
            if ($fileUrl) {
1345
                $aOnClick = 'return top.openUrlInWindow(' . GeneralUtility::quoteJSvalue($fileUrl) . ', \'WebFile\');';
1346
                $cells['view'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($aOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.view') . '">' . $this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL)->render() . '</a>';
1347
            } else {
1348
                $cells['view'] = $this->spaceIcon;
1349
            }
1350
        } else {
1351
            $cells['view'] = $this->spaceIcon;
1352
        }
1353
1354
        // replace file
1355
        if ($fileOrFolderObject instanceof File && $fileOrFolderObject->checkActionPermission('replace')) {
1356
            $url = (string)$uriBuilder->buildUriFromRoute('file_replace', ['target' => $fullIdentifier, 'uid' => $fileOrFolderObject->getUid()]);
1357
            $replaceOnClick = 'top.list_frame.location.href = ' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.list_frame.document.location.pathname+top.list_frame.document.location.search);return false;';
1358
            $cells['replace'] = '<a href="#" class="btn btn-default" onclick="' . $replaceOnClick . '"  title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.replace') . '">' . $this->iconFactory->getIcon('actions-edit-replace', Icon::SIZE_SMALL)->render() . '</a>';
1359
        }
1360
1361
        // rename the file
1362
        if ($fileOrFolderObject->checkActionPermission('rename')) {
1363
            $url = (string)$uriBuilder->buildUriFromRoute('file_rename', ['target' => $fullIdentifier]);
1364
            $renameOnClick = 'top.list_frame.location.href = ' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.list_frame.document.location.pathname+top.list_frame.document.location.search);return false;';
1365
            $cells['rename'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($renameOnClick) . '"  title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.rename') . '">' . $this->iconFactory->getIcon('actions-edit-rename', Icon::SIZE_SMALL)->render() . '</a>';
1366
        } else {
1367
            $cells['rename'] = $this->spaceIcon;
1368
        }
1369
1370
        // upload files
1371
        if ($fileOrFolderObject->getStorage()->checkUserActionPermission('add', 'File') && $fileOrFolderObject->checkActionPermission('write')) {
1372
            if ($fileOrFolderObject instanceof Folder) {
1373
                $url = (string)$uriBuilder->buildUriFromRoute('file_upload', ['target' => $fullIdentifier]);
1374
                $uploadOnClick = 'top.list_frame.location.href = ' . GeneralUtility::quoteJSvalue($url) . '+\'&returnUrl=\'+top.rawurlencode(top.list_frame.document.location.pathname+top.list_frame.document.location.search);return false;';
1375
                $cells['upload'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($uploadOnClick) . '"  title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.upload')) . '">' . $this->iconFactory->getIcon('actions-edit-upload', Icon::SIZE_SMALL)->render() . '</a>';
1376
            }
1377
        }
1378
1379
        if ($fileOrFolderObject->checkActionPermission('read')) {
1380
            $infoOnClick = '';
1381
            if ($fileOrFolderObject instanceof Folder) {
1382
                $infoOnClick = 'top.launchView( \'_FOLDER\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
1383
            } elseif ($fileOrFolderObject instanceof File) {
1384
                $infoOnClick = 'top.launchView( \'_FILE\', ' . GeneralUtility::quoteJSvalue($fullIdentifier) . ');return false;';
1385
            }
1386
            $cells['info'] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars($infoOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.info') . '">' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . '</a>';
1387
        } else {
1388
            $cells['info'] = $this->spaceIcon;
1389
        }
1390
1391
        // copy the file
1392
        if ($fileOrFolderObject->checkActionPermission('copy') && $this->clipObj->current === 'normal') {
1393
            $copyTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.copy'));
1394
            $copyIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render();
1395
1396
            if ($isSel === 'copy') {
1397
                $copyIcon = $this->iconFactory->getIcon('actions-edit-copy-release', Icon::SIZE_SMALL)->render();
1398
            }
1399
1400
            $cells['copy'] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 1, ($isSel === 'copy'))) . '" title="' . $copyTitle . '">' . $copyIcon . '</a>';
1401
        }
1402
1403
        // cut the file
1404
        if ($fileOrFolderObject->checkActionPermission('move') && $this->clipObj->current === 'normal') {
1405
            $cutTitle = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.cut'));
1406
            $cutIcon = $this->iconFactory->getIcon('actions-edit-cut', Icon::SIZE_SMALL)->render();
1407
1408
            if ($isSel === 'cut') {
1409
                $cutIcon = $this->iconFactory->getIcon('actions-edit-cut-release', Icon::SIZE_SMALL)->render();
1410
            }
1411
1412
            $cells['cut'] = '<a class="btn btn-default" href="' . htmlspecialchars($this->clipObj->selUrlFile($fullIdentifier, 0, ($isSel === 'cut'))) . '" title="' . $cutTitle . '">' . $cutIcon . '</a>';
1413
        }
1414
1415
        // delete the file
1416
        if ($fileOrFolderObject->checkActionPermission('delete')) {
1417
            $identifier = $fileOrFolderObject->getIdentifier();
1418
            if ($fileOrFolderObject instanceof Folder) {
1419
                $referenceCountText = BackendUtility::referenceCount('_FILE', $identifier, ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.referencesToFolder'));
1420
                $deleteType = 'delete_folder';
1421
            } else {
1422
                $referenceCountText = BackendUtility::referenceCount('sys_file', $fileOrFolderObject->getUid(), ' ' . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.referencesToFile'));
1423
                $deleteType = 'delete_file';
1424
            }
1425
1426
            if ($this->getBackendUser()->jsConfirmation(JsConfirmation::DELETE)) {
1427
                $confirmationCheck = '1';
1428
            } else {
1429
                $confirmationCheck = '0';
1430
            }
1431
1432
            $deleteUrl = (string)$uriBuilder->buildUriFromRoute('tce_file');
1433
            $confirmationMessage = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.delete'), $fileOrFolderObject->getName()) . $referenceCountText;
1434
            $title = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.delete');
1435
            $cells['delete'] = '<a href="#" class="btn btn-default t3js-filelist-delete" data-content="' . htmlspecialchars($confirmationMessage)
1436
                . '" data-check="' . $confirmationCheck
1437
                . '" data-delete-url="' . htmlspecialchars($deleteUrl)
1438
                . '" data-title="' . htmlspecialchars($title)
1439
                . '" data-identifier="' . htmlspecialchars($fileOrFolderObject->getCombinedIdentifier())
1440
                . '" data-delete-type="' . $deleteType
1441
                . '" title="' . htmlspecialchars($title) . '">'
1442
                . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() . '</a>';
1443
        } else {
1444
            $cells['delete'] = $this->spaceIcon;
1445
        }
1446
1447
        // Hook for manipulating edit icons.
1448
        $cells['__fileOrFolderObject'] = $fileOrFolderObject;
1449
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['fileList']['editIconsHook'] ?? [] as $className) {
1450
            $hookObject = GeneralUtility::makeInstance($className);
1451
            if (!$hookObject instanceof FileListEditIconHookInterface) {
1452
                throw new \UnexpectedValueException(
1453
                    $className . ' must implement interface ' . FileListEditIconHookInterface::class,
1454
                    1235225797
1455
                );
1456
            }
1457
            $hookObject->manipulateEditIcons($cells, $this);
1458
        }
1459
        unset($cells['__fileOrFolderObject']);
1460
        // Compile items into a DIV-element:
1461
        return '<div class="btn-group">' . implode('', $cells) . '</div>';
1462
    }
1463
1464
    /**
1465
     * Make reference count
1466
     *
1467
     * @param File|Folder $fileOrFolderObject Array with information about the file/directory for which to make the clipboard panel for the listing.
1468
     * @return string HTML
1469
     */
1470
    public function makeRef($fileOrFolderObject)
1471
    {
1472
        if ($fileOrFolderObject instanceof FolderInterface) {
1473
            return '-';
1474
        }
1475
        // Look up the file in the sys_refindex.
1476
        // Exclude sys_file_metadata records as these are no use references
1477
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
1478
        $referenceCount = $queryBuilder->count('*')
1479
            ->from('sys_refindex')
1480
            ->where(
1481
                $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
1482
                $queryBuilder->expr()->eq(
1483
                    'ref_table',
1484
                    $queryBuilder->createNamedParameter('sys_file', \PDO::PARAM_STR)
1485
                ),
1486
                $queryBuilder->expr()->eq(
1487
                    'ref_uid',
1488
                    $queryBuilder->createNamedParameter($fileOrFolderObject->getUid(), \PDO::PARAM_INT)
1489
                ),
1490
                $queryBuilder->expr()->neq(
1491
                    'tablename',
1492
                    $queryBuilder->createNamedParameter('sys_file_metadata', \PDO::PARAM_STR)
1493
                )
1494
            )
1495
            ->execute()
1496
            ->fetchColumn();
1497
1498
        return $this->generateReferenceToolTip($referenceCount, '\'_FILE\', ' . GeneralUtility::quoteJSvalue($fileOrFolderObject->getCombinedIdentifier()));
1499
    }
1500
1501
    /**
1502
     * Returns an instance of LanguageService
1503
     *
1504
     * @return \TYPO3\CMS\Core\Localization\LanguageService
1505
     */
1506
    protected function getLanguageService()
1507
    {
1508
        return $GLOBALS['LANG'];
1509
    }
1510
1511
    /**
1512
     * Returns the current BE user.
1513
     *
1514
     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1515
     */
1516
    protected function getBackendUser()
1517
    {
1518
        return $GLOBALS['BE_USER'];
1519
    }
1520
1521
    /**
1522
     * Sets the script url depending on being a module or script request
1523
     */
1524
    protected function determineScriptUrl()
1525
    {
1526
        if ($routePath = GeneralUtility::_GP('route')) {
1527
            $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
1528
            $this->thisScript = (string)$uriBuilder->buildUriFromRoutePath($routePath);
1529
        } else {
1530
            $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
1531
        }
1532
    }
1533
1534
    /**
1535
     * @return string
1536
     */
1537
    protected function getThisScript()
1538
    {
1539
        return strpos($this->thisScript, '?') === false ? $this->thisScript . '?' : $this->thisScript . '&';
1540
    }
1541
1542
    /**
1543
     * Gets an instance of TranslationConfigurationProvider
1544
     *
1545
     * @return TranslationConfigurationProvider
1546
     */
1547
    protected function getTranslateTools()
1548
    {
1549
        if (!isset($this->translateTools)) {
1550
            $this->translateTools = GeneralUtility::makeInstance(TranslationConfigurationProvider::class);
1551
        }
1552
        return $this->translateTools;
1553
    }
1554
1555
    /**
1556
     * Generates HTML code for a Reference tooltip out of
1557
     * sys_refindex records you hand over
1558
     *
1559
     * @param int $references number of records from sys_refindex table
1560
     * @param string $launchViewParameter JavaScript String, which will be passed as parameters to top.launchView
1561
     * @return string
1562
     */
1563
    protected function generateReferenceToolTip($references, $launchViewParameter = '')
1564
    {
1565
        if (!$references) {
1566
            $htmlCode = '-';
1567
        } else {
1568
            $htmlCode = '<a href="#"';
1569
            if ($launchViewParameter !== '') {
1570
                $htmlCode .= ' onclick="' . htmlspecialchars(
1571
                        ('top.launchView(' . $launchViewParameter . '); return false;')
1572
                    ) . '"';
1573
            }
1574
            $htmlCode .= ' title="' . htmlspecialchars(
1575
                    $this->getLanguageService()->sL(
1576
                        'LLL:EXT:backend/Resources/Private/Language/locallang.xlf:show_references'
1577
                    ) . ' (' . $references . ')'
1578
                ) . '">';
1579
            $htmlCode .= $references;
1580
            $htmlCode .= '</a>';
1581
        }
1582
        return $htmlCode;
1583
    }
1584
}
1585