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