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