a0c3975888c03569704cee69ff6fc75fc2cade29
[Packages/TYPO3.CMS.git] / t3lib / tree / pagetree / class.t3lib_tree_pagetree_dataprovider.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010-2011 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 * Abstract 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
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
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 $subpage = t3lib_befunc::getRecordWSOL('pages', $uid, '*', '', TRUE, $unsetMovePointers);
192 return $subpage;
193 }
194
195 /**
196 * Returns a node collection of filtered nodes
197 *
198 * @param t3lib_tree_Node $node
199 * @param string $searchFilter
200 * @param integer $mountPoint
201 * @return void
202 */
203 public function getFilteredNodes(t3lib_tree_Node $node, $searchFilter, $mountPoint = 0) {
204 /** @var $nodeCollection t3lib_tree_pagetree_NodeCollection */
205 $nodeCollection = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
206
207 $records = $this->getSubpages(-1, $searchFilter);
208 if (!is_array($records) || !count($records)) {
209 return $nodeCollection;
210 } elseif (count($records) > 500) {
211 return $nodeCollection;
212 }
213
214 // check no temporary mountpoint is used
215 $mountPoints = intval($GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint']);
216 if (!$mountPoints) {
217 $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts());
218 $mountPoints = array_unique($mountPoints);
219 } else {
220 $mountPoints = array($mountPoints);
221 }
222
223 $isNumericSearchFilter = (is_numeric($searchFilter) && $searchFilter > 0);
224 $nodeId = intval($node->getId());
225 foreach ($records as $record) {
226 $record = t3lib_tree_pagetree_Commands::getNodeRecord($record['uid']);
227 if (intval($record['pid']) === -1 || in_array($record['uid'], $this->hiddenRecords)) {
228 continue;
229 }
230
231 $rootline = t3lib_BEfunc::BEgetRootLine(
232 $record['uid'], '', ($GLOBALS['BE_USER']->workspace != 0)
233 );
234 $rootline = array_reverse($rootline);
235 if ($nodeId === 0) {
236 array_shift($rootline);
237 }
238
239 if ($mountPoints != array(0)) {
240 $isInsideMountPoints = FALSE;
241 foreach ($rootline as $rootlineElement) {
242 if (in_array(intval($rootlineElement['uid']), $mountPoints, TRUE)) {
243 $isInsideMountPoints = TRUE;
244 break;
245 }
246 }
247
248 if (!$isInsideMountPoints) {
249 continue;
250 }
251 }
252 $reference = $nodeCollection;
253
254 $inFilteredRootline = FALSE;
255 $amountOfRootlineElements = count($rootline);
256 for ($i = 0; $i < $amountOfRootlineElements; ++$i) {
257 $rootlineElement = $rootline[$i];
258 if (intval($rootlineElement['pid']) === $nodeId || intval($rootlineElement['uid']) === $nodeId) {
259 $inFilteredRootline = TRUE;
260 }
261
262 if (!$inFilteredRootline) {
263 continue;
264 }
265
266 $rootlineElement = t3lib_tree_pagetree_Commands::getNodeRecord($rootlineElement['uid']);
267 $ident = intval($rootlineElement['sorting']) . intval($rootlineElement['uid']);
268 if ($reference && $reference->offsetExists($ident)) {
269 /** @var $refNode t3lib_tree_pagetree_Node */
270 $refNode = $reference->offsetGet($ident);
271 $refNode->setExpanded(TRUE);
272 $refNode->setLeaf(FALSE);
273
274 $reference = $refNode->getChildNodes();
275 if ($reference == NULL) {
276 $reference = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
277 $refNode->setChildNodes($reference);
278 }
279 } else {
280 $refNode = t3lib_tree_pagetree_Commands::getNewNode($rootlineElement, $mountPoint);
281 $replacement = '<span class="typo3-pagetree-filteringTree-highlight">$1</span>';
282 if ($isNumericSearchFilter && intval($rootlineElement['uid']) === intval($searchFilter)) {
283 $text = str_replace('$1', $refNode->getText(), $replacement);
284 } else {
285 $text = preg_replace('/(' . $searchFilter . ')/i', $replacement, $refNode->getText());
286 }
287
288 $refNode->setText(
289 $text,
290 $refNode->getTextSourceField(),
291 $refNode->getPrefix(),
292 $refNode->getSuffix()
293 );
294
295 /** @var $childCollection t3lib_tree_pagetree_NodeCollection */
296 $childCollection = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
297
298 if (($i + 1) >= $amountOfRootlineElements) {
299 $childNodes = $this->getNodes($refNode, $mountPoint);
300 foreach ($childNodes as $childNode) {
301 /** @var $childNode t3lib_tree_pagetree_Node */
302 $childRecord = $childNode->getRecord();
303 $childIdent = intval($childRecord['sorting']) . intval($childRecord['uid']);
304 $childCollection->offsetSet($childIdent, $childNode);
305 }
306 $refNode->setChildNodes($childNodes);
307 }
308
309 $refNode->setChildNodes($childCollection);
310 $reference->offsetSet($ident, $refNode);
311 $reference->ksort();
312
313 $reference = $childCollection;
314 }
315 }
316 }
317
318 foreach ($this->processCollectionHookObjects as $hookObject) {
319 /** @var $hookObject t3lib_tree_pagetree_interfaces_collectionprocessor */
320 $hookObject->postProcessFilteredNodes($node, $searchFilter, $mountPoint, $nodeCollection);
321 }
322
323 return $nodeCollection;
324 }
325
326 /**
327 * Returns the page tree mounts for the current user
328 *
329 * Note: If you add the search filter parameter, the nodes will be filtered by this string.
330 *
331 * @param string $searchFilter
332 * @return array
333 */
334 public function getTreeMounts($searchFilter = '') {
335 /** @var $nodeCollection t3lib_tree_pagetree_NodeCollection */
336 $nodeCollection = t3lib_div::makeInstance('t3lib_tree_pagetree_NodeCollection');
337
338 $isTemporaryMountPoint = FALSE;
339 $rootNodeIsVirtual = FALSE;
340 $mountPoints = intval($GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint']);
341 if (!$mountPoints) {
342 $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts());
343 $mountPoints = array_unique($mountPoints);
344 if (!in_array(0, $mountPoints)) {
345 $rootNodeIsVirtual = TRUE;
346 // use a virtual root
347 // the real mountpoints will be fetched in getNodes() then
348 // since those will be the "subpages" of the virtual root
349 $mountPoints = array(0);
350 }
351 } else {
352 $isTemporaryMountPoint = TRUE;
353 $mountPoints = array($mountPoints);
354 }
355
356 if (!count($mountPoints)) {
357 return $nodeCollection;
358 }
359
360 $showRootlineAboveMounts = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.showPathAboveMounts');
361 foreach ($mountPoints as $mountPoint) {
362 if ($mountPoint === 0) {
363 $sitename = 'TYPO3';
364 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] !== '') {
365 $sitename = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
366 }
367
368 $record = array(
369 'uid' => 0,
370 'title' => $sitename,
371 );
372 $subNode = t3lib_tree_pagetree_Commands::getNewNode($record);
373 $subNode->setLabelIsEditable(FALSE);
374 if ($rootNodeIsVirtual) {
375 $subNode->setType('virtual_root');
376 $subNode->setIsDropTarget(FALSE);
377 } else {
378 $subNode->setType('pages_root');
379 $subNode->setIsDropTarget(TRUE);
380 }
381 } else {
382 if (in_array($mountPoint, $this->hiddenRecords)) {
383 continue;
384 }
385
386 $record = $this->getRecordWithWorkspaceOverlay($mountPoint);
387 if (!$record) {
388 continue;
389 }
390
391 $subNode = t3lib_tree_pagetree_Commands::getNewNode($record, $mountPoint);
392 if ($showRootlineAboveMounts && !$isTemporaryMountPoint) {
393 $rootline = t3lib_tree_pagetree_Commands::getMountPointPath($record['uid']);
394 $subNode->setReadableRootline($rootline);
395 }
396 }
397
398 if (count($mountPoints) <= 1) {
399 $subNode->setExpanded(TRUE);
400 $subNode->setCls('typo3-pagetree-node-notExpandable');
401 }
402
403 $subNode->setIsMountPoint(TRUE);
404 $subNode->setDraggable(FALSE);
405
406 if ($searchFilter === '') {
407 $childNodes = $this->getNodes($subNode, $mountPoint);
408 } else {
409 $childNodes = $this->getFilteredNodes($subNode, $searchFilter, $mountPoint);
410 $subNode->setExpanded(TRUE);
411 }
412
413 $subNode->setChildNodes($childNodes);
414 $nodeCollection->append($subNode);
415 }
416
417 foreach ($this->processCollectionHookObjects as $hookObject) {
418 /** @var $hookObject t3lib_tree_pagetree_interfaces_collectionprocessor */
419 $hookObject->postProcessGetTreeMounts($searchFilter, $nodeCollection);
420 }
421
422 return $nodeCollection;
423 }
424
425 /**
426 * Returns the where clause for fetching pages
427 *
428 * @param integer $id
429 * @param string $searchFilter
430 * @return string
431 */
432 protected function getWhereClause($id, $searchFilter = '') {
433 $where = $GLOBALS['BE_USER']->getPagePermsClause(1) .
434 t3lib_BEfunc::deleteClause('pages') .
435 t3lib_BEfunc::versioningPlaceholderClause('pages');
436
437 if (is_numeric($id) && $id >= 0) {
438 $where .= ' AND pid= ' . $GLOBALS['TYPO3_DB']->fullQuoteStr(intval($id), 'pages');
439 }
440
441 if ($searchFilter !== '') {
442 if (is_numeric($searchFilter) && $searchFilter > 0) {
443 $seachWhere .= 'uid = ' . intval($searchFilter) . ' OR ';
444 }
445
446 $searchFilter = $GLOBALS['TYPO3_DB']->fullQuoteStr('%' . $searchFilter . '%', 'pages');
447 $useNavTitle = $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.showNavTitle');
448
449 if ($useNavTitle) {
450 $seachWhere .= '(nav_title LIKE ' . $searchFilter .
451 ' OR (nav_title = "" && title LIKE ' . $searchFilter . '))';
452 } else {
453 $seachWhere .= 'title LIKE ' . $searchFilter;
454 }
455
456 $where .= ' AND (' . $seachWhere . ')';
457 }
458
459 return $where;
460 }
461
462 /**
463 * Returns all sub-pages of a given id
464 *
465 * @param integer $id
466 * @param string $searchFilter
467 * @return array
468 */
469 protected function getSubpages($id, $searchFilter = '') {
470 $where = $this->getWhereClause($id, $searchFilter);
471 $subpages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
472 'uid', 'pages', $where, '', 'sorting', '', 'uid'
473 );
474
475 return $subpages;
476 }
477
478 /**
479 * Returns TRUE if the node has child's
480 *
481 * @param integer $id
482 * @return boolean
483 */
484 protected function hasNodeSubPages($id) {
485 $where = $this->getWhereClause($id);
486 $subpage = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
487 'uid', 'pages', $where, '', 'sorting', '', 'uid'
488 );
489
490 $returnValue = TRUE;
491 if (!$subpage['uid']) {
492 $returnValue = FALSE;
493 }
494
495 return $returnValue;
496 }
497 }
498
499 ?>