TYPO3 CMS  TYPO3_7-6
PreviewHook.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Version\Hook;
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 
23 
29 {
35  protected $previewKey = 'ADMCMD_prev';
36 
42  protected $tsfeObj;
43 
49  protected $previewConfiguration = false;
50 
57  protected $forceReadPermissions = false;
58 
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  $GLOBALS['TYPO3_CONF_VARS'],
79  GeneralUtility::_GP('id'),
80  GeneralUtility::_GP('type'),
81  GeneralUtility::_GP('no_cache'),
82  GeneralUtility::_GP('cHash'),
83  GeneralUtility::_GP('jumpurl'),
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 
106  public function initializePreviewUser(&$params, &$pObj)
107  {
108  // if there is a valid BE user, and the full workspace should be previewed, the workspacePreview option should be set
109  $workspaceUid = $this->previewConfiguration['fullWorkspace'];
110  $workspaceRecord = null;
111  if ((is_null($params['BE_USER']) || $params['BE_USER'] === false) && $this->previewConfiguration !== false && $this->previewConfiguration['BEUSER_uid'] > 0) {
112  // First initialize a temp user object and resolve usergroup information
114  $tempBackendUser = $this->createFrontendBackendUser();
115  $tempBackendUser->userTS_dontGetCached = 1;
116  $tempBackendUser->setBeUserByUid($this->previewConfiguration['BEUSER_uid']);
117  if ($tempBackendUser->user['uid']) {
118  $tempBackendUser->unpack_uc('');
119  $tempBackendUser->fetchGroupData();
120  // Handle degradation of admin users
121  if ($tempBackendUser->isAdmin() && ExtensionManagementUtility::isLoaded('workspaces')) {
122  $workspaceRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
123  'uid, adminusers, reviewers, members, db_mountpoints',
124  'sys_workspace',
125  'pid=0 AND uid=' . (int)$workspaceUid . BackendUtility::deleteClause('sys_workspace')
126  );
127  // Either use configured workspace mount or current page id, if admin user does not have any page mounts
128  if (empty($tempBackendUser->groupData['webmounts'])) {
129  $tempBackendUser->groupData['webmounts'] = !empty($workspaceRecord['db_mountpoints']) ? $workspaceRecord['db_mountpoints'] : $pObj->id;
130  }
131  // Force add degraded admin user as member of this workspace
132  $workspaceRecord['members'] = 'be_users_' . $this->previewConfiguration['BEUSER_uid'];
133  // Force read permission for degraded admin user
134  $this->forceReadPermissions = true;
135  }
136  // Store only needed information in the real simulate backend
137  $BE_USER = $this->createFrontendBackendUser();
138  $BE_USER->userTS_dontGetCached = 1;
139  $BE_USER->user = $tempBackendUser->user;
140  $BE_USER->user['admin'] = 0;
141  $BE_USER->groupData['webmounts'] = $tempBackendUser->groupData['webmounts'];
142  $BE_USER->groupList = $tempBackendUser->groupList;
143  $BE_USER->userGroups = $tempBackendUser->userGroups;
144  $BE_USER->userGroupsUID = $tempBackendUser->userGroupsUID;
145  $pObj->beUserLogin = true;
146  } else {
147  $BE_USER = null;
148  $pObj->beUserLogin = false;
149  }
150  unset($tempBackendUser);
151  $params['BE_USER'] = $BE_USER;
152  }
153  if ($pObj->beUserLogin
154  && is_object($params['BE_USER'])
156  ) {
157  if ($workspaceUid == 0
158  || $workspaceUid >= -1
159  && $params['BE_USER']->checkWorkspace($workspaceRecord ?: $workspaceUid)
160  && $params['BE_USER']->isInWebMount($pObj->id)
161  ) {
162  // Check Access to workspace. Live (0) is OK to preview for all.
163  $pObj->workspacePreview = (int)$workspaceUid;
164  } else {
165  // No preview, will default to "Live" at the moment
166  $pObj->workspacePreview = -99;
167  }
168  }
169  }
170 
180  public function overridePagePermissionClause(array $parameters)
181  {
182  $clause = $parameters['currentClause'];
183  if ($parameters['perms'] & 1 && $this->forceReadPermissions) {
184  $clause = ' 1=1';
185  }
186  return $clause;
187  }
188 
198  public function overridePermissionCalculation(array $parameters)
199  {
200  $permissions = $parameters['outputPermissions'];
201  if (!($permissions & Permission::PAGE_SHOW) && $this->forceReadPermissions) {
202  $permissions |= Permission::PAGE_SHOW;
203  }
204  return $permissions;
205  }
206 
223  public function getPreviewConfiguration()
224  {
225  $inputCode = $this->getPreviewInputCode();
226  // If input code is available and shall not be ignored, look up the settings
227  if ($inputCode && $inputCode !== 'IGNORE') {
228  // "log out"
229  if ($inputCode == 'LOGOUT') {
230  setcookie($this->previewKey, '', 0, GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
231  if ($this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate']) {
232  $templateFile = PATH_site . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate'];
233  if (@is_file($templateFile)) {
234  $message = GeneralUtility::getUrl(PATH_site . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate']);
235  } else {
236  $message = '<strong>ERROR!</strong><br>Template File "'
237  . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate']
238  . '" configured with $TYPO3_CONF_VARS["FE"]["workspacePreviewLogoutTemplate"] not found. Please contact webmaster about this problem.';
239  }
240  } else {
241  $message = 'You logged out from Workspace preview mode. Click this link to <a href="%1$s">go back to the website</a>';
242  }
243  $returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GET('returnUrl'));
244  die(sprintf($message, htmlspecialchars(preg_replace('/\\&?' . $this->previewKey . '=[[:alnum:]]+/', '', $returnUrl))));
245  }
246  // Look for keyword configuration record:
247  $where = 'keyword=' . $this->getDatabaseConnection()->fullQuoteStr($inputCode, 'sys_preview') . ' AND endtime>' . $GLOBALS['EXEC_TIME'];
248  $previewData = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'sys_preview', $where);
249  // Get: Backend login status, Frontend login status
250  // - Make sure to remove fe/be cookies (temporarily);
251  // BE already done in ADMCMD_preview_postInit()
252  if (is_array($previewData)) {
253  if (empty(GeneralUtility::_POST())) {
254  // Unserialize configuration:
255  $previewConfig = unserialize($previewData['config']);
256  // For full workspace preview we only ADD a get variable
257  // to set the preview of the workspace - so all other Get
258  // vars are accepted. Hope this is not a security problem.
259  // Still posting is not allowed and even if a backend user
260  // get initialized it shouldn't lead to situations where
261  // users can use those credentials.
262  if ($previewConfig['fullWorkspace']) {
263  // Set the workspace preview value:
264  GeneralUtility::_GETset($previewConfig['fullWorkspace'], 'ADMCMD_previewWS');
265  // If ADMCMD_prev is set the $inputCode value cannot come
266  // from a cookie and we set that cookie here. Next time it will
267  // be found from the cookie if ADMCMD_prev is not set again...
268  if (GeneralUtility::_GP($this->previewKey)) {
269  // Lifetime is 1 hour, does it matter much?
270  // Requires the user to click the link from their email again if it expires.
271  setcookie($this->previewKey, GeneralUtility::_GP($this->previewKey), 0, GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
272  }
273  return $previewConfig;
274  } elseif (GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '=' . $inputCode === GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
275  // Set GET variables
276  $GET_VARS = '';
277  parse_str($previewConfig['getVars'], $GET_VARS);
278  GeneralUtility::_GETset($GET_VARS);
279  // Return preview keyword configuration
280  return $previewConfig;
281  } else {
282  // This check is to prevent people from setting additional
283  // GET vars via realurl or other URL path based ways of passing parameters.
284  throw new \Exception(htmlspecialchars('Request URL did not match "'
285  . GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '='
286  . $inputCode . '"', 1294585190));
287  }
288  } else {
289  throw new \Exception('POST requests are incompatible with keyword preview.', 1294585191);
290  }
291  } else {
292  throw new \Exception('ADMCMD command could not be executed! (No keyword configuration found)', 1294585192);
293  }
294  }
295  return false;
296  }
297 
303  protected function getPreviewInputCode()
304  {
305  $inputCode = GeneralUtility::_GP($this->previewKey);
306  // If no inputcode and a cookie is set, load input code from cookie:
307  if (!$inputCode && $_COOKIE[$this->previewKey]) {
308  $inputCode = $_COOKIE[$this->previewKey];
309  }
310  return $inputCode;
311  }
312 
327  public function compilePreviewKeyword($getVarsStr, $backendUserUid, $ttl = 172800, $fullWorkspace = null)
328  {
329  $fieldData = [
330  'keyword' => md5(uniqid(microtime(), true)),
331  'tstamp' => $GLOBALS['EXEC_TIME'],
332  'endtime' => $GLOBALS['EXEC_TIME'] + $ttl,
333  'config' => serialize([
334  'fullWorkspace' => $fullWorkspace,
335  'getVars' => $getVarsStr,
336  'BEUSER_uid' => $backendUserUid
337  ])
338  ];
339  $this->getDatabaseConnection()->exec_INSERTquery('sys_preview', $fieldData);
340  return $fieldData['keyword'];
341  }
342 
350  public function getPreviewLinkLifetime()
351  {
352  $ttlHours = (int)$GLOBALS['BE_USER']->getTSConfigVal('options.workspaces.previewLinkTTLHours');
353  return $ttlHours ? $ttlHours : 24 * 2;
354  }
355 
359  protected function getDatabaseConnection()
360  {
361  return $GLOBALS['TYPO3_DB'];
362  }
363 
367  protected function createFrontendBackendUser()
368  {
370  FrontendBackendUserAuthentication::class
371  );
372  }
373 }
overridePagePermissionClause(array $parameters)
compilePreviewKeyword($getVarsStr, $backendUserUid, $ttl=172800, $fullWorkspace=null)
static _GETset($inputGet, $key='')
overridePermissionCalculation(array $parameters)
static getUrl($url, $includeHeader=0, $requestHeaders=false, &$report=null)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static deleteClause($table, $tableAlias='')