[TASK] Introduce Enum constants for t3ver_state
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Tree / View / FolderTreeView.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tree\View;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Backend\Utility\IconUtility;
31 use TYPO3\CMS\Core\Resource\FolderInterface;
32 use TYPO3\CMS\Core\Utility\GeneralUtility;
33
34 /**
35 * Generate a folder tree,
36 * specially made for browsing folders in the File module
37 *
38 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
39 * @coauthor René Fritz <r.fritz@colorcube.de>
40 */
41 class FolderTreeView extends \TYPO3\CMS\Backend\Tree\View\AbstractTreeView {
42
43 /**
44 * The users' file Storages
45 *
46 * @var \TYPO3\CMS\Core\Resource\ResourceStorage[]
47 */
48 protected $storages = NULL;
49
50 /**
51 * @var array
52 */
53 protected $storageHashNumbers;
54
55 /**
56 * Indicates, whether the AJAX call was successful,
57 * i.e. the requested page has been found
58 *
59 * @var boolean
60 */
61 protected $ajaxStatus = FALSE;
62
63 /**
64 * Constructor function of the class
65 */
66 public function __construct() {
67 parent::init();
68 $this->storages = $GLOBALS['BE_USER']->getFileStorages();
69 $this->treeName = 'folder';
70 // Don't apply any title
71 $this->titleAttrib = '';
72 $this->domIdPrefix = 'folder';
73 }
74
75 /**
76 * Generate the plus/minus icon for the browsable tree.
77 *
78 * @param \TYPO3\CMS\Core\Resource\Folder $folderObject Entry folder object
79 * @param integer $subFolderCounter The current entry number
80 * @param integer $totalSubFolders The total number of entries. If equal to $a, a "bottom" element is returned.
81 * @param integer $nextCount The number of sub-elements to the current element.
82 * @param boolean $isExpanded The element was expanded to render subelements if this flag is set.
83 * @return string Image tag with the plus/minus icon.
84 * @internal
85 * @see \TYPO3\CMS\Backend\Tree\View\PageTreeView::PMicon()
86 */
87 public function PMicon(\TYPO3\CMS\Core\Resource\Folder $folderObject, $subFolderCounter, $totalSubFolders, $nextCount, $isExpanded) {
88 $PM = $nextCount ? ($isExpanded ? 'minus' : 'plus') : 'join';
89 $BTM = $subFolderCounter == $totalSubFolders ? 'bottom' : '';
90 $icon = '<img' . IconUtility::skinImg($this->backPath, ('gfx/ol/' . $PM . $BTM . '.gif'), 'width="18" height="16"') . ' alt="" />';
91 if ($nextCount) {
92 $cmd = $this->generateExpandCollapseParameter($this->bank, !$isExpanded, $folderObject);
93 $icon = $this->PMiconATagWrap($icon, $cmd, !$isExpanded);
94 }
95 return $icon;
96 }
97
98 /**
99 * Wrap the plus/minus icon in a link
100 *
101 * @param string $icon HTML string to wrap, probably an image tag.
102 * @param string $cmd Command for 'PM' get var
103 * @param boolean $isExpand Whether to be expanded
104 * @return string Link-wrapped input string
105 * @internal
106 */
107 public function PMiconATagWrap($icon, $cmd, $isExpand = TRUE) {
108 if ($this->thisScript) {
109 // Activates dynamic AJAX based tree
110 $scopeData = '';
111 $scopeHash = '';
112 // $this->scope is defined in TBE_FolderTree
113 if (!empty($this->scope)) {
114 $scopeData = serialize($this->scope);
115 $scopeHash = GeneralUtility::hmac($scopeData);
116 }
117 $js = htmlspecialchars('Tree.load(\'' . $cmd . '\', ' . intval($isExpand) . ', this, \'' . $scopeData . '\', \'' . $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->getStorage()->hasFolder('/')) {
139 $theFolderIcon = $GLOBALS['TBE_TEMPLATE']->wrapClickMenuOnIcon($theFolderIcon, $folderObject->getCombinedIdentifier(), '', 0);
140 }
141 } elseif (!strcmp($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 integer $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->getStorage()->hasFolder('/')) {
160 return $title;
161 }
162 $aOnClick = 'return jumpTo(\'' . $this->getJumpToParam($folderObject) . '\', this, \'' . $this->domIdPrefix . $this->getId($folderObject) . '\', ' . $bank . ');';
163 $CSM = ' oncontextmenu="' . htmlspecialchars($GLOBALS['TBE_TEMPLATE']->wrapClickMenuOnIcon('', $folderObject->getCombinedIdentifier(), '', 0, ('&bank=' . $this->bank), '', TRUE)) . '"';
164
165 return '<a href="#" title="' . htmlspecialchars($title) . '" onclick="' . htmlspecialchars($aOnClick) . '"' . $CSM . '>' . $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 integer 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 integer $titleLen Title length (30)
194 * @return string The title
195 */
196 public function getTitleStr($row, $titleLen = 30) {
197 return $row['_title'] ? $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 * @todo Define visibility
206 */
207 public function getTitleAttrib(\TYPO3\CMS\Core\Resource\Folder $folderObject) {
208 return htmlspecialchars($folderObject->getName());
209 }
210
211 /**
212 * Will create and return the HTML code for a browsable tree of folders.
213 * Is based on the mounts found in the internal array ->MOUNTS (set in the constructor)
214 *
215 * @return string HTML code for the browsable tree
216 */
217 public function getBrowsableTree() {
218 // Get stored tree structure AND updating it if needed according to incoming PM GET var.
219 $this->initializePositionSaving();
220 // Init done:
221 $treeItems = array();
222 // Traverse mounts:
223 foreach ($this->storages as $storageObject) {
224 $this->getBrowseableTreeForStorage($storageObject);
225 // Add tree:
226 $treeItems = array_merge($treeItems, $this->tree);
227 // if this is an AJAX call, don't run through all mounts, only
228 // show the expansion of the current one, not the rest of the mounts
229 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
230
231 }
232 }
233 return $this->printTree($treeItems);
234 }
235
236 /**
237 * Get a tree for one storage
238 *
239 * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storageObject
240 * @return void
241 */
242 public function getBrowseableTreeForStorage(\TYPO3\CMS\Core\Resource\ResourceStorage $storageObject) {
243 // If there are filemounts, show each, otherwise just the rootlevel folder
244 $fileMounts = $storageObject->getFileMounts();
245 $rootLevelFolders = array();
246 if (count($fileMounts)) {
247 foreach ($fileMounts as $fileMountInfo) {
248 $rootLevelFolders[] = array(
249 'folder' => $fileMountInfo['folder'],
250 'name' => $fileMountInfo['title']
251 );
252 }
253 } else {
254 $rootLevelFolders[] = array(
255 'folder' => $storageObject->getRootLevelFolder(),
256 'name' => $storageObject->getName()
257 );
258 }
259 // Clean the tree
260 $this->reset();
261 // Go through all "root level folders" of this tree (can be the rootlevel folder or any file mount points)
262 foreach ($rootLevelFolders as $rootLevelFolderInfo) {
263 /** @var $rootLevelFolder \TYPO3\CMS\Core\Resource\Folder */
264 $rootLevelFolder = $rootLevelFolderInfo['folder'];
265 $rootLevelFolderName = $rootLevelFolderInfo['name'];
266 $folderHashSpecUID = GeneralUtility::md5int($rootLevelFolder->getCombinedIdentifier());
267 $this->specUIDmap[$folderHashSpecUID] = $rootLevelFolder->getCombinedIdentifier();
268 // Hash key
269 $storageHashNumber = $this->getShortHashNumberForStorage($storageObject, $rootLevelFolder);
270 // Set first:
271 $this->bank = $storageHashNumber;
272 $isOpen = $this->stored[$storageHashNumber][$folderHashSpecUID] || $this->expandFirst;
273 // Set PM icon:
274 $cmd = $this->generateExpandCollapseParameter($this->bank, !$isOpen, $rootLevelFolder);
275 if (!$storageObject->isBrowsable() || $this->getNumberOfSubfolders($rootLevelFolder) === 0) {
276 $rootIcon = 'blank';
277 } elseif (!$isOpen) {
278 $rootIcon = 'plusonly';
279 } else {
280 $rootIcon = 'minusonly';
281 }
282 $icon = '<img' . IconUtility::skinImg($this->backPath, ('gfx/ol/' . $rootIcon . '.gif')) . ' alt="" />';
283 // Only link icon if storage is browseable
284 if (in_array($rootIcon, array('minusonly', 'plusonly'))) {
285 $firstHtml = $this->PM_ATagWrap($icon, $cmd);
286 } else {
287 $firstHtml = $icon;
288 }
289 // @todo: create sprite icons for user/group mounts etc
290 if ($storageObject->isBrowsable() === FALSE) {
291 $icon = 'apps-filetree-folder-locked';
292 } else {
293 $icon = 'apps-filetree-root';
294 }
295 // Mark a storage which is not online, as offline
296 // maybe someday there will be a special icon for this
297 if ($storageObject->isOnline() === FALSE) {
298 $rootLevelFolderName .= ' (' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_file.xlf:sys_file_storage.isOffline') . ')';
299 }
300 // Preparing rootRec for the mount
301 $firstHtml .= $this->wrapIcon(IconUtility::getSpriteIcon($icon), $rootLevelFolder);
302 $row = array(
303 'uid' => $folderHashSpecUID,
304 'title' => $rootLevelFolderName,
305 'path' => $rootLevelFolder->getCombinedIdentifier(),
306 'folder' => $rootLevelFolder
307 );
308 // Add the storage root to ->tree
309 $this->tree[] = array(
310 'HTML' => $firstHtml,
311 'row' => $row,
312 'bank' => $this->bank,
313 // hasSub is TRUE when the root of the storage is expanded
314 'hasSub' => $isOpen && $storageObject->isBrowsable()
315 );
316 // If the mount is expanded, go down:
317 if ($isOpen && $storageObject->isBrowsable()) {
318 // Set depth:
319 $this->getFolderTree($rootLevelFolder, 999);
320 }
321 }
322 }
323
324 /**
325 * Fetches the data for the tree
326 *
327 * @param \TYPO3\CMS\Core\Resource\Folder $folderObject the folderobject
328 * @param integer $depth Max depth (recursivity limit)
329 * @param string $type HTML-code prefix for recursive calls.
330 * @return integer The count of items on the level
331 * @see getBrowsableTree()
332 */
333 public function getFolderTree(\TYPO3\CMS\Core\Resource\Folder $folderObject, $depth = 999, $type = '') {
334 $depth = intval($depth);
335
336 // This generates the directory tree
337 /* array of \TYPO3\CMS\Core\Resource\Folder */
338 $subFolders = $folderObject->getSubfolders();
339 $subFolders = \TYPO3\CMS\Core\Resource\Utility\ListUtility::resolveSpecialFolderNames($subFolders);
340 uksort($subFolders, 'strnatcasecmp');
341
342 $totalSubFolders = count($subFolders);
343 $HTML = '';
344 $subFolderCounter = 0;
345 foreach ($subFolders as $subFolderName => $subFolder) {
346 $subFolderCounter++;
347 // Reserve space.
348 $this->tree[] = array();
349 // Get the key for this space
350 end($this->tree);
351 $treeKey = key($this->tree);
352 $specUID = GeneralUtility::md5int($subFolder->getCombinedIdentifier());
353 $this->specUIDmap[$specUID] = $subFolder->getCombinedIdentifier();
354 $row = array(
355 'uid' => $specUID,
356 'path' => $subFolder->getCombinedIdentifier(),
357 'title' => $subFolderName,
358 'folder' => $subFolder
359 );
360 // Make a recursive call to the next level
361 if ($depth > 1 && $this->expandNext($specUID)) {
362 $nextCount = $this->getFolderTree($subFolder, $depth - 1, $type);
363 // Set "did expand" flag
364 $isOpen = 1;
365 } else {
366 $nextCount = $this->getNumberOfSubfolders($subFolder);
367 // Clear "did expand" flag
368 $isOpen = 0;
369 }
370 // Set HTML-icons, if any:
371 if ($this->makeHTML) {
372 $HTML = $this->PMicon($subFolder, $subFolderCounter, $totalSubFolders, $nextCount, $isOpen);
373 if ($subFolder->checkActionPermission('write')) {
374 $type = '';
375 $overlays = array();
376 } else {
377 $type = 'readonly';
378 $overlays = array('status-overlay-locked' => array());
379 }
380 if ($isOpen) {
381 $icon = 'apps-filetree-folder-opened';
382 } else {
383 $icon = 'apps-filetree-folder-default';
384 }
385 $role = $subFolder->getRole();
386 if ($role !== FolderInterface::ROLE_DEFAULT) {
387 $row['_title'] = '<strong>' . $subFolderName . '</strong>';
388 }
389 if ($role === FolderInterface::ROLE_TEMPORARY) {
390 $icon = 'apps-filetree-folder-temp';
391 } elseif ($role === FolderInterface::ROLE_RECYCLER) {
392 $icon = 'apps-filetree-folder-recycler';
393 }
394 $icon = IconUtility::getSpriteIcon($icon, array('title' => $subFolderName), $overlays);
395 $HTML .= $this->wrapIcon($icon, $subFolder);
396 }
397 // Finally, add the row/HTML content to the ->tree array in the reserved key.
398 $this->tree[$treeKey] = array(
399 'row' => $row,
400 'HTML' => $HTML,
401 'hasSub' => $nextCount && $this->expandNext($specUID),
402 'isFirst' => $subFolderCounter == 1,
403 'isLast' => FALSE,
404 'invertedDepth' => $depth,
405 'bank' => $this->bank
406 );
407 }
408 if ($subFolderCounter > 0) {
409 $this->tree[$treeKey]['isLast'] = TRUE;
410 }
411 return $totalSubFolders;
412 }
413
414 /**
415 * Compiles the HTML code for displaying the structure found inside the ->tree array
416 *
417 * @param array|string $treeItems "tree-array" - if blank string, the internal ->tree array is used.
418 * @return string The HTML code for the tree
419 */
420 public function printTree($treeItems = '') {
421 $doExpand = FALSE;
422 $doCollapse = FALSE;
423 $ajaxOutput = '';
424 $titleLength = intval($this->BE_USER->uc['titleLen']);
425 if (!is_array($treeItems)) {
426 $treeItems = $this->tree;
427 }
428 $out = '
429 <!-- TYPO3 folder tree structure. -->
430 <ul class="tree" id="treeRoot">
431 ';
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>
459 ';
460 }
461 // Add CSS classes to the list item
462 if ($treeItem['hasSub']) {
463 $classAttr .= ' expanded';
464 }
465 if ($treeItem['isLast']) {
466 $classAttr .= ' last';
467 }
468 $itemHTML .= '
469 <li id="' . $idAttr . '" ' . ($classAttr ? ' class="' . trim($classAttr) . '"' : '') . '><div class="treeLinkItem">' . $treeItem['HTML'] . $this->wrapTitle($this->getTitleStr($treeItem['row'], $titleLength), $folderObject, $treeItem['bank']) . '</div>';
470 if (!$treeItem['hasSub']) {
471 $itemHTML .= '</li>
472 ';
473 }
474 // We have to remember if this is the last one
475 // on level X so the last child on level X+1 closes the <ul>-tag
476 if ($treeItem['isLast'] && !($doExpand && $isExpandedFolderIdentifier)) {
477 $closeDepth[$treeItem['invertedDepth']] = 1;
478 }
479 // If this is the last one and does not have subitems, we need to close
480 // the tree as long as the upper levels have last items too
481 if ($treeItem['isLast'] && !$treeItem['hasSub'] && !$doCollapse && !($doExpand && $isExpandedFolderIdentifier)) {
482 for ($i = $treeItem['invertedDepth']; $closeDepth[$i] == 1; $i++) {
483 $closeDepth[$i] = 0;
484 $itemHTML .= '</ul></li>
485 ';
486 }
487 }
488 // Ajax request: collapse
489 if ($doCollapse && $isExpandedFolderIdentifier) {
490 $this->ajaxStatus = TRUE;
491 return $itemHTML;
492 }
493 // Ajax request: expand
494 if ($doExpand && $isExpandedFolderIdentifier) {
495 $ajaxOutput .= $itemHTML;
496 $invertedDepthOfAjaxRequestedItem = $treeItem['invertedDepth'];
497 } elseif ($invertedDepthOfAjaxRequestedItem) {
498 if ($treeItem['invertedDepth'] < $invertedDepthOfAjaxRequestedItem) {
499 $ajaxOutput .= $itemHTML;
500 } else {
501 $this->ajaxStatus = TRUE;
502 return $ajaxOutput;
503 }
504 }
505 $out .= $itemHTML;
506 }
507 // If this is a AJAX request, output directly
508 if ($ajaxOutput) {
509 $this->ajaxStatus = TRUE;
510 return $ajaxOutput;
511 }
512 // Finally close the first ul
513 $out .= '</ul>
514 ';
515 return $out;
516 }
517
518 /**
519 * Counts the number of directories in a file path.
520 *
521 * @param string $file File path.
522 * @return integer
523 * @deprecated since TYPO3 6.0, as the folder objects do the counting automatically
524 */
525 public function getCount($file) {
526 GeneralUtility::logDeprecatedFunction();
527 // This generates the directory tree
528 $dirs = GeneralUtility::get_dirs($file);
529 $c = 0;
530 if (is_array($dirs)) {
531 $c = count($dirs);
532 }
533 return $c;
534 }
535
536 /**
537 * Counts the number of directories in a file path.
538 *
539 * @param \TYPO3\CMS\Core\Resource\Folder $folderObject File path.
540 * @return integer
541 */
542 public function getNumberOfSubfolders(\TYPO3\CMS\Core\Resource\Folder $folderObject) {
543 $subFolders = $folderObject->getSubfolders();
544 return count($subFolders);
545 }
546
547 /**
548 * Get stored tree structure AND updating it if needed according to incoming PM GET var.
549 *
550 * @return void
551 * @access private
552 * @todo Define visibility
553 */
554 public function initializePositionSaving() {
555 // Get stored tree structure:
556 $this->stored = unserialize($this->BE_USER->uc['browseTrees'][$this->treeName]);
557 $this->getShortHashNumberForStorage();
558 // PM action:
559 // (If an plus/minus icon has been clicked,
560 // the PM GET var is sent and we must update the stored positions in the tree):
561 // 0: mount key, 1: set/clear boolean, 2: item ID (cannot contain "_"), 3: treeName
562 list($storageHashNumber, $doExpand, $numericFolderHash, $treeName) = $this->evaluateExpandCollapseParameter();
563 if ($treeName && $treeName == $this->treeName) {
564 if (in_array($storageHashNumber, $this->storageHashNumbers)) {
565 if ($doExpand == 1) {
566 // Set
567 $this->stored[$storageHashNumber][$numericFolderHash] = 1;
568 } else {
569 // Clear
570 unset($this->stored[$storageHashNumber][$numericFolderHash]);
571 }
572 $this->savePosition();
573 }
574 }
575 }
576
577 /**
578 * Helper method to map md5-hash to shorter number
579 *
580 * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storageObject
581 * @param \TYPO3\CMS\Core\Resource\Folder $startingPointFolder
582 * @return integer
583 */
584 protected function getShortHashNumberForStorage(\TYPO3\CMS\Core\Resource\ResourceStorage $storageObject = NULL, \TYPO3\CMS\Core\Resource\Folder $startingPointFolder = NULL) {
585 if (!$this->storageHashNumbers) {
586 $this->storageHashNumbers = array();
587 // Mapping md5-hash to shorter number:
588 $hashMap = array();
589 foreach ($this->storages as $storageUid => $storage) {
590 $fileMounts = $storage->getFileMounts();
591 if (count($fileMounts)) {
592 foreach ($fileMounts as $fileMount) {
593 $nkey = hexdec(substr(GeneralUtility::md5int($fileMount['folder']->getCombinedIdentifier()), 0, 4));
594 $this->storageHashNumbers[$storageUid . $fileMount['folder']->getCombinedIdentifier()] = $nkey;
595 }
596 } else {
597 $folder = $storage->getRootLevelFolder();
598 $nkey = hexdec(substr(GeneralUtility::md5int($folder->getCombinedIdentifier()), 0, 4));
599 $this->storageHashNumbers[$storageUid . $folder->getCombinedIdentifier()] = $nkey;
600 }
601 }
602 }
603 if ($storageObject) {
604 if ($startingPointFolder) {
605 return $this->storageHashNumbers[$storageObject->getUid() . $startingPointFolder->getCombinedIdentifier()];
606 } else {
607 return $this->storageHashNumbers[$storageObject->getUid()];
608 }
609 } else {
610 return NULL;
611 }
612 }
613
614 /**
615 * Gets the values from the Expand/Collapse Parameter (&PM)
616 * previously known as "PM" (plus/minus)
617 * PM action:
618 * (If an plus/minus icon has been clicked,
619 * the PM GET var is sent and we must update the stored positions in the tree):
620 * 0: mount key, 1: set/clear boolean, 2: item ID (cannot contain "_"), 3: treeName
621 *
622 * @param string $PM The "plus/minus" command
623 * @return array
624 */
625 protected function evaluateExpandCollapseParameter($PM = NULL) {
626 if ($PM === NULL) {
627 $PM = GeneralUtility::_GP('PM');
628 // IE takes anchor as parameter
629 if (($PMpos = strpos($PM, '#')) !== FALSE) {
630 $PM = substr($PM, 0, $PMpos);
631 }
632 }
633 // Take the first three parameters
634 list($mountKey, $doExpand, $folderIdentifier) = explode('_', $PM, 3);
635 // In case the folder identifier contains "_", we just need to get the fourth/last parameter
636 list($folderIdentifier, $treeName) = GeneralUtility::revExplode('_', $folderIdentifier, 2);
637 return array(
638 $mountKey,
639 $doExpand,
640 $folderIdentifier,
641 $treeName
642 );
643 }
644
645 /**
646 * Generates the "PM" string to sent to expand/collapse items
647 *
648 * @param string $mountKey The mount key / storage UID
649 * @param boolean $doExpand Whether to expand/collapse
650 * @param \TYPO3\CMS\Core\Resource\Folder $folderObject The folder object
651 * @param string $treeName The name of the tree
652 * @return string
653 */
654 protected function generateExpandCollapseParameter($mountKey = NULL, $doExpand = FALSE, \TYPO3\CMS\Core\Resource\Folder $folderObject = NULL, $treeName = NULL) {
655 $parts = array(
656 $mountKey !== NULL ? $mountKey : $this->bank,
657 $doExpand == 1 ? 1 : 0,
658 $folderObject !== NULL ? GeneralUtility::md5int($folderObject->getCombinedIdentifier()) : '',
659 $treeName !== NULL ? $treeName : $this->treeName
660 );
661 return implode('_', $parts);
662 }
663
664 /**
665 * Gets the AJAX status.
666 *
667 * @return boolean
668 */
669 public function getAjaxStatus() {
670 return $this->ajaxStatus;
671 }
672
673 }