TYPO3 CMS  TYPO3_6-2
RootlineUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Utility;
3 
22 
26  protected $pageUid;
27 
32 
36  protected $parsedMountPointParameters = array();
37 
41  protected $languageUid = 0;
42 
46  protected $workspaceUid = 0;
47 
51  protected $versionPreview = FALSE;
52 
56  static protected $cache = NULL;
57 
61  static protected $localCache = array();
62 
68  static protected $rootlineFields = array(
69  'pid',
70  'uid',
71  't3ver_oid',
72  't3ver_wsid',
73  't3ver_state',
74  'title',
75  'alias',
76  'nav_title',
77  'media',
78  'layout',
79  'hidden',
80  'starttime',
81  'endtime',
82  'fe_group',
83  'extendToSubpages',
84  'doktype',
85  'TSconfig',
86  'storage_pid',
87  'is_siteroot',
88  'mount_pid',
89  'mount_pid_ol',
90  'fe_login_mode',
91  'backend_layout_next_level'
92  );
93 
99  protected $pageContext;
100 
104  protected $cacheIdentifier;
105 
109  static protected $pageRecordCache = array();
110 
115 
122  public function __construct($uid, $mountPointParameter = '', \TYPO3\CMS\Frontend\Page\PageRepository $context = NULL) {
123  $this->pageUid = (int)$uid;
124  $this->mountPointParameter = trim($mountPointParameter);
125  if ($context === NULL) {
126  if (isset($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE']->sys_page)) {
127  $this->pageContext = $GLOBALS['TSFE']->sys_page;
128  } else {
129  $this->pageContext = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
130  }
131  } else {
132  $this->pageContext = $context;
133  }
134  $this->initializeObject();
135  }
136 
143  protected function initializeObject() {
144  $this->languageUid = (int)$this->pageContext->sys_language_uid;
145  $this->workspaceUid = (int)$this->pageContext->versioningWorkspaceId;
146  $this->versionPreview = $this->pageContext->versioningPreview;
147  if ($this->mountPointParameter !== '') {
148  if (!$GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
149  throw new \RuntimeException('Mount-Point Pages are disabled for this installation. Cannot resolve a Rootline for a page with Mount-Points', 1343462896);
150  } else {
151  $this->parseMountPointParameter();
152  }
153  }
154  if (self::$cache === NULL) {
155  self::$cache = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_rootline');
156  }
157  self::$rootlineFields = array_merge(self::$rootlineFields, \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['addRootLineFields'], TRUE));
158  self::$rootlineFields = array_unique(self::$rootlineFields);
159  $this->databaseConnection = $GLOBALS['TYPO3_DB'];
160 
161  $this->cacheIdentifier = $this->getCacheIdentifier();
162  }
163 
171  static public function purgeCaches() {
172  self::$localCache = array();
173  self::$pageRecordCache = array();
174  }
175 
182  public function getCacheIdentifier($otherUid = NULL) {
183 
184  $mountPointParameter = (string)$this->mountPointParameter;
185  if ($mountPointParameter !== '' && strpos($mountPointParameter, ',') !== FALSE) {
186  $mountPointParameter = str_replace(',', '__', $mountPointParameter);
187  }
188 
189  return implode('_', array(
190  $otherUid !== NULL ? (int)$otherUid : $this->pageUid,
192  $this->languageUid,
193  $this->workspaceUid,
194  $this->versionPreview ? 1 : 0
195  ));
196  }
197 
203  public function get() {
204  if (!isset(static::$localCache[$this->cacheIdentifier])) {
205  $entry = static::$cache->get($this->cacheIdentifier);
206  if (!$entry) {
207  $this->generateRootlineCache();
208  } else {
209  static::$localCache[$this->cacheIdentifier] = $entry;
210  $depth = count($entry);
211  // Populate the root-lines for parent pages as well
212  // since they are part of the current root-line
213  while ($depth > 1) {
214  --$depth;
215  $parentCacheIdentifier = $this->getCacheIdentifier($entry[$depth - 1]['uid']);
216  // Abort if the root-line of the parent page is
217  // already in the local cache data
218  if (isset(static::$localCache[$parentCacheIdentifier])) {
219  break;
220  }
221  // Behaves similar to array_shift(), but preserves
222  // the array keys - which contain the page ids here
223  $entry = array_slice($entry, 1, NULL, TRUE);
224  static::$localCache[$parentCacheIdentifier] = $entry;
225  }
226  }
227  }
228  return static::$localCache[$this->cacheIdentifier];
229  }
230 
238  protected function getRecordArray($uid) {
239  $currentCacheIdentifier = $this->getCacheIdentifier($uid);
240  if (!isset(self::$pageRecordCache[$currentCacheIdentifier])) {
241  $row = $this->databaseConnection->exec_SELECTgetSingleRow(implode(',', self::$rootlineFields), 'pages', 'uid = ' . (int)$uid . ' AND pages.deleted = 0 AND pages.doktype <> ' . \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_RECYCLER);
242  if (empty($row)) {
243  throw new \RuntimeException('Could not fetch page data for uid ' . $uid . '.', 1343589451);
244  }
245  $this->pageContext->versionOL('pages', $row, FALSE, TRUE);
246  $this->pageContext->fixVersioningPid('pages', $row);
247  if (is_array($row)) {
248  if ($this->languageUid > 0) {
249  $row = $this->pageContext->getPageOverlay($row, $this->languageUid);
250  }
251  $row = $this->enrichWithRelationFields(isset($row['_PAGES_OVERLAY_UID']) ? $row['_PAGES_OVERLAY_UID'] : $uid, $row);
252  self::$pageRecordCache[$currentCacheIdentifier] = $row;
253  }
254  }
255  if (!is_array(self::$pageRecordCache[$currentCacheIdentifier])) {
256  throw new \RuntimeException('Broken rootline. Could not resolve page with uid ' . $uid . '.', 1343464101);
257  }
258  return self::$pageRecordCache[$currentCacheIdentifier];
259  }
260 
269  protected function enrichWithRelationFields($uid, array $pageRecord) {
270  $pageOverlayFields = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['pageOverlayFields']);
271  foreach ($GLOBALS['TCA']['pages']['columns'] as $column => $configuration) {
272  if ($this->columnHasRelationToResolve($configuration)) {
273  $configuration = $configuration['config'];
274  if ($configuration['MM']) {
276  $loadDBGroup = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
277  $loadDBGroup->start(
278  $pageRecord[$column],
279  isset($configuration['allowed']) ? $configuration['allowed'] : $configuration['foreign_table'],
280  $configuration['MM'],
281  $uid,
282  'pages',
283  $configuration
284  );
285  $relatedUids = isset($loadDBGroup->tableArray[$configuration['foreign_table']])
286  ? $loadDBGroup->tableArray[$configuration['foreign_table']]
287  : array();
288  } else {
289  $columnIsOverlaid = in_array($column, $pageOverlayFields, TRUE);
290  $table = $configuration['foreign_table'];
291  $field = $configuration['foreign_field'];
292  $whereClauseParts = array($field . ' = ' . (int)($columnIsOverlaid ? $uid : $pageRecord['uid']));
293  if (isset($configuration['foreign_match_fields']) && is_array($configuration['foreign_match_fields'])) {
294  foreach ($configuration['foreign_match_fields'] as $field => $value) {
295  $whereClauseParts[] = $field . ' = ' . $this->databaseConnection->fullQuoteStr($value, $table);
296  }
297  }
298  if (isset($configuration['foreign_table_field'])) {
299  if ((int)$this->languageUid > 0 && $columnIsOverlaid) {
300  $whereClauseParts[] = trim($configuration['foreign_table_field']) . ' = \'pages_language_overlay\'';
301  } else {
302  $whereClauseParts[] = trim($configuration['foreign_table_field']) . ' = \'pages\'';
303  }
304  }
305  if (isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'])) {
306  $whereClauseParts[] = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'] . ' = 0';
307  }
308  $whereClause = implode(' AND ', $whereClauseParts);
309  $whereClause .= $this->pageContext->deleteClause($table);
310  $orderBy = isset($configuration['foreign_sortby']) ? $configuration['foreign_sortby'] : '';
311  $rows = $this->databaseConnection->exec_SELECTgetRows('uid', $table, $whereClause, '', $orderBy);
312  if (!is_array($rows)) {
313  throw new \RuntimeException('Could to resolve related records for page ' . $uid . ' and foreign_table ' . htmlspecialchars($configuration['foreign_table']), 1343589452);
314  }
315  $relatedUids = array();
316  foreach ($rows as $row) {
317  $relatedUids[] = $row['uid'];
318  }
319  }
320  $pageRecord[$column] = implode(',', $relatedUids);
321  }
322  }
323  return $pageRecord;
324  }
325 
333  protected function columnHasRelationToResolve(array $configuration) {
334  $configuration = $configuration['config'];
335  if (!empty($configuration['MM']) && !empty($configuration['type']) && in_array($configuration['type'], array('select', 'inline', 'group'))) {
336  return TRUE;
337  }
338  if (!empty($configuration['foreign_field']) && !empty($configuration['type']) && in_array($configuration['type'], array('select', 'inline'))) {
339  return TRUE;
340  }
341  return FALSE;
342  }
343 
350  protected function generateRootlineCache() {
351  $page = $this->getRecordArray($this->pageUid);
352  // If the current page is a mounted (according to the MP parameter) handle the mount-point
353  if ($this->isMountedPage()) {
354  $mountPoint = $this->getRecordArray($this->parsedMountPointParameters[$this->pageUid]);
355  $page = $this->processMountedPage($page, $mountPoint);
356  $parentUid = $mountPoint['pid'];
357  // Anyhow after reaching the mount-point, we have to go up that rootline
358  unset($this->parsedMountPointParameters[$this->pageUid]);
359  } else {
360  $parentUid = $page['pid'];
361  }
362  $cacheTags = array('pageId_' . $page['uid']);
363  if ($parentUid > 0) {
364  // Get rootline of (and including) parent page
365  $mountPointParameter = count($this->parsedMountPointParameters) > 0 ? $this->mountPointParameter : '';
367  $rootline = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\RootlineUtility', $parentUid, $mountPointParameter, $this->pageContext);
368  $rootline = $rootline->get();
369  // retrieve cache tags of parent rootline
370  foreach ($rootline as $entry) {
371  $cacheTags[] = 'pageId_' . $entry['uid'];
372  if ($entry['uid'] == $this->pageUid) {
373  throw new \RuntimeException('Circular connection in rootline for page with uid ' . $this->pageUid . ' found. Check your mountpoint configuration.', 1343464103);
374  }
375  }
376  } else {
377  $rootline = array();
378  }
379  array_push($rootline, $page);
380  krsort($rootline);
381  static::$cache->set($this->cacheIdentifier, $rootline, $cacheTags);
382  static::$localCache[$this->cacheIdentifier] = $rootline;
383  }
384 
391  public function isMountedPage() {
392  return in_array($this->pageUid, array_keys($this->parsedMountPointParameters));
393  }
394 
403  protected function processMountedPage(array $mountedPageData, array $mountPointPageData) {
404  if ($mountPointPageData['mount_pid'] != $mountedPageData['uid']) {
405  throw new \RuntimeException('Broken rootline. Mountpoint parameter does not match the actual rootline. mount_pid (' . $mountPointPageData['mount_pid'] . ') does not match page uid (' . $mountedPageData['uid'] . ').', 1343464100);
406  }
407  // Current page replaces the original mount-page
408  if ($mountPointPageData['mount_pid_ol']) {
409  $mountedPageData['_MOUNT_OL'] = TRUE;
410  $mountedPageData['_MOUNT_PAGE'] = array(
411  'uid' => $mountPointPageData['uid'],
412  'pid' => $mountPointPageData['pid'],
413  'title' => $mountPointPageData['title']
414  );
415  } else {
416  // The mount-page is not replaced, the mount-page itself has to be used
417  $mountedPageData = $mountPointPageData;
418  }
419  $mountedPageData['_MOUNTED_FROM'] = $this->pageUid;
420  $mountedPageData['_MP_PARAM'] = $this->pageUid . '-' . $mountPointPageData['uid'];
421  return $mountedPageData;
422  }
423 
431  protected function parseMountPointParameter() {
432  $mountPoints = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->mountPointParameter);
433  foreach ($mountPoints as $mP) {
434  list($mountedPageUid, $mountPageUid) = \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode('-', $mP);
435  $this->parsedMountPointParameters[$mountedPageUid] = $mountPageUid;
436  }
437  }
438 
439 }
__construct($uid, $mountPointParameter='', \TYPO3\CMS\Frontend\Page\PageRepository $context=NULL)
static intExplode($delimiter, $string, $removeEmptyValues=FALSE, $limit=0)
$uid
Definition: server.php:36
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
processMountedPage(array $mountedPageData, array $mountPointPageData)
columnHasRelationToResolve(array $configuration)
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]