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