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