TYPO3 CMS  TYPO3_7-6
WorkspaceService.php
Go to the documentation of this file.
1 <?php
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 
22 
27 {
31  protected $pageCache = [];
32 
36  protected $versionsOnPageCache = [];
37 
38  const TABLE_WORKSPACE = 'sys_workspace';
39  const SELECT_ALL_WORKSPACES = -98;
40  const LIVE_WORKSPACE_ID = 0;
47  public function getAvailableWorkspaces()
48  {
49  $availableWorkspaces = [];
50  // add default workspaces
51  if ($GLOBALS['BE_USER']->checkWorkspace(['uid' => (string)self::LIVE_WORKSPACE_ID])) {
52  $availableWorkspaces[self::LIVE_WORKSPACE_ID] = self::getWorkspaceTitle(self::LIVE_WORKSPACE_ID);
53  }
54  // add custom workspaces (selecting all, filtering by BE_USER check):
55  $customWorkspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, title, adminusers, members', 'sys_workspace', 'pid = 0' . BackendUtility::deleteClause('sys_workspace'), '', 'title');
56  if (!empty($customWorkspaces)) {
57  foreach ($customWorkspaces as $workspace) {
58  if ($GLOBALS['BE_USER']->checkWorkspace($workspace)) {
59  $availableWorkspaces[$workspace['uid']] = $workspace['title'];
60  }
61  }
62  }
63  return $availableWorkspaces;
64  }
65 
71  public function getCurrentWorkspace()
72  {
73  $workspaceId = $GLOBALS['BE_USER']->workspace;
74  $activeId = $GLOBALS['BE_USER']->getSessionData('tx_workspace_activeWorkspace');
75 
76  // Avoid invalid workspace settings
77  if ($activeId !== null && $activeId !== self::SELECT_ALL_WORKSPACES) {
78  $availableWorkspaces = $this->getAvailableWorkspaces();
79  if (isset($availableWorkspaces[$activeId])) {
80  $workspaceId = $activeId;
81  }
82  }
83 
84  return $workspaceId;
85  }
86 
94  public static function getWorkspaceTitle($wsId)
95  {
96  $title = false;
97  switch ($wsId) {
98  case self::LIVE_WORKSPACE_ID:
99  $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xlf:shortcut_onlineWS');
100  break;
101  default:
102  $labelField = $GLOBALS['TCA']['sys_workspace']['ctrl']['label'];
103  $wsRecord = BackendUtility::getRecord('sys_workspace', $wsId, 'uid,' . $labelField);
104  if (is_array($wsRecord)) {
105  $title = $wsRecord[$labelField];
106  }
107  }
108  if ($title === false) {
109  throw new \InvalidArgumentException('No such workspace defined');
110  }
111  return $title;
112  }
113 
123  public function getCmdArrayForPublishWS($wsid, $doSwap, $pageId = 0, $language = null)
124  {
125  $wsid = (int)$wsid;
126  $cmd = [];
127  if ($wsid >= -1 && $wsid !== 0) {
128  // Define stage to select:
129  $stage = -99;
130  if ($wsid > 0) {
131  $workspaceRec = BackendUtility::getRecord('sys_workspace', $wsid);
132  if ($workspaceRec['publish_access'] & 1) {
134  }
135  }
136  // Select all versions to swap:
137  $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, $pageId ?: -1, 999, 'tables_modify', $language);
138  // Traverse the selection to build CMD array:
139  foreach ($versions as $table => $records) {
140  foreach ($records as $rec) {
141  // Build the cmd Array:
142  $cmd[$table][$rec['t3ver_oid']]['version'] = ['action' => 'swap', 'swapWith' => $rec['uid'], 'swapIntoWS' => $doSwap ? 1 : 0];
143  }
144  }
145  }
146  return $cmd;
147  }
148 
158  public function getCmdArrayForFlushWS($wsid, $flush = true, $pageId = 0, $language = null)
159  {
160  $wsid = (int)$wsid;
161  $cmd = [];
162  if ($wsid >= -1 && $wsid !== 0) {
163  // Define stage to select:
164  $stage = -99;
165  // Select all versions to swap:
166  $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, $pageId ?: -1, 999, 'tables_modify', $language);
167  // Traverse the selection to build CMD array:
168  foreach ($versions as $table => $records) {
169  foreach ($records as $rec) {
170  // Build the cmd Array:
171  $cmd[$table][$rec['uid']]['version'] = ['action' => $flush ? 'flush' : 'clearWSID'];
172  }
173  }
174  }
175  return $cmd;
176  }
177 
192  public function selectVersionsInWorkspace($wsid, $filter = 0, $stage = -99, $pageId = -1, $recursionLevel = 0, $selectionType = 'tables_select', $language = null)
193  {
194  $wsid = (int)$wsid;
195  $filter = (int)$filter;
196  $output = [];
197  // Contains either nothing or a list with live-uids
198  if ($pageId != -1 && $recursionLevel > 0) {
199  $pageList = $this->getTreeUids($pageId, $wsid, $recursionLevel);
200  } elseif ($pageId != -1) {
201  $pageList = $pageId;
202  } else {
203  $pageList = '';
204  // check if person may only see a "virtual" page-root
205  $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts());
206  $mountPoints = array_unique($mountPoints);
207  if (!in_array(0, $mountPoints)) {
208  $tempPageIds = [];
209  foreach ($mountPoints as $mountPoint) {
210  $tempPageIds[] = $this->getTreeUids($mountPoint, $wsid, $recursionLevel);
211  }
212  $pageList = implode(',', $tempPageIds);
213  $pageList = implode(',', array_unique(explode(',', $pageList)));
214  }
215  }
216  // Traversing all tables supporting versioning:
217  foreach ($GLOBALS['TCA'] as $table => $cfg) {
218  // we do not collect records from tables without permissions on them.
219  if (!$GLOBALS['BE_USER']->check($selectionType, $table)) {
220  continue;
221  }
222  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
223  $recs = $this->selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language);
224  $moveRecs = $this->getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage);
225  $recs = array_merge($recs, $moveRecs);
226  $recs = $this->filterPermittedElements($recs, $table);
227  if (!empty($recs)) {
228  $output[$table] = $recs;
229  }
230  }
231  }
232  return $output;
233  }
234 
246  protected function selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language = null)
247  {
248  // Include root level page as there might be some records with where root level restriction is ignored (e.g. FAL records)
249  if ($pageList !== '' && BackendUtility::isRootLevelRestrictionIgnored($table)) {
250  $pageList .= ',0';
251  }
252  $isTableLocalizable = BackendUtility::isTableLocalizable($table);
253  $languageParentField = '';
254  // If table is not localizable, but localized reocrds shall
255  // be collected, an empty result array needs to be returned:
256  if ($isTableLocalizable === false && $language > 0) {
257  return [];
258  } elseif ($isTableLocalizable) {
259  $languageParentField = 'A.' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . ', ';
260  }
261  $fields = 'A.uid, A.t3ver_oid, A.t3ver_stage, ' . $languageParentField . 'B.pid AS wspid, B.pid AS livepid';
262  if ($isTableLocalizable) {
263  $fields .= ', A.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
264  }
265  $from = $table . ' A,' . $table . ' B';
266  // Table A is the offline version and pid=-1 defines offline
267  $where = 'A.pid=-1 AND A.t3ver_state!=' . new VersionState(VersionState::MOVE_POINTER);
268  if ($pageList) {
269  $pidField = $table === 'pages' ? 'uid' : 'pid';
270  $pidConstraint = strstr($pageList, ',') ? ' IN (' . $pageList . ')' : '=' . $pageList;
271  $where .= ' AND B.' . $pidField . $pidConstraint;
272  }
273  if ($isTableLocalizable && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($language)) {
274  $where .= ' AND A.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . $language;
275  }
276  // For "real" workspace numbers, select by that.
277  // If = -98, select all that are NOT online (zero).
278  // Anything else below -1 will not select on the wsid and therefore select all!
279  if ($wsid > self::SELECT_ALL_WORKSPACES) {
280  $where .= ' AND A.t3ver_wsid=' . $wsid;
281  } elseif ($wsid === self::SELECT_ALL_WORKSPACES) {
282  $where .= ' AND A.t3ver_wsid!=0';
283  }
284  // lifecycle filter:
285  // 1 = select all drafts (never-published),
286  // 2 = select all published one or more times (archive/multiple)
287  if ($filter === 1 || $filter === 2) {
288  $where .= ' AND A.t3ver_count ' . ($filter === 1 ? '= 0' : '> 0');
289  }
290  if ($stage != -99) {
291  $where .= ' AND A.t3ver_stage=' . (int)$stage;
292  }
293  // Table B (online) must have PID >= 0 to signify being online.
294  $where .= ' AND B.pid>=0';
295  // ... and finally the join between the two tables.
296  $where .= ' AND A.t3ver_oid=B.uid';
297  $where .= BackendUtility::deleteClause($table, 'A');
298  $where .= BackendUtility::deleteClause($table, 'B');
299  // Select all records from this table in the database from the workspace
300  // This joins the online version with the offline version as tables A and B
301  // Order by UID, mostly to have a sorting in the backend overview module which doesn't "jump around" when swapping.
302  $res = $this->getDatabaseConnection()->exec_SELECTgetRows($fields, $from, $where, '', 'B.uid');
303  return is_array($res) ? $res : [];
304  }
305 
316  protected function getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage)
317  {
318  // Aliases:
319  // A - moveTo placeholder
320  // B - online record
321  // C - moveFrom placeholder
322  $fields = 'A.pid AS wspid, B.uid AS t3ver_oid, C.uid AS uid, B.pid AS livepid';
323  $from = $table . ' A, ' . $table . ' B,' . $table . ' C';
324  $where = 'A.t3ver_state=' . new VersionState(VersionState::MOVE_PLACEHOLDER) . ' AND B.pid>0 AND B.t3ver_state='
325  . new VersionState(VersionState::DEFAULT_STATE) . ' AND B.t3ver_wsid=0 AND C.pid=-1 AND C.t3ver_state='
327  if ($wsid > self::SELECT_ALL_WORKSPACES) {
328  $where .= ' AND A.t3ver_wsid=' . $wsid . ' AND C.t3ver_wsid=' . $wsid;
329  } elseif ($wsid === self::SELECT_ALL_WORKSPACES) {
330  $where .= ' AND A.t3ver_wsid!=0 AND C.t3ver_wsid!=0 ';
331  }
332  // lifecycle filter:
333  // 1 = select all drafts (never-published),
334  // 2 = select all published one or more times (archive/multiple)
335  if ($filter === 1 || $filter === 2) {
336  $where .= ' AND C.t3ver_count ' . ($filter === 1 ? '= 0' : '> 0');
337  }
338  if ($stage != -99) {
339  $where .= ' AND C.t3ver_stage=' . (int)$stage;
340  }
341  if ($pageList) {
342  $pidField = $table === 'pages' ? 'B.uid' : 'A.pid';
343  $pidConstraint = strstr($pageList, ',') ? ' IN (' . $pageList . ')' : '=' . $pageList;
344  $where .= ' AND ' . $pidField . $pidConstraint;
345  }
346  $where .= ' AND A.t3ver_move_id = B.uid AND B.uid = C.t3ver_oid';
347  $where .= BackendUtility::deleteClause($table, 'A');
348  $where .= BackendUtility::deleteClause($table, 'B');
349  $where .= BackendUtility::deleteClause($table, 'C');
350  $res = $this->getDatabaseConnection()->exec_SELECTgetRows($fields, $from, $where, '', 'A.uid');
351  return is_array($res) ? $res : [];
352  }
353 
362  protected function getTreeUids($pageId, $wsid, $recursionLevel)
363  {
364  // Reusing existing functionality with the drawback that
365  // mount points are not covered yet
366  $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
368  $searchObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\QueryView::class);
369  if ($pageId > 0) {
370  $pageList = $searchObj->getTreeList($pageId, $recursionLevel, 0, $perms_clause);
371  } else {
372  $mountPoints = $GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint'];
373  if (!is_array($mountPoints) || empty($mountPoints)) {
374  $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts());
375  $mountPoints = array_unique($mountPoints);
376  }
377  $newList = [];
378  foreach ($mountPoints as $mountPoint) {
379  $newList[] = $searchObj->getTreeList($mountPoint, $recursionLevel, 0, $perms_clause);
380  }
381  $pageList = implode(',', $newList);
382  }
383  unset($searchObj);
384  if (BackendUtility::isTableWorkspaceEnabled('pages') && $pageList) {
385  // Remove the "subbranch" if a page was moved away
386  $movedAwayPages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, pid, t3ver_move_id', 'pages', 't3ver_move_id IN (' . $pageList . ') AND t3ver_wsid=' . (int)$wsid . BackendUtility::deleteClause('pages'), '', 'uid', '', 't3ver_move_id');
387  $pageIds = GeneralUtility::intExplode(',', $pageList, true);
388  // move all pages away
389  $newList = array_diff($pageIds, array_keys($movedAwayPages));
390  // keep current page in the list
391  $newList[] = $pageId;
392  // move back in if still connected to the "remaining" pages
393  do {
394  $changed = false;
395  foreach ($movedAwayPages as $uid => $rec) {
396  if (in_array($rec['pid'], $newList) && !in_array($uid, $newList)) {
397  $newList[] = $uid;
398  $changed = true;
399  }
400  }
401  } while ($changed);
402  $pageList = implode(',', $newList);
403  // In case moving pages is enabled we need to replace all move-to pointer with their origin
404  $pages = $this->getDatabaseConnection()->exec_SELECTgetRows('uid, t3ver_move_id', 'pages', 'uid IN (' . $pageList . ')' . BackendUtility::deleteClause('pages'), '', 'uid', '', 'uid');
405  $newList = [];
406  $pageIds = GeneralUtility::intExplode(',', $pageList, true);
407  if (!in_array($pageId, $pageIds)) {
408  $pageIds[] = $pageId;
409  }
410  foreach ($pageIds as $pageId) {
411  if ((int)$pages[$pageId]['t3ver_move_id'] > 0) {
412  $newList[] = (int)$pages[$pageId]['t3ver_move_id'];
413  } else {
414  $newList[] = $pageId;
415  }
416  }
417  $pageList = implode(',', $newList);
418  }
419  return $pageList;
420  }
421 
429  protected function filterPermittedElements($recs, $table)
430  {
431  $permittedElements = [];
432  if (is_array($recs)) {
433  foreach ($recs as $rec) {
434  if ($this->isPageAccessibleForCurrentUser($table, $rec) && $this->isLanguageAccessibleForCurrentUser($table, $rec)) {
435  $permittedElements[] = $rec;
436  }
437  }
438  }
439  return $permittedElements;
440  }
441 
449  protected function isPageAccessibleForCurrentUser($table, array $record)
450  {
451  $pageIdField = $table === 'pages' ? 'uid' : 'wspid';
452  $pageId = isset($record[$pageIdField]) ? (int)$record[$pageIdField] : null;
453  if ($pageId === null) {
454  return false;
455  }
456  if ($pageId === 0 && BackendUtility::isRootLevelRestrictionIgnored($table)) {
457  return true;
458  }
459  $page = BackendUtility::getRecord('pages', $pageId, 'uid,pid,perms_userid,perms_user,perms_groupid,perms_group,perms_everybody');
460 
461  return $GLOBALS['BE_USER']->doesUserHaveAccess($page, 1);
462  }
463 
471  protected function isLanguageAccessibleForCurrentUser($table, array $record)
472  {
474  $languageUid = $record[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
475  } else {
476  return true;
477  }
478  return $GLOBALS['BE_USER']->checkLanguageAccess($languageUid);
479  }
480 
487  public static function isOldStyleWorkspaceUsed()
488  {
489  $cacheKey = 'workspace-oldstyleworkspace-notused';
490  $cacheResult = $GLOBALS['BE_USER']->getSessionData($cacheKey);
491  if (!$cacheResult) {
492  $where = 'adminusers != \'\' AND adminusers NOT LIKE \'%be_users%\' AND adminusers NOT LIKE \'%be_groups%\' AND deleted=0';
493  $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace', $where);
494  $oldStyleWorkspaceIsUsed = $count > 0;
495  $GLOBALS['BE_USER']->setAndSaveSessionData($cacheKey, !$oldStyleWorkspaceIsUsed);
496  } else {
497  $oldStyleWorkspaceIsUsed = !$cacheResult;
498  }
499  return $oldStyleWorkspaceIsUsed;
500  }
501 
509  public static function isNewPage($id, $language = 0)
510  {
511  $isNewPage = false;
512  // If the language is not default, check state of overlay
513  if ($language > 0) {
514  $whereClause = 'pid = ' . (int)$id;
515  $whereClause .= ' AND ' . $GLOBALS['TCA']['pages_language_overlay']['ctrl']['languageField'] . ' = ' . (int)$language;
516  $whereClause .= ' AND t3ver_wsid = ' . (int)$GLOBALS['BE_USER']->workspace;
517  $whereClause .= BackendUtility::deleteClause('pages_language_overlay');
518  $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('t3ver_state', 'pages_language_overlay', $whereClause);
519  if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
520  $isNewPage = VersionState::cast($row['t3ver_state'])->equals(VersionState::NEW_PLACEHOLDER);
521  }
522  } else {
523  $rec = BackendUtility::getRecord('pages', $id, 't3ver_state');
524  if (is_array($rec)) {
525  $isNewPage = VersionState::cast($rec['t3ver_state'])->equals(VersionState::NEW_PLACEHOLDER);
526  }
527  }
528  return $isNewPage;
529  }
530 
541  public static function viewSingleRecord($table, $uid, array $liveRecord = null, array $versionRecord = null)
542  {
543  if ($table === 'pages') {
545  }
546 
547  if ($liveRecord === null) {
548  $liveRecord = BackendUtility::getLiveVersionOfRecord($table, $uid);
549  }
550  if ($versionRecord === null) {
551  $versionRecord = BackendUtility::getRecord($table, $uid);
552  }
553  if (VersionState::cast($versionRecord['t3ver_state'])->equals(VersionState::MOVE_POINTER)) {
554  $movePlaceholder = BackendUtility::getMovePlaceholder($table, $liveRecord['uid'], 'pid');
555  }
556 
557  // Directly use pid value and consider move placeholders
558  $previewPageId = (empty($movePlaceholder['pid']) ? $liveRecord['pid'] : $movePlaceholder['pid']);
559  $additionalParameters = '&tx_workspaces_web_workspacesworkspaces[previewWS]=' . $versionRecord['t3ver_wsid'];
560  // Add language parameter if record is a localization
562  $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
563  if ($versionRecord[$languageField] > 0) {
564  $additionalParameters .= '&L=' . $versionRecord[$languageField];
565  }
566  }
567 
568  $pageTsConfig = BackendUtility::getPagesTSconfig($previewPageId);
569  $viewUrl = '';
570 
571  // Directly use determined direct page id
572  if ($table === 'pages_language_overlay' || $table === 'tt_content') {
573  $viewUrl = BackendUtility::viewOnClick($previewPageId, '', '', '', '', $additionalParameters);
574  // Analyze Page TSconfig options.workspaces.previewPageId
575  } elseif (!empty($pageTsConfig['options.']['workspaces.']['previewPageId.'][$table]) || !empty($pageTsConfig['options.']['workspaces.']['previewPageId'])) {
576  if (!empty($pageTsConfig['options.']['workspaces.']['previewPageId.'][$table])) {
577  $previewConfiguration = $pageTsConfig['options.']['workspaces.']['previewPageId.'][$table];
578  } else {
579  $previewConfiguration = $pageTsConfig['options.']['workspaces.']['previewPageId'];
580  }
581  // Extract possible settings (e.g. "field:pid")
582  list($previewKey, $previewValue) = explode(':', $previewConfiguration, 2);
583  if ($previewKey === 'field') {
584  $previewPageId = (int)$liveRecord[$previewValue];
585  } else {
586  $previewPageId = (int)$previewConfiguration;
587  }
588  $viewUrl = BackendUtility::viewOnClick($previewPageId, '', '', '', '', $additionalParameters);
589  // Call user function to render the single record view
590  } elseif (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['viewSingleRecord'])) {
591  $_params = [
592  'table' => $table,
593  'uid' => $uid,
594  'record' => $liveRecord,
595  'liveRecord' => $liveRecord,
596  'versionRecord' => $versionRecord,
597  ];
598  $_funcRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['viewSingleRecord'];
599  $null = null;
600  $viewUrl = GeneralUtility::callUserFunction($_funcRef, $_params, $null);
601  }
602 
603  return $viewUrl;
604  }
605 
613  public function canCreatePreviewLink($pageUid, $workspaceUid)
614  {
615  $result = true;
616  if ($pageUid > 0 && $workspaceUid > 0) {
617  $pageRecord = BackendUtility::getRecord('pages', $pageUid);
618  BackendUtility::workspaceOL('pages', $pageRecord, $workspaceUid);
619  if (
620  !GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['FE']['content_doktypes'], $pageRecord['doktype'])
621  || VersionState::cast($pageRecord['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)
622  ) {
623  $result = false;
624  }
625  } else {
626  $result = false;
627  }
628  return $result;
629  }
630 
638  {
639  $previewObject = GeneralUtility::makeInstance(\TYPO3\CMS\Version\Hook\PreviewHook::class);
640  $timeToLiveHours = $previewObject->getPreviewLinkLifetime();
641  $previewKeyword = $previewObject->compilePreviewKeyword('', $GLOBALS['BE_USER']->user['uid'], $timeToLiveHours * 3600, $this->getCurrentWorkspace());
642  $linkParams = [
643  'ADMCMD_prev' => $previewKeyword,
644  'id' => $uid
645  ];
646  return BackendUtility::getViewDomain($uid) . '/index.php?' . GeneralUtility::implodeArrayForUrl('', $linkParams);
647  }
648 
656  public function generateWorkspaceSplittedPreviewLink($uid, $addDomain = false)
657  {
658  // In case a $pageUid is submitted we need to make sure it points to a live-page
659  if ($uid > 0) {
660  $uid = $this->getLivePageUid($uid);
661  }
663  $uriBuilder = $this->getObjectManager()->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
664  $redirect = 'index.php?redirect_url=';
665  // @todo this should maybe be changed so that the extbase URI Builder can deal with module names directly
666  $originalM = GeneralUtility::_GET('M');
667  GeneralUtility::_GETset('web_WorkspacesWorkspaces', 'M');
668  $viewScript = $uriBuilder->uriFor('index', [], 'Preview', 'workspaces', 'web_workspacesworkspaces') . '&id=';
669  GeneralUtility::_GETset($originalM, 'M');
670  if ($addDomain === true) {
671  return BackendUtility::getViewDomain($uid) . $redirect . urlencode($viewScript) . $uid;
672  } else {
673  return $viewScript;
674  }
675  }
676 
684  {
685  $previewUrl = $this->generateWorkspacePreviewLink($uid);
686  $previewLanguages = $this->getAvailableLanguages($uid);
687  $previewLinks = [];
688 
689  foreach ($previewLanguages as $languageUid => $language) {
690  $previewLinks[$language] = $previewUrl . '&L=' . $languageUid;
691  }
692 
693  return $previewLinks;
694  }
695 
704  public function getLivePageUid($uid)
705  {
706  if (!isset($this->pageCache[$uid])) {
707  $pageRecord = BackendUtility::getRecord('pages', $uid);
708  if (is_array($pageRecord)) {
709  $this->pageCache[$uid] = $pageRecord['t3ver_oid'] ? $pageRecord['t3ver_oid'] : $uid;
710  } else {
711  throw new \InvalidArgumentException('uid is supposed to point to an existing page - given value was: ' . $uid, 1290628113);
712  }
713  }
714  return $this->pageCache[$uid];
715  }
716 
724  public function hasPageRecordVersions($workspace, $pageId)
725  {
726  $workspace = (int)$workspace;
727  $pageId = (int)$pageId;
728  if ($workspace === 0) {
729  return false;
730  }
731 
732  if (isset($this->versionsOnPageCache[$pageId][$workspace])) {
733  return $this->versionsOnPageCache[$pageId][$workspace];
734  }
735 
736  if (!empty($this->versionsOnPageCache)) {
737  return false;
738  }
739 
740  $this->versionsOnPageCache[$pageId][$workspace] = false;
741  foreach ($GLOBALS['TCA'] as $tableName => $tableConfiguration) {
742  if ($tableName === 'pages' || empty($tableConfiguration['ctrl']['versioningWS'])) {
743  continue;
744  }
745  // Consider records that are moved to a different page
746  $movePointer = new VersionState(VersionState::MOVE_POINTER);
747  $joinStatement = '(A.t3ver_oid=B.uid AND A.t3ver_state<>' . $movePointer
748  . ' OR A.t3ver_oid=B.t3ver_move_id AND A.t3ver_state=' . $movePointer . ')';
749 
750  // Select all records from this table in the database from the workspace
751  // This joins the online version with the offline version as tables A and B
752  $records = $this->getDatabaseConnection()->exec_SELECTgetRows(
753  'B.uid as live_uid, B.pid as live_pid, A.uid as offline_uid',
754  $tableName . ' A,' . $tableName . ' B',
755  'A.pid=-1 AND A.t3ver_wsid=' . $workspace . ' AND ' . $joinStatement .
756  BackendUtility::deleteClause($tableName, 'A') . BackendUtility::deleteClause($tableName, 'B'),
757  'live_pid'
758  );
759 
760  if (!empty($records)) {
761  foreach ($records as $record) {
762  $this->versionsOnPageCache[$record['live_pid']][$workspace] = true;
763  }
764  }
765  }
766 
767  return $this->versionsOnPageCache[$pageId][$workspace];
768  }
769 
773  protected function getDatabaseConnection()
774  {
775  return $GLOBALS['TYPO3_DB'];
776  }
777 
781  protected function getObjectManager()
782  {
783  return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
784  }
785 
792  public function getAvailableLanguages($pageId)
793  {
794  $languageOptions = [];
796  $translationConfigurationProvider = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider::class);
797  $systemLanguages = $translationConfigurationProvider->getSystemLanguages($pageId);
798 
799  if ($GLOBALS['BE_USER']->checkLanguageAccess(0)) {
800  // Use configured label for default language
801  $languageOptions[0] = $systemLanguages[0]['title'];
802  }
803  $pages = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $pageId);
804 
805  if (!is_array($pages)) {
806  return $languageOptions;
807  }
808 
809  foreach ($pages as $page) {
810  $languageId = (int)$page['sys_language_uid'];
811  // Only add links to active languages the user has access to
812  if (isset($systemLanguages[$languageId]) && $GLOBALS['BE_USER']->checkLanguageAccess($languageId)) {
813  $languageOptions[$page['sys_language_uid']] = $systemLanguages[$languageId]['title'];
814  }
815  }
816 
817  return $languageOptions;
818  }
819 }
getCmdArrayForPublishWS($wsid, $doSwap, $pageId=0, $language=null)
static viewSingleRecord($table, $uid, array $liveRecord=null, array $versionRecord=null)
static getPagesTSconfig($id, $rootLine=null, $returnPartArray=false)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
getCmdArrayForFlushWS($wsid, $flush=true, $pageId=0, $language=null)
getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage)
static getRecordsByField($theTable, $theField, $theValue, $whereClause='', $groupBy='', $orderBy='', $limit='', $useDeleteClause=true)
static workspaceOL($table, &$row, $wsid=-99, $unsetMovePointers=false)
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language=null)
static _GETset($inputGet, $key='')
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
selectVersionsInWorkspace($wsid, $filter=0, $stage=-99, $pageId=-1, $recursionLevel=0, $selectionType='tables_select', $language=null)
static viewOnClick($pageUid, $backPath='', $rootLine=null, $anchorSection='', $alternativeUrl='', $additionalGetVars='', $switchFocus=true)
$uid
Definition: server.php:38
static getLiveVersionOfRecord($table, $uid, $fields=' *')
static getMovePlaceholder($table, $uid, $fields=' *', $workspace=null)
static getRecord($table, $uid, $fields=' *', $where='', $useDeleteClause=true)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static deleteClause($table, $tableAlias='')