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