TYPO3 CMS  TYPO3_8-7
PreviewHook.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 
24 
30 {
36  protected $previewKey = 'ADMCMD_prev';
37 
43  protected $tsfeObj;
44 
50  protected $previewConfiguration = false;
51 
58  protected $forceReadPermissions = false;
59 
67  public function checkForPreview($params, &$pObj)
68  {
69  $this->tsfeObj = $pObj;
70  $this->previewConfiguration = $this->getPreviewConfiguration();
71  if (is_array($this->previewConfiguration)) {
72  // In case of a keyword-authenticated preview,
73  // re-initialize the TSFE object:
74  // because the GET variables are taken from the preview
75  // configuration
76  $this->tsfeObj = GeneralUtility::makeInstance(
77  \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::class,
78  null,
79  GeneralUtility::_GP('id'),
80  GeneralUtility::_GP('type'),
81  GeneralUtility::_GP('no_cache'),
82  GeneralUtility::_GP('cHash'),
83  null,
84  GeneralUtility::_GP('MP'),
85  GeneralUtility::_GP('RDCT')
86  );
87  $GLOBALS['TSFE'] = $this->tsfeObj;
88  // Configuration after initialization of TSFE object.
89  // Basically this unsets the BE cookie if any and forces
90  // the BE user set according to the preview configuration.
91  // @previouslyknownas TSFE->ADMCMD_preview_postInit
92  // Clear cookies:
93  unset($_COOKIE['be_typo_user']);
94  }
95  }
96 
105  public function initializePreviewUser(&$params, &$pObj)
106  {
107  // if there is a valid BE user, and the full workspace should be previewed, the workspacePreview option should be set
108  $workspaceUid = $this->previewConfiguration['fullWorkspace'];
109  $workspaceRecord = null;
110  if ((is_null($params['BE_USER']) || $params['BE_USER'] === false) && $this->previewConfiguration !== false && $this->previewConfiguration['BEUSER_uid'] > 0) {
111  // First initialize a temp user object and resolve usergroup information
113  $tempBackendUser = $this->createFrontendBackendUser();
114  $tempBackendUser->userTS_dontGetCached = 1;
115  $tempBackendUser->setBeUserByUid($this->previewConfiguration['BEUSER_uid']);
116  if ($tempBackendUser->user['uid']) {
117  $tempBackendUser->unpack_uc();
118  $tempBackendUser->setTemporaryWorkspace($workspaceUid);
119  $tempBackendUser->user['workspace_id'] = $workspaceUid;
120  $tempBackendUser->fetchGroupData();
121  // Handle degradation of admin users
122  if ($tempBackendUser->isAdmin()) {
123  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
124  ->getQueryBuilderForTable('sys_workspace');
125 
126  $queryBuilder->getRestrictions()
127  ->removeAll()
128  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
129  ->add(GeneralUtility::makeInstance(RootLevelRestriction::class));
130 
131  $workspaceRecord = $queryBuilder
132  ->select('uid', 'adminusers', 'reviewers', 'members', 'db_mountpoints')
133  ->from('sys_workspace')
134  ->where(
135  $queryBuilder->expr()->eq(
136  'uid',
137  $queryBuilder->createNamedParameter($workspaceUid, \PDO::PARAM_INT)
138  )
139  )
140  ->execute()
141  ->fetch();
142 
143  // Either use configured workspace mount (of the workspace) or current page id
144  if (empty($tempBackendUser->groupData['webmounts'])) {
145  $tempBackendUser->groupData['webmounts'] = !empty($workspaceRecord['db_mountpoints']) ? $workspaceRecord['db_mountpoints'] : $pObj->id;
146  }
147  // Force add degraded admin user as member of this workspace
148  $workspaceRecord['members'] = 'be_users_' . $this->previewConfiguration['BEUSER_uid'];
149  // Force read permission for degraded admin user
150  $this->forceReadPermissions = true;
151  }
152  // Store only needed information in the real simulate backend
153  $BE_USER = $this->createFrontendBackendUser();
154  $BE_USER->userTS_dontGetCached = 1;
155  $BE_USER->user = $tempBackendUser->user;
156  $BE_USER->user['admin'] = 0;
157  $BE_USER->groupData['webmounts'] = $tempBackendUser->groupData['webmounts'];
158  $BE_USER->groupList = $tempBackendUser->groupList;
159  $BE_USER->userGroups = $tempBackendUser->userGroups;
160  $BE_USER->userGroupsUID = $tempBackendUser->userGroupsUID;
161  $pObj->beUserLogin = true;
162  } else {
163  $BE_USER = null;
164  $pObj->beUserLogin = false;
165  }
166  unset($tempBackendUser);
167  $params['BE_USER'] = $BE_USER;
168  }
169  if ($pObj->beUserLogin
170  && is_object($params['BE_USER'])
172  ) {
173  if ($workspaceUid == 0
174  || $workspaceUid >= -1
175  && $params['BE_USER']->checkWorkspace($workspaceRecord ?: $workspaceUid)
176  && $params['BE_USER']->isInWebMount($pObj->id)
177  ) {
178  // Check Access to workspace. Live (0) is OK to preview for all.
179  $pObj->workspacePreview = (int)$workspaceUid;
180  } else {
181  // No preview, will default to "Live" at the moment
182  $pObj->workspacePreview = -99;
183  }
184  }
185  }
186 
196  public function overridePagePermissionClause(array $parameters)
197  {
198  $clause = $parameters['currentClause'];
199  if ($parameters['perms'] & 1 && $this->forceReadPermissions) {
200  $clause = ' 1=1';
201  }
202  return $clause;
203  }
204 
214  public function overridePermissionCalculation(array $parameters)
215  {
216  $permissions = $parameters['outputPermissions'];
217  if (!($permissions & Permission::PAGE_SHOW) && $this->forceReadPermissions) {
218  $permissions |= Permission::PAGE_SHOW;
219  }
220  return $permissions;
221  }
222 
239  public function getPreviewConfiguration()
240  {
241  $inputCode = $this->getPreviewInputCode();
242  // If input code is available and shall not be ignored, look up the settings
243  if ($inputCode && $inputCode !== 'IGNORE') {
244  // "log out"
245  if ($inputCode === 'LOGOUT') {
246  setcookie($this->previewKey, '', 0, GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
247  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate']) {
248  $templateFile = PATH_site . $GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate'];
249  if (@is_file($templateFile)) {
250  $message = file_get_contents($templateFile);
251  } else {
252  $message = '<strong>ERROR!</strong><br>Template File "'
253  . $GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate']
254  . '" configured with $TYPO3_CONF_VARS["FE"]["workspacePreviewLogoutTemplate"] not found. Please contact webmaster about this problem.';
255  }
256  } else {
257  $message = 'You logged out from Workspace preview mode. Click this link to <a href="%1$s">go back to the website</a>';
258  }
259  $returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GET('returnUrl'));
260  die(sprintf($message, htmlspecialchars(preg_replace('/\\&?' . $this->previewKey . '=[[:alnum:]]+/', '', $returnUrl))));
261  }
262  // Look for keyword configuration record:
263  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
264  ->getQueryBuilderForTable('sys_preview');
265 
266  $previewData = $queryBuilder
267  ->select('*')
268  ->from('sys_preview')
269  ->where(
270  $queryBuilder->expr()->eq(
271  'keyword',
272  $queryBuilder->createNamedParameter($inputCode, \PDO::PARAM_STR)
273  ),
274  $queryBuilder->expr()->gt(
275  'endtime',
276  $queryBuilder->createNamedParameter($GLOBALS['EXEC_TIME'], \PDO::PARAM_INT)
277  )
278  )
279  ->setMaxResults(1)
280  ->execute()
281  ->fetch();
282 
283  // Get: Backend login status, Frontend login status
284  // - Make sure to remove fe/be cookies (temporarily);
285  // BE already done in ADMCMD_preview_postInit()
286  if (is_array($previewData)) {
287  if (empty(GeneralUtility::_POST())) {
288  // Unserialize configuration:
289  $previewConfig = unserialize($previewData['config']);
290  // For full workspace preview we only ADD a get variable
291  // to set the preview of the workspace - so all other Get
292  // vars are accepted. Hope this is not a security problem.
293  // Still posting is not allowed and even if a backend user
294  // get initialized it shouldn't lead to situations where
295  // users can use those credentials.
296  if ($previewConfig['fullWorkspace']) {
297  // Set the workspace preview value:
298  GeneralUtility::_GETset($previewConfig['fullWorkspace'], 'ADMCMD_previewWS');
299  // If ADMCMD_prev is set the $inputCode value cannot come
300  // from a cookie and we set that cookie here. Next time it will
301  // be found from the cookie if ADMCMD_prev is not set again...
302  if (GeneralUtility::_GP($this->previewKey)) {
303  // Lifetime is 1 hour, does it matter much?
304  // Requires the user to click the link from their email again if it expires.
305  setcookie($this->previewKey, GeneralUtility::_GP($this->previewKey), 0, GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), null, null, true);
306  }
307  return $previewConfig;
308  }
309  if (GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '=' . $inputCode === GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
310  // Set GET variables
311  $GET_VARS = '';
312  parse_str($previewConfig['getVars'], $GET_VARS);
313  GeneralUtility::_GETset($GET_VARS);
314  // Return preview keyword configuration
315  return $previewConfig;
316  }
317  // This check is to prevent people from setting additional
318  // GET vars via realurl or other URL path based ways of passing parameters.
319  throw new \Exception(htmlspecialchars('Request URL did not match "'
320  . GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '='
321  . $inputCode . '"', 1294585190));
322  }
323  throw new \Exception('POST requests are incompatible with keyword preview.', 1294585191);
324  }
325  throw new \Exception('ADMCMD command could not be executed! (No keyword configuration found)', 1294585192);
326  }
327  return false;
328  }
329 
335  protected function getPreviewInputCode()
336  {
337  $inputCode = GeneralUtility::_GP($this->previewKey);
338  // If no inputcode and a cookie is set, load input code from cookie:
339  if (!$inputCode && $_COOKIE[$this->previewKey]) {
340  $inputCode = $_COOKIE[$this->previewKey];
341  }
342  return $inputCode;
343  }
344 
359  public function compilePreviewKeyword($getVarsStr, $backendUserUid, $ttl = 172800, $fullWorkspace = null)
360  {
361  $fieldData = [
362  'keyword' => md5(uniqid(microtime(), true)),
363  'tstamp' => $GLOBALS['EXEC_TIME'],
364  'endtime' => $GLOBALS['EXEC_TIME'] + $ttl,
365  'config' => serialize([
366  'fullWorkspace' => $fullWorkspace,
367  'getVars' => $getVarsStr,
368  'BEUSER_uid' => $backendUserUid
369  ])
370  ];
371  GeneralUtility::makeInstance(ConnectionPool::class)
372  ->getConnectionForTable('sys_preview')
373  ->insert(
374  'sys_preview',
375  $fieldData
376  );
377 
378  return $fieldData['keyword'];
379  }
380 
388  public function getPreviewLinkLifetime()
389  {
390  $ttlHours = (int)$GLOBALS['BE_USER']->getTSConfigVal('options.workspaces.previewLinkTTLHours');
391  return $ttlHours ? $ttlHours : 24 * 2;
392  }
393 
397  protected function createFrontendBackendUser()
398  {
400  FrontendBackendUserAuthentication::class
401  );
402  }
403 }
overridePermissionCalculation(array $parameters)
overridePagePermissionClause(array $parameters)
compilePreviewKeyword($getVarsStr, $backendUserUid, $ttl=172800, $fullWorkspace=null)
static makeInstance($className,... $constructorArguments)
static _GETset($inputGet, $key='')
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']