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