From 4e7937b4f272e21752a2f061d41d5e9c6fe0bf1e Mon Sep 17 00:00:00 2001 From: Benni Mack Date: Mon, 26 Feb 2018 07:02:27 +0100 Subject: [PATCH] [TASK] Move workspaces preview hook to PSR-15 middleware Frontend-related hooks are migrated into a PSR-15 compatible middleware to initialize workspace behaviour and previewing functionality. Resolves: #84040 Releases: master Change-Id: I8a86665aad95a84d4d65700949d861b96d006c13 Reviewed-on: https://review.typo3.org/55899 Tested-by: TYPO3com Reviewed-by: Susanne Moog Tested-by: Susanne Moog Reviewed-by: Daniel Gorges Tested-by: Daniel Gorges Reviewed-by: Benni Mack Tested-by: Benni Mack --- .../PreviewUserAuthentication.php | 30 ++ .../workspaces/Classes/Hook/PreviewHook.php | 171 +-------- .../Hook/TypoScriptFrontendControllerHook.php | 135 ------- .../Classes/Middleware/WorkspacePreview.php | 358 ++++++++++++++++++ .../Configuration/RequestMiddlewares.php | 21 + typo3/sysext/workspaces/ext_localconf.php | 4 - 6 files changed, 410 insertions(+), 309 deletions(-) delete mode 100644 typo3/sysext/workspaces/Classes/Hook/TypoScriptFrontendControllerHook.php create mode 100644 typo3/sysext/workspaces/Classes/Middleware/WorkspacePreview.php create mode 100644 typo3/sysext/workspaces/Configuration/RequestMiddlewares.php diff --git a/typo3/sysext/workspaces/Classes/Authentication/PreviewUserAuthentication.php b/typo3/sysext/workspaces/Classes/Authentication/PreviewUserAuthentication.php index a29c946e1482..2ddc33e96f76 100644 --- a/typo3/sysext/workspaces/Classes/Authentication/PreviewUserAuthentication.php +++ b/typo3/sysext/workspaces/Classes/Authentication/PreviewUserAuthentication.php @@ -107,4 +107,34 @@ public function calcPerms($row) { return Permission::PAGE_SHOW; } + + /** + * Stub to ensure that frontend editing is not possible as a preview user + * + * @return bool + */ + public function initializeFrontendEdit() + { + return false; + } + + /** + * Stub to ensure that frontend editing is not possible as a preview user + * + * @return bool + */ + public function isFrontendEditingActive() + { + return false; + } + + /** + * Stub to ensure that admin panel is not visible as a preview user + * + * @return bool + */ + public function isAdminPanelVisible() + { + return false; + } } diff --git a/typo3/sysext/workspaces/Classes/Hook/PreviewHook.php b/typo3/sysext/workspaces/Classes/Hook/PreviewHook.php index 3086344ae3ed..70a450509999 100644 --- a/typo3/sysext/workspaces/Classes/Hook/PreviewHook.php +++ b/typo3/sysext/workspaces/Classes/Hook/PreviewHook.php @@ -16,7 +16,6 @@ use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Workspaces\Authentication\PreviewUserAuthentication; /** * Hook for checking if the preview mode is activated @@ -24,174 +23,6 @@ */ class PreviewHook { - /** - * the GET parameter to be used - * - * @var string - */ - protected $previewKey = 'ADMCMD_prev'; - - /** - * preview configuration - * - * @var array - */ - protected $previewConfiguration = false; - - /** - * Hook after the regular BE user has been initialized - * if there is a preview configuration - * the BE user of the preview configuration gets initialized and - * is used instead for the current request, overriding any existing - * authenticated backend user. - * - * @param array $params holding the BE_USER object - * @param \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController $pObj - */ - public function initializePreviewUser(&$params, &$pObj) - { - $this->previewConfiguration = $this->getPreviewConfiguration(); - // if there is a valid BE user, and the full workspace should be previewed, the workspacePreview option should be set - $workspaceUid = (int)$this->previewConfiguration['fullWorkspace']; - if ($workspaceUid > 0) { - $previewUser = GeneralUtility::makeInstance(PreviewUserAuthentication::class); - $previewUser->setWebmounts([$pObj->id]); - if ($previewUser->setTemporaryWorkspace($workspaceUid)) { - $params['BE_USER'] = $previewUser; - $pObj->beUserLogin = true; - } else { - $params['BE_USER'] = null; - $pObj->beUserLogin = false; - } - } - - // If "ADMCMD_noBeUser" is set, then ensure that there is no workspace preview and no BE User logged in. - // This option is solely used to ensure that a be user can preview the live version of a page in the - // workspace preview module. - if (GeneralUtility::_GET('ADMCMD_noBeUser')) { - $params['BE_USER'] = null; - $pObj->beUserLogin = false; - // Caching is disabled, because otherwise generated URLs could include the ADMCMD_noBeUser parameter - $pObj->set_no_cache('GET Parameter ADMCMD_noBeUser was given', true); - } - } - - /** - * Looking for an ADMCMD_prev code, looks it up if found and returns configuration data. - * Background: From the backend a request to the frontend to show a page, possibly with - * workspace preview can be "recorded" and associated with a keyword. - * When the frontend is requested with this keyword the associated request parameters are - * restored from the database AND the backend user is loaded - only for that request. - * The main point is that a special URL valid for a limited time, - * eg. http://localhost/typo3site/index.php?ADMCMD_prev=035d9bf938bd23cb657735f68a8cedbf will - * open up for a preview that doesn't require login. Thus it's useful for sending in an email - * to someone without backend account. - * This can also be used to generate previews of hidden pages, start/endtimes, usergroups and - * those other settings from the Admin Panel - just not implemented yet. - * - * @throws \Exception - * @return array Preview configuration array from sys_preview record. - */ - public function getPreviewConfiguration() - { - $inputCode = $this->getPreviewInputCode(); - // If input code is available and shall not be ignored, look up the settings - if ($inputCode && $inputCode !== 'IGNORE') { - // "log out" - if ($inputCode === 'LOGOUT') { - setcookie($this->previewKey, '', 0, GeneralUtility::getIndpEnv('TYPO3_SITE_PATH')); - if ($GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate']) { - $templateFile = PATH_site . $GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate']; - if (@is_file($templateFile)) { - $message = file_get_contents($templateFile); - } else { - $message = 'ERROR!
Template File "' - . $GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate'] - . '" configured with $TYPO3_CONF_VARS["FE"]["workspacePreviewLogoutTemplate"] not found. Please contact webmaster about this problem.'; - } - } else { - $message = 'You logged out from Workspace preview mode. Click this link to go back to the website'; - } - $returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GET('returnUrl')); - die(sprintf($message, htmlspecialchars(preg_replace('/\\&?' . $this->previewKey . '=[[:alnum:]]+/', '', $returnUrl)))); - } - // Look for keyword configuration record: - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable('sys_preview'); - - $previewData = $queryBuilder - ->select('*') - ->from('sys_preview') - ->where( - $queryBuilder->expr()->eq( - 'keyword', - $queryBuilder->createNamedParameter($inputCode, \PDO::PARAM_STR) - ), - $queryBuilder->expr()->gt( - 'endtime', - $queryBuilder->createNamedParameter($GLOBALS['EXEC_TIME'], \PDO::PARAM_INT) - ) - ) - ->setMaxResults(1) - ->execute() - ->fetch(); - - // Get: Backend login status, Frontend login status - // - Make sure to remove fe/be cookies (temporarily); - // BE already done in ADMCMD_preview_postInit() - if (is_array($previewData)) { - if (empty(GeneralUtility::_POST())) { - // Unserialize configuration: - $previewConfig = unserialize($previewData['config']); - // For full workspace preview we only ADD a get variable - // to set the preview of the workspace - so all other Get - // vars are accepted. Hope this is not a security problem. - // Still posting is not allowed and even if a backend user - // get initialized it shouldn't lead to situations where - // users can use those credentials. - if ($previewConfig['fullWorkspace']) { - // If ADMCMD_prev is set the $inputCode value cannot come - // from a cookie and we set that cookie here. Next time it will - // be found from the cookie if ADMCMD_prev is not set again... - if (GeneralUtility::_GP($this->previewKey)) { - // Lifetime is 1 hour, does it matter much? - // Requires the user to click the link from their email again if it expires. - setcookie($this->previewKey, GeneralUtility::_GP($this->previewKey), 0, GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), null, null, true); - } - return $previewConfig; - } - if (GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '=' . $inputCode === GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) { - // Return preview keyword configuration - return $previewConfig; - } - // This check is to prevent people from setting additional - // GET vars via realurl or other URL path based ways of passing parameters. - throw new \Exception(htmlspecialchars('Request URL did not match "' - . GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '=' - . $inputCode . '"', 1294585190)); - } - throw new \Exception('POST requests are incompatible with keyword preview.', 1294585191); - } - throw new \Exception('ADMCMD command could not be executed! (No keyword configuration found)', 1294585192); - } - return false; - } - - /** - * returns the input code value from the admin command variable - * - * @return string Input code - */ - protected function getPreviewInputCode() - { - $inputCode = GeneralUtility::_GP($this->previewKey); - // If no inputcode and a cookie is set, load input code from cookie: - if (!$inputCode && $_COOKIE[$this->previewKey]) { - $inputCode = $_COOKIE[$this->previewKey]; - } - return $inputCode; - } - /** * Set preview keyword, eg: * $previewUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.$this->compilePreviewKeyword($GLOBALS['BE_USER']->user['uid'], 120); @@ -211,7 +42,7 @@ public function compilePreviewKeyword($backendUserUid, $ttl = 172800, $fullWorks 'keyword' => md5(uniqid(microtime(), true)), 'tstamp' => $GLOBALS['EXEC_TIME'], 'endtime' => $GLOBALS['EXEC_TIME'] + $ttl, - 'config' => serialize([ + 'config' => json_encode([ 'fullWorkspace' => $fullWorkspace, 'BEUSER_uid' => $backendUserUid ]) diff --git a/typo3/sysext/workspaces/Classes/Hook/TypoScriptFrontendControllerHook.php b/typo3/sysext/workspaces/Classes/Hook/TypoScriptFrontendControllerHook.php deleted file mode 100644 index edbce850646b..000000000000 --- a/typo3/sysext/workspaces/Classes/Hook/TypoScriptFrontendControllerHook.php +++ /dev/null @@ -1,135 +0,0 @@ -doWorkspacePreview()) { - return ''; - } - - if (empty($this->getBackendUserAuthentication()->getSessionData('workspaces.backend_domain'))) { - $backendDomain = \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'); - } else { - $backendDomain = $this->getBackendUserAuthentication()->getSessionData('workspaces.backend_domain'); - } - - $content = $pObj->cObj->cObjGetSingle('FLUIDTEMPLATE', [ - 'file' => 'EXT:workspaces/Resources/Private/Templates/Preview/Preview.html', - 'variables.' => [ - 'backendDomain' => 'TEXT', - 'backendDomain.' => ['value' => $backendDomain] - ] - ]); - - if (!isset($pObj->config['config']['disablePreviewNotification']) || (int)$pObj->config['config']['disablePreviewNotification'] !== 1) { - // get the title of the current workspace - $currentWorkspaceId = $pObj->whichWorkspace(); - $currentWorkspaceTitle = $this->getWorkspaceTitle($currentWorkspaceId); - $currentWorkspaceTitle = htmlspecialchars($currentWorkspaceTitle); - if ($pObj->config['config']['message_preview_workspace']) { - $content .= sprintf( - $pObj->config['config']['message_preview_workspace'], - $currentWorkspaceTitle, - $currentWorkspaceId ?? -99 - ); - } else { - $text = LocalizationUtility::translate( - 'LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:previewText', - 'workspaces', - [$currentWorkspaceTitle, $currentWorkspaceId ?? -99] - ); - if ($pObj->doWorkspacePreview()) { - $urlForStoppingPreview = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?ADMCMD_prev=LOGOUT&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')); - $text .= '
Stop preview'; - } - $styles = []; - $styles[] = 'position: fixed'; - $styles[] = 'top: 15px'; - $styles[] = 'right: 15px'; - $styles[] = 'padding: 8px 18px'; - $styles[] = 'background: #fff3cd'; - $styles[] = 'border: 1px solid #ffeeba'; - $styles[] = 'font-family: sans-serif'; - $styles[] = 'font-size: 14px'; - $styles[] = 'font-weight: bold'; - $styles[] = 'color: #856404'; - $styles[] = 'z-index: 20000'; - $styles[] = 'user-select: none'; - $styles[] = 'pointer-events:none'; - $styles[] = 'text-align: center'; - $styles[] = 'border-radius: 2px'; - $content .= '
' . $text . '
'; - } - } - return $content; - } - - /** - * Fetches the title of the workspace - * - * @param $workspaceId - * @return string the title of the workspace - */ - protected function getWorkspaceTitle(int $workspaceId): string - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable('sys_workspace'); - $title = $queryBuilder - ->select('title') - ->from('sys_workspace') - ->where( - $queryBuilder->expr()->eq( - 'uid', - $queryBuilder->createNamedParameter($workspaceId, \PDO::PARAM_INT) - ) - ) - ->execute() - ->fetchColumn(); - return $title !== false ? $title : ''; - } - - /** - * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication - */ - protected function getBackendUserAuthentication() - { - return $GLOBALS['BE_USER']; - } -} diff --git a/typo3/sysext/workspaces/Classes/Middleware/WorkspacePreview.php b/typo3/sysext/workspaces/Classes/Middleware/WorkspacePreview.php new file mode 100644 index 000000000000..bb8a8f17b833 --- /dev/null +++ b/typo3/sysext/workspaces/Classes/Middleware/WorkspacePreview.php @@ -0,0 +1,358 @@ +getPreviewInputCode($request); + if ($keyword) { + switch ($keyword) { + case 'IGNORE': + break; + case 'LOGOUT': + // "log out", and unset the cookie + $this->setCookie('', $request->getAttribute('normalizedParams')); + $message = $this->getLogoutTemplateMessage($request->getQueryParams()['returnUrl'] ?? ''); + return new HtmlResponse($message); + default: + // A keyword was found in a query parameter or in a cookie + // If the keyword is valid, activate a BE User and override any existing BE Users + $configuration = $this->getPreviewConfigurationFromRequest($request, $keyword); + if (is_array($configuration) && $configuration['fullWorkspace'] > 0) { + $previewUser = $this->initializePreviewUser( + (int)$configuration['fullWorkspace'], + $GLOBALS['TSFE']->id + ); + if ($previewUser) { + $GLOBALS['BE_USER'] = $previewUser; + $GLOBALS['TSFE']->beUserLogin = true; + } + } + } + } + + // If "ADMCMD_noBeUser" is set, then ensure that there is no workspace preview and no BE User logged in. + // This option is solely used to ensure that a be user can preview the live version of a page in the + // workspace preview module. + if ($request->getQueryParams()['ADMCMD_noBeUser']) { + $GLOBALS['BE_USER'] = null; + $GLOBALS['TSFE']->beUserLogin = false; + // Caching is disabled, because otherwise generated URLs could include the ADMCMD_noBeUser parameter + $GLOBALS['TSFE']->set_no_cache('GET Parameter ADMCMD_noBeUser was given', true); + } + + $response = $handler->handle($request); + + // Add a info box to the frontend content + if ($GLOBALS['TSFE']->doWorkspacePreview() && $GLOBALS['TSFE']->isOutputting()) { + $previewInfo = $this->renderPreviewInfo($GLOBALS['TSFE'], $request->getAttribute('normalizedParams')); + $body = $response->getBody(); + $body->rewind(); + $content = $body->getContents(); + $content = str_ireplace('', $previewInfo . '', $content); + $body = new Stream('php://temp', 'rw'); + $body->write($content); + $response = $response->withBody($body); + } + + return $response; + } + + /** + * Renders the logout template when the "logout" button was pressed. + * Returns a string which can be put into a HttpResponse. + * + * @param string $returnUrl + * @return string + */ + protected function getLogoutTemplateMessage(string $returnUrl = ''): string + { + if ($GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate']) { + $templateFile = GeneralUtility::getFileAbsFileName($GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate']); + if (@is_file($templateFile)) { + $message = file_get_contents($templateFile); + } else { + $message = 'ERROR!
Template File "' + . $GLOBALS['TYPO3_CONF_VARS']['FE']['workspacePreviewLogoutTemplate'] + . '" configured with $TYPO3_CONF_VARS["FE"]["workspacePreviewLogoutTemplate"] not found. Please contact webmaster about this problem.'; + } + } else { + $message = 'You logged out from Workspace preview mode. Click this link to go back to the website'; + } + $returnUrl = GeneralUtility::sanitizeLocalUrl($returnUrl); + $returnUrl = $this->removePreviewParameterFromUrl($returnUrl); + return sprintf($message, htmlspecialchars($returnUrl)); + } + + /** + * Looking for an ADMCMD_prev code, looks it up if found and returns configuration data. + * Background: From the backend a request to the frontend to show a page, possibly with + * workspace preview can be "recorded" and associated with a keyword. + * When the frontend is requested with this keyword the associated request parameters are + * restored from the database AND the backend user is loaded - only for that request. + * The main point is that a special URL valid for a limited time, + * eg. http://localhost/typo3site/index.php?ADMCMD_prev=035d9bf938bd23cb657735f68a8cedbf will + * open up for a preview that doesn't require login. Thus it's useful for sending in an email + * to someone without backend account. + * + * @param ServerRequestInterface $request + * @param string $inputCode + * @return array Preview configuration array from sys_preview record. + * @throws \Exception + */ + protected function getPreviewConfigurationFromRequest(ServerRequestInterface $request, string $inputCode) + { + $previewData = $this->getPreviewData($inputCode); + if (!is_array($previewData)) { + throw new \Exception('ADMCMD command could not be executed! (No keyword configuration found)', 1294585192); + } + if ($request->getMethod() === 'POST') { + throw new \Exception('POST requests are incompatible with keyword preview.', 1294585191); + } + // Validate configuration + $previewConfig = json_decode($previewData['config'], true); + if (!$previewConfig['fullWorkspace']) { + throw new \Exception('Preview configuration did not include a workspace preview', 1294585190); + } + // If the GET parameter ADMCMD_prev is set, then a cookie is set for the next request + if ($request->getQueryParams()[$this->previewKey] ?? false) { + $this->setCookie($inputCode, $request->getAttribute('normalizedParams')); + } + return $previewConfig; + } + + /** + * Creates a preview user and sets the workspace ID and the current page ID (for accessing the page) + * + * @param int $workspaceUid the workspace ID to set + * @param mixed $requestedPageId pageID or alias to the current page + * @return PreviewUserAuthentication|bool if the set up of the workspace was successful, the user is returned. + */ + protected function initializePreviewUser(int $workspaceUid, $requestedPageId) + { + if ($workspaceUid > 0) { + $previewUser = GeneralUtility::makeInstance(PreviewUserAuthentication::class); + $previewUser->setWebmounts([$requestedPageId]); + if ($previewUser->setTemporaryWorkspace($workspaceUid)) { + return $previewUser; + } + } + return false; + } + + /** + * Sets a cookie for logging in a preview user + * + * @param string $inputCode + * @param NormalizedParams $normalizedParams + */ + protected function setCookie(string $inputCode, NormalizedParams $normalizedParams) + { + setcookie($this->previewKey, $inputCode, 0, $normalizedParams->getSitePath(), '', true, true); + } + + /** + * Returns the input code value from the admin command variable + * If no inputcode and a cookie is set, load input code from cookie + * + * @param ServerRequestInterface $request + * @return string keyword + */ + protected function getPreviewInputCode(ServerRequestInterface $request): string + { + return $request->getQueryParams()[$this->previewKey] ?? $request->getCookieParams()[$this->previewKey] ?? ''; + } + + /** + * Look for keyword configuration record in the database, but check if the keyword has expired already + * + * @param string $keyword + * @return mixed array of the result set or null + */ + protected function getPreviewData(string $keyword) + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_preview'); + return $queryBuilder + ->select('*') + ->from('sys_preview') + ->where( + $queryBuilder->expr()->eq( + 'keyword', + $queryBuilder->createNamedParameter($keyword) + ), + $queryBuilder->expr()->gt( + 'endtime', + $queryBuilder->createNamedParameter($GLOBALS['EXEC_TIME'], \PDO::PARAM_INT) + ) + ) + ->setMaxResults(1) + ->execute() + ->fetch(); + } + + /** + * Code regarding adding a custom preview message, when previewing a workspace + */ + + /** + * Renders a message at the bottom of the HTML page, can be modified via + * + * config.disablePreviewNotification = 1 (to disable the additional info text) + * + * and + * + * config.message_preview_workspace = This is not the online version but the version of "%s" workspace (ID: %s). + * + * via TypoScript. + * + * @param TypoScriptFrontendController $tsfe + * @param NormalizedParams $normalizedParams + * @return string + */ + protected function renderPreviewInfo(TypoScriptFrontendController $tsfe, NormalizedParams $normalizedParams): string + { + $backendDomain = $GLOBALS['BE_USER']->getSessionData('workspaces.backend_domain') ?: $normalizedParams->getRequestHostOnly(); + + $content = $tsfe->cObj->cObjGetSingle('FLUIDTEMPLATE', [ + 'file' => 'EXT:workspaces/Resources/Private/Templates/Preview/Preview.html', + 'variables.' => [ + 'backendDomain' => 'TEXT', + 'backendDomain.' => ['value' => $backendDomain] + ] + ]); + + if (!isset($tsfe->config['config']['disablePreviewNotification']) || (int)$tsfe->config['config']['disablePreviewNotification'] !== 1) { + // get the title of the current workspace + $currentWorkspaceId = $tsfe->whichWorkspace(); + $currentWorkspaceTitle = $this->getWorkspaceTitle($currentWorkspaceId); + $currentWorkspaceTitle = htmlspecialchars($currentWorkspaceTitle); + if ($tsfe->config['config']['message_preview_workspace']) { + $content .= sprintf( + $tsfe->config['config']['message_preview_workspace'], + $currentWorkspaceTitle, + $currentWorkspaceId ?? -99 + ); + } else { + $text = LocalizationUtility::translate( + 'LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:previewText', + 'workspaces', + [$currentWorkspaceTitle, $currentWorkspaceId ?? -99] + ); + $text = htmlspecialchars($text); + if ($GLOBALS['BE_USER'] instanceof PreviewUserAuthentication) { + $url = $this->removePreviewParameterFromUrl($normalizedParams->getRequestUri()); + $urlForStoppingPreview = $normalizedParams->getSiteUrl() . 'index.php?returnUrl=' . rawurlencode($url) . '&ADMCMD_prev=LOGOUT'; + $text .= '
Stop preview'; + } + $styles = []; + $styles[] = 'position: fixed'; + $styles[] = 'top: 15px'; + $styles[] = 'right: 15px'; + $styles[] = 'padding: 8px 18px'; + $styles[] = 'background: #fff3cd'; + $styles[] = 'border: 1px solid #ffeeba'; + $styles[] = 'font-family: sans-serif'; + $styles[] = 'font-size: 14px'; + $styles[] = 'font-weight: bold'; + $styles[] = 'color: #856404'; + $styles[] = 'z-index: 20000'; + $styles[] = 'user-select: none'; + $styles[] = 'pointer-events: none'; + $styles[] = 'text-align: center'; + $styles[] = 'border-radius: 2px'; + $content .= '
' . $text . '
'; + } + } + return $content; + } + + /** + * Fetches the title of the workspace + * + * @param $workspaceId + * @return string the title of the workspace + */ + protected function getWorkspaceTitle(int $workspaceId): string + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_workspace'); + $title = $queryBuilder + ->select('title') + ->from('sys_workspace') + ->where( + $queryBuilder->expr()->eq( + 'uid', + $queryBuilder->createNamedParameter($workspaceId, \PDO::PARAM_INT) + ) + ) + ->execute() + ->fetchColumn(); + return $title !== false ? $title : ''; + } + + /** + * Used for generating URLs (e.g. in logout page) without the existing ADMCMD_prev keyword as GET variable + * + * @param string $url + * @return string + */ + protected function removePreviewParameterFromUrl(string $url): string + { + return (string)preg_replace('/\\&?' . $this->previewKey . '=[[:alnum:]]+/', '', $url); + } +} diff --git a/typo3/sysext/workspaces/Configuration/RequestMiddlewares.php b/typo3/sysext/workspaces/Configuration/RequestMiddlewares.php new file mode 100644 index 000000000000..37b37c8ff968 --- /dev/null +++ b/typo3/sysext/workspaces/Configuration/RequestMiddlewares.php @@ -0,0 +1,21 @@ + [ + 'typo3/cms-workspaces/preview' => [ + 'target' => \TYPO3\CMS\Workspaces\Middleware\WorkspacePreview::class, + 'after' => [ + 'typo3/cms-core/normalized-params-attribute', + // TSFE is needed to store information about the preview + 'typo3/cms-frontend/tsfe', + // A preview user will override an existing logged-in backend user + 'typo3/cms-frontend/backend-user-authentication', + // Ensure, when a preview text is added, that the content length headers are added later-on + 'typo3/cms-frontend/content-length-headers', + 'typo3/cms-frontend/output-compression' + ] + ], + ] +]; diff --git a/typo3/sysext/workspaces/ext_localconf.php b/typo3/sysext/workspaces/ext_localconf.php index 677cff6f2dcc..c115222b6c21 100644 --- a/typo3/sysext/workspaces/ext_localconf.php +++ b/typo3/sysext/workspaces/ext_localconf.php @@ -26,12 +26,8 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['workspaces'] = \TYPO3\CMS\Workspaces\Hook\DataHandlerHook::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass']['version'] = \TYPO3\CMS\Workspaces\Hook\DataHandlerHook::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass']['workspaces'] = \TYPO3\CMS\Workspaces\Hook\BackendUtilityHook::class; -$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_previewInfo']['workspaces'] = \TYPO3\CMS\Workspaces\Hook\TypoScriptFrontendControllerHook::class . '->renderPreviewInfo'; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck']['workspaces'] = \TYPO3\CMS\Workspaces\Hook\BackendUtilityHook::class . '->makeEditForm_accessCheck'; -// Register hook to check for the preview mode in the FE -$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['postBeUser']['version_preview'] = \TYPO3\CMS\Workspaces\Hook\PreviewHook::class . '->initializePreviewUser'; - // Register workspaces cache if not already done in localconf.php or a previously loaded extension. if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['workspaces_cache'] ?? false)) { $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['workspaces_cache'] = [