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