91 parent::__construct();
93 $this->storages = $this->BE_USER->getFileStorages();
94 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
110 public function PMicon($folderObject, $subFolderCounter, $totalSubFolders, $nextCount, $isExpanded)
131 if (empty($this->scope)) {
133 'class' => static::class,
138 if ($this->thisScript) {
140 $scopeData = json_encode($this->scope);
141 $scopeHash = GeneralUtility::hmac($scopeData);
142 $js = htmlspecialchars(
'Tree.load(' . GeneralUtility::quoteJSvalue($cmd) .
', ' . (
int)$isExpand .
', this, ' . GeneralUtility::quoteJSvalue($scopeData) .
', ' . GeneralUtility::quoteJSvalue($scopeHash) .
');');
143 return '<a class="list-tree-control' . (!$isExpand ?
' list-tree-control-open' :
' list-tree-control-closed') .
'" onclick="' . $js .
'"><i class="fa"></i></a>';
155 $link = $this->thisScript ?
' href="' . htmlspecialchars($this->
getThisScript() .
'PM=' . $cmd) .
'"' :
'';
156 return '<a class="list-tree-control list-tree-control-' . ($isOpen ?
'open' :
'closed') .
'"' . $link .
'><i class="fa"></i></a>';
168 public function wrapIcon($icon, $folderObject)
173 if (!$this->ext_IconMode) {
175 if (!$folderObject instanceof InaccessibleFolder) {
179 } elseif ($this->ext_IconMode ===
'titlelink') {
180 $aOnClick =
'return jumpTo(' . GeneralUtility::quoteJSvalue($this->
getJumpToParam($folderObject)) .
',this,' . GeneralUtility::quoteJSvalue($this->domIdPrefix . $this->
getId($folderObject)) .
',' . $this->bank .
');';
181 $theFolderIcon =
'<a href="#" onclick="' . htmlspecialchars($aOnClick) .
'">' . $icon .
'</a>';
183 return $theFolderIcon;
199 if ($folderObject instanceof InaccessibleFolder) {
202 $aOnClick =
'return jumpTo(' . GeneralUtility::quoteJSvalue($this->
getJumpToParam($folderObject)) .
', this, ' . GeneralUtility::quoteJSvalue($this->domIdPrefix . $this->
getId($folderObject)) .
', ' .
$bank .
');';
206 return '<a href="#" title="' . htmlspecialchars(strip_tags(
$title)) .
'" onclick="' . htmlspecialchars($aOnClick) .
'" ' . GeneralUtility::implodeAttributes($clickMenuParts) .
'>' .
$title .
'</a>';
216 public function getId($folderObject)
218 return GeneralUtility::md5int($folderObject->getCombinedIdentifier());
230 return rawurlencode($folderObject->getCombinedIdentifier());
243 return $row[
'_title'] ?? parent::getTitleStr($row, $titleLen);
255 return htmlspecialchars($folderObject->getName());
271 foreach ($this->storages as $storageObject) {
274 $treeItems = array_merge($treeItems, $this->tree);
288 $rootLevelFolders = [];
289 if (!empty($fileMounts)) {
290 foreach ($fileMounts as $fileMountInfo) {
291 $rootLevelFolders[] = [
292 'folder' => $fileMountInfo[
'folder'],
293 'name' => $fileMountInfo[
'title']
296 } elseif ($this->BE_USER->isAdmin()) {
297 $rootLevelFolders[] = [
299 'name' => $storageObject->
getName()
305 foreach ($rootLevelFolders as $rootLevelFolderInfo) {
307 $rootLevelFolder = $rootLevelFolderInfo[
'folder'];
308 $rootLevelFolderName = $rootLevelFolderInfo[
'name'];
309 $folderHashSpecUID = GeneralUtility::md5int($rootLevelFolder->getCombinedIdentifier());
310 $this->specUIDmap[$folderHashSpecUID] = $rootLevelFolder->getCombinedIdentifier();
314 $this->bank = $storageHashNumber;
319 if (!$storageObject->
isBrowsable() || $this->getNumberOfSubfolders($rootLevelFolder) === 0) {
326 if ($storageObject->
isOnline() ===
false) {
327 $rootLevelFolderName .=
' (' . $this->
getLanguageService()->
sL(
'LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.isOffline') .
')';
330 $icon = $this->iconFactory->getIconForResource($rootLevelFolder,
Icon::SIZE_SMALL,
null, [
'mount-root' =>
true]);
331 $firstHtml .= $this->
wrapIcon($icon, $rootLevelFolder);
333 'uid' => $folderHashSpecUID,
334 'title' => $rootLevelFolderName,
335 'path' => $rootLevelFolder->getCombinedIdentifier(),
336 'folder' => $rootLevelFolder
340 'HTML' => $firstHtml,
345 'invertedDepth' => 1000,
365 public function getFolderTree(Folder $folderObject, $depth = 999, $type =
'')
367 $depth = (int)$depth;
371 if ($folderObject instanceof InaccessibleFolder) {
374 $subFolders = $folderObject->getSubfolders();
376 uksort($subFolders,
'strnatcasecmp');
379 $totalSubFolders = count($subFolders);
381 $subFolderCounter = 0;
384 foreach ($subFolders as $subFolderName => $subFolder) {
390 $isLocked = $subFolder instanceof InaccessibleFolder;
391 $treeKey = key($this->tree);
392 $specUID = GeneralUtility::md5int($subFolder->getCombinedIdentifier());
393 $this->specUIDmap[$specUID] = $subFolder->getCombinedIdentifier();
396 'path' => $subFolder->getCombinedIdentifier(),
397 'title' => $subFolderName,
398 'folder' => $subFolder
401 if (!$isLocked && $depth > 1 && $this->
expandNext($specUID)) {
402 $nextCount = $this->
getFolderTree($subFolder, $depth - 1, $type);
411 if ($this->makeHTML) {
412 $HTML = $this->
PMicon($subFolder, $subFolderCounter, $totalSubFolders, $nextCount, $isOpen);
415 $role = $subFolder->getRole();
417 $row[
'_title'] =
'<strong>' . $subFolderName .
'</strong>';
419 $icon =
'<span title="' . htmlspecialchars($subFolderName) .
'">'
420 . $this->iconFactory->getIconForResource($subFolder,
Icon::SIZE_SMALL,
null, [
'folder-open' => (
bool)$isOpen])
422 $HTML .= $this->
wrapIcon($icon, $subFolder);
425 $this->tree[$treeKey] = [
428 'hasSub' => $nextCount && $this->
expandNext($specUID),
429 'isFirst' => $subFolderCounter == 1,
431 'invertedDepth' => $depth,
435 if ($subFolderCounter > 0) {
436 $this->tree[$treeKey][
'isLast'] =
true;
438 return $totalSubFolders;
452 $titleLength = (int)$this->BE_USER->uc[
'titleLen'];
453 if (!is_array($treeItems)) {
457 if (empty($treeItems)) {
458 $message = GeneralUtility::makeInstance(
460 $this->
getLanguageService()->sL(
'LLL:EXT:backend/Resources/Private/Language/locallang.xlf:foldertreeview.noFolders.message'),
461 $this->
getLanguageService()->sL(
'LLL:EXT:backend/Resources/Private/Language/locallang.xlf:foldertreeview.noFolders.title'),
465 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
467 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
468 $defaultFlashMessageQueue->enqueue($message);
469 return $defaultFlashMessageQueue->renderFlashMessages();
472 $expandedFolderHash =
'';
473 $invertedDepthOfAjaxRequestedItem = 0;
474 $out =
'<ul class="list-tree list-tree-root">';
476 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
478 if ($expandCollapseCommand == 1) {
487 foreach ($treeItems as $treeItem) {
489 $folderObject = $treeItem[
'row'][
'folder'];
490 $classAttr = $treeItem[
'row'][
'_CSSCLASS'] ??
'';
491 $folderIdentifier = $folderObject->getCombinedIdentifier();
493 $isExpandedFolderIdentifier = $expandedFolderHash == GeneralUtility::md5int($folderIdentifier);
494 $idAttr = htmlspecialchars($this->domIdPrefix . $this->
getId($folderObject) .
'_' . $treeItem[
'bank']);
498 if (!empty($treeItem[
'isFirst']) && !$doCollapse && !($doExpand && $isExpandedFolderIdentifier)) {
499 $itemHTML =
'<ul class="list-tree">';
502 if (!empty($treeItem[
'hasSub'])) {
503 $classAttr .=
' list-tree-control-open';
506 <li id="' . $idAttr .
'" ' . ($classAttr ?
' class="' . trim($classAttr) .
'"' :
'') .
'><span class="list-tree-group">' . $treeItem[
'HTML'] . $this->
wrapTitle($this->
getTitleStr($treeItem[
'row'], $titleLength), $folderObject, $treeItem[
'bank']) .
'</span>';
507 if (empty($treeItem[
'hasSub'])) {
508 $itemHTML .=
'</li>';
512 if (!empty($treeItem[
'isLast']) && !($doExpand && $isExpandedFolderIdentifier)) {
513 $closeDepth[$treeItem[
'invertedDepth']] = 1;
517 if (!empty($treeItem[
'isLast']) && empty($treeItem[
'hasSub']) && !$doCollapse && !($doExpand && $isExpandedFolderIdentifier)) {
518 for ($i = $treeItem[
'invertedDepth']; !empty($closeDepth[$i]); $i++) {
520 $itemHTML .=
'</ul></li>';
524 if ($doCollapse && $isExpandedFolderIdentifier) {
525 $this->ajaxStatus =
true;
529 if ($doExpand && $isExpandedFolderIdentifier) {
530 $ajaxOutput .= $itemHTML;
531 $invertedDepthOfAjaxRequestedItem = $treeItem[
'invertedDepth'];
532 } elseif ($invertedDepthOfAjaxRequestedItem) {
533 if ($treeItem[
'invertedDepth'] && ($treeItem[
'invertedDepth'] < $invertedDepthOfAjaxRequestedItem)) {
534 $ajaxOutput .= $itemHTML;
536 $this->ajaxStatus =
true;
544 $this->ajaxStatus =
true;
561 $tableName =
'sys_filemounts';
563 $tableName =
'sys_file_storage';
565 $tableName =
'sys_file';
580 return count($subFolders);
591 $this->stored = unserialize($this->BE_USER->uc[
'browseTrees'][$this->treeName], [
'allowed_classes' =>
false]);
599 if (in_array($storageHashNumber, $this->storageHashNumbers)) {
600 if ($doExpand == 1) {
602 $this->stored[$storageHashNumber][$numericFolderHash] = 1;
605 unset($this->stored[$storageHashNumber][$numericFolderHash]);
622 if (!$this->storageHashNumbers) {
623 $this->storageHashNumbers = [];
624 foreach ($this->storages as $storageUid => $storage) {
625 $fileMounts = $storage->getFileMounts();
626 if (!empty($fileMounts)) {
627 foreach ($fileMounts as $fileMount) {
628 $nkey = hexdec(substr(GeneralUtility::md5int($fileMount[
'folder']->getCombinedIdentifier()), 0, 4));
629 $this->storageHashNumbers[$storageUid . $fileMount[
'folder']->getCombinedIdentifier()] = $nkey;
632 $folder = $storage->getRootLevelFolder();
633 $nkey = hexdec(substr(GeneralUtility::md5int($folder->getCombinedIdentifier()), 0, 4));
634 $this->storageHashNumbers[$storageUid . $folder->getCombinedIdentifier()] = $nkey;
638 if ($storageObject) {
639 if ($startingPointFolder) {
640 return $this->storageHashNumbers[$storageObject->
getUid() . $startingPointFolder->getCombinedIdentifier()];
642 return $this->storageHashNumbers[$storageObject->
getUid()];
661 $PM = GeneralUtility::_GP(
'PM');
663 if (($PMpos = strpos($PM,
'#')) !==
false) {
664 $PM = substr($PM, 0, $PMpos);
668 list($mountKey, $doExpand, $folderIdentifier) = array_pad(explode(
'_', $PM, 3), 3,
null);
670 list($folderIdentifier,
$treeName) = array_pad(GeneralUtility::revExplode(
'_', $folderIdentifier, 2), 2,
null);
693 $doExpand == 1 ? 1 : 0,
694 $folderObject !==
null ? GeneralUtility::md5int($folderObject->getCombinedIdentifier()) :
'',
697 return implode(
'_', $parts);