[TASK] Clean up workspace preview top bar 31/55931/7
authorBenni Mack <benni@typo3.org>
Tue, 27 Feb 2018 20:11:20 +0000 (21:11 +0100)
committerBenni Mack <benni@typo3.org>
Thu, 1 Mar 2018 06:05:17 +0000 (07:05 +0100)
The preview controller is now not attached to the backend module anymore,
as the Preview functionality ("Split Bar on top") is technically a completely
separate backend preview and completely unrelated to the workspaces
Backend Module.

This change also removes several hacks to generate Backend URLs.

Resolves: #84074
Releases: master
Change-Id: Id0ce7093f6d3ac94c8e4a9cfecd78027d26d45df
Reviewed-on: https://review.typo3.org/55931
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Daniel Gorges <daniel.gorges@b13.de>
Tested-by: Daniel Gorges <daniel.gorges@b13.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
17 files changed:
typo3/sysext/workspaces/Classes/Controller/AbstractController.php [deleted file]
typo3/sysext/workspaces/Classes/Controller/AjaxController.php
typo3/sysext/workspaces/Classes/Controller/PreviewController.php
typo3/sysext/workspaces/Classes/Controller/ReviewController.php
typo3/sysext/workspaces/Classes/Hook/BackendUtilityHook.php
typo3/sysext/workspaces/Classes/Middleware/WorkspacePreview.php
typo3/sysext/workspaces/Classes/Service/WorkspaceService.php
typo3/sysext/workspaces/Configuration/Backend/Routes.php [new file with mode: 0644]
typo3/sysext/workspaces/Resources/Private/Layouts/Empty.html [deleted file]
typo3/sysext/workspaces/Resources/Private/Layouts/Popup.html [deleted file]
typo3/sysext/workspaces/Resources/Private/Partials/Legend.html
typo3/sysext/workspaces/Resources/Private/Partials/Preview/StageButtons.html
typo3/sysext/workspaces/Resources/Private/Templates/Preview/Help.html [deleted file]
typo3/sysext/workspaces/Resources/Private/Templates/Preview/Index.html
typo3/sysext/workspaces/Resources/Private/Templates/Preview/NewPage.html [deleted file]
typo3/sysext/workspaces/Resources/Private/Templates/Preview/Preview.html [deleted file]
typo3/sysext/workspaces/ext_tables.php

diff --git a/typo3/sysext/workspaces/Classes/Controller/AbstractController.php b/typo3/sysext/workspaces/Classes/Controller/AbstractController.php
deleted file mode 100644 (file)
index 40eb444..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-<?php
-namespace TYPO3\CMS\Workspaces\Controller;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Backend\View\BackendTemplateView;
-use TYPO3\CMS\Core\Imaging\Icon;
-use TYPO3\CMS\Core\Imaging\IconFactory;
-use TYPO3\CMS\Core\Page\PageRenderer;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
-use TYPO3\CMS\Workspaces\Service\AdditionalColumnService;
-use TYPO3\CMS\Workspaces\Service\AdditionalResourceService;
-
-/**
- * Abstract action controller.
- */
-class AbstractController extends ActionController
-{
-    /**
-     * @var string
-     */
-    protected $defaultViewObjectName = BackendTemplateView::class;
-
-    /**
-     * @var BackendTemplateView
-     */
-    protected $view;
-
-    /**
-     * @var string Key of the extension this controller belongs to
-     */
-    protected $extensionName = 'Workspaces';
-
-    /**
-     * @var PageRenderer
-     */
-    protected $pageRenderer;
-
-    /**
-     * @var int
-     */
-    protected $pageId;
-
-    /**
-     * Initializes the controller before invoking an action method.
-     */
-    protected function initializeAction()
-    {
-        $this->pageRenderer = $this->getPageRenderer();
-        // @todo Evaluate how the (int) typecast can be used with Extbase validators/filters
-        $this->pageId = (int)GeneralUtility::_GP('id');
-        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
-        $lang = $this->getLanguageService();
-        $icons = [
-            'language' => $iconFactory->getIcon('flags-multiple', Icon::SIZE_SMALL)->render(),
-            'integrity' => $iconFactory->getIcon('status-dialog-information', Icon::SIZE_SMALL)->render(),
-            'success' => $iconFactory->getIcon('status-dialog-ok', Icon::SIZE_SMALL)->render(),
-            'info' => $iconFactory->getIcon('status-dialog-information', Icon::SIZE_SMALL)->render(),
-            'warning' => $iconFactory->getIcon('status-dialog-warning', Icon::SIZE_SMALL)->render(),
-            'error' => $iconFactory->getIcon('status-dialog-error', Icon::SIZE_SMALL)->render()
-        ];
-        $this->pageRenderer->addInlineSetting('Workspaces', 'icons', $icons);
-        $this->pageRenderer->addInlineSetting('Workspaces', 'id', $this->pageId);
-        $this->pageRenderer->addInlineSetting('Workspaces', 'depth', $this->pageId === 0 ? 999 : 1);
-        $this->pageRenderer->addInlineSetting('Workspaces', 'language', $this->getLanguageSelection());
-        $this->pageRenderer->addInlineLanguageLabelArray([
-            'title' => $lang->getLL('title'),
-            'path' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.path'),
-            'table' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.table'),
-            'depth' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_perm.xlf:Depth'),
-            'depth_0' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
-            'depth_1' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
-            'depth_2' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
-            'depth_3' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
-            'depth_4' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
-            'depth_infi' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_infi')
-        ]);
-        $this->pageRenderer->addInlineLanguageLabelFile('EXT:workspaces/Resources/Private/Language/locallang.xlf');
-        $this->assignExtensionSettings();
-    }
-
-    /**
-     * Assigns additional Workspace settings to TYPO3.settings.Workspaces.extension
-     */
-    protected function assignExtensionSettings()
-    {
-        $extension = [
-            'AdditionalColumn' => [
-                'Definition' => [],
-                'Handler' => [],
-            ],
-        ];
-
-        $extension['AdditionalColumn']['Definition'] = $this->getAdditionalColumnService()->getDefinition();
-        $extension['AdditionalColumn']['Handler'] = $this->getAdditionalColumnService()->getHandler();
-        $this->pageRenderer->addInlineSetting('Workspaces', 'extension', $extension);
-    }
-
-    /**
-     * Gets the selected language.
-     *
-     * @return string
-     */
-    protected function getLanguageSelection()
-    {
-        $language = 'all';
-        $backendUser = $this->getBackendUser();
-        if (isset($backendUser->uc['moduleData']['Workspaces'][$backendUser->workspace]['language'])) {
-            $language = $backendUser->uc['moduleData']['Workspaces'][$backendUser->workspace]['language'];
-        }
-        return $language;
-    }
-
-    /**
-     * @return AdditionalColumnService
-     */
-    protected function getAdditionalColumnService()
-    {
-        return $this->objectManager->get(AdditionalColumnService::class);
-    }
-
-    /**
-     * @return AdditionalResourceService
-     */
-    protected function getAdditionalResourceService()
-    {
-        return $this->objectManager->get(AdditionalResourceService::class);
-    }
-
-    /**
-     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
-     */
-    protected function getBackendUser()
-    {
-        return $GLOBALS['BE_USER'];
-    }
-
-    /**
-     * @return \TYPO3\CMS\Core\Localization\LanguageService
-     */
-    protected function getLanguageService()
-    {
-        return $GLOBALS['LANG'];
-    }
-
-    /**
-     * @return PageRenderer
-     */
-    protected function getPageRenderer()
-    {
-        return GeneralUtility::makeInstance(PageRenderer::class);
-    }
-}
index 014ea51..0eafc7e 100644 (file)
@@ -18,6 +18,7 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
 
 /**
  * Implements the AJAX functionality for the various asynchronous calls
@@ -50,7 +51,7 @@ class AjaxController
                 ' AND pages.t3ver_wsid IN (0, ' . $workspaceId . ')'
             );
             if ($page) {
-                if ($this->getBackendUser()->doesUserHaveAccess($page, 1)) {
+                if ($this->getBackendUser()->doesUserHaveAccess($page, Permission::PAGE_SHOW)) {
                     break;
                 }
             } else {
index 3850a7d..a5774a0 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+declare(strict_types = 1);
 namespace TYPO3\CMS\Workspaces\Controller;
 
 /*
@@ -14,21 +15,22 @@ namespace TYPO3\CMS\Workspaces\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Backend\Routing\UriBuilder;
+use TYPO3\CMS\Backend\Template\ModuleTemplate;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Backend\View\BackendTemplateView;
-use TYPO3\CMS\Core\Messaging\FlashMessage;
-use TYPO3\CMS\Core\Messaging\FlashMessageService;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\PathUtility;
-use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
-use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
+use TYPO3\CMS\Fluid\View\StandaloneView;
 use TYPO3\CMS\Workspaces\Service\StagesService;
 use TYPO3\CMS\Workspaces\Service\WorkspaceService;
 
 /**
  * Implements the preview controller of the workspace module.
  */
-class PreviewController extends AbstractController
+class PreviewController
 {
     /**
      * @var StagesService
@@ -41,140 +43,138 @@ class PreviewController extends AbstractController
     protected $workspaceService;
 
     /**
-     * Set up the doc header properly here
+     * @var int
+     */
+    protected $pageId;
+
+    /**
+     * ModuleTemplate object
      *
-     * @param ViewInterface $view
+     * @var ModuleTemplate
      */
-    protected function initializeView(ViewInterface $view)
-    {
-        if ($view instanceof BackendTemplateView) {
-            /** @var BackendTemplateView $view */
-            parent::initializeView($view);
-            $view->getModuleTemplate()->getDocHeaderComponent()->disable();
-            $this->view->getModuleTemplate()->setFlashMessageQueue($this->controllerContext->getFlashMessageQueue());
-        }
-    }
+    protected $moduleTemplate;
+
+    /**
+     * @var \TYPO3Fluid\Fluid\View\ViewInterface
+     */
+    protected $view;
 
     /**
-     * Initializes the controller before invoking an action method.
+     * Set up the module template
      */
-    protected function initializeAction()
+    public function __construct()
     {
-        parent::initializeAction();
-        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
-        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
         $this->stageService = GeneralUtility::makeInstance(StagesService::class);
         $this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class);
+        $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
+        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Workspaces/Preview');
+        $this->moduleTemplate->getDocHeaderComponent()->disable();
+        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
         $states = $this->getBackendUser()->uc['moduleData']['Workspaces']['States'];
-        $this->pageRenderer->addInlineSetting('Workspaces', 'States', $states);
-        $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_edit'));
-        $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_history'));
-        // @todo this part should be done with inlineLocallanglabels
-        $this->pageRenderer->addJsInlineCode('workspace-inline-code', $this->generateJavascript());
+        $this->moduleTemplate->getPageRenderer()->addInlineSetting('Workspaces', 'States', $states);
+        $this->moduleTemplate->getPageRenderer()->addInlineSetting('FormEngine', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_edit'));
+        $this->moduleTemplate->getPageRenderer()->addInlineSetting('RecordHistory', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_history'));
+        $this->moduleTemplate->getPageRenderer()->addJsInlineCode('workspace-inline-code', $this->generateJavascript());
+        $this->moduleTemplate->getPageRenderer()->addCssFile('EXT:workspaces/Resources/Public/Css/preview.css');
+        $this->moduleTemplate->getPageRenderer()->addInlineLanguageLabelFile('EXT:workspaces/Resources/Private/Language/locallang.xlf');
+    }
+
+    /**
+     * Sets up the view
+     *
+     * @param string $templateName
+     */
+    protected function initializeView(string $templateName)
+    {
+        $this->view = GeneralUtility::makeInstance(StandaloneView::class);
+        $this->view->setTemplate($templateName);
+        $this->view->setTemplateRootPaths(['EXT:workspaces/Resources/Private/Templates/Preview']);
+        $this->view->setPartialRootPaths(['EXT:workspaces/Resources/Private/Partials']);
+        $this->view->setLayoutRootPaths(['EXT:workspaces/Resources/Private/Layouts']);
     }
 
     /**
      * Basically makes sure that the workspace preview is rendered.
      * The preview itself consists of three frames, so there are
-     * only the frames-urls we've to generate here
+     * only the frames-urls we have to generate here
      *
-     * @param int $previewWS
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
      */
-    public function indexAction($previewWS = null)
+    public function handleRequest(ServerRequestInterface $request): ResponseInterface
     {
-        $backendUser = $this->getBackendUser();
+        $this->initializeView('Index');
 
         // Get all the GET parameters to pass them on to the frames
-        $queryParameters = GeneralUtility::_GET();
+        $queryParameters = $request->getQueryParams();
+
+        $previewWS = $queryParameters['previewWS'] ?? null;
+        $this->pageId = (int)$queryParameters['id'];
 
-        // Remove the GET parameters related to the workspaces module and the page id
-        unset($queryParameters['tx_workspaces_web_workspacesworkspaces']);
-        unset($queryParameters['route']);
-        unset($queryParameters['id']);
+        // Remove the GET parameters related to the workspaces module
+        unset($queryParameters['route'], $queryParameters['token'], $queryParameters['previewWS']);
 
         // Assemble a query string from the retrieved parameters
         $queryString = GeneralUtility::implodeArrayForUrl('', $queryParameters);
 
         // fetch the next and previous stage
-        $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $this->pageId, ($recursionLevel = 0), ($selectionType = 'tables_modify'));
+        $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), 1, -99, $this->pageId, 0, 'tables_modify');
         list(, $nextStage) = $this->stageService->getNextStageForElementCollection($workspaceItemsArray);
         list(, $previousStage) = $this->stageService->getPreviousStageForElementCollection($workspaceItemsArray);
-        /** @var $wsService WorkspaceService */
-        $wsService = GeneralUtility::makeInstance(WorkspaceService::class);
-        $wsList = $wsService->getAvailableWorkspaces();
-        $activeWorkspace = $backendUser->workspace;
-        if (!is_null($previewWS)) {
-            if (in_array($previewWS, array_keys($wsList)) && $activeWorkspace != $previewWS) {
-                $activeWorkspace = $previewWS;
-                $backendUser->setWorkspace($activeWorkspace);
-                BackendUtility::setUpdateSignal('updatePageTree');
-            }
+        $availableWorkspaces = $this->workspaceService->getAvailableWorkspaces();
+        $activeWorkspace = $this->getBackendUser()->workspace;
+        if ($previewWS !== null && in_array($previewWS, array_keys($availableWorkspaces)) && $activeWorkspace != $previewWS) {
+            $activeWorkspace = $previewWS;
+            $this->getBackendUser()->setWorkspace($activeWorkspace);
+            BackendUtility::setUpdateSignal('updatePageTree');
         }
-        /** @var $uriBuilder UriBuilder */
-        $uriBuilder = $this->objectManager->get(UriBuilder::class);
-        $wsSettingsPath = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
-        $wsSettingsUri = $uriBuilder->uriFor('singleIndex', [], ReviewController::class, 'workspaces', 'web_workspacesworkspaces');
-        $wsSettingsParams = '&tx_workspaces_web_workspacesworkspaces[controller]=Review';
-        $wsSettingsUrl = $wsSettingsPath . $wsSettingsUri . $wsSettingsParams;
-        $viewDomain = BackendUtility::getViewDomain($this->pageId);
-        $wsBaseUrl = $viewDomain . '/index.php?id=' . $this->pageId . $queryString;
-        // @todo - handle new pages here
-        // branchpoints are not handled anymore because this feature is not supposed anymore
-        if (WorkspaceService::isNewPage($this->pageId)) {
-            $wsNewPageUri = $uriBuilder->uriFor('newPage', [], self::class, 'workspaces', 'web_workspacesworkspaces');
-            $wsNewPageParams = '&tx_workspaces_web_workspacesworkspaces[controller]=Preview';
-            $liveUrl = $wsSettingsPath . $wsNewPageUri . $wsNewPageParams . '&ADMCMD_prev=IGNORE';
-        } else {
-            $liveUrl = $wsBaseUrl . '&ADMCMD_noBeUser=1&ADMCMD_prev=IGNORE';
+
+        // Base URL for frontend preview links
+        $previewBaseUrl = BackendUtility::getViewDomain($this->pageId) . '/index.php?' . $queryString;
+
+        // Build the "list view" link to the review controller
+        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
+        $wsSettingsUrl = $uriBuilder->buildUriFromRoute('web_WorkspacesWorkspaces', [
+            'tx_workspaces_web_workspacesworkspaces' => ['action' => 'singleIndex']
+        ], UriBuilder::ABSOLUTE_URL);
+
+        if (!WorkspaceService::isNewPage($this->pageId)) {
+            $liveUrl = $previewBaseUrl . '&ADMCMD_noBeUser=1&ADMCMD_prev=IGNORE';
         }
-        $wsUrl = $wsBaseUrl . '&ADMCMD_prev=IGNORE&ADMCMD_view=1&ADMCMD_editIcons=1';
-        $backendDomain = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY');
+        $wsUrl = $previewBaseUrl . '&ADMCMD_prev=IGNORE&ADMCMD_view=1&ADMCMD_editIcons=1';
+
+        // Evaluate available preview modes
         $splitPreviewTsConfig = BackendUtility::getModTSconfig($this->pageId, 'workspaces.splitPreviewModes');
         $splitPreviewModes = GeneralUtility::trimExplode(',', $splitPreviewTsConfig['value']);
         $allPreviewModes = ['slider', 'vbox', 'hbox'];
         if (!array_intersect($splitPreviewModes, $allPreviewModes)) {
             $splitPreviewModes = $allPreviewModes;
         }
+        $this->moduleTemplate->getPageRenderer()->addInlineSetting('Workspaces', 'SplitPreviewModes', $splitPreviewModes);
 
-        $wsList = $wsService->getAvailableWorkspaces();
-        $activeWorkspace = $backendUser->workspace;
-
-        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Workspaces/Preview');
-        $this->pageRenderer->addInlineSetting('Workspaces', 'SplitPreviewModes', $splitPreviewModes);
-
-        $cssFile = 'EXT:workspaces/Resources/Public/Css/preview.css';
-        $cssFile = GeneralUtility::getFileAbsFileName($cssFile);
-        $this->pageRenderer->addCssFile(PathUtility::getAbsoluteWebPath($cssFile));
-
-        $backendUser->setAndSaveSessionData('workspaces.backend_domain', GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'));
-
-        $logoPath = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Images/typo3_logo_orange.svg');
-        $logoWidth = 22;
-        $logoHeight = 22;
+        $backendDomain = $request->getAttribute('normalizedParams')->getRequestHostOnly();
+        $this->getBackendUser()->setAndSaveSessionData('workspaces.backend_domain', $backendDomain);
 
         $this->view->assignMultiple([
-            'logoUrl' => PathUtility::getAbsoluteWebPath($logoPath),
             'logoLink' => TYPO3_URL_GENERAL,
-            'logoWidth' => $logoWidth,
-            'logoHeight' => $logoHeight,
-            'liveUrl' => $liveUrl,
+            'liveUrl' => $liveUrl ?? false,
             'wsUrl' => $wsUrl,
             'wsSettingsUrl' => $wsSettingsUrl,
             'backendDomain' => $backendDomain,
-            'activeWorkspace' => $wsList[$activeWorkspace],
+            'activeWorkspace' => $availableWorkspaces[$activeWorkspace],
             'splitPreviewModes' => $splitPreviewModes,
             'firstPreviewMode' => current($splitPreviewModes),
-            'enablePreviousStageButton' => !$this->isInvalidStage($previousStage),
-            'enableNextStageButton' => !$this->isInvalidStage($nextStage),
-            'enableDiscardStageButton' => !$this->isInvalidStage($nextStage) || !$this->isInvalidStage($previousStage),
+            'enablePreviousStageButton' => $this->isValidStage($previousStage),
+            'enableNextStageButton' => $this->isValidStage($nextStage),
+            'enableDiscardStageButton' => $this->isValidStage($nextStage) || $this->isValidStage($previousStage),
             'nextStage' => $nextStage['title'],
             'nextStageId' => $nextStage['uid'],
             'prevStage' => $previousStage['title'],
             'prevStageId' => $previousStage['uid'],
         ]);
-        foreach ($this->getAdditionalResourceService()->getLocalizationResources() as $localizationResource) {
-            $this->pageRenderer->addInlineLanguageLabelFile($localizationResource);
-        }
+
+        $this->moduleTemplate->setContent($this->view->render());
+        return new HtmlResponse($this->moduleTemplate->renderContent());
     }
 
     /**
@@ -183,44 +183,30 @@ class PreviewController extends AbstractController
      * @param array $stageArray
      * @return bool
      */
-    protected function isInvalidStage($stageArray)
-    {
-        return !(is_array($stageArray) && !empty($stageArray));
-    }
-
-    /**
-     */
-    public function newPageAction()
+    protected function isValidStage($stageArray): bool
     {
-        /** @var FlashMessage $flashMessage */
-        $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:info.newpage.detail'), $this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:info.newpage'), FlashMessage::INFO);
-        /** @var $flashMessageService FlashMessageService */
-        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
-        /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
-        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
-        $defaultFlashMessageQueue->enqueue($flashMessage);
+        return is_array($stageArray) && !empty($stageArray);
     }
 
     /**
      * Generates the JavaScript code for the backend,
      * and since we're loading a backend module outside of the actual backend
-     * this copies parts of the index.php?M=main module
+     * this copies parts of the backend main script.
      *
      * @return string
      */
-    protected function generateJavascript()
+    protected function generateJavascript(): string
     {
-        $backendUser = $this->getBackendUser();
         // If another page module was specified, replace the default Page module with the new one
-        $newPageModule = trim($backendUser->getTSConfigVal('options.overridePageModule'));
-        $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
-        if (!$backendUser->check('modules', $pageModule)) {
+        $pageModule = trim($this->getBackendUser()->getTSConfigVal('options.overridePageModule') ?? '');
+        $pageModule = BackendUtility::isModuleSetInTBE_MODULES($pageModule) ? $pageModule : 'web_layout';
+        if (!$this->getBackendUser()->check('modules', $pageModule)) {
             $pageModule = '';
         }
         $t3Configuration = [
-            'username' => htmlspecialchars($backendUser->user['username']),
+            'username' => htmlspecialchars($this->getBackendUser()->user['username']),
             'pageModule' => $pageModule,
-            'inWorkspace' => $backendUser->workspace !== 0,
+            'inWorkspace' => $this->getBackendUser()->workspace !== 0,
             'showRefreshLoginPopup' => (bool)($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] ?? false)
         ];
 
@@ -228,17 +214,9 @@ class PreviewController extends AbstractController
     }
 
     /**
-     * @return \TYPO3\CMS\Core\Localization\LanguageService
-     */
-    protected function getLanguageService()
-    {
-        return $GLOBALS['LANG'];
-    }
-
-    /**
-     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
+     * @return BackendUserAuthentication
      */
-    protected function getBackendUser()
+    protected function getBackendUser(): BackendUserAuthentication
     {
         return $GLOBALS['BE_USER'];
     }
index 17b86cf..70143ee 100644 (file)
@@ -14,19 +14,47 @@ namespace TYPO3\CMS\Workspaces\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Backend\View\BackendTemplateView;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Imaging\Icon;
+use TYPO3\CMS\Core\Imaging\IconFactory;
+use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
 use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
+use TYPO3\CMS\Workspaces\Service\AdditionalColumnService;
+use TYPO3\CMS\Workspaces\Service\AdditionalResourceService;
 use TYPO3\CMS\Workspaces\Service\WorkspaceService;
 
 /**
  * Review controller.
  */
-class ReviewController extends AbstractController
+class ReviewController extends ActionController
 {
     /**
+     * @var string
+     */
+    protected $defaultViewObjectName = BackendTemplateView::class;
+
+    /**
+     * @var BackendTemplateView
+     */
+    protected $view;
+
+    /**
+     * @var PageRenderer
+     */
+    protected $pageRenderer;
+
+    /**
+     * @var int
+     */
+    protected $pageId;
+
+    /**
      * Set up the doc header properly here
      *
      * @param ViewInterface $view
@@ -59,6 +87,56 @@ class ReviewController extends AbstractController
     }
 
     /**
+     * Initializes the controller before invoking an action method.
+     */
+    protected function initializeAction()
+    {
+        $this->pageRenderer = $this->getPageRenderer();
+        // @todo Evaluate how the (int) typecast can be used with Extbase validators/filters
+        $this->pageId = (int)GeneralUtility::_GP('id');
+        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
+        $lang = $this->getLanguageService();
+        $icons = [
+            'language' => $iconFactory->getIcon('flags-multiple', Icon::SIZE_SMALL)->render(),
+            'integrity' => $iconFactory->getIcon('status-dialog-information', Icon::SIZE_SMALL)->render(),
+            'success' => $iconFactory->getIcon('status-dialog-ok', Icon::SIZE_SMALL)->render(),
+            'info' => $iconFactory->getIcon('status-dialog-information', Icon::SIZE_SMALL)->render(),
+            'warning' => $iconFactory->getIcon('status-dialog-warning', Icon::SIZE_SMALL)->render(),
+            'error' => $iconFactory->getIcon('status-dialog-error', Icon::SIZE_SMALL)->render()
+        ];
+        $this->pageRenderer->addInlineSetting('Workspaces', 'icons', $icons);
+        $this->pageRenderer->addInlineSetting('Workspaces', 'id', $this->pageId);
+        $this->pageRenderer->addInlineSetting('Workspaces', 'depth', $this->pageId === 0 ? 999 : 1);
+        $this->pageRenderer->addInlineSetting('Workspaces', 'language', $this->getLanguageSelection());
+        $this->pageRenderer->addInlineLanguageLabelArray([
+            'title' => $lang->getLL('title'),
+            'path' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.path'),
+            'table' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.table'),
+            'depth' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_perm.xlf:Depth'),
+            'depth_0' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
+            'depth_1' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
+            'depth_2' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
+            'depth_3' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
+            'depth_4' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
+            'depth_infi' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_infi')
+        ]);
+        $this->pageRenderer->addInlineLanguageLabelFile('EXT:workspaces/Resources/Private/Language/locallang.xlf');
+        $states = $this->getBackendUser()->uc['moduleData']['Workspaces']['States'];
+        $this->pageRenderer->addInlineSetting('Workspaces', 'States', $states);
+
+        foreach ($this->getAdditionalResourceService()->getLocalizationResources() as $localizationResource) {
+            $this->pageRenderer->addInlineLanguageLabelFile($localizationResource);
+        }
+        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
+        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Workspaces/Backend');
+        $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_edit'));
+        $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_history'));
+        $this->pageRenderer->addInlineSetting('Workspaces', 'id', (int)GeneralUtility::_GP('id'));
+
+        $this->assignExtensionSettings();
+    }
+
+    /**
      * Renders the review module user dependent with all workspaces.
      * The module will show all records of one workspace.
      */
@@ -98,12 +176,9 @@ class ReviewController extends AbstractController
                 }
             }
         }
-        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
-        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
         $this->pageRenderer->addInlineSetting('Workspaces', 'isLiveWorkspace', (int)$backendUser->workspace === 0);
         $this->pageRenderer->addInlineSetting('Workspaces', 'workspaceTabs', $this->prepareWorkspaceTabs($wsList, $activeWorkspace));
         $this->pageRenderer->addInlineSetting('Workspaces', 'activeWorkspaceId', $activeWorkspace);
-        $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_edit'));
         $workspaceIsAccessible = !($backendUser->workspace === 0 && !$backendUser->isAdmin());
         $this->view->assignMultiple([
             'showGrid' => $workspaceIsAccessible,
@@ -113,7 +188,6 @@ class ReviewController extends AbstractController
             'workspaceList' => $this->prepareWorkspaceTabs($wsList, $activeWorkspace),
             'activeWorkspaceUid' => $activeWorkspace,
             'activeWorkspaceTitle' => WorkspaceService::getWorkspaceTitle($activeWorkspace),
-            'showPreviewLink' => $wsService->canCreatePreviewLink(GeneralUtility::_GP('id'), $activeWorkspace)
         ]);
 
         if ($wsService->canCreatePreviewLink(GeneralUtility::_GP('id'), $activeWorkspace)) {
@@ -151,8 +225,7 @@ class ReviewController extends AbstractController
             'showGrid' => true,
             'showLegend' => true,
             'workspaceList' => $this->prepareWorkspaceTabs($wsList, $activeWorkspace),
-            'activeWorkspaceUid' => WorkspaceService::SELECT_ALL_WORKSPACES,
-            'showPreviewLink', false
+            'activeWorkspaceUid' => WorkspaceService::SELECT_ALL_WORKSPACES
         ]);
         $this->getBackendUser()->setAndSaveSessionData('tx_workspace_activeWorkspace', WorkspaceService::SELECT_ALL_WORKSPACES);
         // set flag for javascript
@@ -174,7 +247,7 @@ class ReviewController extends AbstractController
         $this->view->assignMultiple([
             'pageUid' => (int)GeneralUtility::_GP('id'),
             'showGrid' => true,
-            'workspaceList' => $this->prepareWorkspaceTabs($wsList, $activeWorkspace, false),
+            'workspaceList' => $this->prepareWorkspaceTabs($wsList, (int)$activeWorkspace, false),
             'activeWorkspaceUid' => $activeWorkspace,
             'backendDomain' => $backendDomain
         ]);
@@ -189,27 +262,6 @@ class ReviewController extends AbstractController
     }
 
     /**
-     * Initializes the controller before invoking an action method.
-     */
-    protected function initializeAction()
-    {
-        parent::initializeAction();
-        $states = $this->getBackendUser()->uc['moduleData']['Workspaces']['States'];
-        $this->pageRenderer->addInlineSetting('Workspaces', 'States', $states);
-
-        foreach ($this->getAdditionalResourceService()->getLocalizationResources() as $localizationResource) {
-            $this->pageRenderer->addInlineLanguageLabelFile($localizationResource);
-        }
-        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
-        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
-
-        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Workspaces/Backend');
-        $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_edit'));
-        $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_history'));
-        $this->pageRenderer->addInlineSetting('Workspaces', 'id', (int)GeneralUtility::_GP('id'));
-    }
-
-    /**
      * Prepares available workspace tabs.
      *
      * @param array $workspaceList
@@ -217,7 +269,7 @@ class ReviewController extends AbstractController
      * @param bool $showAllWorkspaceTab
      * @return array
      */
-    protected function prepareWorkspaceTabs(array $workspaceList, $activeWorkspace, $showAllWorkspaceTab = true)
+    protected function prepareWorkspaceTabs(array $workspaceList, int $activeWorkspace, bool $showAllWorkspaceTab = true)
     {
         $tabs = [];
 
@@ -260,11 +312,11 @@ class ReviewController extends AbstractController
      * @param int $workspaceId
      * @return string
      */
-    protected function getModuleUri($workspaceId)
+    protected function getModuleUri(int $workspaceId): string
     {
         $parameters = [
-            'id' => (int)$this->pageId,
-            'workspace' => (int)$workspaceId,
+            'id' => $this->pageId,
+            'workspace' => $workspaceId,
         ];
         // The "all workspaces" tab is handled in fullIndexAction
         // which is required as additional GET parameter in the URI then
@@ -272,15 +324,71 @@ class ReviewController extends AbstractController
             $this->uriBuilder->reset()->uriFor('fullIndex');
             $parameters = array_merge($parameters, $this->uriBuilder->getArguments());
         }
-        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
-        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
+        /** @var UriBuilder $uriBuilder */
+        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
         return (string)$uriBuilder->buildUriFromRoute('web_WorkspacesWorkspaces', $parameters);
     }
 
     /**
-     * @return \TYPO3\CMS\Core\Localization\LanguageService
+     * Assigns additional Workspace settings to TYPO3.settings.Workspaces.extension
+     */
+    protected function assignExtensionSettings()
+    {
+        $extension = [
+            'AdditionalColumn' => [
+                'Definition' => [],
+                'Handler' => [],
+            ],
+        ];
+
+        $extension['AdditionalColumn']['Definition'] = $this->getAdditionalColumnService()->getDefinition();
+        $extension['AdditionalColumn']['Handler'] = $this->getAdditionalColumnService()->getHandler();
+        $this->pageRenderer->addInlineSetting('Workspaces', 'extension', $extension);
+    }
+
+    /**
+     * Gets the selected language.
+     *
+     * @return string
+     */
+    protected function getLanguageSelection(): string
+    {
+        $language = 'all';
+        $backendUser = $this->getBackendUser();
+        if (isset($backendUser->uc['moduleData']['Workspaces'][$backendUser->workspace]['language'])) {
+            $language = $backendUser->uc['moduleData']['Workspaces'][$backendUser->workspace]['language'];
+        }
+        return $language;
+    }
+
+    /**
+     * @return AdditionalColumnService
+     */
+    protected function getAdditionalColumnService(): AdditionalColumnService
+    {
+        return $this->objectManager->get(AdditionalColumnService::class);
+    }
+
+    /**
+     * @return AdditionalResourceService
+     */
+    protected function getAdditionalResourceService(): AdditionalResourceService
+    {
+        return $this->objectManager->get(AdditionalResourceService::class);
+    }
+
+    /**
+     * @return PageRenderer
+     */
+    protected function getPageRenderer(): PageRenderer
+    {
+        return GeneralUtility::makeInstance(PageRenderer::class);
+    }
+
+    /**
+     * @return LanguageService
      */
-    protected function getLanguageService()
+    protected function getLanguageService(): LanguageService
     {
         return $GLOBALS['LANG'];
     }
@@ -288,7 +396,7 @@ class ReviewController extends AbstractController
     /**
      * @return BackendUserAuthentication
      */
-    protected function getBackendUser()
+    protected function getBackendUser(): BackendUserAuthentication
     {
         return $GLOBALS['BE_USER'];
     }
index 217c6ad..c4da949 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+declare(strict_types = 1);
 namespace TYPO3\CMS\Workspaces\Hook;
 
 /*
@@ -14,17 +15,23 @@ namespace TYPO3\CMS\Workspaces\Hook;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Messaging\FlashMessage;
+use TYPO3\CMS\Core\Messaging\FlashMessageService;
+use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Workspaces\Service\StagesService;
+use TYPO3\CMS\Workspaces\Service\WorkspaceService;
 
 /**
  * Befunc service
  */
-class BackendUtilityHook implements \TYPO3\CMS\Core\SingletonInterface
+class BackendUtilityHook implements SingletonInterface
 {
     /**
      * Gets a singleton instance of this object.
      *
-     * @return \TYPO3\CMS\Workspaces\Hook\BackendUtilityHook
+     * @return BackendUtilityHook
      */
     public static function getInstance()
     {
@@ -46,21 +53,11 @@ class BackendUtilityHook implements \TYPO3\CMS\Core\SingletonInterface
     public function preProcess(&$pageUid, $backPath, $rootLine, $anchorSection, &$viewScript, $additionalGetVars, $switchFocus)
     {
         if ($GLOBALS['BE_USER']->workspace !== 0) {
-            $viewScript = $this->getWorkspaceService()->generateWorkspaceSplittedPreviewLink($pageUid);
+            $viewScript = GeneralUtility::makeInstance(WorkspaceService::class)->generateWorkspaceSplittedPreviewLink($pageUid);
         }
     }
 
     /**
-     * Gets an instance of the workspaces service.
-     *
-     * @return \TYPO3\CMS\Workspaces\Service\WorkspaceService
-     */
-    protected function getWorkspaceService()
-    {
-        return GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class);
-    }
-
-    /**
      * Use that hook to show an info message in case someone starts editing
      * a staged element
      *
@@ -70,15 +67,15 @@ class BackendUtilityHook implements \TYPO3\CMS\Core\SingletonInterface
     public function makeEditForm_accessCheck($params)
     {
         if ($GLOBALS['BE_USER']->workspace !== 0 && $GLOBALS['TCA'][$params['table']]['ctrl']['versioningWS']) {
-            $record = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordWSOL($params['table'], $params['uid']);
-            if (abs($record['t3ver_stage']) > \TYPO3\CMS\Workspaces\Service\StagesService::STAGE_EDIT_ID) {
-                $stages = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class);
+            $record = BackendUtility::getRecordWSOL($params['table'], $params['uid']);
+            if (abs($record['t3ver_stage']) > StagesService::STAGE_EDIT_ID) {
+                $stages = GeneralUtility::makeInstance(StagesService::class);
                 $stageName = $stages->getStageTitle($record['t3ver_stage']);
-                $editingName = $stages->getStageTitle(\TYPO3\CMS\Workspaces\Service\StagesService::STAGE_EDIT_ID);
+                $editingName = $stages->getStageTitle(StagesService::STAGE_EDIT_ID);
                 $message = $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:info.elementAlreadyModified');
-                $flashMessage = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessage::class, sprintf($message, $stageName, $editingName), '', \TYPO3\CMS\Core\Messaging\FlashMessage::INFO, true);
-                /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
-                $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
+                $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, sprintf($message, $stageName, $editingName), '', FlashMessage::INFO, true);
+                /** @var $flashMessageService FlashMessageService */
+                $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
                 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
                 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
                 $defaultFlashMessageQueue->enqueue($flashMessage);
index bb8a8f1..dc436ed 100644 (file)
@@ -268,13 +268,10 @@ class WorkspacePreview implements MiddlewareInterface
     {
         $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]
-            ]
-        ]);
+        $content = '<script type="text/javascript">
+       // having this is very important, otherwise the parent.resize call will fail
+       document.domain = ' . GeneralUtility::quoteJSvalue($backendDomain) . ';
+</script>';
 
         if (!isset($tsfe->config['config']['disablePreviewNotification']) || (int)$tsfe->config['config']['disablePreviewNotification'] !== 1) {
             // get the title of the current workspace
index 1d2df1d..dd4649b 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Workspaces\Service;
  */
 
 use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
+use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
@@ -757,7 +758,7 @@ class WorkspaceService implements SingletonInterface
 
         // Directly use pid value and consider move placeholders
         $previewPageId = (empty($movePlaceholder['pid']) ? $liveRecord['pid'] : $movePlaceholder['pid']);
-        $additionalParameters = '&tx_workspaces_web_workspacesworkspaces[previewWS]=' . $versionRecord['t3ver_wsid'];
+        $additionalParameters = '&previewWS=' . $versionRecord['t3ver_wsid'];
         // Add language parameter if record is a localization
         if (BackendUtility::isTableLocalizable($table)) {
             $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
@@ -857,14 +858,12 @@ class WorkspaceService implements SingletonInterface
         if ($uid > 0) {
             $uid = $this->getLivePageUid($uid);
         }
-        /** @var $uriBuilder \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder */
-        $uriBuilder = $this->getObjectManager()->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
-        $redirect = 'index.php?redirect_url=';
-        $viewScript = $uriBuilder
-            ->setArguments(['route' => '/web/WorkspacesWorkspaces/'])
-            ->uriFor('index', [], 'Preview', 'workspaces', 'web_workspacesworkspaces') . '&id=';
+        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
+        // the actual uid will be appended directly in BackendUtility Hook
+        $viewScript = $uriBuilder->buildUriFromRoute('workspace_previewcontrols', ['id' => '']);
         if ($addDomain === true) {
-            return BackendUtility::getViewDomain($uid) . $redirect . urlencode($viewScript) . $uid;
+            $viewScript = $uriBuilder->buildUriFromRoute('workspace_previewcontrols', ['id' => $uid]);
+            return BackendUtility::getViewDomain($uid) . 'index.php?redirect_url=' . urlencode($viewScript);
         }
         return $viewScript;
     }
diff --git a/typo3/sysext/workspaces/Configuration/Backend/Routes.php b/typo3/sysext/workspaces/Configuration/Backend/Routes.php
new file mode 100644 (file)
index 0000000..510c596
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Definitions for routes provided by EXT:workspaces
+ */
+return [
+    'workspace_previewcontrols' => [
+        'path' => '/workspace/preview-control/',
+        'target' => \TYPO3\CMS\Workspaces\Controller\PreviewController::class . '::handleRequest'
+    ]
+];
diff --git a/typo3/sysext/workspaces/Resources/Private/Layouts/Empty.html b/typo3/sysext/workspaces/Resources/Private/Layouts/Empty.html
deleted file mode 100644 (file)
index 54d9aa0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<f:render section="main" />
\ No newline at end of file
diff --git a/typo3/sysext/workspaces/Resources/Private/Layouts/Popup.html b/typo3/sysext/workspaces/Resources/Private/Layouts/Popup.html
deleted file mode 100644 (file)
index 8b997bb..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<f:render section="main" />
-<script type="text/javascript">
-       document.domain = '{backendDomain}';
-</script>
index fa91e9d..45ef08a 100644 (file)
@@ -7,4 +7,4 @@
        <dd><span  class="item-state-hidden"><f:translate key="legend.hidden" /></span>&nbsp;&nbsp;&bull;&nbsp;</dd>
        <dd><span class="item-state-deleted"><f:translate key="legend.deleted" /></span></dd>
 </dl>
-</html>
\ No newline at end of file
+</html>
index 5f74f59..f388e32 100644 (file)
@@ -6,6 +6,6 @@
        <input type="button" class="btn btn-sm btn-success" value="{nextStage}" data-stage-id="{nextStageId}" data-action="send-to-stage" data-direction="next">
 </f:if>
 <f:if condition="{enableDiscardStageButton}">
-       <input type="button" class="btn btn-sm btn-danger" value="{f:translate(key: 'label_doaction_discard')}" data-action="discard">
+       <input type="button" class="btn btn-sm btn-danger" value="{f:translate(key: 'label_doaction_discard', extensionName: 'workspaces')}" data-action="discard">
 </f:if>
-</html>
\ No newline at end of file
+</html>
diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Help.html b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Help.html
deleted file mode 100644 (file)
index 1dd38b7..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
-<f:layout name="nodoc" />
-
-<f:section name="main">Help contents - not yet defined</f:section>
-</html>
\ No newline at end of file
index bdfcd6c..885dbb8 100644 (file)
@@ -1,22 +1,20 @@
 <html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
-<f:layout name="popup" />
-
-<f:section name="main">
-       <div id="typo3-topbar">
-               <div class="typo3-topbar-container" role="navigation" id="typo3-top-container">
-                       <div class="typo3-topbar-site">
-                               <a class="typo3-topbar-site-logo" href="{logoLink}" target="_blank">
-                                       <img src="{logoUrl}" width="{logoWidth}" height="{logoHeight}" title="TYPO3 Content Management System" alt="">
-                               </a>
-                               <span class="typo3-topbar-site-name">{activeWorkspace}</span>
-                       </div>
-                       <div class="typo3-topbar-tabs t3js-workspace-tabs">
-                               <ul class="nav nav-tabs" role="tablist">
-                                       <li role="presentation" class="active"><a href="#visual" aria-controls="visual" role="tab" data-toggle="tab" data-actions="true"><f:translate key="preview.visualPreview" /></a></li>
-                                       <li role="presentation"><a href="#list" aria-controls="list" role="tab" data-toggle="tab" data-actions="false"><f:translate key="preview.listView" /></a></li>
-                               </ul>
-                       </div>
-                       <div class="typo3-topbar-workspace-actions t3js-workspace-actions">
+<div id="typo3-topbar">
+       <div class="typo3-topbar-container" role="navigation" id="typo3-top-container">
+               <div class="typo3-topbar-site">
+                       <a class="typo3-topbar-site-logo" href="{logoLink}" target="_blank">
+                               <img src="{f:uri.resource(path: 'Images/typo3_logo_orange.svg', extensionName: 'backend')}" width="22" height="22" title="TYPO3 Content Management System" alt="">
+                       </a>
+                       <span class="typo3-topbar-site-name">{activeWorkspace}</span>
+               </div>
+               <div class="typo3-topbar-tabs t3js-workspace-tabs">
+                       <ul class="nav nav-tabs" role="tablist">
+                               <li role="presentation" class="active"><a href="#visual" aria-controls="visual" role="tab" data-toggle="tab" data-actions="true"><f:translate key="preview.visualPreview" extensionName="workspaces" /></a></li>
+                               <li role="presentation"><a href="#list" aria-controls="list" role="tab" data-toggle="tab" data-actions="false"><f:translate key="preview.listView" extensionName="workspaces" /></a></li>
+                       </ul>
+               </div>
+               <div class="typo3-topbar-workspace-actions t3js-workspace-actions">
+                       <f:if condition="{liveUrl}">
                                <div class="workspace-action">
                                        <div class="slider-wrapper">
                                                <span>Published Version</span><div
                                                <span>Staged Version</span>
                                        </div>
                                </div>
-                               <div class="workspace-action t3js-stage-buttons">
-                                       <f:render partial="Preview/StageButtons" arguments="{_all}"/>
-                               </div>
-                               <div class="workspace-action t3js-preview-mode">
-                                       <div class="btn-group">
-                                               <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                                                       <span class="t3js-active-preview-mode active-preview-mode" data-active-preview-mode="{firstPreviewMode}"><f:translate key="preview.mode{firstPreviewMode -> f:format.case(mode: 'capital')}" /></span> <span class="caret"></span>
-                                               </button>
-                                               <ul class="dropdown-menu dropdown-menu-right">
-                                                       <f:for each="{splitPreviewModes}" as="mode">
-                                                               <li><a href="#" data-preview-mode="{mode}"><span><f:translate key="preview.mode{mode -> f:format.case(mode: 'capital')}" /></span></a></li>
-                                                       </f:for>
-                                               </ul>
-                                       </div>
-                               </div>
+                       </f:if>
+                       <div class="workspace-action t3js-stage-buttons">
+                               <f:render partial="Preview/StageButtons" arguments="{_all}"/>
+                       </div>
+                       <div class="workspace-action t3js-preview-mode">
+                               <f:if condition="{liveUrl}">
+                                       <f:then>
+                                               <div class="btn-group">
+                                                       <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                                               <span class="t3js-active-preview-mode active-preview-mode" data-active-preview-mode="{firstPreviewMode}"><f:translate key="preview.mode{firstPreviewMode -> f:format.case(mode: 'capital')}" extensionName="workspaces" /></span> <span class="caret"></span>
+                                                       </button>
+                                                       <ul class="dropdown-menu dropdown-menu-right">
+                                                               <f:for each="{splitPreviewModes}" as="mode">
+                                                                       <li><a href="#" data-preview-mode="{mode}"><span><f:translate key="preview.mode{mode -> f:format.case(mode: 'capital')}" extensionName="workspaces" /></span></a></li>
+                                                               </f:for>
+                                                       </ul>
+                                               </div>
+                                       </f:then>
+                                       <f:else>
+                                               <span><f:translate key="info.newpage.detail" extensionName="workspaces" /></span>
+                                       </f:else>
+                               </f:if>
                        </div>
                </div>
        </div>
+</div>
 
-       <div role="tabpanel" class="workspace-panel">
-               <div class="tab-content">
-                       <div role="tabpanel" class="tab-pane active workspaces preview-mode-slider" id="visual">
-                               <div class="t3js-workspace-preview">
+<div role="tabpanel" class="workspace-panel">
+       <div class="tab-content">
+               <div role="tabpanel" class="tab-pane active workspaces preview-mode-slider" id="visual">
+                       <div class="t3js-workspace-preview">
+                               <f:if condition="{liveUrl}">
                                        <iframe src="{liveUrl}" style="height: 0px;" id="live-view"></iframe>
-                                       <iframe src="{wsUrl}" id="workspace-view"></iframe>
-                               </div>
-                       </div>
-                       <div role="tabpanel" class="tab-pane workspaces" id="list">
-                               <iframe src="{wsSettingsUrl}" id="workspace-list"></iframe>
+                               </f:if>
+                               <iframe src="{wsUrl}" id="workspace-view"></iframe>
                        </div>
                </div>
-
+               <div role="tabpanel" class="tab-pane workspaces" id="list">
+                       <iframe src="{wsSettingsUrl}" id="workspace-list"></iframe>
+               </div>
        </div>
-</f:section>
-</html>
\ No newline at end of file
+</div>
+<script type="text/javascript">
+       document.domain = '{backendDomain}';
+</script>
+</html>
diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/NewPage.html b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/NewPage.html
deleted file mode 100644 (file)
index 6207ef5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
-<f:layout name="nodoc" />
-
-<f:section name="main"></f:section>
-</html>
\ No newline at end of file
diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Preview.html b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Preview.html
deleted file mode 100644 (file)
index 0c16f8d..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<script type="text/javascript">
-       // having this is very important, otherwise the parent.resize call will fail
-       document.domain = '{backendDomain}';
-</script>
\ No newline at end of file
index e4383b1..ac93ad4 100644 (file)
@@ -9,8 +9,7 @@ defined('TYPO3_MODE') or die();
     'before:info',
     [
         // An array holding the controller-action-combinations that are accessible
-        'Review' => 'index,fullIndex,singleIndex',
-        'Preview' => 'index,newPage'
+        'Review' => 'index,fullIndex,singleIndex'
     ],
     [
         'access' => 'user,group',