[TASK] Add more unit tests for t3lib_tree_pagetree_DataProvider
[Packages/TYPO3.CMS.git] / t3lib / tree / pagetree / class.t3lib_tree_pagetree_dataprovider.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010-2012 TYPO3 Tree Team <http://forge.typo3.org/projects/typo3v4-extjstrees>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Page tree data provider.
30 *
31 * @author Stefan Galinski <stefan.galinski@gmail.com>
32 * @package TYPO3
33 * @subpackage t3lib
34 */
35 class t3lib_tree_pagetree_DataProvider extends t3lib_tree_AbstractDataProvider {
36 /**
37 * Node limit that should be loaded for this request per mount
38 *
39 * @var integer
40 */
41 protected $nodeLimit = 0;
42
43 /**
44 * Current amount of nodes
45 *
46 * @var integer
47 */
48 protected $nodeCounter = 0;
49
50 /**
51 * Hidden Records
52 *
53 * @var array<string>
54 */
55 protected $hiddenRecords = array();
56
57 /**
58 * Process collection hook objects
59 *
60 * @var array<t3lib_tree_pagetree_interfaces_collectionprocessor>
61 */
62 protected $processCollectionHookObjects = array();
63
64 /**
65 * Constructor
66 *
67 * @param integer $nodeLimit (optional)
68 */
69 public function __construct($nodeLimit = NULL) {
70 if ($nodeLimit === NULL) {
71 $nodeLimit = $GLOBALS['TYPO3_CONF_VARS']['BE']['pageTree']['preloadLimit'];
72 }
73 $this->nodeLimit = abs(intval($nodeLimit));
74
75 $this->hiddenRecords = t3lib_div::trimExplode(
76 ',',
77 $GLOBALS['BE_USER']->getTSConfigVal('options.hideRecords.pages')
78 );
79
80 $hookElements = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/tree/pagetree/class.t3lib_tree_pagetree_dataprovider.php']['postProcessCollections'];
81 if (is_array($hookElements)) {
82 foreach ($hookElements as $classRef) {
83 /** @var $hookObject t3lib_tree_pagetree_interfaces_collectionprocessor */
84 $hookObject = t3lib_div::getUserObj($classRef);
85 if ($hookObject instanceof t3lib_tree_pagetree_interfaces_collectionprocessor) {
86 $this->processCollectionHookObjects[] = $hookObject;
87 }
88 }
89 }
90 }
91
92 /**
93 * Returns the root node.
94 *
95 * @return t3lib_tree_Node the root node
96 */
97 public function getRoot() {
98 /** @var $node t3lib_tree_pagetree_Node */
99 $node = t3lib_div::makeInstance('t3lib_tree_pagetree_Node');
100 $node->setId('root');
101 $node->setExpanded(TRUE);
102
103 return $node;
104 }
105
106 /**
107 * Fetches the sub-nodes of the given node
108 *
109 * @param t3lib_tree_Node $node
110 * @param integer $mountPoint
111 * @param integer $level internally used variable as a recursion limiter
112 * @return t3lib_tree_NodeCollection
113 */
114 public function getNodes(t3lib_tree_Node $node, $mountPoint = 0, $level = 0) {
115 /** @var $nodeCollection t3lib_tree_pagetree_NodeCollection */
116 $nodeCollection = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
117
118 if ($level >= 99) {
119 return $nodeCollection;
120 }
121
122 $subpages = $this->getSubpages($node->getId());
123
124 // check if fetching subpages the "root"-page
125 // and in case of a virtual root return the mountpoints as virtual "subpages"
126 if (intval($node->getId()) === 0) {
127 // check no temporary mountpoint is used
128 if (!intval($GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint'])) {
129 $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts());
130 $mountPoints = array_unique($mountPoints);
131 if (!in_array(0, $mountPoints)) {
132 // using a virtual root node
133 // so then return the mount points here as "subpages" of the first node
134 $subpages = array();
135 foreach ($mountPoints as $webMountPoint) {
136 $subpages[] = array(
137 'uid' => $webMountPoint,
138 'isMountPoint' => TRUE,
139 );
140 }
141 }
142 }
143 }
144
145 if (is_array($subpages) && count($subpages) > 0) {
146 foreach ($subpages as $subpage) {
147 if (in_array($subpage['uid'], $this->hiddenRecords)) {
148 continue;
149 }
150
151 // must be calculated above getRecordWithWorkspaceOverlay,
152 // because the information is lost otherwise
153 $isMountPoint = ($subpage['isMountPoint'] === TRUE);
154
155 $subpage = $this->getRecordWithWorkspaceOverlay($subpage['uid'], TRUE);
156
157 if (!$subpage) {
158 continue;
159 }
160
161 $subNode = t3lib_tree_pagetree_Commands::getNewNode($subpage, $mountPoint);
162 $subNode->setIsMountPoint($isMountPoint);
163 if ($this->nodeCounter < $this->nodeLimit) {
164 $childNodes = $this->getNodes($subNode, $mountPoint, $level + 1);
165 $subNode->setChildNodes($childNodes);
166 $this->nodeCounter += $childNodes->count();
167 } else {
168 $subNode->setLeaf(!$this->hasNodeSubPages($subNode->getId()));
169 }
170
171 $nodeCollection->append($subNode);
172 }
173 }
174
175 foreach ($this->processCollectionHookObjects as $hookObject) {
176 /** @var $hookObject t3lib_tree_pagetree_interfaces_collectionprocessor */
177 $hookObject->postProcessGetNodes($node, $mountPoint, $level, $nodeCollection);
178 }
179
180 return $nodeCollection;
181 }
182
183 /**
184 * Wrapper method for t3lib_befunc::getRecordWSOL
185 *
186 * @param integer $uid The page id
187 * @param boolean $unsetMovePointers Whether to unset move pointers
188 * @return array
189 */
190 protected function getRecordWithWorkspaceOverlay($uid, $unsetMovePointers = FALSE) {
191 return t3lib_befunc::getRecordWSOL('pages', $uid, '*', '', TRUE, $unsetMovePointers);
192 }
193
194 /**
195 * Returns a node collection of filtered nodes
196 *
197 * @param t3lib_tree_Node $node
198 * @param string $searchFilter
199 * @param integer $mountPoint
200 * @return t3lib_tree_pagetree_NodeCollection the filtered nodes
201 */
202 public function getFilteredNodes(t3lib_tree_Node $node, $searchFilter, $mountPoint = 0) {
203 /** @var $nodeCollection t3lib_tree_pagetree_NodeCollection */
204 $nodeCollection = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
205
206 $records = $this->getSubpages(-1, $searchFilter);
207 if (!is_array($records) || !count($records)) {
208 return $nodeCollection;
209 } elseif (count($records) > 500) {
210 return $nodeCollection;
211 }
212
213 // check no temporary mountpoint is used
214 $mountPoints = intval($GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint']);
215 if (!$mountPoints) {
216 $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts());
217 $mountPoints = array_unique($mountPoints);
218 } else {
219 $mountPoints = array($mountPoints);
220 }
221
222 $isNumericSearchFilter = (is_numeric($searchFilter) && $searchFilter > 0);
223 $nodeId = intval($node->getId());
224 foreach ($records as $record) {
225 $record = t3lib_tree_pagetree_Commands::getNodeRecord($record['uid']);
226 if (intval($record['pid']) === -1 || in_array($record['uid'], $this->hiddenRecords)) {
227 continue;
228 }
229
230 $rootline = t3lib_BEfunc::BEgetRootLine(
231 $record['uid'], '', ($GLOBALS['BE_USER']->workspace != 0)
232 );
233 $rootline = array_reverse($rootline);
234 if ($nodeId === 0) {
235 array_shift($rootline);
236 }
237
238 if ($mountPoints != array(0)) {
239 $isInsideMountPoints = FALSE;
240 foreach ($rootline as $rootlineElement) {
241 if (in_array(intval($rootlineElement['uid']), $mountPoints, TRUE)) {
242 $isInsideMountPoints = TRUE;
243 break;
244 }
245 }
246
247 if (!$isInsideMountPoints) {
248 continue;
249 }
250 }
251 $reference = $nodeCollection;
252
253 $inFilteredRootline = FALSE;
254 $amountOfRootlineElements = count($rootline);
255 for ($i = 0; $i < $amountOfRootlineElements; ++$i) {
256 $rootlineElement = $rootline[$i];
257 if (intval($rootlineElement['pid']) === $nodeId || intval($rootlineElement['uid']) === $nodeId) {
258 $inFilteredRootline = TRUE;
259 }
260
261 if (!$inFilteredRootline) {
262 continue;
263 }
264
265 $rootlineElement = t3lib_tree_pagetree_Commands::getNodeRecord($rootlineElement['uid']);
266 $ident = intval($rootlineElement['sorting']) . intval($rootlineElement['uid']);
267 if ($reference && $reference->offsetExists($ident)) {
268 /** @var $refNode t3lib_tree_pagetree_Node */
269 $refNode = $reference->offsetGet($ident);
270 $refNode->setExpanded(TRUE);
271 $refNode->setLeaf(FALSE);
272
273 $reference = $refNode->getChildNodes();
274 if ($reference == NULL) {
275 $reference = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
276 $refNode->setChildNodes($reference);
277 }
278 } else {
279 $refNode = t3lib_tree_pagetree_Commands::getNewNode($rootlineElement, $mountPoint);
280 $replacement = '<span class="typo3-pagetree-filteringTree-highlight">$1</span>';
281 if ($isNumericSearchFilter && intval($rootlineElement['uid']) === intval($searchFilter)) {
282 $text = str_replace('$1', $refNode->getText(), $replacement);
283 } else {
284 $text = preg_replace('/(' . $searchFilter . ')/i', $replacement, $refNode->getText());
285 }
286
287 $refNode->setText(
288 $text,
289 $refNode->getTextSourceField(),
290 $refNode->getPrefix(),
291 $refNode->getSuffix()
292 );
293
294 /** @var $childCollection t3lib_tree_pagetree_NodeCollection */
295 $childCollection = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
296
297 if (($i + 1) >= $amountOfRootlineElements) {
298 $childNodes = $this->getNodes($refNode, $mountPoint);
299 foreach ($childNodes as $childNode) {
300 /** @var $childNode t3lib_tree_pagetree_Node */
301 $childRecord = $childNode->getRecord();
302 $childIdent = intval($childRecord['sorting']) . intval($childRecord['uid']);
303 $childCollection->offsetSet($childIdent, $childNode);
304 }
305 $refNode->setChildNodes($childNodes);
306 }
307
308 $refNode->setChildNodes($childCollection);
309 $reference->offsetSet($ident, $refNode);
310 $reference->ksort();
311
312 $reference = $childCollection;
313 }
314 }
315 }
316
317 foreach ($this->processCollectionHookObjects as $hookObject) {
318 /** @var $hookObject t3lib_tree_pagetree_interfaces_collectionprocessor */
319 $hookObject->postProcessFilteredNodes($node, $searchFilter, $mountPoint, $nodeCollection);
320 }
321
322 return $nodeCollection;
323 }
324
325 /**
326 * Returns the page tree mounts for the current user
327 *
328 * Note: If you add the search filter parameter, the nodes will be filtered by this string.
329 *
330 * @param string $searchFilter
331 * @return t3lib_tree_pagetree_NodeCollection
332 */
333 public function getTreeMounts($searchFilter = '') {
334 /** @var $nodeCollection t3lib_tree_pagetree_NodeCollection */
335 $nodeCollection = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
336
337 $isTemporaryMountPoint = FALSE;
338 $rootNodeIsVirtual = FALSE;
339 $mountPoints = intval($GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint']);
340 if (!$mountPoints) {
341 $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts());
342 $mountPoints = array_unique($mountPoints);
343 if (!in_array(0, $mountPoints)) {
344 $rootNodeIsVirtual = TRUE;
345 // use a virtual root
346 // the real mountpoints will be fetched in getNodes() then
347 // since those will be the "subpages" of the virtual root
348 $mountPoints = array(0);
349 }
350 } else {
351 $isTemporaryMountPoint = TRUE;
352 $mountPoints = array($mountPoints);
353 }
354
355 if (!count($mountPoints)) {
356 return $nodeCollection;
357 }
358
359 $showRootlineAboveMounts = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.showPathAboveMounts');
360 foreach ($mountPoints as $mountPoint) {
361 if ($mountPoint === 0) {
362 $sitename = 'TYPO3';
363 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] !== '') {
364 $sitename = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
365 }
366
367 $record = array(
368 'uid' => 0,
369 'title' => $sitename,
370 );
371 $subNode = t3lib_tree_pagetree_Commands::getNewNode($record);
372 $subNode->setLabelIsEditable(FALSE);
373 if ($rootNodeIsVirtual) {
374 $subNode->setType('virtual_root');
375 $subNode->setIsDropTarget(FALSE);
376 } else {
377 $subNode->setType('pages_root');
378 $subNode->setIsDropTarget(TRUE);
379 }
380 } else {
381 if (in_array($mountPoint, $this->hiddenRecords)) {
382 continue;
383 }
384
385 $record = $this->getRecordWithWorkspaceOverlay($mountPoint);
386 if (!$record) {
387 continue;
388 }
389
390 $subNode = t3lib_tree_pagetree_Commands::getNewNode($record, $mountPoint);
391 if ($showRootlineAboveMounts && !$isTemporaryMountPoint) {
392 $rootline = t3lib_tree_pagetree_Commands::getMountPointPath($record['uid']);
393 $subNode->setReadableRootline($rootline);
394 }
395 }
396
397 if (count($mountPoints) <= 1) {
398 $subNode->setExpanded(TRUE);
399 $subNode->setCls('typo3-pagetree-node-notExpandable');
400 }
401
402 $subNode->setIsMountPoint(TRUE);
403 $subNode->setDraggable(FALSE);
404
405 if ($searchFilter === '') {
406 $childNodes = $this->getNodes($subNode, $mountPoint);
407 } else {
408 $childNodes = $this->getFilteredNodes($subNode, $searchFilter, $mountPoint);
409 $subNode->setExpanded(TRUE);
410 }
411
412 $subNode->setChildNodes($childNodes);
413 $nodeCollection->append($subNode);
414 }
415
416 foreach ($this->processCollectionHookObjects as $hookObject) {
417 /** @var $hookObject t3lib_tree_pagetree_interfaces_collectionprocessor */
418 $hookObject->postProcessGetTreeMounts($searchFilter, $nodeCollection);
419 }
420
421 return $nodeCollection;
422 }
423
424 /**
425 * Returns the where clause for fetching pages
426 *
427 * @param integer $id
428 * @param string $searchFilter
429 * @return string
430 */
431 protected function getWhereClause($id, $searchFilter = '') {
432 $where = $GLOBALS['BE_USER']->getPagePermsClause(1) .
433 t3lib_BEfunc::deleteClause('pages') .
434 t3lib_BEfunc::versioningPlaceholderClause('pages');
435
436 if (is_numeric($id) && $id >= 0) {
437 $where .= ' AND pid= ' . $GLOBALS['TYPO3_DB']->fullQuoteStr(intval($id), 'pages');
438 }
439
440 if ($searchFilter !== '') {
441 if (is_numeric($searchFilter) && $searchFilter > 0) {
442 $searchWhere .= 'uid = ' . intval($searchFilter) . ' OR ';
443 }
444
445 $searchFilter = $GLOBALS['TYPO3_DB']->fullQuoteStr('%' . $searchFilter . '%', 'pages');
446 $useNavTitle = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.showNavTitle');
447
448 if ($useNavTitle) {
449 $searchWhere .= '(nav_title LIKE ' . $searchFilter .
450 ' OR (nav_title = "" AND title LIKE ' . $searchFilter . '))';
451 } else {
452 $searchWhere .= 'title LIKE ' . $searchFilter;
453 }
454
455 $where .= ' AND (' . $searchWhere . ')';
456 }
457
458 return $where;
459 }
460
461 /**
462 * Returns all sub-pages of a given id
463 *
464 * @param integer $id
465 * @param string $searchFilter
466 * @return array
467 */
468 protected function getSubpages($id, $searchFilter = '') {
469 $where = $this->getWhereClause($id, $searchFilter);
470 return $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
471 'uid', 'pages', $where, '', 'sorting', '', 'uid'
472 );
473 }
474
475 /**
476 * Returns TRUE if the node has child's
477 *
478 * @param integer $id
479 * @return boolean
480 */
481 protected function hasNodeSubPages($id) {
482 $where = $this->getWhereClause($id);
483 $subpage = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
484 'uid', 'pages', $where, '', 'sorting', '', 'uid'
485 );
486
487 $returnValue = TRUE;
488 if (!$subpage['uid']) {
489 $returnValue = FALSE;
490 }
491
492 return $returnValue;
493 }
494 }
495 ?>