[TASK] Remove NULL initialization and redundant assignments
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Tree / View / FolderTreeView.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tree\View;
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\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Imaging\Icon;
19 use TYPO3\CMS\Core\Imaging\IconFactory;
20 use TYPO3\CMS\Core\Localization\LanguageService;
21 use TYPO3\CMS\Core\Messaging\FlashMessage;
22 use TYPO3\CMS\Core\Messaging\FlashMessageService;
23 use TYPO3\CMS\Core\Resource\Folder;
24 use TYPO3\CMS\Core\Resource\FolderInterface;
25 use TYPO3\CMS\Core\Resource\InaccessibleFolder;
26 use TYPO3\CMS\Core\Resource\ResourceStorage;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28
29 /**
30 * Generate a folder tree,
31 * specially made for browsing folders in the File module
32 */
33 class FolderTreeView extends AbstractTreeView
34 {
35 /**
36 * The users' file Storages
37 *
38 * @var ResourceStorage[]
39 */
40 protected $storages;
41
42 /**
43 * @var array
44 */
45 protected $storageHashNumbers;
46
47 /**
48 * Indicates, whether the AJAX call was successful,
49 * i.e. the requested page has been found
50 *
51 * @var bool
52 */
53 protected $ajaxStatus = false;
54
55 /**
56 * @var array
57 */
58 protected $scope;
59
60 /**
61 * @var IconFactory
62 */
63 protected $iconFactory;
64
65 /**
66 * override to not use a title attribute
67 * @var string
68 */
69 public $titleAttrib = '';
70
71 /**
72 * override to use this treeName
73 * does not need to be set in __construct()
74 * @var string
75 */
76 public $treeName = 'folder';
77
78 /**
79 * override to use this domIdPrefix
80 * @var string
81 */
82 public $domIdPrefix = 'folder';
83
84 /**
85 * Constructor function of the class
86 */
87 public function __construct()
88 {
89 parent::__construct();
90 $this->init();
91 $this->storages = $this->BE_USER->getFileStorages();
92 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
93 }
94
95 /**
96 * Generate the plus/minus icon for the browsable tree.
97 *
98 * @param Folder $folderObject Entry folder object
99 * @param int $subFolderCounter The current entry number
100 * @param int $totalSubFolders The total number of entries. If equal to $a, a "bottom" element is returned.
101 * @param int $nextCount The number of sub-elements to the current element.
102 * @param bool $isExpanded The element was expanded to render subelements if this flag is set.
103 *
104 * @return string Image tag with the plus/minus icon.
105 * @internal
106 * @see \TYPO3\CMS\Backend\Tree\View\PageTreeView::PMicon()
107 */
108 public function PMicon($folderObject, $subFolderCounter, $totalSubFolders, $nextCount, $isExpanded)
109 {
110 $icon = '';
111 if ($nextCount) {
112 $cmd = $this->generateExpandCollapseParameter($this->bank, !$isExpanded, $folderObject);
113 $icon = $this->PMiconATagWrap($icon, $cmd, !$isExpanded);
114 }
115 return $icon;
116 }
117
118 /**
119 * Wrap the plus/minus icon in a link
120 *
121 * @param string $icon HTML string to wrap, probably an image tag.
122 * @param string $cmd Command for 'PM' get var
123 * @param bool $isExpand Whether to be expanded
124 * @return string Link-wrapped input string
125 * @internal
126 */
127 public function PMiconATagWrap($icon, $cmd, $isExpand = true)
128 {
129 if (empty($this->scope)) {
130 $this->scope = [
131 'class' => static::class,
132 'script' => $this->thisScript,
133 ];
134 }
135
136 if ($this->thisScript) {
137 // Activates dynamic AJAX based tree
138 $scopeData = serialize($this->scope);
139 $scopeHash = GeneralUtility::hmac($scopeData);
140 $js = htmlspecialchars('Tree.load(' . GeneralUtility::quoteJSvalue($cmd) . ', ' . (int)$isExpand . ', this, ' . GeneralUtility::quoteJSvalue($scopeData) . ', ' . GeneralUtility::quoteJSvalue($scopeHash) . ');');
141 return '<a class="list-tree-control' . (!$isExpand ? ' list-tree-control-open' : ' list-tree-control-closed') . '" onclick="' . $js . '"><i class="fa"></i></a>';
142 }
143 return $icon;
144 }
145
146 /**
147 * @param string $cmd
148 * @param bool $isOpen
149 * @return string
150 */
151 protected function renderPMIconAndLink($cmd, $isOpen)
152 {
153 $link = $this->thisScript ? ' href="' . htmlspecialchars($this->getThisScript() . 'PM=' . $cmd) . '"' : '';
154 return '<a class="list-tree-control list-tree-control-' . ($isOpen ? 'open' : 'closed') . '"' . $link . '><i class="fa"></i></a>';
155 }
156
157 /**
158 * Wrapping the folder icon
159 *
160 * @param string $icon The image tag for the icon
161 * @param Folder $folderObject The row for the current element
162 *
163 * @return string The processed icon input value.
164 * @internal
165 */
166 public function wrapIcon($icon, $folderObject)
167 {
168 // Add title attribute to input icon tag
169 $theFolderIcon = '';
170 // Wrap icon in click-menu link.
171 if (!$this->ext_IconMode) {
172 // Check storage access to wrap with click menu
173 if (!$folderObject instanceof InaccessibleFolder) {
174 $tableName = $this->getTableNameForClickMenu($folderObject);
175 $theFolderIcon = BackendUtility::wrapClickMenuOnIcon($icon, $tableName, $folderObject->getCombinedIdentifier(), 'tree');
176 }
177 } elseif ($this->ext_IconMode === 'titlelink') {
178 $aOnClick = 'return jumpTo(' . GeneralUtility::quoteJSvalue($this->getJumpToParam($folderObject)) . ',this,' . GeneralUtility::quoteJSvalue($this->domIdPrefix . $this->getId($folderObject)) . ',' . $this->bank . ');';
179 $theFolderIcon = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $icon . '</a>';
180 }
181 return $theFolderIcon;
182 }
183
184 /**
185 * Wrapping $title in a-tags.
186 *
187 * @param string $title Title string
188 * @param Folder $folderObject the folder record
189 * @param int $bank Bank pointer (which mount point number)
190 *
191 * @return string
192 * @internal
193 */
194 public function wrapTitle($title, $folderObject, $bank = 0)
195 {
196 // Check storage access to wrap with click menu
197 if ($folderObject instanceof InaccessibleFolder) {
198 return $title;
199 }
200 $aOnClick = 'return jumpTo(' . GeneralUtility::quoteJSvalue($this->getJumpToParam($folderObject)) . ', this, ' . GeneralUtility::quoteJSvalue($this->domIdPrefix . $this->getId($folderObject)) . ', ' . $bank . ');';
201 $tableName = $this->getTableNameForClickMenu($folderObject);
202 $clickMenuParts = BackendUtility::wrapClickMenuOnIcon('', $tableName, $folderObject->getCombinedIdentifier(), 'tree', '', '', true);
203
204 return '<a href="#" title="' . htmlspecialchars(strip_tags($title)) . '" onclick="' . htmlspecialchars($aOnClick) . '" ' . GeneralUtility::implodeAttributes($clickMenuParts) . '>' . $title . '</a>';
205 }
206
207 /**
208 * Returns the id from the record - for folders, this is an md5 hash.
209 *
210 * @param Folder $folderObject The folder object
211 *
212 * @return int The "uid" field value.
213 */
214 public function getId($folderObject)
215 {
216 return GeneralUtility::md5int($folderObject->getCombinedIdentifier());
217 }
218
219 /**
220 * Returns jump-url parameter value.
221 *
222 * @param Folder $folderObject The folder object
223 *
224 * @return string The jump-url parameter.
225 */
226 public function getJumpToParam($folderObject)
227 {
228 return rawurlencode($folderObject->getCombinedIdentifier());
229 }
230
231 /**
232 * Returns the title for the input record. If blank, a "no title" label (localized) will be returned.
233 * '_title' is used for setting an alternative title for folders.
234 *
235 * @param array $row The input row array (where the key "_title" is used for the title)
236 * @param int $titleLen Title length (30)
237 * @return string The title
238 */
239 public function getTitleStr($row, $titleLen = 30)
240 {
241 return $row['_title'] ?? parent::getTitleStr($row, $titleLen);
242 }
243
244 /**
245 * Returns the value for the image "title" attribute
246 *
247 * @param Folder $folderObject The folder to be used
248 *
249 * @return string The attribute value (is htmlspecialchared() already)
250 */
251 public function getTitleAttrib($folderObject)
252 {
253 return htmlspecialchars($folderObject->getName());
254 }
255
256 /**
257 * Will create and return the HTML code for a browsable tree of folders.
258 * Is based on the mounts found in the internal array ->MOUNTS (set in the constructor)
259 *
260 * @return string HTML code for the browsable tree
261 */
262 public function getBrowsableTree()
263 {
264 // Get stored tree structure AND updating it if needed according to incoming PM GET var.
265 $this->initializePositionSaving();
266 // Init done:
267 $treeItems = [];
268 // Traverse mounts:
269 foreach ($this->storages as $storageObject) {
270 $this->getBrowseableTreeForStorage($storageObject);
271 // Add tree:
272 $treeItems = array_merge($treeItems, $this->tree);
273 }
274 return $this->printTree($treeItems);
275 }
276
277 /**
278 * Get a tree for one storage
279 *
280 * @param ResourceStorage $storageObject
281 */
282 public function getBrowseableTreeForStorage(ResourceStorage $storageObject)
283 {
284 // If there are filemounts, show each, otherwise just the rootlevel folder
285 $fileMounts = $storageObject->getFileMounts();
286 $rootLevelFolders = [];
287 if (!empty($fileMounts)) {
288 foreach ($fileMounts as $fileMountInfo) {
289 $rootLevelFolders[] = [
290 'folder' => $fileMountInfo['folder'],
291 'name' => $fileMountInfo['title']
292 ];
293 }
294 } elseif ($this->BE_USER->isAdmin()) {
295 $rootLevelFolders[] = [
296 'folder' => $storageObject->getRootLevelFolder(),
297 'name' => $storageObject->getName()
298 ];
299 }
300 // Clean the tree
301 $this->reset();
302 // Go through all "root level folders" of this tree (can be the rootlevel folder or any file mount points)
303 foreach ($rootLevelFolders as $rootLevelFolderInfo) {
304 /** @var $rootLevelFolder Folder */
305 $rootLevelFolder = $rootLevelFolderInfo['folder'];
306 $rootLevelFolderName = $rootLevelFolderInfo['name'];
307 $folderHashSpecUID = GeneralUtility::md5int($rootLevelFolder->getCombinedIdentifier());
308 $this->specUIDmap[$folderHashSpecUID] = $rootLevelFolder->getCombinedIdentifier();
309 // Hash key
310 $storageHashNumber = $this->getShortHashNumberForStorage($storageObject, $rootLevelFolder);
311 // Set first:
312 $this->bank = $storageHashNumber;
313 $isOpen = $this->stored[$storageHashNumber][$folderHashSpecUID] || $this->expandFirst;
314 // Set PM icon:
315 $cmd = $this->generateExpandCollapseParameter($this->bank, !$isOpen, $rootLevelFolder);
316 // Only show and link icon if storage is browseable
317 if (!$storageObject->isBrowsable() || $this->getNumberOfSubfolders($rootLevelFolder) === 0) {
318 $firstHtml = '';
319 } else {
320 $firstHtml = $this->renderPMIconAndLink($cmd, $isOpen);
321 }
322 // Mark a storage which is not online, as offline
323 // maybe someday there will be a special icon for this
324 if ($storageObject->isOnline() === false) {
325 $rootLevelFolderName .= ' (' . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.isOffline') . ')';
326 }
327 // Preparing rootRec for the mount
328 $icon = $this->iconFactory->getIconForResource($rootLevelFolder, Icon::SIZE_SMALL, null, ['mount-root' => true]);
329 $firstHtml .= $this->wrapIcon($icon, $rootLevelFolder);
330 $row = [
331 'uid' => $folderHashSpecUID,
332 'title' => $rootLevelFolderName,
333 'path' => $rootLevelFolder->getCombinedIdentifier(),
334 'folder' => $rootLevelFolder
335 ];
336 // Add the storage root to ->tree
337 $this->tree[] = [
338 'HTML' => $firstHtml,
339 'row' => $row,
340 'bank' => $this->bank,
341 // hasSub is TRUE when the root of the storage is expanded
342 'hasSub' => $isOpen && $storageObject->isBrowsable(),
343 'invertedDepth' => 1000,
344 ];
345 // If the mount is expanded, go down:
346 if ($isOpen && $storageObject->isBrowsable()) {
347 // Set depth:
348 $this->getFolderTree($rootLevelFolder, 999);
349 }
350 }
351 }
352
353 /**
354 * Fetches the data for the tree
355 *
356 * @param Folder $folderObject the folderobject
357 * @param int $depth Max depth (recursivity limit)
358 * @param string $type HTML-code prefix for recursive calls.
359 *
360 * @return int The count of items on the level
361 * @see getBrowsableTree()
362 */
363 public function getFolderTree(Folder $folderObject, $depth = 999, $type = '')
364 {
365 $depth = (int)$depth;
366
367 // This generates the directory tree
368 /* array of \TYPO3\CMS\Core\Resource\Folder */
369 if ($folderObject instanceof InaccessibleFolder) {
370 $subFolders = [];
371 } else {
372 $subFolders = $folderObject->getSubfolders();
373 $subFolders = \TYPO3\CMS\Core\Resource\Utility\ListUtility::resolveSpecialFolderNames($subFolders);
374 uksort($subFolders, 'strnatcasecmp');
375 }
376
377 $totalSubFolders = count($subFolders);
378 $HTML = '';
379 $subFolderCounter = 0;
380 $treeKey = '';
381 /** @var Folder $subFolder */
382 foreach ($subFolders as $subFolderName => $subFolder) {
383 $subFolderCounter++;
384 // Reserve space.
385 $this->tree[] = [];
386 // Get the key for this space
387 end($this->tree);
388 $isLocked = $subFolder instanceof InaccessibleFolder;
389 $treeKey = key($this->tree);
390 $specUID = GeneralUtility::md5int($subFolder->getCombinedIdentifier());
391 $this->specUIDmap[$specUID] = $subFolder->getCombinedIdentifier();
392 $row = [
393 'uid' => $specUID,
394 'path' => $subFolder->getCombinedIdentifier(),
395 'title' => $subFolderName,
396 'folder' => $subFolder
397 ];
398 // Make a recursive call to the next level
399 if (!$isLocked && $depth > 1 && $this->expandNext($specUID)) {
400 $nextCount = $this->getFolderTree($subFolder, $depth - 1, $type);
401 // Set "did expand" flag
402 $isOpen = 1;
403 } else {
404 $nextCount = $isLocked ? 0 : $this->getNumberOfSubfolders($subFolder);
405 // Clear "did expand" flag
406 $isOpen = 0;
407 }
408 // Set HTML-icons, if any:
409 if ($this->makeHTML) {
410 $HTML = $this->PMicon($subFolder, $subFolderCounter, $totalSubFolders, $nextCount, $isOpen);
411 $type = '';
412
413 $role = $subFolder->getRole();
414 if ($role !== FolderInterface::ROLE_DEFAULT) {
415 $row['_title'] = '<strong>' . $subFolderName . '</strong>';
416 }
417 $icon = '<span title="' . htmlspecialchars($subFolderName) . '">'
418 . $this->iconFactory->getIconForResource($subFolder, Icon::SIZE_SMALL, null, ['folder-open' => (bool)$isOpen])
419 . '</span>';
420 $HTML .= $this->wrapIcon($icon, $subFolder);
421 }
422 // Finally, add the row/HTML content to the ->tree array in the reserved key.
423 $this->tree[$treeKey] = [
424 'row' => $row,
425 'HTML' => $HTML,
426 'hasSub' => $nextCount && $this->expandNext($specUID),
427 'isFirst' => $subFolderCounter == 1,
428 'isLast' => false,
429 'invertedDepth' => $depth,
430 'bank' => $this->bank
431 ];
432 }
433 if ($subFolderCounter > 0) {
434 $this->tree[$treeKey]['isLast'] = true;
435 }
436 return $totalSubFolders;
437 }
438
439 /**
440 * Compiles the HTML code for displaying the structure found inside the ->tree array
441 *
442 * @param array|string $treeItems "tree-array" - if blank string, the internal ->tree array is used.
443 * @return string The HTML code for the tree
444 */
445 public function printTree($treeItems = '')
446 {
447 $doExpand = false;
448 $doCollapse = false;
449 $ajaxOutput = '';
450 $titleLength = (int)$this->BE_USER->uc['titleLen'];
451 if (!is_array($treeItems)) {
452 $treeItems = $this->tree;
453 }
454
455 if (empty($treeItems)) {
456 $message = GeneralUtility::makeInstance(
457 FlashMessage::class,
458 $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:foldertreeview.noFolders.message'),
459 $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:foldertreeview.noFolders.title'),
460 FlashMessage::INFO
461 );
462 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
463 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
464 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
465 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
466 $defaultFlashMessageQueue->enqueue($message);
467 return $defaultFlashMessageQueue->renderFlashMessages();
468 }
469
470 $expandedFolderHash = '';
471 $invertedDepthOfAjaxRequestedItem = 0;
472 $out = '<ul class="list-tree list-tree-root">';
473 // Evaluate AJAX request
474 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
475 list(, $expandCollapseCommand, $expandedFolderHash, ) = $this->evaluateExpandCollapseParameter();
476 if ($expandCollapseCommand == 1) {
477 $doExpand = true;
478 } else {
479 $doCollapse = true;
480 }
481 }
482 // We need to count the opened <ul>'s every time we dig into another level,
483 // so we know how many we have to close when all children are done rendering
484 $closeDepth = [];
485 foreach ($treeItems as $treeItem) {
486 /** @var $folderObject Folder */
487 $folderObject = $treeItem['row']['folder'];
488 $classAttr = $treeItem['row']['_CSSCLASS'] ?? '';
489 $folderIdentifier = $folderObject->getCombinedIdentifier();
490 // this is set if the AJAX request has just opened this folder (via the PM command)
491 $isExpandedFolderIdentifier = $expandedFolderHash == GeneralUtility::md5int($folderIdentifier);
492 $idAttr = htmlspecialchars($this->domIdPrefix . $this->getId($folderObject) . '_' . $treeItem['bank']);
493 $itemHTML = '';
494 // If this item is the start of a new level,
495 // then a new level <ul> is needed, but not in ajax mode
496 if (!empty($treeItem['isFirst']) && !$doCollapse && !($doExpand && $isExpandedFolderIdentifier)) {
497 $itemHTML = '<ul class="list-tree">';
498 }
499 // Add CSS classes to the list item
500 if (!empty($treeItem['hasSub'])) {
501 $classAttr .= ' list-tree-control-open';
502 }
503 $itemHTML .= '
504 <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>';
505 if (empty($treeItem['hasSub'])) {
506 $itemHTML .= '</li>';
507 }
508 // We have to remember if this is the last one
509 // on level X so the last child on level X+1 closes the <ul>-tag
510 if (!empty($treeItem['isLast']) && !($doExpand && $isExpandedFolderIdentifier)) {
511 $closeDepth[$treeItem['invertedDepth']] = 1;
512 }
513 // If this is the last one and does not have subitems, we need to close
514 // the tree as long as the upper levels have last items too
515 if (!empty($treeItem['isLast']) && empty($treeItem['hasSub']) && !$doCollapse && !($doExpand && $isExpandedFolderIdentifier)) {
516 for ($i = $treeItem['invertedDepth']; !empty($closeDepth[$i]); $i++) {
517 $closeDepth[$i] = 0;
518 $itemHTML .= '</ul></li>';
519 }
520 }
521 // Ajax request: collapse
522 if ($doCollapse && $isExpandedFolderIdentifier) {
523 $this->ajaxStatus = true;
524 return $itemHTML;
525 }
526 // Ajax request: expand
527 if ($doExpand && $isExpandedFolderIdentifier) {
528 $ajaxOutput .= $itemHTML;
529 $invertedDepthOfAjaxRequestedItem = $treeItem['invertedDepth'];
530 } elseif ($invertedDepthOfAjaxRequestedItem) {
531 if ($treeItem['invertedDepth'] && ($treeItem['invertedDepth'] < $invertedDepthOfAjaxRequestedItem)) {
532 $ajaxOutput .= $itemHTML;
533 } else {
534 $this->ajaxStatus = true;
535 return $ajaxOutput;
536 }
537 }
538 $out .= $itemHTML;
539 }
540 // If this is an AJAX request, output directly
541 if ($ajaxOutput) {
542 $this->ajaxStatus = true;
543 return $ajaxOutput;
544 }
545 // Finally close the first ul
546 $out .= '</ul>';
547 return $out;
548 }
549
550 /**
551 * Returns table name for click menu
552 *
553 * @param Folder $folderObject
554 * @return string
555 */
556 protected function getTableNameForClickMenu(Folder $folderObject)
557 {
558 if (strpos($folderObject->getRole(), FolderInterface::ROLE_MOUNT) !== false) {
559 $tableName = 'sys_filemounts';
560 } elseif ($folderObject->getIdentifier() === $folderObject->getStorage()->getRootLevelFolder()->getIdentifier()) {
561 $tableName = 'sys_file_storage';
562 } else {
563 $tableName = 'sys_file';
564 }
565 return $tableName;
566 }
567
568 /**
569 * Counts the number of directories in a file path.
570 *
571 * @param Folder $folderObject File path.
572 *
573 * @return int
574 */
575 public function getNumberOfSubfolders(Folder $folderObject)
576 {
577 $subFolders = $folderObject->getSubfolders();
578 return count($subFolders);
579 }
580
581 /**
582 * Get stored tree structure AND updating it if needed according to incoming PM GET var.
583 *
584 * @access private
585 */
586 public function initializePositionSaving()
587 {
588 // Get stored tree structure:
589 $this->stored = unserialize($this->BE_USER->uc['browseTrees'][$this->treeName]);
590 $this->getShortHashNumberForStorage();
591 // PM action:
592 // (If an plus/minus icon has been clicked,
593 // the PM GET var is sent and we must update the stored positions in the tree):
594 // 0: mount key, 1: set/clear boolean, 2: item ID (cannot contain "_"), 3: treeName
595 list($storageHashNumber, $doExpand, $numericFolderHash, $treeName) = $this->evaluateExpandCollapseParameter();
596 if ($treeName && $treeName == $this->treeName) {
597 if (in_array($storageHashNumber, $this->storageHashNumbers)) {
598 if ($doExpand == 1) {
599 // Set
600 $this->stored[$storageHashNumber][$numericFolderHash] = 1;
601 } else {
602 // Clear
603 unset($this->stored[$storageHashNumber][$numericFolderHash]);
604 }
605 $this->savePosition();
606 }
607 }
608 }
609
610 /**
611 * Helper method to map md5-hash to shorter number
612 *
613 * @param ResourceStorage $storageObject
614 * @param Folder $startingPointFolder
615 *
616 * @return int
617 */
618 protected function getShortHashNumberForStorage(ResourceStorage $storageObject = null, Folder $startingPointFolder = null)
619 {
620 if (!$this->storageHashNumbers) {
621 $this->storageHashNumbers = [];
622 foreach ($this->storages as $storageUid => $storage) {
623 $fileMounts = $storage->getFileMounts();
624 if (!empty($fileMounts)) {
625 foreach ($fileMounts as $fileMount) {
626 $nkey = hexdec(substr(GeneralUtility::md5int($fileMount['folder']->getCombinedIdentifier()), 0, 4));
627 $this->storageHashNumbers[$storageUid . $fileMount['folder']->getCombinedIdentifier()] = $nkey;
628 }
629 } else {
630 $folder = $storage->getRootLevelFolder();
631 $nkey = hexdec(substr(GeneralUtility::md5int($folder->getCombinedIdentifier()), 0, 4));
632 $this->storageHashNumbers[$storageUid . $folder->getCombinedIdentifier()] = $nkey;
633 }
634 }
635 }
636 if ($storageObject) {
637 if ($startingPointFolder) {
638 return $this->storageHashNumbers[$storageObject->getUid() . $startingPointFolder->getCombinedIdentifier()];
639 }
640 return $this->storageHashNumbers[$storageObject->getUid()];
641 }
642 return null;
643 }
644
645 /**
646 * Gets the values from the Expand/Collapse Parameter (&PM)
647 * previously known as "PM" (plus/minus)
648 * PM action:
649 * (If an plus/minus icon has been clicked,
650 * the PM GET var is sent and we must update the stored positions in the tree):
651 * 0: mount key, 1: set/clear boolean, 2: item ID (cannot contain "_"), 3: treeName
652 *
653 * @param string $PM The "plus/minus" command
654 * @return array
655 */
656 protected function evaluateExpandCollapseParameter($PM = null)
657 {
658 if ($PM === null) {
659 $PM = GeneralUtility::_GP('PM');
660 // IE takes anchor as parameter
661 if (($PMpos = strpos($PM, '#')) !== false) {
662 $PM = substr($PM, 0, $PMpos);
663 }
664 }
665 // Take the first three parameters
666 list($mountKey, $doExpand, $folderIdentifier) = array_pad(explode('_', $PM, 3), 3, null);
667 // In case the folder identifier contains "_", we just need to get the fourth/last parameter
668 list($folderIdentifier, $treeName) = array_pad(GeneralUtility::revExplode('_', $folderIdentifier, 2), 2, null);
669 return [
670 $mountKey,
671 $doExpand,
672 $folderIdentifier,
673 $treeName
674 ];
675 }
676
677 /**
678 * Generates the "PM" string to sent to expand/collapse items
679 *
680 * @param string $mountKey The mount key / storage UID
681 * @param bool $doExpand Whether to expand/collapse
682 * @param Folder $folderObject The folder object
683 * @param string $treeName The name of the tree
684 *
685 * @return string
686 */
687 protected function generateExpandCollapseParameter($mountKey = null, $doExpand = false, Folder $folderObject = null, $treeName = null)
688 {
689 $parts = [
690 $mountKey ?? $this->bank,
691 $doExpand == 1 ? 1 : 0,
692 $folderObject !== null ? GeneralUtility::md5int($folderObject->getCombinedIdentifier()) : '',
693 $treeName ?? $this->treeName
694 ];
695 return implode('_', $parts);
696 }
697
698 /**
699 * Gets the AJAX status.
700 *
701 * @return bool
702 */
703 public function getAjaxStatus()
704 {
705 return $this->ajaxStatus;
706 }
707
708 /**
709 * @return LanguageService
710 */
711 protected function getLanguageService()
712 {
713 return $GLOBALS['LANG'];
714 }
715 }