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